mirror of
https://github.com/golang/go
synced 2024-11-20 01:14:40 -07:00
runtime: allocate maps' first bucket table lazily
Motivated by garbage profiling in HTTP benchmarks. This changes means new empty maps are just one small allocation (the HMap) instead the HMap + the relatively larger h->buckets allocation. This helps maps which remain empty throughout their life. benchmark old ns/op new ns/op delta BenchmarkNewEmptyMap 196 107 -45.41% benchmark old allocs new allocs delta BenchmarkNewEmptyMap 2 1 -50.00% benchmark old bytes new bytes delta BenchmarkNewEmptyMap 195 50 -74.36% R=khr, golang-dev, r CC=golang-dev https://golang.org/cl/7722046
This commit is contained in:
parent
b735eeb323
commit
5b3ff61be6
@ -255,10 +255,15 @@ hash_init(MapType *t, Hmap *h, uint32 hint)
|
|||||||
// allocate initial hash table
|
// allocate initial hash table
|
||||||
// If hint is large zeroing this memory could take a while.
|
// If hint is large zeroing this memory could take a while.
|
||||||
if(checkgc) mstats.next_gc = mstats.heap_alloc;
|
if(checkgc) mstats.next_gc = mstats.heap_alloc;
|
||||||
buckets = runtime·mallocgc(bucketsize << B, 0, 1, 0);
|
if(B == 0) {
|
||||||
for(i = 0; i < (uintptr)1 << B; i++) {
|
// done lazily later.
|
||||||
b = (Bucket*)(buckets + i * bucketsize);
|
buckets = nil;
|
||||||
clearbucket(b);
|
} else {
|
||||||
|
buckets = runtime·mallocgc(bucketsize << B, 0, 1, 0);
|
||||||
|
for(i = 0; i < (uintptr)1 << B; i++) {
|
||||||
|
b = (Bucket*)(buckets + i * bucketsize);
|
||||||
|
clearbucket(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize Hmap
|
// initialize Hmap
|
||||||
@ -485,6 +490,8 @@ hash_lookup(MapType *t, Hmap *h, byte **keyp)
|
|||||||
key = *keyp;
|
key = *keyp;
|
||||||
if(docheck)
|
if(docheck)
|
||||||
check(t, h);
|
check(t, h);
|
||||||
|
if(h->count == 0)
|
||||||
|
return nil;
|
||||||
hash = h->hash0;
|
hash = h->hash0;
|
||||||
t->key->alg->hash(&hash, t->key->size, key);
|
t->key->alg->hash(&hash, t->key->size, key);
|
||||||
bucket = hash & (((uintptr)1 << h->B) - 1);
|
bucket = hash & (((uintptr)1 << h->B) - 1);
|
||||||
@ -572,6 +579,12 @@ hash_insert(MapType *t, Hmap *h, void *key, void *value)
|
|||||||
check(t, h);
|
check(t, h);
|
||||||
hash = h->hash0;
|
hash = h->hash0;
|
||||||
t->key->alg->hash(&hash, t->key->size, key);
|
t->key->alg->hash(&hash, t->key->size, key);
|
||||||
|
if(h->buckets == nil) {
|
||||||
|
h->buckets = runtime·mallocgc(h->bucketsize, 0, 1, 0);
|
||||||
|
b = (Bucket*)(h->buckets);
|
||||||
|
clearbucket(b);
|
||||||
|
}
|
||||||
|
|
||||||
again:
|
again:
|
||||||
bucket = hash & (((uintptr)1 << h->B) - 1);
|
bucket = hash & (((uintptr)1 << h->B) - 1);
|
||||||
if(h->oldbuckets != nil)
|
if(h->oldbuckets != nil)
|
||||||
@ -659,6 +672,8 @@ hash_remove(MapType *t, Hmap *h, void *key)
|
|||||||
|
|
||||||
if(docheck)
|
if(docheck)
|
||||||
check(t, h);
|
check(t, h);
|
||||||
|
if(h->count == 0)
|
||||||
|
return;
|
||||||
hash = h->hash0;
|
hash = h->hash0;
|
||||||
t->key->alg->hash(&hash, t->key->size, key);
|
t->key->alg->hash(&hash, t->key->size, key);
|
||||||
bucket = hash & (((uintptr)1 << h->B) - 1);
|
bucket = hash & (((uintptr)1 << h->B) - 1);
|
||||||
@ -749,6 +764,12 @@ hash_iter_init(MapType *t, Hmap *h, struct hash_iter *it)
|
|||||||
|
|
||||||
// Remember we have an iterator at this level.
|
// Remember we have an iterator at this level.
|
||||||
h->flags |= Iterator;
|
h->flags |= Iterator;
|
||||||
|
|
||||||
|
if(h->buckets == nil) {
|
||||||
|
// Empty map. Force next hash_next to exit without
|
||||||
|
// evalulating h->bucket.
|
||||||
|
it->wrapped = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializes it->key and it->value to the next key/value pair
|
// initializes it->key and it->value to the next key/value pair
|
||||||
@ -848,7 +869,7 @@ next:
|
|||||||
bool
|
bool
|
||||||
hash_gciter_init (Hmap *h, struct hash_gciter *it)
|
hash_gciter_init (Hmap *h, struct hash_gciter *it)
|
||||||
{
|
{
|
||||||
// GC during map initialization
|
// GC during map initialization or on an empty map.
|
||||||
if(h->buckets == nil)
|
if(h->buckets == nil)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -280,3 +280,10 @@ func TestEmptyKeyAndValue(t *testing.T) {
|
|||||||
t.Errorf("empty key returned wrong value")
|
t.Errorf("empty key returned wrong value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkNewEmptyMap(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = make(map[int]int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user