Golang Go语言中 &struct{} vs struct{} 应该如何选择?

发布于 1周前 作者 bupafengyu 来自 Go语言

有两个数据结构( struct ),就像下面这样:

// 1
type Struct1 struct {
    String string
}

type Structs1 []*Struct1 // Difference

type Test1Struct struct { S Structs1 }

// 2 type Struct2 struct { String string }

type Structs2 []Struct2 // Difference

type Test2Struct struct { S Structs2 }

他们的不同之处在于一个存的是structPointer,另一个存的是structValue

我做了一些测试,(似乎)表明使用Pointer的时候要比使用Value的时候更快:

[rain[@localhost](/user/localhost) valuevspointor]$ go test -test.bench .
testing: warning: no tests to run
PASS
Benchmark1Pointer-2                5     229612872 ns/op
Benchmark2Value-2                  1    1367639458 ns/op
Benchmark3HugeSetPointer-2         1    1100069534 ns/op
Benchmark4HugeSetValue-2           1    4037397573 ns/op
ok      _/home/rain/Develpment/Meta/valuevspointor  10.775s

(当然这个测试说不定是有问题的,代码: https://gist.github.com/raincious/b70e17abfd08f4764683

按照上面的测试,想要建立一个新的 struct 应该优先使用&struct{}而不是struct{},那么问题来了,如果是这样的话,struct{}的意义何在?

或者应该问:如何决定应该使用&struct{}还是struct{}
(比如在需要注意性能和 GC 的情况下)

感谢。


Golang Go语言中 &struct{} vs struct{} 应该如何选择?

更多关于Golang Go语言中 &struct{} vs struct{} 应该如何选择?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

一个值被传递的时候会被复制,也就是如果是指针,就复制指针,如果是 struct ,就复制整个 struct.
建立一个&struct{}会先建立一个 struct{},在建立一个指针指向他
遍历一个[]*struct{} 会访问一串连续的指针,访问每个指针的时候会随机访问到这个指针指向的地址, GC 也是一样的道理。
孰轻孰重,这个就要靠你自己去判断了。

更多关于Golang Go语言中 &struct{} vs struct{} 应该如何选择?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在考虑性能的情况下,需要考虑你的使用模式是否是缓存友好的。
指针占用空间小,拷贝代价小,但是访问数据需要一次间接跳转,并不是缓存友好的。
struct 存在拷贝代价,但是使用时是缓存友好的。
在拥有多级缓存的现代 cpu 上,栈上的变量有很大可能完全位于缓存中,以至于小尺寸 struct 拷贝速度非常快。但指针所使用的内存可能并没有被预读到缓存中,这将导致使用指针变量反而更慢。
在这篇文章中有一段
https://talks.golang.org/2012/splash.article

The key point is that Go gives the programmer tools to limit allocation by controlling the layout of data structures. Consider this simple type definition of a data structure containing a buffer (array) of bytes:

type X struct {
a, b, c int
buf [256]byte
}

In Java, the buf field would require a second allocation and accesses to it a second level of indirection. In Go, however, the buffer is allocated in a single block of memory along with the containing struct and no indirection is required. For systems programming, this design can have a better performance as well as reducing the number of items known to the collector. At scale it can make a significant difference.

可以间接说明这个问题,通过指针间接访问变量是有代价的。

至于你的测试可能还不足以说明问题,因为 string 需要支持不同长度,肯定在内部实现上需要动态内存的,因此也就是一定存在间接访问。你需要做的测试需要对比 struct 成员全部是固定大小。一个紧凑的,连续累加 int 的测试或许可以有非常大的差异。至于指针更快还是 struct 更快取决于你的内存访问模式。如果是大数据(比如超过 cpu 二级缓存大小)拷贝为主,那就选指针。如果是连续访问元素,那么就应该选 struct

通常情况下,简单的回答是,如果 struct 很大,用指针更快,反之用值更快
但什么是 “很大”,显然不容易判断

所以我的观点是,上面说的都没什么用
如果要在意这个性能,就分别做性能测试,哪个快用哪个

在Go语言中,&struct{}struct{} 的选择取决于具体的使用场景和需求。

  1. 内存占用

    • struct{} 是一个空结构体实例,它本身不占用除结构体本身以外的额外内存空间(在大多数实现中,空结构体实例的内存占用可以忽略不计)。
    • &struct{} 是一个指向空结构体实例的指针。虽然空结构体实例本身占用很少的内存,但指针本身需要一定的内存空间来存储地址信息。
  2. 使用场景

    • struct{} 常用于需要一个占位符或仅表示某种状态但不携带任何数据的场景。例如,在需要返回一个空值但又不想使用 nilinterface{} 时,可以使用 struct{}
    • &struct{} 常用于需要传递或存储结构体指针的场景。例如,在通道(channel)中传递空结构体指针以表示某种信号或事件时,使用 &struct{} 可以避免拷贝结构体实例,提高效率。
  3. 性能考虑

    • 在性能敏感的场景中,如果不需要指针的间接引用功能,直接使用 struct{} 可能更为高效,因为它避免了指针的额外开销。
    • 在需要动态访问或修改结构体内容的场景中,使用指针 &struct{} 是必要的。

综上所述,选择 &struct{} 还是 struct{} 应根据具体需求来决定。如果仅需要一个占位符且不需要指针功能,struct{} 是一个不错的选择;如果需要指针的灵活性或效率上的考虑(如通道通信),则 &struct{} 可能更为合适。

回到顶部