请教一下,下面两个基准测试的结果为什么会不一样,我看到生成的汇编也有很大不同,原因是什么
- 从基准测试的结果来看,第一个的性能更好一些,第二个的结果和接口调用方法的一样,更慢一点。
- 从我对泛型的认识来看,基准测试的结果至少也是相似。
1.不使用类型约束的情况
~~~go
type lesser[T ~int | ~string] struct {
}
func (l lesser[T]) Less(a, b T) bool {
// 不同的类型可以有具体的实现
return a < b
}
// comp 改成具体的泛型类型
func testB[T ~int | ~string](b *testing.B, arr []T, comp lesser[T]) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = comp.Less(arr[0], arr[99999])
}
}
// 这样的结果要比上面快的多
func BenchmarkTestB(b *testing.B) {
// 模拟生成长度为10000000的随机数切片
arr := getTestArrSize(10000000)
testB(b, arr, lesser[int]{})
}
~~~
2.使用类型约束的情况
~~~go
type Comp[T any] interface {
Less(i, j T) bool
}
type lesser2[T ~int | ~string] struct {}
// 值类型和指针类型的接收器对结果没有实质性的影响
func (l *lesser2[T]) Less(a, b T) bool {
return a < b
}
//Comp用作类型约束,comp为符合Comp[T]的类型的参数
func testD[T ~int | ~string, C Comp[T]](b *testing.B, arr []T, comp C) {
// 打印的值和原类型的大小一样,从大小上来看comp不是接口
// comp 也没有发生逃逸
// 如果C类型是接口那为什么没有逃逸并且类型大小也和原来一样呢
// 从gcflag打印的结果来看也被内联了
// fmt.Println(unsafe.Sizeof(comp))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = comp.Less(arr[0], arr[99999])
}
}
// 这种测试的结果和直接使用接口调用差不多
// 但是理论上应该和第二种情况一样才对
func BenchmarkTestD(b *testing.B) {
arr := getTestArrSize(10000000)
l := lesser2[int]{}
testD(b, arr, &l)
}
~~~
- 测试结果
![结果.png](https://static.golangjob.cn/220414/8a529494fb97f464d49ee18e7f934372.png)
首先可以肯定的是,和泛型约束没关系
```golang
comp.Less
```
问题出在这个调用,很明显,前面的方法是编译直接优化,但是第二种是一个接口,至少需要一次指针跳转
#1
更多评论
明白了,非常感谢,我一直以为类型C是某个实现了SortAble的具体类型而不是一个接口
不过,如果C类型是接口那为什么没有逃逸并且类型大小也和原来一样?
#2