V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
raincious
V2EX  ›  Go 编程语言

Golang:&struct{} vs struct{} 应该如何选择?☺

  •  
  •   raincious · 2015-12-19 15:35:14 +08:00 · 2130 次点击
    这是一个创建于 3053 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有两个数据结构( 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 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 的情况下)

    感谢。

    3 条回复    2015-12-20 12:01:56 +08:00
    chzyer
        1
    chzyer  
       2015-12-19 15:45:39 +08:00   ❤️ 1
    一个值被传递的时候会被复制,也就是如果是指针,就复制指针,如果是 struct ,就复制整个 struct.
    建立一个&struct{}会先建立一个 struct{},在建立一个指针指向他
    遍历一个[]*struct{} 会访问一串连续的指针,访问每个指针的时候会随机访问到这个指针指向的地址, GC 也是一样的道理。
    孰轻孰重,这个就要靠你自己去判断了。
    comicfans44
        2
    comicfans44  
       2015-12-19 20:56:51 +08:00   ❤️ 2
    在考虑性能的情况下,需要考虑你的使用模式是否是缓存友好的。
    指针占用空间小,拷贝代价小,但是访问数据需要一次间接跳转,并不是缓存友好的。
    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
    CRVV
        3
    CRVV  
       2015-12-20 12:01:56 +08:00   ❤️ 1
    通常情况下,简单的回答是,如果 struct 很大,用指针更快,反之用值更快
    但什么是 “很大”,显然不容易判断

    所以我的观点是,上面说的都没什么用
    如果要在意这个性能,就分别做性能测试,哪个快用哪个
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5350 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 52ms · UTC 05:59 · PVG 13:59 · LAX 22:59 · JFK 01:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.