TL;DR:它确实取决于类型分布,但接口是最安全的选择,除非您确定类型会以常规块的形式出现。还要考虑如果你的代码不经常执行,分支预测器也不会被预热。
长解释:
在 darwin/amd64 上的 go1.9.2
BenchmarkIntmethod-4 2000000000 1.67 ns/op
BenchmarkInterface-4 2000000000 1.89 ns/op
BenchmarkTypeSwitch-4 2000000000 1.26 ns/op
BenchmarkTypeAssertion-4 2000000000 1.41 ns/op
BenchmarkTypeAssertionNoCheck-4 2000000000 1.61 ns/op
这里要注意的重要一点是,只有一个分支的类型开关与使用接口的比较不是很公平。CPU 分支预测器将变得非常热、非常快并给出非常好的结果。更好的基准测试将使用伪随机类型和与伪随机接收器的接口。显然,我们需要删除静态方法调度并坚持只使用接口而不是类型开关(类型断言也变得不那么有意义,因为它需要大量的 if 语句,而且没有人会写它而不是使用类型开关)。这是代码:
package main
import (
"testing"
)
type myint0 int64
type myint1 int64
type myint2 int64
type myint3 int64
type myint4 int64
type myint5 int64
type myint6 int64
type myint7 int64
type myint8 int64
type myint9 int64
type DoStuff interface {
doStuff()
}
func (i myint0) doStuff() {
i += 0
}
func (i myint1) doStuff() {
i += 1
}
func (i myint2) doStuff() {
i += 2
}
func (i myint3) doStuff() {
i += 3
}
func (i myint4) doStuff() {
i += 4
}
func (i myint5) doStuff() {
i += 5
}
func (i myint6) doStuff() {
i += 6
}
func (i myint7) doStuff() {
i += 7
}
func (i myint8) doStuff() {
i += 8
}
func (i myint9) doStuff() {
i += 9
}
// Randomly generated
var input []DoStuff = []DoStuff{myint0(0), myint1(0), myint1(0), myint5(0), myint6(0), myint7(0), myint6(0), myint9(0), myint7(0), myint7(0), myint6(0), myint2(0), myint9(0), myint0(0), myint2(0), myint3(0), myint5(0), myint1(0), myint4(0), myint0(0), myi
nt4(0), myint3(0), myint9(0), myint3(0), myint9(0), myint5(0), myint0(0), myint0(0), myint8(0), myint1(0)}
func BenchmarkInterface(b *testing.B) {
doStuffInterface(b.N)
}
func BenchmarkTypeSwitch(b *testing.B) {
doStuffSwitch(b.N)
}
func doStuffInterface(n int) {
for k := 0; k < n; k++ {
for _, in := range input {
in.doStuff()
}
}
}
func doStuffSwitch(n int) {
for k := 0; k < n; k++ {
for _, in := range input {
switch v := in.(type) {
case *myint0:
v.doStuff()
case *myint1:
v.doStuff()
case *myint2:
v.doStuff()
case *myint3:
v.doStuff()
case *myint4:
v.doStuff()
case *myint5:
v.doStuff()
case *myint6:
v.doStuff()
case *myint7:
v.doStuff()
case *myint8:
v.doStuff()
case *myint9:
v.doStuff()
}
}
}
}
结果:
go test -bench .
goos: darwin
goarch: amd64
pkg: test
BenchmarkInterface-4 20000000 74.0 ns/op
BenchmarkTypeSwitch-4 20000000 119 ns/op
PASS
ok test 4.067s
类型越多,分布越随机,win接口越大。
为了显示这种差异,我将代码更改为对随机选择进行基准测试,而不是始终选择相同的类型。在这种情况下,typeswitch 再次更快,而 interface 速度相同,代码如下:
package main
import (
"testing"
)
type myint0 int64
type myint1 int64
type myint2 int64
type myint3 int64
type myint4 int64
type myint5 int64
type myint6 int64
type myint7 int64
type myint8 int64
type myint9 int64
type DoStuff interface {
doStuff()
}
func (i myint0) doStuff() {
i += 0
}
func (i myint1) doStuff() {
i += 1
}
func (i myint2) doStuff() {
i += 2
}
func (i myint3) doStuff() {
i += 3
}
func (i myint4) doStuff() {
i += 4
}
func (i myint5) doStuff() {
i += 5
}
func (i myint6) doStuff() {
i += 6
}
func (i myint7) doStuff() {
i += 7
}
func (i myint8) doStuff() {
i += 8
}
func (i myint9) doStuff() {
i += 9
}
// Randomly generated
var randInput []DoStuff = []DoStuff{myint0(0), myint1(0), myint1(0), myint5(0), myint6(0), myint7(0), myint6(0), myint9(0), myint7(0), myint7(0), myint6(0), myint2(0), myint9(0), myint0(0), myint2(0), myint3(0), myint5(0), myint1(0), myint4(0), myint0(0),
myint4(0), myint3(0), myint9(0), myint3(0), myint9(0), myint5(0), myint0(0), myint0(0), myint8(0), myint1(0)}
var oneInput []DoStuff = []DoStuff{myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0),
myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0), myint1(0)}
func BenchmarkRandomInterface(b *testing.B) {
doStuffInterface(randInput, b.N)
}
func BenchmarkRandomTypeSwitch(b *testing.B) {
doStuffSwitch(randInput, b.N)
}
func BenchmarkOneInterface(b *testing.B) {
doStuffInterface(oneInput, b.N)
}
func BenchmarkOneTypeSwitch(b *testing.B) {
doStuffSwitch(oneInput, b.N)
}
func doStuffInterface(input []DoStuff, n int) {
for k := 0; k < n; k++ {
for _, in := range input {
in.doStuff()
}
}
}
func doStuffSwitch(input []DoStuff, n int) {
for k := 0; k < n; k++ {
for _, in := range input {
switch v := in.(type) {
case *myint0:
v.doStuff()
case *myint1:
v.doStuff()
case *myint2:
v.doStuff()
case *myint3:
v.doStuff()
case *myint4:
v.doStuff()
case *myint5:
v.doStuff()
case *myint6:
v.doStuff()
case *myint7:
v.doStuff()
case *myint8:
v.doStuff()
case *myint9:
v.doStuff()
}
}
}
}
结果如下:
BenchmarkRandomInterface-4 20000000 76.9 ns/op
BenchmarkRandomTypeSwitch-4 20000000 115 ns/op
BenchmarkOneInterface-4 20000000 76.6 ns/op
BenchmarkOneTypeSwitch-4 20000000 68.1 ns/op