去隐式转换到接口是否分配内存?

问题描述 投票:0回答:3

当定义具有

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

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

go
3个回答
8
投票

我发现的关于 Go 中接口内存分配的最好解释仍然是 Go 核心程序员之一 Russ Cox 的这篇文章。非常值得一读。

http://research.swtch.com/interfaces

我挑选了一些最有趣的部分:

接口中存储的值可能是任意大,但只有一个 word 专用于保存接口结构中的值,因此 分配在堆上分配一块内存并记录 指针位于单字槽中。

...

调用 fmt.Printf(),Go 编译器生成调用 fmt.Printf() 的代码 来自 itable 的适当函数指针,传递接口 值的数据字作为函数的第一个(仅在本例中) 争论。

Go 的动态类型转换意味着它对于 编译器或链接器来预先计算所有可能的 itables:也有 许多(接口类型、具体类型)对,并且大多数都不需要。 相反,编译器为每个类型生成一个类型描述结构 具体类型如 Binary 或 int 或 func(map[int]string)。除其他外 元数据,类型描述结构包含一个列表 该类型实现的方法。

...

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

...

如果涉及的接口类型为空——它没有方法——那么 itable 除了保持指向原始指针的指针之外没有任何作用 类型。在这种情况下,itable可以被删除并且值可以指向 直接输入类型。

因为 Go 具有静态类型提示和动态方法查找,所以它可以将查找从调用站点移回值存储在接口中的位置。


1
投票

转换为

interface{}
是一个独立于可变参数的概念,可变参数包含在切片中并且可以是任何类型。然而,从分配的意义上来说,这些可能都是免费的,只要它们不逃逸到堆(在 GC 工具链中)。

您从像

fmt
这样的
Printf
函数中看到的多余分配将来自反射,而不是来自
interface{}
或可变参数的使用。

如果您关心效率,避免间接总是比不更有效,因此使用正确的值类型将产生更高效的代码。不过差异可能很小,因此请先对代码进行基准测试,然后再进行较小的优化。


0
投票

Go 传递参数 copy_by_value,所以它无论如何都会分配内存。如果可能的话,您总是应该最好避免使用interface{}。在所描述的情况下,您的函数将需要反映参数才能使用它们。反射是相当昂贵的操作,这就是为什么

fmt.Printf()
如此缓慢。

© www.soinside.com 2019 - 2024. All rights reserved.