我们都熟悉这个概念,即使我们没有意识到它:当你在学校学习算术时,无论你是处理整数、分数还是实数,你都会使用相同的数学符号。在软件编程中,这个概念被称为多态性。准确地说,在软件编程中,多态性是指可以通过同一个接口访问不同类型的对象。
Go 编程语言通过“接口”的概念具有“多态性”。如果您是 Java 程序员,它有点类似于 Java 中的接口。
让我们举例说明。有时我们喜欢“迭代”一组值……我们通常将这个概念的软件实现称为迭代器。你可以在 Go 中指定一个迭代器作为接口……一旦你有了它,你就可以定义像 a 函数这样的函数来计算元素的数量:
类型IntIterable接口{ 下一个( )布尔 下一个( ) uint32 重置( ) } func Count ( i IntIterable ) ( count int ) { 计数= 0 我。重置( ) 对于我。下一个( ) { 我。下一个( ) 计数+ + } 返回 }
到目前为止,一切都很好。这个函数 Count 将统计满足接口(hasNext、Next、Reset)的任何 Go 实例中元素的数量:Go 中实现这些函数的任何结构都可以被该函数处理。
接下来,您可以为 Go 提供“IntIterable”接口的实际实现:
类型IntArray结构{ 数组[ ] uint32 索引整数 } func ( a * IntArray ) HasNext ( ) bool { 返回一个。 index < len (一个数组) } func ( a * IntArray ) Next ( ) uint32 { 一个。索引+ + 返回一个。数组[一个。指数- 1 ] } func ( a * IntArray ) Reset ( ) { 一个。索引= 0 }
神奇的是,您可以在IntArray的实例上调用Count(&array) ,它将计算元素的数量。这似乎比专门的代码要好得多,例如……
func CountArray ( a * IntArray ) ( count int ) { 计数= 0 一个。重置( ) 对于一个。下一个( ) { 一个。下一个( ) 计数+ + } 返回 }
不幸的是,它并不完全更好,因为专用函数可能比采用接口的函数快得多。对于相当大的数组,专用函数在我的一些测试中大约快两倍:
计数(接口) | 2 纳秒/元素 |
计数数组 | 1 纳秒/元素 |
您的结果会有所不同,但概念仍然存在:Go 不确保接口在计算上是免费的。如果是性能瓶颈,您有责任相应地优化代码。
遗憾的是,这两个函数都太慢了:元素数量的计算实际上应该是免费的(对于相当大的数组,0 ns/元素)因为它只是数组的长度,在整个基准测试中都是一个常数。事实上,在我的基准测试中,数组的大小甚至在编译时就已经知道了。
原文: https://lemire.me/blog/2023/04/14/interfaces-are-not-free-in-go/