基于Go泛型实现类C++ std::hash的哈希函数及性能对比
问题:用Go泛型实现类似C++ std::hash的哈希函数
我正在学习Go 1.18版本引入的泛型特性,希望实现一个可返回任意可哈希对象哈希值的函数,其理想行为与C++中的std::hash模板类似(例如std::hash<type>{}(x))。我已定义了Hashable接口:
type Hashable interface {int | string}
但不清楚后续如何推进。我需要为int、string等不同类型实现不同的哈希逻辑,且希望实现逻辑在编译时确定,以避免运行时类型检查的开销。请问是否可以通过Go泛型实现类似C++ STL中的这种哈希函数?
编辑补充:基准测试结果与泛型性能分析
我针对泛型方案做了基准测试,发现即使禁用类型推断,该方案的效率仍低于类型切换实现,推测Go泛型函数调用存在运行时开销。以下是测试代码及结果:
hash.go
package hash import ( "hash/fnv" ) type Hashable interface { int | string } func Hash(x any) int { switch v := x.(type) { case int: return v case string: return stringHash(v) default: panic("hash function of this type not implemented") } } func intHash(x int) int { return x } func stringHash(x string) int { h := fnv.New32a() h.Write([]byte(x)) return int(h.Sum32()) } type Hint int type Hstring string type Hasher interface { Hash() int } func (x Hint) Hash() int { return int(x) } func (x Hstring) Hash() int { h := fnv.New32a() h.Write([]byte(x)) return int(h.Sum32()) } type HashableAlias interface { Hint | Hstring Hasher } func GetHash[T HashableAlias](h T) int { return h.Hash() }
hash_test.go
package hash import ( "testing" ) func BenchmarkHashStringTypeSwitch(b *testing.B) { s := "func (this *LRUCache) Get(key int) int {" for i := 0; i < b.N; i++ { Hash(s) } } func BenchmarkHashString(b *testing.B) { s := "func (this *LRUCache) Get(key int) int {" for i := 0; i < b.N; i++ { stringHash(s) } } func BenchmarkHashIntTypeSwitch(b *testing.B) { n := 123456 for i := 0; i < b.N; i++ { Hash(n) } } func BenchmarkHashInt(b *testing.B) { n := 123456 for i := 0; i < b.N; i++ { intHash(n) } } func BenchmarkGetHashInt(b *testing.B) { n := 123456 for i := 0; i < b.N; i++ { GetHash(Hint(n)) } } func BenchmarkGetHashIntHalfConvert(b *testing.B) { var n Hint = 123456 for i := 0; i < b.N; i++ { GetHash(n) } } func BenchmarkGetHashIntMethod(b *testing.B) { n := 123456 for i := 0; i < b.N; i++ { Hint(n).Hash() } } func BenchmarkGetHashIntNoInfer(b *testing.B) { n := 123456 f := GetHash[Hint] for i := 0; i < b.N; i++ { f(Hint(n)) } } func BenchmarkGetHashString(b *testing.B) { s := "func (this *LRUCache) Get(key int) int {" for i := 0; i < b.N; i++ { GetHash(Hstring(s)) } }
基准测试输出(go version go1.19.4 darwin/arm64)
BenchmarkExtendibleHashTable-10 9066850 148.8 ns/op BenchmarkHashStringTypeSwitch-10 26299448 43.56 ns/op BenchmarkHashString-10 25259280 43.05 ns/op BenchmarkHashIntTypeSwitch-10 1000000000 0.6236 ns/op BenchmarkHashInt-10 1000000000 0.3114 ns/op BenchmarkGetHashInt-10 552110401 2.109 ns/op BenchmarkGetHashIntHalfConvert-10 576661430 2.125 ns/op BenchmarkGetHashIntMethod-10 1000000000 0.3118 ns/op BenchmarkGetHashIntNoInfer-10 560186821 2.119 ns/op BenchmarkGetHashString-10 26585163 44.29 ns/op
内容的提问来源于stack exchange,提问作者Zacchaeus
相关产品推荐
相关产品推荐

