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

Oli*_*ver 51 performance go slice

我经常使用切片结构.这是一个这样的结构的例子:

type MyStruct struct {
    val1, val2, val3    int
    text1, text2, text3 string
    list                []SomeType
}
Run Code Online (Sandbox Code Playgroud)

所以我定义我的切片如下:

[]MyStruct
Run Code Online (Sandbox Code Playgroud)

假设我在那里有大约一百万个元素,我正在努力研究切片:

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

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

[]*MyStruct
Run Code Online (Sandbox Code Playgroud)

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

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

Rus*_*gan 32

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

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{})
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

BenchmarkAppendingStructs  1000000        3528 ns/op
BenchmarkAppendingPointers 5000000         246 ns/op
Run Code Online (Sandbox Code Playgroud)

带走:我们在几纳秒之内.对于小切片可能微不足道.但对于数百万的操作,它是毫秒和微秒之间的差异.

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

  • 我更进一步(除了预分配列表)和附加非空结构与随机数据,并且点比结构慢约10%:`BenchmarkAppendingStructs-8 5000000 387 ns/op BenchmarkAppendingPointers-8 3000000 422 ns/op ` (5认同)
  • 所以使用指针切片没有缺点,为什么我没有看到他们比结构切片更频繁地使用它? (2认同)
  • Golang工具包具有基准测试功能。gotest将上述“基准***”功能识别为基准测试。请参阅Golang文档。 (2认同)
  • 基准测试揭示了使用指针相对于直接结构的直接好处 - 但如何衡量长期 GC 影响呢? (2认同)

Eva*_*van 9

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

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

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

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