小编典典

内存有效方式

go

我有两个用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之间。


阅读 240

收藏
2020-07-02

共1个答案

小编典典

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

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

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

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


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

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

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

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

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

2020-07-02