hashset 是一種很是高效的數據結構,插入和查詢的複雜度都是 O(1),基本上能知足大部分場景的性能需求,但在一些特殊的場景下,頻次很是高的調用依然會成爲性能瓶頸(用 pprof 分析),好比廣告裏面的定向邏輯,在一次請求中過濾邏輯可能會執行上千次,而其中有些過濾恰好都是一些枚舉值,好比性別定向,年齡定向等等,對於這種能夠用枚舉表示的值能夠用 bitset 優化,能有20多倍的性能提高git
bitset 的本質也是一種 hashset,只不過哈希桶用一個 uint64 來表示了,uint64 中的每一位用來表明一個元素是否存在,若是爲1表示存在,爲0表示不存在,而插入和查詢操做就變成了位運算github
bitset 的實現比較容易,下面這個是一個只支持枚舉值不超過64的版本,固然也能夠拓展到任意長度,使用一個 uint64 數組做爲 hash 桶便可golang
type BitSet struct { bit uint64 } func (bs *BitSet) Add(i uint64) { bs.bit |= 1 << i } func (bs *BitSet) Del(i uint64) { bs.bit &= ^(1 << i) } func (bs BitSet) Has(i uint64) bool { return bs.bit&(1<<i) != 0 }
func BenchmarkSetContains(b *testing.B) { bitset := NewBitSet() hashset := map[uint64]struct{}{} for _, i := range []uint64{1, 2, 4, 10} { bitset.Add(i) hashset[i] = struct{}{} } b.Run("bitset", func(b *testing.B) { for i := 0; i < b.N; i++ { for i := uint64(0); i < uint64(10); i++ { _ = bitset.Has(i) } } }) b.Run("hashset", func(b *testing.B) { for i := 0; i < b.N; i++ { for i := uint64(0); i < uint64(10); i++ { _, _ = hashset[i] } } }) }
BenchmarkSetContains/bitset-8 500000000 3.81 ns/op 0 B/op 0 allocs/op BenchmarkSetContains/hashset-8 20000000 89.4 ns/op 0 B/op 0 allocs/op
能夠看到 bitset 相比 hashset 有20多倍的性能提高數組
轉載請註明出處
本文連接:http://www.hatlonely.com/2018/04/12/golang-性能優化之-bitset-代替-set/性能優化