Golang 1.19版本中泛型函数性能略微下降的原因是什么

Golang 1.19版本中泛型函数性能略微下降的原因是什么 代码

package main

import (
	"testing"
)

func Add[T int](a, b T) T {
	return a + b
}

func AddInterface(a, b interface{}) interface{} {
	return a.(int) + b.(int)
}

func AddInt(i, j int) int {
	return i + j
}

func BenchmarkAddGeneric(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_ = Add[int](i, i+1)
	}
}

func BenchmarkAddInterface(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_ = AddInterface(i, i+1)
	}
}

func BenchmarkAddInt(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_ = AddInt(i, i+1)
	}
}

go version go1.18.9 darwin/arm64

BenchmarkAddGeneric-10      	1000000000	         0.3187 ns/op
BenchmarkAddInterface-10    	1000000000	         0.3146 ns/op 
BenchmarkAddInt-10          	1000000000	         0.3162 ns/op

go version go1.19.9 darwin/arm64

BenchmarkAddGeneric-10      	1000000000	         0.9443 ns/op
BenchmarkAddInterface-10    	1000000000	         0.3174 ns/op
BenchmarkAddInt-10          	1000000000	         0.3143 ns/op

go version go1.20.5 darwin/arm64

BenchmarkAddGeneric-10      	1000000000	         0.3164 ns/op
BenchmarkAddInterface-10    	1000000000	         0.3152 ns/op
BenchmarkAddInt-10          	1000000000	         0.3162 ns/op

更多关于Golang 1.19版本中泛型函数性能略微下降的原因是什么的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

你好 @Ryo

我可以复现你的结果,但我无法解释原因。我推测这是运行时或编译器中的一个错误,该错误在 1.19.9 和 1.20.5 版本之间得到了修复。

更多关于Golang 1.19版本中泛型函数性能略微下降的原因是什么的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go 1.19版本中,泛型函数性能下降的主要原因是编译器对泛型代码生成的中间表示(IR)进行了重构,特别是在类型参数处理方面引入了额外的间接调用开销。这个变化在Go 1.20中通过优化得到了修复。

具体来说,Go 1.19在编译泛型函数时,对于简单的类型参数(如int)会生成额外的类型检查代码,导致函数调用路径变长。以下是示例代码的基准测试结果分析:

// Go 1.19中泛型函数编译后的近似伪代码表示
func Add[int](a, b int) int {
    // 1.19版本会增加类型描述符检查
    typeDesc := getTypeDesc[int]()
    if typeDesc != intTypeDesc {
        panic("type mismatch")
    }
    return a + b
}

相比之下,非泛型函数直接编译为机器码:

// 非泛型函数的直接编译
func AddInt(i, j int) int {
    return i + j  // 直接对应ADD指令
}

在Go 1.20中,编译器团队优化了泛型函数的代码生成,特别是对于具体类型实例化的泛型函数,会直接生成特化版本:

// Go 1.20优化后的编译策略
func Add_int(a, b int) int {  // 直接生成特化版本
    return a + b
}

可以通过查看生成的汇编代码来验证这个差异:

# 查看泛型函数的汇编输出
go build -gcflags="-S" main.go 2>&1 | grep -A 10 "main.Add"

在实际应用中,如果遇到性能敏感的泛型代码,可以考虑以下编码方式:

// 对于性能关键的泛型函数,可以使用类型断言优化
func FastAdd[T int | int32 | int64](a, b T) T {
    switch any(a).(type) {
    case int:
        return T(any(a).(int) + any(b).(int))
    case int32:
        return T(any(a).(int32) + any(b).(int32))
    case int64:
        return T(any(a).(int64) + any(b).(int64))
    default:
        return a + b
    }
}

这个性能问题在Go 1.19.1之后的版本中已经部分修复,在Go 1.20中完全解决。对于生产环境,建议升级到Go 1.20或更高版本以获得最佳的泛型性能。

回到顶部