当定义具有类型interface{}
的可变参数的函数(例如Printf
)时,参数显然被隐式转换为接口实例。
此转换是否意味着内存分配?转换速度快吗?当关注代码效率时,我应该避免使用可变参数函数吗?
答案 0 :(得分:4)
我发现关于Go中接口内存分配的最佳解释仍然是来自Rus Cox的核心Go程序员之一。值得一读。
<强> http://research.swtch.com/interfaces 强>
我选择了一些最有趣的部分:
存储在接口中的值可能是任意大的,但只有一个 word专门用于保存接口结构中的值,所以 赋值在堆上分配一块内存并记录 单字槽中的指针。
...
调用fmt.Printf(),Go编译器生成调用的代码 适当的函数指针来自itable,传递接口 value的数据字作为函数的第一个(仅在此示例中) 参数。
Go的动态类型转换意味着它不合理 编译器或链接器预先计算所有可能的itables:也有 许多(接口类型,具体类型)对,大多数将不需要。 相反,编译器为每个生成类型描述结构 具体类型如Binary或int或func(map [int] string)。除其他外 元数据,类型描述结构包含一个列表 由该类型实现的方法。
...
接口运行时通过查找每个方法来计算itable 在具体类型的接口类型的方法表中列出 方法表。生成器运行后缓存itable,因此 这种对应只需要计算一次。
...
如果涉及的接口类型为空 - 它没有方法 - 那么 除了将指针保持在原始状态之外,itable没有用处 类型。在这种情况下,可以删除itable并且值可以指向 在类型直接。
因为Go有动态方法查找的静态类型提示,它可以将查询从调用站点移回到值存储在接口中的点。
答案 1 :(得分:2)
转换为interface{}
是一个独立于可变参数的概念,它包含在切片中,可以是任何类型。但是,只要它们不会逃逸到堆中(在GC工具链中),这些都可能在分配意义上是免费的。
您从fmt
函数中看到的超额分配(例如Printf
)将来自反思,而不是使用interface{}
或可变参数。
如果您关注效率,那么避免间接将始终更有效,因此使用正确的值类型将产生更高效的代码。但差异可能很小,因此在对自己进行微小优化之前先对代码进行基准测试。
答案 2 :(得分:0)
Go传递参数copy_by_value,因此它无论如何都会进行内存分配。如果可能的话,你总是应该更好地避免使用接口{}。在描述的情况下,您的函数将需要反映使用它们的参数。反思是非常昂贵的操作,这就是fmt.Printf()
如此缓慢的原因。