我经常使用切片结构。以下是这种结构的一个例子:
type MyStruct struct {
val1, val2, val3 int
text1, text2, text3 string
list []SomeType
}
所以我按如下方式定义我的切片:
[]MyStruct
让我们说我在那里有大约一百万个元素,并且我在切片上工作很重:
我的理解是,这导致了很多实际结构的混乱。另一种方法是创建一个指向结构的指针:
[]*MyStruct
现在结构保持原样,我们只处理指针,我认为这些指针占用的空间较小,因此可以使我的操作更快。但是现在我给垃圾收集器做了很多工作。
答案 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后的功能需求(将其传递给函数时是否需要可变?等。)