Golang Go语言中 interface.Method 性能为何会比 switch type 差?

Golang Go语言中 interface.Method 性能为何会比 switch type 差?

测试代码:

package main

import (
	"testing"
)

type InterfaceA interface {
	Name() string
}

type InterfaceB interface {
	Name() string
	Add()
}

type A struct {
	v int
}

func (*A) Name() string {
	return "A"
}

func (a *A) Add() {
	a.v += 1
}

type B struct {
	A
}

func (*B) Name() string {
	return "B"
}

func BenchmarkNormal(b *testing.B) {
	switchFunc := func(v *A) {
		v.Add()
	}
	for i := 0; i < b.N; i++ {
		v := new(A)
		switchFunc(v)
	}
}

func BenchmarkInterface(b *testing.B) {
	switchFunc := func(v interface{}) {
		switch n := v.(type) {
		case *A:
			n.Add()
		case *B:
			n.Add()
		}
	}
	for i := 0; i < b.N; i++ {
		v := new(A)
		switchFunc(v)
	}
}

func BenchmarkInterface1(b *testing.B) {
	switchFunc := func(v InterfaceA) {
		switch v.Name() {
		case "A":
			v.(*A).Add()
		case "B":
			v.(*B).Add()
		}
	}
	for i := 0; i < b.N; i++ {
		v := new(A)
		switchFunc(v)
	}
}

func BenchmarkInterface2(b *testing.B) {
	switchFunc := func(v interface{}) {
		v.(InterfaceB).Add()
	}
	for i := 0; i < b.N; i++ {
		v := new(A)
		switchFunc(v)
	}
}

func BenchmarkInterface3(b *testing.B) {
	switchFunc := func(v InterfaceB) {
		v.Add()
	}
	for i := 0; i < b.N; i++ {
		v := new(A)
		switchFunc(v)
	}
}

func BenchmarkInterface4(b *testing.B) {
	switchFunc := func(v InterfaceB) {
		v.Name()
	}
	for i := 0; i < b.N; i++ {
		v := new(A)
		switchFunc(v)
	}
}

func BenchmarkInterface5(b *testing.B) {
	switchFunc := func(v InterfaceB) {
		v.Name()
		v.Add()
	}
	for i := 0; i < b.N; i++ {
		v := new(A)
		switchFunc(v)
	}
}

测试结果:

└──╼ go test -test.bench=".*" . -benchmem 
goos: darwin
goarch: amd64
pkg: org
cpu: Intel(R) Core(TM) i5-8279U CPU @ 2.40GHz
BenchmarkNormal-8       	1000000000	         0.2542 ns/op	       0 B/op	       0 allocs/op
BenchmarkInterface-8    	1000000000	         0.8415 ns/op	       0 B/op	       0 allocs/op
BenchmarkInterface1-8   	72095432	        15.48 ns/op	       8 B/op	       1 allocs/op
BenchmarkInterface2-8   	55137806	        21.07 ns/op	       8 B/op	       1 allocs/op
BenchmarkInterface3-8   	799164643	         1.449 ns/op	       0 B/op	       0 allocs/op
BenchmarkInterface4-8   	767046265	         1.519 ns/op	       0 B/op	       0 allocs/op
BenchmarkInterface5-8   	72075118	        15.82 ns/op	       8 B/op	       1 allocs/op
PASS
ok  	org	7.915s

还有就是 interface 接口单独测试 Name()或者 Add()性能都差不多,可一旦两个一起调用,性能几乎减少了 10 倍,有大佬研究过这个问题吗?


更多关于Golang Go语言中 interface.Method 性能为何会比 switch type 差?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

内存逃逸?

更多关于Golang Go语言中 interface.Method 性能为何会比 switch type 差?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


能享受到 cpu 的分支预测吧

直接 switch 再 call method 是一次条件跳转再加上一次 call/ret,如果你的 code footprint 很小,这些都能存在 L1 BTB 里,BPU 不需要额外的周期就能访问到。
而 interface method 应该是间接跳转,BPU 需要查 ITA(iBTB)才能预测目标地址,这个操作比直接跳转更加昂贵,因为 ITA 通常比较大,延迟可能更接近 L2 BTB 。

另外可以反汇编看看编译器有没有做什么优化。

所有 1 allocs/op 的 bench 都是逃逸(编译器判断 new 的变量有外部引用)。
不过没搞清楚你要比较的到底是哪两个。🤣

是内联优化,BenchmarkInterface 、BenchmarkInterface3 、BenchmarkInterface3 这三个都在内联优化完全被展开了,并没有两次函数调用的行为,所以性能很高,你这个测试是在 1.16 后面跑的吧,1.16 以前内联优化并没有去掉运行时类型检查,所以性能并没那么高

非常感谢,我再研究一下

在Golang(Go语言)中,interface.Method 的性能通常会比 switch type 差,这主要归因于接口的动态分派特性和Go语言的运行时机制。

  1. 动态分派:接口在Go中是一种抽象类型,它允许定义对象的行为而不需要了解对象的具体类型。当一个接口变量调用其方法时,Go运行时需要在运行时确定具体实现该方法的具体类型,这称为动态分派。这个过程涉及到额外的查找和跳转,从而增加了执行开销。

  2. 类型断言和类型转换:在使用 interface.Method 时,如果需要在接口类型和具体类型之间进行转换,会涉及到类型断言(type assertion)或类型转换(type conversion),这些操作同样需要运行时支持,并且可能引入性能损耗。

  3. switch type优化:相比之下,switch type 语句是编译时确定的,Go编译器可以对它进行优化,比如生成类型特定的代码路径,从而避免了运行时的类型查找和跳转。这种编译时优化使得 switch type 在执行时能够更高效地确定要执行的代码路径。

  4. 缓存和内联:编译器还可以对 switch type 进行内联优化,将相关代码直接嵌入到调用点,减少函数调用的开销。此外,现代处理器对分支预测的优化也能显著提高 switch type 的性能。

综上所述,由于接口的动态分派特性和运行时开销,interface.Method 的性能通常会比 switch type 差。然而,在需要高度抽象和灵活性的场景中,接口仍然是Go语言中不可或缺的工具。

回到顶部