当前位置: 首页 > 面试题库 >

内存有效方式

马侯林
2023-03-14
问题内容

我有两个用Go编写的类似程序的示例。该代码的主要目的是使用结构中的值对结构进行排序。

指针示例

package main

import (
    "fmt"
    "sort"
)

type payload struct {
    data string
    value  float64
}

type container struct {
    counter int
    storage map[int]*payload
}

type payloadSlice []*payload

// Len is part of sort.Interface.
func (p payloadSlice) Len() int {
    return len(p)
}

// Swap is part of sort.Interface.
func (p payloadSlice) Swap(i, j int) {
    p[i], p[j] = p[j], p[i]
}

// Less is part of sort.Interface. We use count as the value to sort by
func (p payloadSlice) Less(i, j int) bool {
    return p[i].value < p[j].value
}
func main() {
    name := "special_unique_name"
    var m = map[string]container{
        name: {counter: 10, storage: map[int]*payload{
            5: {data: "epsilon", value: 55},8: {data: "theta", value: 85},4: {data: "delta", value: 48},1: {data: "alpha", value: 14},10: {data: "kappa", value: 101},
            3: {data: "gamma", value: 31},6: {data: "zeta", value: 63},2: {data: "beta", value: 26},9: {data: "iota", value: 92},7: {data: "eta", value: 79},
        }},
    }
    s := make(payloadSlice, 0, len(m[name].storage))
    for _, v := range m[name].storage {
        s = append(s, v)
    }
    sort.Sort(s)

    for _, v := range s {
        fmt.Println(name, v)
    }
}

有值的例子

package main

import (
    "fmt"
    "sort"
)

type payload struct {
    data string
    value  float64
}

type container struct {
    counter int
    storage map[int]payload
}

type payloadSlice []payload

// Len is part of sort.Interface.
func (p payloadSlice) Len() int {
    return len(p)
}

// Swap is part of sort.Interface.
func (p payloadSlice) Swap(i, j int) {
    p[i], p[j] = p[j], p[i]
}

// Less is part of sort.Interface. We use count as the value to sort by
func (p payloadSlice) Less(i, j int) bool {
    return p[i].value < p[j].value
}
func main() {
    name := "special_unique_name"
    var m = map[string]container{
        name: {counter: 10, storage: map[int]payload{
            5: {data: "epsilon", value: 55},8: {data: "theta", value: 85},4: {data: "delta", value: 48},1: {data: "alpha", value: 14},10: {data: "kappa", value: 101},
            3: {data: "gamma", value: 31},6: {data: "zeta", value: 63},2: {data: "beta", value: 26},9: {data: "iota", value: 92},7: {data: "eta", value: 79},
        }},
    }
    s := make(payloadSlice, 0, len(m[name].storage))
    for _, v := range m[name].storage {
        s = append(s, v)
    }
    sort.Sort(s)

    for _, v := range s {
        fmt.Println(name, v)
    }
}

我想知道2分钟:

  1. 哪个示例将提高内存效率?(我想这是一种指针方式)

  2. 如何使用地图中具有不同数量结构的测试数据来衡量这些示例的性能?您能帮我建立基准吗?

我认为地图中每个结构的大小平均在1-2kB之间。


问题答案:

“高效内存”是一个相当宽泛的术语,在诸如Go之类的垃圾收集语言中,可能有很多不同的东西,它们具有独立的堆和栈:

  • 什么使用最少的内存?
  • 是什么造成最小的GC压力?

如果要最大程度地减少应用程序的占用空间,则 可能
希望在多个作用域(例如,多个函数)中使用值的任何时间使用指针。这样可以减少复制,但增加的开销等于指针大小(在64位系统上为8字节)。

如果要最大程度地降低GC压力,则 可能
仅在需要指针语义或基础值很大时才要使用指针。指针将值强制到堆上,该值将进行垃圾回收,而值可以保留在堆栈上,而不是(当函数返回时,堆栈会被完全破坏,这是线程安全的,并且需要没有参考追踪)。


GC压力”的想法是,在堆上创建和销毁的东西越多,垃圾收集器要做的工作就越多,这使处理器时间与应用程序正在执行的实际工作相去甚远。每次在堆上分配时,如果没有足够的空间容纳新值,则垃圾收集器将通过在堆上查找不再需要的值来尝试释放空间。您在堆上分配的次数越多,GC必须运行的频率就越高,这些运行将花费的时间越长。

关于第二个问题,您可以(并且应该!)使用程序包的基准测试工具testing来测量针对特定情况的各种方法的性能。确保使用真实的数据和操作进行测试;使用“虚拟”数据类型的微基准或基准不太可能产生任何值的数据。该软件包的文档以及通过网络搜索可以轻松找到的无数博客文章和教程,应该指导您正确地学习如何在Go中编写和使用基准。

在您的特定情况下,请记住,就这个问题而言,您的数据类型比您想象的要小:在64位系统上为24个字节, 而与string的长度无关
。为什么?因为a在string内部是一个结构,包含一个intfor长度和一个指向基础字节的指针。当您尝试优化内存使用时,请记住,字符串,切片(而不是数组!)和映射都是非常小的结构,其中包含指向其基础数据的指针。

最重要的是过早的优化是万恶之源 。您应该为两件事编写代码:功能和可读性。当指针提供您所需的功能并使用直观时,请使用指针语义。如果
测量 资源问题(CPU或内存), 应该对应用程序进行概要分析,以找到问题的根源,对其进行优先级分配和优化。

在您测量并分析了性能问题之前,您不会遇到性能问题。



 类似资料:
  • 问题内容: 我存储了1.11亿个键值对(一个键可以有多个值-最多2/3),它们的键是50位整数,值是32位(最大)整数。现在,我的要求是: 快速插入(键,值)对[允许重复] 基于键快速检索一个或多个值。 这里基于MultiMap给出了一个很好的解决方案。但是,我想在主内存中存储更多键/值对,而不会降低性能。我从网络文章中研究到B +树,R+树,B树,紧凑多图等可以是一个很好的解决方案。有谁能够帮我

  • 问题内容: 由于Redis尝试将字符串解析为64位带符号整数,因此存储32位带符号整数而不是基数10个整数字符串的二进制表示是个好主意吗? 在我们的系统中,我们列出了许多32位带符号整数ID。 问题答案: 在内部,Redis以最有效的方式存储字符串。将整数强制为基数10的字符串实际上会占用更多的内存。 这是Redis存储字符串的方式- 小于10000的整数存储在共享内存池中,并且没有任何内存开销。

  • 问题内容: 我不是太在意时间效率(这种操作很少见),而是在内存效率上: 我可以在不将所有值都临时设置两次的情况下增加数组吗? 有没有比创建一个新数组并复制所有值更有效的方法来增长大型数组?喜欢,将其与新的连接起来吗? 将固定大小的数组存储在另一个数组中并重新分配/复制该顶级数组会怎样?会保留实际值吗? 我知道ArrayList,但是我需要对访问数组进行大量控制,并且访问必须非常快。举例来说,我想我

  • 问题内容: 考虑以下代码: 似乎对象的内部数组()从未缩小。因此,当我向地图添加10000个元素,并且在调用之后,它将在其内部数组中保留10000个null。因此,我的问题是,JVM如何处理没有任何内容的数组,因此内存有效吗? 问题答案: 这个想法是仅在您想重新使用时才调用。重复使用对象的原因仅应与以前使用过的原因相同,因此,您的条目数将大致相同。为避免无用的收缩和容量调整,在调用时应保持不变。

  • 问题内容: 我有一个〜10M记录的MySQL表,可以使用SqlAlchemy进行交互。我发现对这个表的大子集的查询将消耗过多的内存,即使我以为我使用的是内置生成器,它可以智能地获取数据集的一口大小的块: 为了避免这种情况,我发现我必须构建自己的迭代器,该迭代器会分块地进行处理: 这是正常的还是关于SA内置发电机我缺少什么? 这个问题的答案似乎表明内存消耗是不希望的。 问题答案: 大多数DBAPI实

  • 问题内容: 我试图回显由数组组成的json编码数组,但我不知道这不是让我打印该东西。这是我的代码: 错误:格式错误的JSON:意外的“ A”有时是“ I” 当我删除print_r行时,我会得到:没有收到响应当我在打印$ json数组时,我会得到153,但没有其他输出。 我尝试过的事情: 我读了一些解决类似问题的解决方案,例如,您需要使用array_values(): 相同的回复:“未收到回复” 我