价值接收器与Golang的指针接收器? [英] Value receiver vs. Pointer receiver in Golang?

查看:101
本文介绍了价值接收器与Golang的指针接收器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很不清楚在哪种情况下,我希望使用价值接收者而不是总是使用指针接收者。

从文档中回顾:

  type T struct {
a int
}
func(tv T)Mv(int)int {return 0} //值接收器
func(tp * T)Mp(f float32)float32 {return 1} //指针接收器

docs 也表示对于基本类型,切片和小型结构等类型,值接收器非常便宜,因此除非方法的语义需要指针,值接收器



第一点表示它非常便宜,但问题在于指针接收器更便宜。所以我做了一个很小的基准测试(要点代码),它向我显示,指针接收器甚至更快对于只有一个字符串字段的结构。结果如下:

$ $ p $ $ code $ //结构一个空字符串属性
BenchmarkChangePointerReceiver 2000000000 0.36 ns / op
BenchmarkChangeItValueReceiver 500000000 3.62 ns / op


//结构1零int属性
BenchmarkChangePointerReceiver 2000000000 0.36 ns / op
BenchmarkChangeItValueReceiver 2000000000 0.36 ns / op

(编辑:请注意,第二点在新版本中无效,请参阅评论)。第二点它说,它是高效和清晰,这更多的是品味的问题,不是吗?就我个人而言,我更喜欢使用到处都是一致的方式。效率在什么意义上?性能明智似乎指针几乎总是更高效。很少有一个int属性的测试运行显示价值接收器的最小优势(范围为0.01-0.1 ns / op)

有人可以告诉我一个值接收器清楚的情况使指针接收器更有意义?或者我在基准测试中做了什么错误,是否忽视了其他因素?

解决方案

请注意常见问题确实提到了一致性


接下来是一致性。如果某些类型的方法必须具有指针接收器,则其余的方法也应该如此,因此无论类型如何使用,方法集都是一致的。有关详细信息,请参阅方法集部分。

如前所述中:


关于这个规则指针与接收者的值是值方法可以在指针和值上调用
,但指针方法只能在指针上调用


现在:


有人可以告诉我一个值接收器明显更有意义的情况,那么指针接收器?


代码评论评论可以提供帮助:



  • 如果接收者是地图,func或chan ,不要使用指向它的指针。
  • 如果接收者是一个切片,并且该方法不会重新分割或重新分配该切片,则不要使用指向它的指针。
  • li>
  • 如果方法n如果接收者是一个包含 sync.Mutex 的结构,或者接收者是一个包含 sync.Mutex 的结构,或者类似的同步字段,接收器必须是一个指针,以避免复制。
  • 如果接收器是一个大的结构或数组,则指针接收器更有效。多大?假设它等同于将其所有元素作为参数传递给方法。如果感觉太大,它对于接收器来说也太大。

  • 可以同时使用函数或方法,也可以使用此方法调用函数或方法来突出接收器?值类型在调用方法时创建接收者的副本,因此外部更新不会应用于此接收者。如果更改必须在原始接收者中可见,那么接收者必须是一个指针。

  • 如果接收者是结构体,数组或切片,并且它的任何元素都是指向可能如果接收者是一个很小的数组或结构体,它本身就是一个值类型 / strong>(例如,类似于 time.Time 类型),没有可变字段和无指针,或者只是一个简单的基本类型,如int或string, 价值接收者有意义

    值接收者可以减少可以生成的垃圾数量;如果将值传递给value方法,则可以使用堆栈副本而不是在堆上进行分配。(编译器试图在避免此分配方面做出明智的决定,但它不一定总能成功)。不要因为这个原因而不选择值接收器类型。

  • 最后,如有疑问,请使用指针接收器。


粗体部分见于 net / http / server.go#Write()

  //写入将h中描述的标题写入w。 
//
//该方法有一个值接收器,尽管h的大小为
//,因为它阻止了分配。逃逸分析不是
//足够聪明,可以实现此功能不会改变h。
func(h extraHeader)Write(w * bufio.Writer){
...
}


It is very unclear for me in which case I would want to use a value receiver instead of always using a pointer receiver.
To recap from the docs:

type T struct {
    a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver

The docs says also "For types such as basic types, slices, and small structs, a value receiver is very cheap so unless the semantics of the method requires a pointer, a value receiver is efficient and clear."

First point it says it is "very cheap", but the question is more is it cheaper then pointer receiver. So I made a small benchmark (code on gist) which showed me, that pointer receiver is faster even for a struct that has only one string field. These are the results:

// Struct one empty string property
BenchmarkChangePointerReceiver  2000000000               0.36 ns/op
BenchmarkChangeItValueReceiver  500000000                3.62 ns/op


// Struct one zero int property
BenchmarkChangePointerReceiver  2000000000               0.36 ns/op
BenchmarkChangeItValueReceiver  2000000000               0.36 ns/op

(Edit: Please note that second point became invalid in newer go versions, see comments) .
Second point it says, it is "efficient and clear" which is more a matter of taste, isn't it? Personally I prefer consistency by using everywhere same way. Efficiency in what sense? performance wise it seems pointer are almost always more efficient. Few test-runs with one int property showed minimal advantage of Value receiver (range of 0.01-0.1 ns/op)

Can someone tell me a case where a value receiver clearly makes more sense then a pointer receiver? Or am I doing something wrong in the benchmark, did I overlooked other factors?

解决方案

Note that the FAQ does mention consistency

Next is consistency. If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used. See the section on method sets for details.

As mentioned in this thread:

The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers

Now:

Can someone tell me a case where a value receiver clearly makes more sense then a pointer receiver?

The Code Review comment can help:

  • If the receiver is a map, func or chan, don't use a pointer to it.
  • If the receiver is a slice and the method doesn't reslice or reallocate the slice, don't use a pointer to it.
  • If the method needs to mutate the receiver, the receiver must be a pointer.
  • If the receiver is a struct that contains a sync.Mutex or similar synchronizing field, the receiver must be a pointer to avoid copying.
  • If the receiver is a large struct or array, a pointer receiver is more efficient. How large is large? Assume it's equivalent to passing all its elements as arguments to the method. If that feels too large, it's also too large for the receiver.
  • Can function or methods, either concurrently or when called from this method, be mutating the receiver? A value type creates a copy of the receiver when the method is invoked, so outside updates will not be applied to this receiver. If changes must be visible in the original receiver, the receiver must be a pointer.
  • If the receiver is a struct, array or slice and any of its elements is a pointer to something that might be mutating, prefer a pointer receiver, as it will make the intention more clear to the reader.
  • If the receiver is a small array or struct that is naturally a value type (for instance, something like the time.Time type), with no mutable fields and no pointers, or is just a simple basic type such as int or string, a value receiver makes sense.
    A value receiver can reduce the amount of garbage that can be generated; if a value is passed to a value method, an on-stack copy can be used instead of allocating on the heap. (The compiler tries to be smart about avoiding this allocation, but it can't always succeed.) Don't choose a value receiver type for this reason without profiling first.
  • Finally, when in doubt, use a pointer receiver.

The part in bold is found for instance in net/http/server.go#Write():

// Write writes the headers described in h to w.
//
// This method has a value receiver, despite the somewhat large size
// of h, because it prevents an allocation. The escape analysis isn't
// smart enough to realize this function doesn't mutate h.
func (h extraHeader) Write(w *bufio.Writer) {
...
}

这篇关于价值接收器与Golang的指针接收器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆