性能:切片结构与结构指针切片

时间:2014-12-23 14:12:06

标签: performance go slice

我经常使用切片结构。以下是这种结构的一个例子:

type MyStruct struct {
    val1, val2, val3    int
    text1, text2, text3 string
    list                []SomeType
}

所以我按如下方式定义我的切片:

[]MyStruct

让我们说我在那里有大约一百万个元素,并且我在切片上工作很重:

  • 我经常添加新元素。 (元素总数未知。)
  • 我时不时地对它进行排序。
  • 我也删除元素(尽管没有添加新元素)。
  • 我经常读取元素并传递它们(作为函数参数)。
  • 元素本身的内容不会改变。

我的理解是,这导致了很多实际结构的混乱。另一种方法是创建一个指向结构的指针:

[]*MyStruct

现在结构保持原样,我们只处理指针,我认为这些指针占用的空间较小,因此可以使我的操作更快。但是现在我给垃圾收集器做了很多工作。

  • 您是否可以提供有关何时直接使用结构以及何时使用结构指针的一般指导原则?
  • 我应该担心我离开GC的工作量是多少?
  • 复制结构与复制指针的性能开销是否可以忽略不计?
  • 也许一百万元素并不多。当切片变得更大时(但当然仍然适合RAM),所有这些都会如何变化?

3 个答案:

答案 0 :(得分:27)

我自己很好奇。跑一些基准:

type MyStruct struct {
    F1, F2, F3, F4, F5, F6, F7 string
    I1, I2, I3, I4, I5, I6, I7 int64
}

func BenchmarkAppendingStructs(b *testing.B) {
    var s []MyStruct

    for i := 0; i < b.N; i++ {
        s = append(s, MyStruct{})
    }
}

func BenchmarkAppendingPointers(b *testing.B) {
    var s []*MyStruct

    for i := 0; i < b.N; i++ {
        s = append(s, &MyStruct{})
    }
}

结果:

BenchmarkAppendingStructs  1000000        3528 ns/op
BenchmarkAppendingPointers 5000000         246 ns/op

带走:我们以纳秒为单位。对于小切片可能微不足道。但对于数百万的操作,它是毫秒和微秒之间的差异。

顺便说一句,我尝试使用预先分配的切片(容量为1000000)再次运行基准测试,以消除append()定期复制基础数组的开销。附加结构下降了1000ns,附加指针根本没有改变。

答案 1 :(得分:9)

  

您是否可以提供何时直接使用结构以及何时使用结构指针的一般指导原则?

不,这在很大程度上取决于你已经提到过的所有其他因素。

唯一真正的答案是:基准和看。每个案例都是不同的,当你有实际的时间与之合作时,世界上所有的理论都没有什么不同。

(也就是说,我的直觉是使用指针,可能还有sync.Pool来帮助垃圾收集器:http://golang.org/pkg/sync/#Pool

答案 2 :(得分:0)

与映射,切片,通道,函数和方法不同,结构变量是通过复制传递的,这意味着在幕后分配了更多的内存。另一方面,减少指针会减少垃圾收集器的工作。从我的角度来看,我将更多地考虑三件事:结构复杂性,要​​处理的数据量以及创建var后的功能需求(将其传递给函数时是否需要可变?等。)