From dbe3522c7f45771bbd12228b7f17a3fc5ac9d7c7 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Fri, 1 Sep 2017 12:32:38 -0700 Subject: [PATCH] runtime: fix hashmap load factor computation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit overLoadFactor wasn't really doing what it says it does. It was reporting overOrEqualToLoadFactor. That's actually what we want when adding an entry to a map, but it isn't what we want when constructing a map in the first place. The impetus for this change is that if you make a map with a hint of exactly 8 (which happens, for example, with the unitMap in time/format.go), we allocate 2 buckets for it instead of 1. Instead, make overLoadFactor really report when it is > the max allowed load factor, not >=. Adjust the callers who want to ensure that the map is no more than the max load factor after an insertion by adding a +1 to the current (pre-addition) size. Change-Id: Ie8d85344800a9a870036b637b1031ddd9e4b93f9 Reviewed-on: https://go-review.googlesource.com/61053 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: Martin Möhrmann --- src/runtime/export_test.go | 5 +++++ src/runtime/hashmap.go | 6 +++--- src/runtime/hashmap_fast.go | 6 +++--- src/runtime/map_test.go | 29 +++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index b99ee83e3e..8b061e0a82 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -376,3 +376,8 @@ func (rw *RWMutex) Lock() { func (rw *RWMutex) Unlock() { rw.rw.unlock() } + +func MapBuckets(m map[int]int) int { + h := *(**hmap)(unsafe.Pointer(&m)) + return 1 << h.B +} diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index 37bf6e0aeb..cbb1f0defc 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -573,7 +573,7 @@ again: // If we hit the max load factor or we have too many overflow buckets, // and we're not already in the middle of growing, start growing. - if !h.growing() && (overLoadFactor(h.count, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { + if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { hashGrow(t, h) goto again // Growing the table invalidates everything, so try again } @@ -904,7 +904,7 @@ func hashGrow(t *maptype, h *hmap) { // Otherwise, there are too many overflow buckets, // so keep the same number of buckets and "grow" laterally. bigger := uint8(1) - if !overLoadFactor(h.count, h.B) { + if !overLoadFactor(h.count+1, h.B) { bigger = 0 h.flags |= sameSizeGrow } @@ -944,7 +944,7 @@ func hashGrow(t *maptype, h *hmap) { // overLoadFactor reports whether count items placed in 1<= bucketCnt && uintptr(count) >= loadFactorNum*(bucketShift(B)/loadFactorDen) + return count > bucketCnt && uintptr(count) > loadFactorNum*(bucketShift(B)/loadFactorDen) } // tooManyOverflowBuckets reports whether noverflow buckets is too many for a map with 1<