runtime: preallocate maps to size hint actually works

Этот коммит содержится в:
Damian Gryski 2023-03-26 17:28:00 -07:00 коммит произвёл Ron Evans
родитель 464ebc4fe1
коммит 5fee3428bc

Просмотреть файл

@ -65,12 +65,11 @@ func hashmapTopHash(hash uint32) uint8 {
// Create a new hashmap with the given keySize and valueSize. // Create a new hashmap with the given keySize and valueSize.
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashmap { func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashmap {
numBuckets := sizeHint / 8
bucketBits := uint8(0) bucketBits := uint8(0)
for numBuckets != 0 { for hashmapOverLoadFactor(sizeHint, bucketBits) {
numBuckets /= 2
bucketBits++ bucketBits++
} }
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + keySize*8 + valueSize*8 bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + keySize*8 + valueSize*8
buckets := alloc(bucketBufSize*(1<<bucketBits), nil) buckets := alloc(bucketBufSize*(1<<bucketBits), nil)
@ -120,14 +119,14 @@ func hashmapKeyHashAlg(alg hashmapAlgorithm) func(key unsafe.Pointer, n, seed ui
} }
} }
func hashmapShouldGrow(m *hashmap) bool { func hashmapHasSpaceToGrow(m *hashmap) bool {
if m.bucketBits > uint8((unsafe.Sizeof(uintptr(0))*8)-3) { // Over this limit, we're likely to overflow uintptrs during calculations
// Over this limit, we're likely to overflow uintptrs during calculations // or numbers of hash elements. Don't allow any more growth.
// or numbers of hash elements. Don't allow any more growth. // With 29 bits, this is 2^32 elements anyway.
// With 29 bits, this is 2^32 elements anyway. return m.bucketBits <= uint8((unsafe.Sizeof(uintptr(0))*8)-3)
return false }
}
func hashmapOverLoadFactor(n uintptr, bucketBits uint8) bool {
// "maximum" number of elements is 0.75 * buckets * elements per bucket // "maximum" number of elements is 0.75 * buckets * elements per bucket
// to avoid overflow, this is calculated as // to avoid overflow, this is calculated as
// max = 3 * (1/4 * buckets * elements per bucket) // max = 3 * (1/4 * buckets * elements per bucket)
@ -135,8 +134,8 @@ func hashmapShouldGrow(m *hashmap) bool {
// = 3 * (buckets * (8/4) // = 3 * (buckets * (8/4)
// = 3 * (buckets * 2) // = 3 * (buckets * 2)
// = 6 * buckets // = 6 * buckets
max := (uintptr(6) << m.bucketBits) max := (uintptr(6) << bucketBits)
return m.count > max return n > max
} }
// Return the number of entries in this hashmap, called from the len builtin. // Return the number of entries in this hashmap, called from the len builtin.
@ -158,7 +157,7 @@ func hashmapLenUnsafePointer(m unsafe.Pointer) int {
// //
//go:nobounds //go:nobounds
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32) { func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32) {
if hashmapShouldGrow(m) { if hashmapHasSpaceToGrow(m) && hashmapOverLoadFactor(m.count, m.bucketBits) {
hashmapGrow(m) hashmapGrow(m)
// seed changed when we grew; rehash key with new seed // seed changed when we grew; rehash key with new seed
hash = m.keyHash(key, m.keySize, m.seed) hash = m.keyHash(key, m.keySize, m.seed)