Currently map amortizes the growth over several inserts/deletes. This means that if we do several inserts to get map into a "growing" state and than never insert/delete anything and just iterate through map we get much higher cost of iteration, because in the "growing" state we are doing extra hashing (line 934 in map.go hash := t.hasher(k, uintptr(h.hash0)). Consider the following benchmark that adds X strings into a map and than iterates over them a lot:
//go:noinline
func iter(m map[string]string) int {
ret := 0
for _, a := range m {
ret += len(a)
}
return ret
}
func BenchmarkMap(b *testing.B) {
minSize := 50
maxSize := 60
for size := minSize; size < maxSize; size++ {
b.Run(fmt.Sprintf("input_size_%d", size), func(b *testing.B) {
m := make(map[string]string)
for i := 0; i < size; i++ {
key := make([]byte, 300)
value := make([]byte, 300)
rand.Read(key)
rand.Read(value)
m[string(key)] = string(value)
}
x := 0
for i := 0; i < b.N; i++ {
x += iter(m)
}
})
}
}
What did you expect to see?
Stable cost of iteration, growing linearly with the number of elements.
What did you see instead?
For some numbers of elements, performance is 4-5x worse:
Adding size to make fixed the issue, but this is still a cause of a significant and unexpected performance cliff. So growing map on iterations makes sense to avoid performance cliffs like this.
The text was updated successfully, but these errors were encountered:
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
Currently map amortizes the growth over several inserts/deletes. This means that if we do several inserts to get map into a "growing" state and than never insert/delete anything and just iterate through map we get much higher cost of iteration, because in the "growing" state we are doing extra hashing (line 934 in map.go
hash := t.hasher(k, uintptr(h.hash0))
. Consider the following benchmark that adds X strings into a map and than iterates over them a lot:What did you expect to see?
Stable cost of iteration, growing linearly with the number of elements.
What did you see instead?
For some numbers of elements, performance is 4-5x worse:
Adding size to make fixed the issue, but this is still a cause of a significant and unexpected performance cliff. So growing map on iterations makes sense to avoid performance cliffs like this.
The text was updated successfully, but these errors were encountered: