1
0
mirror of https://github.com/golang/go synced 2024-11-19 14:54:43 -07:00

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: Keith Randall <khr@golang.org>
This commit is contained in:
Josh Bleecher Snyder 2017-04-12 14:42:24 -07:00
parent 619af17205
commit a41b1d5052

View File

@ -116,6 +116,11 @@ type hmap struct {
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)
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
// type as containing no pointers. This avoids scanning such maps.
// However, bmap.overflow is a pointer. In order to keep overflow buckets
@ -123,9 +128,8 @@ type hmap struct {
// Overflow is used only if key and value do not contain pointers.
// overflow[0] contains overflow buckets for hmap.buckets.
// overflow[1] contains overflow buckets for hmap.oldbuckets.
// The first indirection allows us to reduce static size of hmap.
// The second indirection allows to store a pointer to the slice in hiter.
overflow *[2]*[]*bmap
// The indirection allows to store a pointer to the slice in hiter.
overflow [2]*[]*bmap
}
// A bucket for a Go map.
@ -201,18 +205,18 @@ func (h *hmap) newoverflow(t *maptype, b *bmap) *bmap {
h.incrnoverflow()
if t.bucket.kind&kindNoPointers != 0 {
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
return ovf
}
func (h *hmap) createOverflow() {
if h.overflow == nil {
h.overflow = new([2]*[]*bmap)
if h.extra == nil {
h.extra = new(mapextra)
}
if h.overflow[0] == nil {
h.overflow[0] = new([]*bmap)
if h.extra.overflow[0] == nil {
h.extra.overflow[0] = new([]*bmap)
}
}
@ -289,6 +293,7 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
}
h.count = 0
h.B = B
h.extra = nil
h.flags = 0
h.hash0 = fastrand()
h.buckets = buckets
@ -709,7 +714,7 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) {
// the table grows and/or overflow buckets are added to the table
// while we are iterating.
h.createOverflow()
it.overflow = *h.overflow
it.overflow = h.extra.overflow
}
// decide where to start
@ -897,13 +902,13 @@ func hashGrow(t *maptype, h *hmap) {
h.nevacuate = 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.
if h.overflow[1] != nil {
if h.extra.overflow[1] != nil {
throw("overflow is not nil")
}
h.overflow[1] = h.overflow[0]
h.overflow[0] = nil
h.extra.overflow[1] = h.extra.overflow[0]
h.extra.overflow[0] = nil
}
// the actual copying of the hash table data is done incrementally
@ -1123,8 +1128,8 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
// Can discard old overflow buckets as well.
// If they are still referenced by an iterator,
// then the iterator holds a pointers to the slice.
if h.overflow != nil {
h.overflow[1] = nil
if h.extra != nil {
h.extra.overflow[1] = nil
}
h.flags &^= sameSizeGrow
}