Go语言 结构切片与指向结构的指针的切片

fnvucqvd  于 2023-09-28  发布在  Go
关注(0)|答案(3)|浏览(83)

我经常使用结构的切片。下面是这样一个结构体的例子:

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

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

[]MyStruct

假设我有大约一百万个元素,我正在大量使用切片:

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

我的理解是,这会导致实际结构的大量 Shuffle 。另一种方法是创建指向结构体的指针切片:

[]*MyStruct

现在结构体保持在它们原来的位置,我们只处理指针,我假设指针占用的空间更小,因此会使我的操作更快。但现在我给了垃圾收集员更多的工作。

  • 你能提供一般的指导方针,什么时候直接使用结构体,什么时候直接使用结构体?什么时候使用指向结构体的指针?
  • 我应该担心我留给GC多少工作吗?
  • 是复制结构的性能开销与复制指针可以忽略不计吗?
  • 也许一百万个元素并不多。当切片变得更大时(当然,仍然适合RAM),所有这些都将如何改变?
wz3gfoph

wz3gfoph1#

我自己也很好奇。运行了一些基准测试:

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,追加指针根本没有改变。

qco9c6ql

qco9c6ql2#

你能提供一般的指导方针,什么时候直接使用结构体,什么时候直接使用结构体?什么时候使用指向结构体的指针?
不,它太依赖于你已经提到的所有其他因素。
唯一真实的答案是:benchmark看看每种情况都是不同的,当你有实际的时间安排时,世界上所有的理论都不会有什么不同。
(That我的直觉是使用指针,可能还有一个sync.Pool来帮助垃圾收集器:http://golang.org/pkg/sync/#Pool)

unftdfkk

unftdfkk3#

与Map、切片、通道、函数和方法不同,结构变量是通过复制传递的,这意味着在后台分配了更多的内存。另一方面,减少指针会减少垃圾收集器的工作量。从我的Angular 来看,我会更多地考虑三件事:结构的复杂性,要处理的数据量,以及创建var后的函数需求(当它被传递到函数中时,它需要是可变的吗?等)

相关问题