Go中的隐式转换到接口是否会进行内存分配?

huangapple go评论81阅读模式
英文:

Go implicit conversion to interface does memory allocation?

问题

当使用类型为interface{}的可变参数(例如Printf)定义函数时,参数显然会被隐式转换为接口实例。

这种转换是否意味着内存分配?这种转换速度快吗?在关注代码效率时,我应该避免使用可变参数函数吗?

英文:

When defining a function with variadic arguments of type interface{} (e.g. Printf), the arguments are apparently implicitly converted to interface instances.

Does this conversion imply memory allocation? Is this conversion fast? When concerned by code efficiency, should I avoid using variadic functions?

答案1

得分: 8

我找到的关于Go语言接口内存分配的最好解释仍然是Rus Cox的这篇文章,他是Go语言核心开发者之一。这篇文章非常值得一读。

http://research.swtch.com/interfaces

我摘录了一些最有趣的部分:

存储在接口中的值可能是任意大的,但只有一个字用于在接口结构中保存该值,因此赋值操作会在堆上分配一块内存,并将指针记录在这个字的位置。

...

调用fmt.Printf()时,Go编译器会生成调用itable中适当函数指针的代码,并将接口值的数据字作为函数的第一个(在这个例子中是唯一的)参数传递。

Go的动态类型转换意味着编译器或链接器无法预先计算所有可能的itable:这样的组合太多了(接口类型,具体类型),而且大多数情况下都不会被使用。相反,编译器为每个具体类型(如Binary、int或func(map[int]string))生成一个类型描述结构。类型描述结构除了其他元数据外,还包含了该类型实现的方法列表。

...

接口运行时通过在具体类型的方法表中查找接口类型的方法表中列出的每个方法来计算itable。运行时在生成itable后会缓存它,因此这种对应关系只需要计算一次。

...

如果涉及的接口类型是空的(没有方法),那么itable除了保存指向原始类型的指针之外没有任何作用。在这种情况下,可以丢弃itable,值可以直接指向类型。

由于Go语言在动态方法查找中具有静态类型的提示,它可以将查找操作从调用点移回到将值存储在接口中的时刻。

英文:

The best explanation i found about the interface memory allocation in Go is still this article from Rus Cox, one of the core Go programmer. It's well worth to read it.

http://research.swtch.com/interfaces

I picked up some of the most interesting parts:

> Values stored in interfaces might be arbitrarily large, but only one
> word is dedicated to holding the value in the interface structure, so
> the assignment allocates a chunk of memory on the heap and records the
> pointer in the one-word slot.
>
> ...
>
> Calling fmt.Printf(), the Go compiler generates code that calls the
> appropriate function pointer from the itable, passing the interface
> value's data word as the function's first (in this example, only)
> argument.
>
> Go's dynamic type conversions mean that it isn't reasonable for the
> compiler or linker to precompute all possible itables: there are too
> many (interface type, concrete type) pairs, and most won't be needed.
> Instead, the compiler generates a type description structure for each
> concrete type like Binary or int or func(map[int]string). Among other
> metadata, the type description structure contains a list of the
> methods implemented by that type.
>
> ...
>
> The interface runtime computes the itable by looking for each method
> listed in the interface type's method table in the concrete type's
> method table. The runtime caches the itable after generating it, so
> that this correspondence need only be computed once.
>
> ...
>
> If the interface type involved is empty—it has no methods—then the
> itable serves no purpose except to hold the pointer to the original
> type. In this case, the itable can be dropped and the value can point
> at the type directly.

Because Go has the hint of static typing to go along with the dynamic method lookups, it can move the lookups back from the call sites to the point when the value is stored in the interface.

答案2

得分: 2

将其转换为interface{}是一个与可变参数不同的概念,可变参数包含在切片中,可以是任意类型。然而,只要它们不逃逸到堆上(在GC工具链中),它们都可能是免费的,即不需要额外的分配。

你在fmt函数(如Printf)中看到的多余分配可能来自反射,而不是使用interface{}或可变参数。

如果你关心效率,避免间接引用总是比不避免更高效,因此使用正确的值类型将产生更高效的代码。不过,差异可能很小,所以在关注微小优化之前,先对代码进行基准测试。

英文:

Converting to an interface{} is a separate concept from variadic arguments which are contained in a slice and can be of any type. However these are all probably free in the sense of allocations as long as they don't escape to the heap (in the GC toolchain).

The excess allocations you would see from fmt functions like Printf are going to be from reflection rather than from the use of interface{} or variadic arguments.

If you're concerned with efficiency though, avoiding indirection will always be more efficient than not, so using the correct value types will yield more efficient code. The difference can be minimal though, so benchmark the code first before concerning yourself with minor optimizations.

答案3

得分: 0

Go传递参数是通过值拷贝的方式,因此它总是会进行内存分配。如果可能的话,你应该尽量避免使用interface{}。在描述的情况下,你的函数将需要使用反射来处理参数。反射是一种相当昂贵的操作,这就是为什么fmt.Printf()很慢的原因。

英文:

Go passes arguments copy_by_value, so it does memory allocation anyway. You always should better avoid using interface{} if possible. In described case your function will need to reflect arguments to use them. Reflection is quite expensive operation that's why fmt.Printf() is so slow.

huangapple
  • 本文由 发表于 2016年9月14日 22:07:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/39492539.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定