1. Go 新的函数调用惯例
在 Go 1.14 版本中,引入了一个新的函数调用惯例,称为带有省略号的参数,或者称为“variadic arguments”。
该新特性使得用户不必手动创建一个切片,就可以向变参函数中传递变量数量的参数,并且可以像往常一样使用这些值。
那么这个新规范能带来多少性能提升呢?
2. 旧的函数调用惯例
在 Go 1.13 及更早版本中,要向函数中传递多个参数,需要将它们显式地放在一个切片中并传递给该函数。
func oldFunction(args []int) {
// do something with args
}
func main() {
arr := []int{1, 2, 3}
oldFunction(arr)
}
这种方式虽然简单易用,但是在参数数量增加时便会出现问题。
2.1 参数过多导致的问题
首先,这种方式会在函数中创建一个新的切片,并将所有的参数复制到该切片中。
这不仅会消耗更多的内存,并且循环计算每个元素的地址,其中大多数都没有被使用。
其次,这样的方式还极易出现错误,例如在传递参数的数量方面。如果切片未能正确地创建,或者未能包含所需的参数数量,那么就会发生严重的运行时错误。
3. 新的函数调用惯例
在 Go 1.14 版本中引入了带省略号的新参数,可以接受任意数量的参数,这使得函数调用更加简单。以下为示例:
func newFunction(args ...int) {
// do something with args
}
func main() {
newFunction(1)
newFunction(1, 2, 3)
newFunction([]int{1, 2, 3}...)
}
新的函数调用惯例扫除了旧方式的一个主要缺陷:现在可以将任何数量的参数传递给该函数,而无需手动创建切片。
3.1 带省略号的参数的实现方式
使用省略号定义参数可达到编写带切片参数的函数的效果,而方法签名将不包含切片类型。
取而代之的是,省略号可用于可以包含零个或多个参数的函数中,并且允许为不同的类型定义变参函数。
func newFunction(args ...interface{}) {
for _, arg := range args {
switch v := arg.(type) {
case int:
// do something with int
case string:
// do something with string
// ...and so on
}
}
}
这里省略号的实现方式与其它语言中并不相同,因为其实现类似于使用“interface{}”类型的切片。
3.2 新的函数调用惯例能带来的性能提升
新的函数调用惯例能使 Go 语言程序的性能提升多少取决于各种因素,包括所传递参数的数量。
通常,新规范比旧的规范更快,因为它不涉及创建切片和循环处理,这意味着在执行更多的操作时,省略号方法可以获得更大的速度提升,而且这种性能提升随着需要处理的参数数量的增加而增加。
根据 Go 团队开发的基准测试,虽然差异不大,但通常情况下,使用省略号传递参数会比手动创建切片和相应的参数传递(旧方式)更快。
4. 总结
根据 Go 团队的性能测试,使用省略号参数的传递方法通常比手动创建切片并将其传递给函数更快。
由于新的函数调用惯例使得函数调用更加简单,函数签名将不再包含切片。
新的规范也有助于避免在创建切片和循环计算每个元素的地址修复参数时出现的错误。