结构的切片与指向结构的指针的切片

我经常处理结构的片段,下面是这样一个结构的例子:

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

因此,我将我的切片定义如下:

[]MyStruct

假设我有大约100万个元素在里面,我正在大量地处理这个切片:

  • 我经常添加新元素。(元素的总数是未知的。)
  • 我时不时会整理一下。
  • 我还删除了元素(尽管没有添加新元素那么多)。
  • 我经常读取元素并传递它们(作为函数参数)。
  • 元素本身的内容不会改变。

我的理解是,这会导致对实际的结构进行大量的重组。另一种方法是创建指向结构的一片指针:

[]*MyStruct

现在,结构仍然保持不变,我们只处理指针,我假设这些指针占用的空间较小,因此会使我的操作更快。但现在我要给垃圾收集工更多的工作。

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

Can you provide general guidelines of when to work with structs directly vs. when to work with pointers to structs?

No, it depends too much on all the other factors you've already mentioned.

The only real answer is: benchmark and see. Every case is different and all the theory in the world doesn't make a difference when you've got actual timings to work with.

(That said, my intuition would be to use pointers, and possibly a sync.Pool to aid the garbage collector: http://golang.org/pkg/sync/#Pool)

Just got curious about this myself. Ran some benchmarks:

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{})
}
}

Results:

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

Take aways: we're in nanoseconds. Probably negligible for small slices. But for millions of ops, it's the difference between milliseconds and microseconds.

Btw, I tried running the benchmark again with slices which were pre-allocated (with a capacity of 1000000) to eliminate overhead from append() periodically copying the underlying array. Appending structs dropped 1000ns, appending pointers didn't change at all.

Unlike maps, slices, channels, functions, and methods, struct variables are passed by copy which means there's more memory allocated behind the scene. On the other hand, reducing pointers result in less work for the garbage collector. From my perspective, I would think more about 3 things: the struct complexity, the quantity of data to handle, and the functional need once you'd have created your var (does it need to be mutable when it's being passed into a function? etc..)