在Go方法中通过值传递“this”会有性能损失吗?

时间:2013-03-10 18:56:02

标签: c++ go

经过9年的C ++开发,我正在探索Go。在C ++中,由于性能损失,除了内置类型的变量之外,按值传递函数的参数是一种不好的做法:参数的所有字段都将被复制,在大多数情况下,这将是一个非常昂贵的操作。

这对Go来说是真的吗?通过值传递“this”仅仅为方法分配“const”语义看起来非常昂贵。 Go编译器是否足够智能以防止在第一次修改之前复制变量?为什么不通过值将“this”传递给Go中的反模式,就像在C / C ++中一样?

4 个答案:

答案 0 :(得分:8)

其他答案都很好,但在我看来,缺少一些信息。

Go中的接收者只是语法糖,如以下code所示:

package main

import "fmt"

type Something struct {
    Value int
}

func (s *Something) ChangeValue(n int) {
    s.Value = n
}

func main() {
    o := new(Something)             // o is of type *Something
    fmt.Println(o.Value)            // Prints 0
    o.ChangeValue(8)                // Changes o.Value to 8
    fmt.Println(o.Value)            // Prints 8
    (*Something).ChangeValue(o, 16) // Same as calling o.ChangeValue(16)
    fmt.Println(o.Value)            // Prints 16
}

基于此,考虑如果ChangeValue的接收者是Something类型的值而不是指向一个的指针会发生什么......

没错!你永远不会通过这种方法实际改变o的{​​{1}}字段。大多数情况下,您使用指针接收器进行封装​​。

答案 1 :(得分:6)

Go中的“this”称为receiver。是的,使用非指针接收器仅模拟“const”语义可能非常昂贵。但Go出于好理由放弃了“const”修饰符。因此,以不必要的复制为代价来接管特定的语言设计决策可能不是一个好主意 - 在任何大于机器词的情况下。

BTW,“this”或“self”与“receiver”之间的术语差异意味着它也有不同的语义。 IIRC,在其他一些语言中不能改变“this”或“self”的值,但在Go中,接收器只是另一个函数参数(实际上是从编译器的角度来看的第一个)。

也就是说,这就是我不鼓励编写接收器变量名为thisself的方法的原因。对于习惯其他语言的人来说,这会产生误导。

一个完全构成的例子,充满希望地说明了这个想法:

func (n *node) walk(f func(*node)) {
        for n != nil {
                f(n)
                n = n.next
        }
}

答案 2 :(得分:5)

我想说你的C ++知识将很好地转化为Go作为函数参数(通过值传递结构)和不存在的内容(内置类型,例如int)。

主要区别在于引用类型,切片,mapchannel。虽然它们似乎是通过值传递的(你不需要使用指针)实际上是通过引用传递的,所以一般不要使用指向切片,地图或通道的指针。

string也很特别 - 它们是引擎类型下的参考类型,但它们也是不可变的,所以直接传递它们。

至于this或Go中调用的接收器的具体情况 - 同样的规则适用(注意你可以将内置类型作为接收器而不像C ++),我不认为编译器足够智能,以避免副本,所以使用指针大结构。

答案 3 :(得分:5)

这取决于接收器的大小。如果接收器少于几十个字节,复制它实际上可能比指针追逐(额外的存储器访问)便宜,如果你传递一个指针就需要。此外,使用指针使得更有可能在堆上分配结构,这给垃圾收集器带来了额外的负担。

在Go中,副本始终是逐字节副本,因此成本仅取决于结构的大小。在C ++中,它可能会调用复制构造函数,这可能会占用大量时间。

因此,除了非常大的对象之外,根据方法的语义和与API其余部分的一致性,使用任何类型的接收器最有意义。