aboutsummaryrefslogtreecommitdiff
path: root/src/internal/runtime/maps/table.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal/runtime/maps/table.go')
-rw-r--r--src/internal/runtime/maps/table.go54
1 files changed, 35 insertions, 19 deletions
diff --git a/src/internal/runtime/maps/table.go b/src/internal/runtime/maps/table.go
index 801479ba88..60f4263100 100644
--- a/src/internal/runtime/maps/table.go
+++ b/src/internal/runtime/maps/table.go
@@ -161,8 +161,6 @@ func (t *table) Get(key unsafe.Pointer) (unsafe.Pointer, bool) {
// TODO(prattmic): We could avoid hashing in a variety of special
// cases.
//
- // - One group maps with simple keys could iterate over all keys and
- // compare them directly.
// - One entry maps could just directly compare the single entry
// without hashing.
// - String keys could do quick checks of a few bytes before hashing.
@@ -243,6 +241,11 @@ func (t *table) getWithKey(hash uintptr, key unsafe.Pointer) (unsafe.Pointer, un
func (t *table) PutSlot(m *Map, hash uintptr, key unsafe.Pointer) (unsafe.Pointer, bool) {
seq := makeProbeSeq(h1(hash), t.groups.lengthMask)
+ // As we look for a match, keep track of the first deleted slot we
+ // find, which we'll use to insert the new entry if necessary.
+ var firstDeletedGroup groupReference
+ var firstDeletedSlot uint32
+
for ; ; seq = seq.next() {
g := t.groups.group(seq.offset)
match := g.ctrls().matchH2(h2(hash))
@@ -265,15 +268,28 @@ func (t *table) PutSlot(m *Map, hash uintptr, key unsafe.Pointer) (unsafe.Pointe
match = match.removeFirst()
}
+ // No existing slot for this key in this group. Is this the end
+ // of the probe sequence?
match = g.ctrls().matchEmpty()
if match != 0 {
// Finding an empty slot means we've reached the end of
// the probe sequence.
+ var i uint32
+
+ // If we found a deleted slot along the way, we can
+ // replace it without consuming growthLeft.
+ if firstDeletedGroup.data != nil {
+ g = firstDeletedGroup
+ i = firstDeletedSlot
+ t.growthLeft++ // will be decremented below to become a no-op.
+ } else {
+ // Otherwise, use the empty slot.
+ i = match.first()
+ }
+
// If there is room left to grow, just insert the new entry.
if t.growthLeft > 0 {
- i := match.first()
-
slotKey := g.key(i)
typedmemmove(t.typ.Key, slotKey, key)
slotElem := g.elem(i)
@@ -287,24 +303,24 @@ func (t *table) PutSlot(m *Map, hash uintptr, key unsafe.Pointer) (unsafe.Pointe
return slotElem, true
}
- // TODO(prattmic): While searching the probe sequence,
- // we may have passed deleted slots which we could use
- // for this entry.
- //
- // At the moment, we leave this behind for
- // rehash to free up.
- //
- // cockroachlabs/swiss restarts search of the probe
- // sequence for a deleted slot.
- //
- // TODO(go.dev/issue/54766): We want this optimization
- // back. We could search for the first deleted slot
- // during the main search, but only use it if we don't
- // find an existing entry.
-
t.rehash(m)
return nil, false
}
+
+ // No empty slots in this group. Check for a deleted
+ // slot, which we'll use if we don't find a match later
+ // in the probe sequence.
+ //
+ // We only need to remember a single deleted slot.
+ if firstDeletedGroup.data == nil {
+ // Since we already checked for empty slots
+ // above, matches here must be deleted slots.
+ match = g.ctrls().matchEmptyOrDeleted()
+ if match != 0 {
+ firstDeletedGroup = g
+ firstDeletedSlot = match.first()
+ }
+ }
}
}