Commit a41b1d50 authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

runtime: convert hmap.overflow into hmap.extra

Any change to how we allocate overflow buckets
will require some extra hmap storage,
but we don't want hmap to grow,
particular as small maps usually don't need overflow buckets.

This CL converts the existing hmap overflow field,
which is usually used for pointer-free maps,
into a generic extra field.

This extra field can be used to hold data that is optional.
If it is valuable enough to do have special
handling of overflow buckets, which are medium-sized,
it is valuable enough to pay an extra alloc and two extra words for.

Adding fields to extra would entail adding overhead to pointer-free maps;
any mapextra fields added would need to be weighed against that.
This CL is just rearrangement, though.

Updates #19931
Updates #19992

Change-Id: If8537a206905b9d4dc6cd9d886184ece671b3f80
Reviewed-on: https://go-review.googlesource.com/40976
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarKeith Randall <khr@golang.org>
parent 619af172
...@@ -116,6 +116,11 @@ type hmap struct { ...@@ -116,6 +116,11 @@ type hmap struct {
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated) nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
// mapextra holds fields that are not present on all maps.
type mapextra struct {
// If both key and value do not contain pointers and are inline, then we mark bucket // If both key and value do not contain pointers and are inline, then we mark bucket
// type as containing no pointers. This avoids scanning such maps. // type as containing no pointers. This avoids scanning such maps.
// However, bmap.overflow is a pointer. In order to keep overflow buckets // However, bmap.overflow is a pointer. In order to keep overflow buckets
...@@ -123,9 +128,8 @@ type hmap struct { ...@@ -123,9 +128,8 @@ type hmap struct {
// Overflow is used only if key and value do not contain pointers. // Overflow is used only if key and value do not contain pointers.
// overflow[0] contains overflow buckets for hmap.buckets. // overflow[0] contains overflow buckets for hmap.buckets.
// overflow[1] contains overflow buckets for hmap.oldbuckets. // overflow[1] contains overflow buckets for hmap.oldbuckets.
// The first indirection allows us to reduce static size of hmap. // The indirection allows to store a pointer to the slice in hiter.
// The second indirection allows to store a pointer to the slice in hiter. overflow [2]*[]*bmap
overflow *[2]*[]*bmap
} }
// A bucket for a Go map. // A bucket for a Go map.
...@@ -201,18 +205,18 @@ func (h *hmap) newoverflow(t *maptype, b *bmap) *bmap { ...@@ -201,18 +205,18 @@ func (h *hmap) newoverflow(t *maptype, b *bmap) *bmap {
h.incrnoverflow() h.incrnoverflow()
if t.bucket.kind&kindNoPointers != 0 { if t.bucket.kind&kindNoPointers != 0 {
h.createOverflow() h.createOverflow()
*h.overflow[0] = append(*h.overflow[0], ovf) *h.extra.overflow[0] = append(*h.extra.overflow[0], ovf)
} }
*(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-sys.PtrSize)) = ovf *(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-sys.PtrSize)) = ovf
return ovf return ovf
} }
func (h *hmap) createOverflow() { func (h *hmap) createOverflow() {
if h.overflow == nil { if h.extra == nil {
h.overflow = new([2]*[]*bmap) h.extra = new(mapextra)
} }
if h.overflow[0] == nil { if h.extra.overflow[0] == nil {
h.overflow[0] = new([]*bmap) h.extra.overflow[0] = new([]*bmap)
} }
} }
...@@ -289,6 +293,7 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap { ...@@ -289,6 +293,7 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
} }
h.count = 0 h.count = 0
h.B = B h.B = B
h.extra = nil
h.flags = 0 h.flags = 0
h.hash0 = fastrand() h.hash0 = fastrand()
h.buckets = buckets h.buckets = buckets
...@@ -709,7 +714,7 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) { ...@@ -709,7 +714,7 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) {
// the table grows and/or overflow buckets are added to the table // the table grows and/or overflow buckets are added to the table
// while we are iterating. // while we are iterating.
h.createOverflow() h.createOverflow()
it.overflow = *h.overflow it.overflow = h.extra.overflow
} }
// decide where to start // decide where to start
...@@ -897,13 +902,13 @@ func hashGrow(t *maptype, h *hmap) { ...@@ -897,13 +902,13 @@ func hashGrow(t *maptype, h *hmap) {
h.nevacuate = 0 h.nevacuate = 0
h.noverflow = 0 h.noverflow = 0
if h.overflow != nil { if h.extra != nil && h.extra.overflow[0] != nil {
// Promote current overflow buckets to the old generation. // Promote current overflow buckets to the old generation.
if h.overflow[1] != nil { if h.extra.overflow[1] != nil {
throw("overflow is not nil") throw("overflow is not nil")
} }
h.overflow[1] = h.overflow[0] h.extra.overflow[1] = h.extra.overflow[0]
h.overflow[0] = nil h.extra.overflow[0] = nil
} }
// the actual copying of the hash table data is done incrementally // the actual copying of the hash table data is done incrementally
...@@ -1123,8 +1128,8 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) { ...@@ -1123,8 +1128,8 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
// Can discard old overflow buckets as well. // Can discard old overflow buckets as well.
// If they are still referenced by an iterator, // If they are still referenced by an iterator,
// then the iterator holds a pointers to the slice. // then the iterator holds a pointers to the slice.
if h.overflow != nil { if h.extra != nil {
h.overflow[1] = nil h.extra.overflow[1] = nil
} }
h.flags &^= sameSizeGrow h.flags &^= sameSizeGrow
} }
......
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