]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: cut struct Hmap back to 48-byte allocation
authorRuss Cox <rsc@golang.org>
Wed, 31 Jul 2013 02:48:03 +0000 (22:48 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 31 Jul 2013 02:48:03 +0000 (22:48 -0400)
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

src/pkg/runtime/hashmap.c
src/pkg/runtime/map_test.go

index 898404cba8147bcaf6fb5568024e8337160b2bc8..b4f940e33544e8f9fa443da4def86428489049e6 100644 (file)
@@ -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)
index 9f9c40d156335fe06b905a38f37c330cc252527a..8fd6dc646376ebf02c73850efcd64e74145546a1 100644 (file)
@@ -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))
+}