Skip to content

Commit 518ee55

Browse files
ShiKaiWibradfitz
authored andcommitted
expvar: improve Map.addKey for large number of keys
The existing implementation has poor performance for inserting large number of keys because it chooses to append the new key to the sorted keys array and then sort the new array again (time complexity is O(nlogn)). The improvement tries to utilize the sorted keys array by searching the index and doing an insertion if any (time complexity is O(logn)). Benchmarked on 4-core machine with `go test -cpu 1,2,4,8 -count 5 -benchmem -bench=.`(the equal results are omitted): name old time/op new time/op delta MapAddDifferentRandom-8 408µs ±11% 69µs ± 3% -82.95% (p=0.008 n=5+5) MapAddDifferentRandom-4 389µs ±19% 69µs ± 2% -82.28% (p=0.008 n=5+5) MapAddDifferentRandom-2 365µs ± 4% 75µs ± 6% -79.51% (p=0.008 n=5+5) MapSetDifferentRandom-8 365µs ± 4% 76µs ±40% -79.07% (p=0.008 n=5+5) MapAddDifferentRandom 366µs ± 3% 78µs ± 6% -78.66% (p=0.008 n=5+5) MapSetDifferentRandom 369µs ± 2% 81µs ±34% -77.99% (p=0.008 n=5+5) MapSetDifferentRandom-2 378µs ±10% 100µs ±32% -73.47% (p=0.008 n=5+5) MapSetDifferentRandom-4 352µs ± 4% 108µs ± 7% -69.40% (p=0.008 n=5+5) IntAdd-2 23.1ns ±21% 15.5ns ±23% -32.79% (p=0.032 n=5+5) IntSet-2 21.4ns ±14% 16.7ns ±17% -22.00% (p=0.016 n=5+5) FloatAdd-8 88.8ns ± 9% 70.8ns ±25% -20.23% (p=0.024 n=5+5) FloatSet-2 22.3ns ±15% 17.8ns ±14% -20.14% (p=0.008 n=5+5) IntAdd-8 21.7ns ± 3% 18.7ns ± 4% -14.00% (p=0.008 n=5+5) MapAddDifferent-8 1.58µs ± 7% 1.42µs ± 6% -10.06% (p=0.016 n=5+5) StringSet-2 42.4ns ± 1% 43.7ns ± 5% +3.07% (p=0.048 n=4+5) FloatSet 8.27ns ± 2% 8.60ns ± 1% +3.94% (p=0.008 n=5+5) FloatAdd 12.5ns ± 2% 13.0ns ± 4% +4.33% (p=0.032 n=5+5) MapSetString-4 94.6ns ± 0% 101.7ns ± 4% +7.55% (p=0.016 n=4+5) MapAddSameSteadyState-2 34.9ns ± 3% 37.7ns ± 5% +8.14% (p=0.008 n=5+5) StringSet-4 34.5ns ± 0% 37.6ns ± 9% +9.02% (p=0.016 n=4+5) MapSetDifferent-8 377ns ± 3% 411ns ± 7% +9.07% (p=0.008 n=5+5) MapAddSameSteadyState 39.1ns ± 2% 42.8ns ± 6% +9.36% (p=0.008 n=5+5) MapAddDifferentSteadyState 172ns ± 4% 190ns ± 9% +10.96% (p=0.016 n=5+5) MapSet 143ns ± 4% 159ns ± 2% +11.06% (p=0.008 n=5+5) MapSet-4 96.9ns ± 5% 107.8ns ± 6% +11.25% (p=0.008 n=5+5) MapSet-2 102ns ± 6% 114ns ± 8% +11.94% (p=0.008 n=5+5) IntSet 8.18ns ± 1% 12.78ns ±13% +56.31% (p=0.008 n=5+5) name old alloc/op new alloc/op delta MapSetDifferentRandom-4 19.8kB ± 0% 16.6kB ± 0% -16.21% (p=0.008 n=5+5) MapSetDifferentRandom 19.8kB ± 0% 16.6kB ± 0% -16.21% (p=0.008 n=5+5) MapSetDifferentRandom-8 19.8kB ± 0% 16.6kB ± 0% -16.20% (p=0.008 n=5+5) MapSetDifferentRandom-2 19.8kB ± 0% 16.6kB ± 0% -16.20% (p=0.008 n=5+5) MapAddDifferentRandom 20.6kB ± 0% 17.4kB ± 0% -15.57% (p=0.008 n=5+5) MapAddDifferentRandom-8 20.6kB ± 0% 17.4kB ± 0% -15.57% (p=0.008 n=5+5) MapAddDifferentRandom-2 20.6kB ± 0% 17.4kB ± 0% -15.56% (p=0.008 n=5+5) MapAddDifferentRandom-4 20.6kB ± 0% 17.4kB ± 0% -15.56% (p=0.008 n=5+5) MapAddDifferent 1.09kB ± 0% 0.96kB ± 0% -11.76% (p=0.008 n=5+5) MapAddDifferent-2 1.09kB ± 0% 0.96kB ± 0% -11.76% (p=0.008 n=5+5) MapAddDifferent-4 1.09kB ± 0% 0.96kB ± 0% -11.76% (p=0.008 n=5+5) MapAddDifferent-8 1.09kB ± 0% 0.96kB ± 0% -11.76% (p=0.008 n=5+5) MapAddSame 480B ± 0% 448B ± 0% -6.67% (p=0.008 n=5+5) MapAddSame-2 480B ± 0% 448B ± 0% -6.67% (p=0.008 n=5+5) MapAddSame-4 480B ± 0% 448B ± 0% -6.67% (p=0.008 n=5+5) MapAddSame-8 480B ± 0% 448B ± 0% -6.67% (p=0.008 n=5+5) name old allocs/op new allocs/op delta MapSetDifferentRandom 423 ± 0% 323 ± 0% -23.64% (p=0.008 n=5+5) MapSetDifferentRandom-2 423 ± 0% 323 ± 0% -23.64% (p=0.008 n=5+5) MapSetDifferentRandom-4 423 ± 0% 323 ± 0% -23.64% (p=0.008 n=5+5) MapSetDifferentRandom-8 423 ± 0% 323 ± 0% -23.64% (p=0.008 n=5+5) MapAddDifferentRandom 523 ± 0% 423 ± 0% -19.12% (p=0.008 n=5+5) MapAddDifferentRandom-2 523 ± 0% 423 ± 0% -19.12% (p=0.008 n=5+5) MapAddDifferentRandom-4 523 ± 0% 423 ± 0% -19.12% (p=0.008 n=5+5) MapAddDifferentRandom-8 523 ± 0% 423 ± 0% -19.12% (p=0.008 n=5+5) MapAddDifferent 31.0 ± 0% 27.0 ± 0% -12.90% (p=0.008 n=5+5) MapAddDifferent-2 31.0 ± 0% 27.0 ± 0% -12.90% (p=0.008 n=5+5) MapAddDifferent-4 31.0 ± 0% 27.0 ± 0% -12.90% (p=0.008 n=5+5) MapAddDifferent-8 31.0 ± 0% 27.0 ± 0% -12.90% (p=0.008 n=5+5) MapAddSame 11.0 ± 0% 10.0 ± 0% -9.09% (p=0.008 n=5+5) MapAddSame-2 11.0 ± 0% 10.0 ± 0% -9.09% (p=0.008 n=5+5) MapAddSame-4 11.0 ± 0% 10.0 ± 0% -9.09% (p=0.008 n=5+5) MapAddSame-8 11.0 ± 0% 10.0 ± 0% -9.09% (p=0.008 n=5+5) Fixes #31414 Change-Id: I2dd655dec9dbbf8d7881a0f3f8edcb3d29199a48 GitHub-Last-Rev: a816fe3 GitHub-Pull-Request: #31418 Reviewed-on: https://go-review.googlesource.com/c/go/+/171718 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 2a931ba commit 518ee55

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

src/expvar/expvar.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,14 @@ func (v *Map) Init() *Map {
141141
func (v *Map) addKey(key string) {
142142
v.keysMu.Lock()
143143
defer v.keysMu.Unlock()
144-
v.keys = append(v.keys, key)
145-
sort.Strings(v.keys)
144+
// Using insertion sort to place key into the already-sorted v.keys.
145+
if i := sort.SearchStrings(v.keys, key); i >= len(v.keys) {
146+
v.keys = append(v.keys, key)
147+
} else if v.keys[i] != key {
148+
v.keys = append(v.keys, "")
149+
copy(v.keys[i+1:], v.keys[i:])
150+
v.keys[i] = key
151+
}
146152
}
147153

148154
func (v *Map) Get(key string) Var {

src/expvar/expvar_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package expvar
66

77
import (
88
"bytes"
9+
"crypto/sha1"
910
"encoding/json"
1011
"fmt"
1112
"net"
@@ -299,6 +300,26 @@ func BenchmarkMapSetDifferent(b *testing.B) {
299300
})
300301
}
301302

303+
// BenchmarkMapSetDifferentRandom simulates such a case where the concerned
304+
// keys of Map.Set are generated dynamically and as a result insertion is
305+
// out of order and the number of the keys may be large.
306+
func BenchmarkMapSetDifferentRandom(b *testing.B) {
307+
keys := make([]string, 100)
308+
for i := range keys {
309+
keys[i] = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprint(i))))
310+
}
311+
312+
v := new(Int)
313+
b.ResetTimer()
314+
315+
for i := 0; i < b.N; i++ {
316+
m := new(Map).Init()
317+
for _, k := range keys {
318+
m.Set(k, v)
319+
}
320+
}
321+
}
322+
302323
func BenchmarkMapSetString(b *testing.B) {
303324
m := new(Map).Init()
304325

@@ -350,6 +371,25 @@ func BenchmarkMapAddDifferent(b *testing.B) {
350371
})
351372
}
352373

374+
// BenchmarkMapAddDifferentRandom simulates such a case where that the concerned
375+
// keys of Map.Add are generated dynamically and as a result insertion is out of
376+
// order and the number of the keys may be large.
377+
func BenchmarkMapAddDifferentRandom(b *testing.B) {
378+
keys := make([]string, 100)
379+
for i := range keys {
380+
keys[i] = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprint(i))))
381+
}
382+
383+
b.ResetTimer()
384+
385+
for i := 0; i < b.N; i++ {
386+
m := new(Map).Init()
387+
for _, k := range keys {
388+
m.Add(k, 1)
389+
}
390+
}
391+
}
392+
353393
func BenchmarkMapAddSameSteadyState(b *testing.B) {
354394
m := new(Map).Init()
355395
b.RunParallel(func(pb *testing.PB) {

0 commit comments

Comments
 (0)