From: Russ Cox Date: Wed, 31 Jul 2013 02:48:03 +0000 (-0400) Subject: runtime: cut struct Hmap back to 48-byte allocation X-Git-Tag: go1.2rc2~885 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=4042b77776fe59c8cff23849745fe9e17146fa66;p=gostls13.git runtime: cut struct Hmap back to 48-byte allocation struct Hmap is the header for a map value. CL 8377046 made flags a uint32 so that it could be updated atomically, but that bumped the struct to 56 bytes, which allocates as 64 bytes (on amd64). hash0 is initialized from runtime.fastrand1, which returns a uint32, so the top 32 bits were always zero anyway. Declare it as a uint32 to reclaim 4 bytes and bring the Hmap size back down to a 48-byte allocation. Fixes #5237. R=golang-dev, khr, khr CC=bradfitz, dvyukov, golang-dev https://golang.org/cl/12034047 --- diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c index 898404cba8..b4f940e335 100644 --- a/src/pkg/runtime/hashmap.c +++ b/src/pkg/runtime/hashmap.c @@ -96,12 +96,12 @@ struct Hmap { uintgo count; // # live cells == size of map. Must be first (used by len() builtin) uint32 flags; + uint32 hash0; // hash seed uint8 B; // log_2 of # of buckets (can hold up to LOAD * 2^B items) uint8 keysize; // key size in bytes uint8 valuesize; // value size in bytes uint16 bucketsize; // bucket size in bytes - uintptr hash0; // hash seed byte *buckets; // array of 2^B Buckets. may be nil if count==0. byte *oldbuckets; // previous bucket array of half the size, non-nil only when growing uintptr nevacuate; // progress counter for evacuation (buckets less than this have been evacuated) diff --git a/src/pkg/runtime/map_test.go b/src/pkg/runtime/map_test.go index 9f9c40d156..8fd6dc6463 100644 --- a/src/pkg/runtime/map_test.go +++ b/src/pkg/runtime/map_test.go @@ -371,3 +371,41 @@ func testMapLookups(t *testing.T, m map[string]string) { } } } + +func TestMapSize(t *testing.T) { + var m map[struct{}]struct{} + size := bytesPerRun(100, func() { + m = make(map[struct{}]struct{}) + }) + if size > 48 { + t.Errorf("size = %v; want <= 48", size) + } +} + +// like testing.AllocsPerRun, but for bytes of memory, not number of allocations. +func bytesPerRun(runs int, f func()) (avg float64) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + + // Warm up the function + f() + + // Measure the starting statistics + var memstats runtime.MemStats + runtime.ReadMemStats(&memstats) + sum := 0 - memstats.Alloc + + // Run the function the specified number of times + for i := 0; i < runs; i++ { + f() + } + + // Read the final statistics + runtime.ReadMemStats(&memstats) + sum += memstats.Alloc + + // Average the mallocs over the runs (not counting the warm-up). + // We are forced to return a float64 because the API is silly, but do + // the division as integers so we can ask if AllocsPerRun()==1 + // instead of AllocsPerRun()<2. + return float64(sum / uint64(runs)) +}