• Josh Bleecher Snyder's avatar
    runtime: preallocate some overflow buckets · 94e44a9c
    Josh Bleecher Snyder authored
    When allocating a non-small array of buckets for a map,
    also preallocate some overflow buckets.
    
    The estimate of the number of overflow buckets
    is based on a simulation of putting mid=(low+high)/2 elements
    into a map, where low is the minimum number of elements
    needed to reach this value of b (according to overLoadFactor),
    and high is the maximum number of elements possible
    to put in this value of b (according to overLoadFactor).
    This estimate is surprisingly reliable and accurate.
    
    The number of overflow buckets needed is quadratic,
    for a fixed value of b.
    Using this mid estimate means that we will overallocate a few
    too many overflow buckets when the actual number of elements is near low,
    and underallocate significantly too few overflow buckets
    when the actual number of elements is near high.
    
    The mechanism introduced in this CL can be re-used for
    other overflow bucket optimizations.
    
    For example, given an initial size hint,
    we could estimate quite precisely the number of overflow buckets.
    This is #19931.
    
    We could also change from "non-nil means end-of-list"
    to "pointer-to-hmap.buckets means end-of-list",
    and then create a linked list of reusable overflow buckets
    when they are freed by map growth.
    That is #19992.
    
    We could also use a similar mechanism to do bulk allocation
    of overflow buckets.
    All these uses can co-exist with only the one additional pointer
    in mapextra, given a little care.
    
    name                  old time/op    new time/op    delta
    MapPopulate/1-8         60.1ns ± 2%    60.3ns ± 2%     ~     (p=0.278 n=19+20)
    MapPopulate/10-8         577ns ± 1%     578ns ± 1%     ~     (p=0.140 n=20+20)
    MapPopulate/100-8       8.06µs ± 1%    8.19µs ± 1%   +1.67%  (p=0.000 n=20+20)
    MapPopulate/1000-8       104µs ± 1%     104µs ± 1%     ~     (p=0.317 n=20+20)
    MapPopulate/10000-8      891µs ± 1%     888µs ± 1%     ~     (p=0.101 n=19+20)
    MapPopulate/100000-8    8.61ms ± 1%    8.58ms ± 0%   -0.34%  (p=0.009 n=20+17)
    
    name                  old alloc/op   new alloc/op   delta
    MapPopulate/1-8          0.00B          0.00B          ~     (all equal)
    MapPopulate/10-8          179B ± 0%      179B ± 0%     ~     (all equal)
    MapPopulate/100-8       3.33kB ± 0%    3.38kB ± 0%   +1.48%  (p=0.000 n=20+16)
    MapPopulate/1000-8      55.5kB ± 0%    53.4kB ± 0%   -3.84%  (p=0.000 n=19+20)
    MapPopulate/10000-8      432kB ± 0%     428kB ± 0%   -1.06%  (p=0.000 n=19+20)
    MapPopulate/100000-8    3.65MB ± 0%    3.62MB ± 0%   -0.70%  (p=0.000 n=20+20)
    
    name                  old allocs/op  new allocs/op  delta
    MapPopulate/1-8           0.00           0.00          ~     (all equal)
    MapPopulate/10-8          1.00 ± 0%      1.00 ± 0%     ~     (all equal)
    MapPopulate/100-8         18.0 ± 0%      17.0 ± 0%   -5.56%  (p=0.000 n=20+20)
    MapPopulate/1000-8        96.0 ± 0%      72.6 ± 1%  -24.38%  (p=0.000 n=20+20)
    MapPopulate/10000-8        625 ± 0%       319 ± 0%  -48.86%  (p=0.000 n=20+20)
    MapPopulate/100000-8     6.23k ± 0%     4.00k ± 0%  -35.79%  (p=0.000 n=20+20)
    
    Change-Id: I01f41cb1374bdb99ccedbc00d04fb9ae43daa204
    Reviewed-on: https://go-review.googlesource.com/40979
    Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
    TryBot-Result: Gobot Gobot <gobot@golang.org>
    Reviewed-by: 's avatarKeith Randall <khr@golang.org>
    94e44a9c
hashmap.go 38.6 KB