Commit 4042b777 authored by Russ Cox's avatar Russ Cox

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
parent 3abaf5ca
...@@ -96,12 +96,12 @@ struct Hmap ...@@ -96,12 +96,12 @@ struct Hmap
{ {
uintgo count; // # live cells == size of map. Must be first (used by len() builtin) uintgo count; // # live cells == size of map. Must be first (used by len() builtin)
uint32 flags; uint32 flags;
uint32 hash0; // hash seed
uint8 B; // log_2 of # of buckets (can hold up to LOAD * 2^B items) uint8 B; // log_2 of # of buckets (can hold up to LOAD * 2^B items)
uint8 keysize; // key size in bytes uint8 keysize; // key size in bytes
uint8 valuesize; // value size in bytes uint8 valuesize; // value size in bytes
uint16 bucketsize; // bucket 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 *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 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) uintptr nevacuate; // progress counter for evacuation (buckets less than this have been evacuated)
......
...@@ -371,3 +371,41 @@ func testMapLookups(t *testing.T, m map[string]string) { ...@@ -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))
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment