1
0
mirror of https://github.com/golang/go synced 2024-11-25 22:37:59 -07:00

internal/runtime/maps: don't copy indirect key/elem when growing maps

We can reuse the same indirect storage when growing, so we don't
need an additional allocation.

Change-Id: I57adb406becfbec648188ec66f4bb2e94d4b9cab
Reviewed-on: https://go-review.googlesource.com/c/go/+/625902
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Keith Randall <khr@google.com>
This commit is contained in:
Keith Randall 2024-11-08 17:17:15 -08:00
parent bcdaac6396
commit d4b0bd28ee
2 changed files with 21 additions and 36 deletions

View File

@ -621,13 +621,7 @@ func (m *Map) growToTable(typ *abi.SwissMapType) {
hash := typ.Hasher(key, m.seed) hash := typ.Hasher(key, m.seed)
// TODO(prattmic): For indirect key/elem, this is tab.uncheckedPutSlot(typ, hash, key, elem)
// allocating new objects for key/elem. That is
// unnecessary; the new table could simply point to the
// existing object.
slotElem := tab.uncheckedPutSlot(typ, hash, key)
typedmemmove(typ.Elem, slotElem, elem)
tab.used++
} }
directory := make([]*table, 1) directory := make([]*table, 1)

View File

@ -357,20 +357,23 @@ func (t *table) PutSlot(typ *abi.SwissMapType, m *Map, hash uintptr, key unsafe.
} }
} }
// uncheckedPutSlot inserts an entry known not to be in the table, returning an // uncheckedPutSlot inserts an entry known not to be in the table.
// entry to the element slot where the element should be written. Used by // This is used for grow/split where we are making a new table from
// PutSlot after it has failed to find an existing entry to overwrite duration // entries in an existing table.
// insertion.
// //
// Updates growthLeft if necessary, but does not update used. // Decrements growthLeft and increments used.
// //
// Requires that the entry does not exist in the table, and that the table has // Requires that the entry does not exist in the table, and that the table has
// room for another element without rehashing. // room for another element without rehashing.
// //
// Requires that there are no deleted entries in the table. // Requires that there are no deleted entries in the table.
// //
// Never returns nil. // For indirect keys and/or elements, the key and elem pointers can be
func (t *table) uncheckedPutSlot(typ *abi.SwissMapType, hash uintptr, key unsafe.Pointer) unsafe.Pointer { // put directly into the map, they do not need to be copied. This
// requires the caller to ensure that the referenced memory never
// changes (by sourcing those pointers from another indirect key/elem
// map).
func (t *table) uncheckedPutSlot(typ *abi.SwissMapType, hash uintptr, key, elem unsafe.Pointer) {
if t.growthLeft == 0 { if t.growthLeft == 0 {
panic("invariant failed: growthLeft is unexpectedly 0") panic("invariant failed: growthLeft is unexpectedly 0")
} }
@ -389,22 +392,22 @@ func (t *table) uncheckedPutSlot(typ *abi.SwissMapType, hash uintptr, key unsafe
slotKey := g.key(typ, i) slotKey := g.key(typ, i)
if typ.IndirectKey() { if typ.IndirectKey() {
kmem := newobject(typ.Key) *(*unsafe.Pointer)(slotKey) = key
*(*unsafe.Pointer)(slotKey) = kmem } else {
slotKey = kmem typedmemmove(typ.Key, slotKey, key)
} }
typedmemmove(typ.Key, slotKey, key)
slotElem := g.elem(typ, i) slotElem := g.elem(typ, i)
if typ.IndirectElem() { if typ.IndirectElem() {
emem := newobject(typ.Elem) *(*unsafe.Pointer)(slotElem) = elem
*(*unsafe.Pointer)(slotElem) = emem } else {
slotElem = emem typedmemmove(typ.Elem, slotElem, elem)
} }
t.growthLeft-- t.growthLeft--
t.used++
g.ctrls().set(i, ctrl(h2(hash))) g.ctrls().set(i, ctrl(h2(hash)))
return slotElem return
} }
} }
} }
@ -1073,13 +1076,7 @@ func (t *table) split(typ *abi.SwissMapType, m *Map) {
} else { } else {
newTable = right newTable = right
} }
// TODO(prattmic): For indirect key/elem, this is newTable.uncheckedPutSlot(typ, hash, key, elem)
// allocating new objects for key/elem. That is
// unnecessary; the new table could simply point to the
// existing object.
slotElem := newTable.uncheckedPutSlot(typ, hash, key)
typedmemmove(typ.Elem, slotElem, elem)
newTable.used++
} }
} }
@ -1115,13 +1112,7 @@ func (t *table) grow(typ *abi.SwissMapType, m *Map, newCapacity uint16) {
hash := typ.Hasher(key, m.seed) hash := typ.Hasher(key, m.seed)
// TODO(prattmic): For indirect key/elem, this is newTable.uncheckedPutSlot(typ, hash, key, elem)
// allocating new objects for key/elem. That is
// unnecessary; the new table could simply point to the
// existing object.
slotElem := newTable.uncheckedPutSlot(typ, hash, key)
typedmemmove(typ.Elem, slotElem, elem)
newTable.used++
} }
} }
} }