diff options
author | Damian Gryski <damian@gryski.com> | 2014-06-17 17:09:50 +0200 |
---|---|---|
committer | Damian Gryski <damian@gryski.com> | 2014-06-17 17:09:50 +0200 |
commit | 89ec0544ae3d563c99516b9d17f90e3d9ca9439b (patch) | |
tree | 1c33d3c5e68b7faea0ae03dd7faba74c45fa9bab | |
parent | d781998583680cda80cf61e0b37dd0cd8da2eb52 (diff) | |
download | golang-groupcache-89ec0544ae3d563c99516b9d17f90e3d9ca9439b.tar.gz |
consistenthash: replace linear search with binary search
The binary search quickly out-paces the linear search, even for a small
number of shards and replicas.
benchmark old ns/op new ns/op delta
BenchmarkGet8 122 122 +0.00%
BenchmarkGet32 471 137 -70.91%
BenchmarkGet128 5619 254 -95.48%
BenchmarkGet512 90302 406 -99.55%
-rw-r--r-- | consistenthash/consistenthash.go | 14 | ||||
-rw-r--r-- | consistenthash/consistenthash_test.go | 24 |
2 files changed, 31 insertions, 7 deletions
diff --git a/consistenthash/consistenthash.go b/consistenthash/consistenthash.go index 455ffd5..a9c56f0 100644 --- a/consistenthash/consistenthash.go +++ b/consistenthash/consistenthash.go @@ -69,13 +69,13 @@ func (m *Map) Get(key string) string { hash := int(m.hash([]byte(key))) - // Linear search for appropriate replica. - for _, v := range m.keys { - if v >= hash { - return m.hashMap[v] - } - } + // Binary search for appropriate replica. + idx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash }) // Means we have cycled back to the first replica. - return m.hashMap[m.keys[0]] + if idx == len(m.keys) { + idx = 0 + } + + return m.hashMap[m.keys[idx]] } diff --git a/consistenthash/consistenthash_test.go b/consistenthash/consistenthash_test.go index a1b96db..f2b82d5 100644 --- a/consistenthash/consistenthash_test.go +++ b/consistenthash/consistenthash_test.go @@ -17,6 +17,7 @@ limitations under the License. package consistenthash import ( + "fmt" "strconv" "testing" ) @@ -84,3 +85,26 @@ func TestConsistency(t *testing.T) { } } + +func BenchmarkGet8(b *testing.B) { benchmarkGet(b, 8) } +func BenchmarkGet32(b *testing.B) { benchmarkGet(b, 32) } +func BenchmarkGet128(b *testing.B) { benchmarkGet(b, 128) } +func BenchmarkGet512(b *testing.B) { benchmarkGet(b, 512) } + +func benchmarkGet(b *testing.B, shards int) { + + hash := New(shards, nil) + + var buckets []string + for i := 0; i < shards; i++ { + buckets = append(buckets, fmt.Sprintf("shard-%d", i)) + } + + hash.Add(buckets...) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + hash.Get(buckets[i&(shards-1)]) + } +} |