Skip to content

Commit

Permalink
internal/rangekey: avoid Transform allocation
Browse files Browse the repository at this point in the history
Avoid an allocation when coalescing range keys as a part of a
MergingIter Transform hook.

```
name                                                old time/op    new time/op    delta
CombinedIteratorSeek/range-key=true/batch=false-16    8.49µs ± 3%    8.37µs ± 2%     ~     (p=0.053 n=9+10)

name                                                old alloc/op   new alloc/op   delta
CombinedIteratorSeek/range-key=true/batch=false-16      221B ± 0%      157B ± 0%  -28.96%  (p=0.000 n=10+10)

name                                                old allocs/op  new allocs/op  delta
CombinedIteratorSeek/range-key=true/batch=false-16      4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.000 n=10+10)
```
  • Loading branch information
jbowens committed Nov 22, 2022
1 parent fece1a6 commit fcf9e40
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 23 deletions.
4 changes: 2 additions & 2 deletions internal/keyspan/merging_iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -844,15 +844,15 @@ func (m *MergingIter) synthesizeKeys(dir int8) (bool, *Span) {
sort.Sort(&m.keys)

// Apply the configured transform. See visibleTransform.
s := Span{
m.span = Span{
Start: m.start,
End: m.end,
Keys: m.keys,
KeysOrder: ByTrailerDesc,
}
// NB: m.heap.cmp is a base.Compare, whereas m.cmp is a method on
// MergingIter.
if err := m.transformer.Transform(m.heap.cmp, s, &m.span); err != nil {
if err := m.transformer.Transform(m.heap.cmp, m.span, &m.span); err != nil {
m.err = err
return false, nil
}
Expand Down
25 changes: 12 additions & 13 deletions internal/rangekey/coalesce.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ type UserIteratorConfig struct {
diter keyspan.DefragmentingIter
liters [manifest.NumLevels]keyspan.LevelIter
litersUsed int
sortBuf keysBySuffix
bufs *Buffers
}

// Buffers holds various buffers used for range key iteration. They're exposed
// so that they may be pooled and reused between iterators.
type Buffers struct {
merging keyspan.MergingBuffers
defragmenting keyspan.DefragmentingBuffers
sortBuf keysBySuffix
}

// PrepareForReuse discards any excessively large buffers.
Expand Down Expand Up @@ -63,6 +64,7 @@ func (ui *UserIteratorConfig) Init(
ui.biter.Init(comparer.Compare, comparer.Split, &ui.miter, lower, upper, hasPrefix, prefix)
ui.diter.Init(comparer, &ui.biter, ui, keyspan.StaticDefragmentReducer, &bufs.defragmenting)
ui.litersUsed = 0
ui.bufs = bufs
return &ui.diter
}

Expand Down Expand Up @@ -101,17 +103,17 @@ func (ui *UserIteratorConfig) Transform(cmp base.Compare, s keyspan.Span, dst *k
// Apply shadowing of keys.
dst.Start = s.Start
dst.End = s.End
ui.sortBuf = keysBySuffix{
ui.bufs.sortBuf = keysBySuffix{
cmp: cmp,
keys: dst.Keys[:0],
keys: ui.bufs.sortBuf.keys[:0],
}
if err := coalesce(&ui.sortBuf, s.Visible(ui.snapshot).Keys, &dst.Keys); err != nil {
if err := coalesce(&ui.bufs.sortBuf, s.Visible(ui.snapshot).Keys); err != nil {
return err
}
// During user iteration over range keys, unsets and deletes don't
// matter. Remove them. This step helps logical defragmentation during
// iteration.
keys := dst.Keys
keys := ui.bufs.sortBuf.keys
dst.Keys = dst.Keys[:0]
for i := range keys {
switch keys[i].Kind() {
Expand Down Expand Up @@ -225,15 +227,17 @@ func Coalesce(cmp base.Compare, keys []keyspan.Key, dst *[]keyspan.Key) error {
cmp: cmp,
keys: (*dst)[:0],
}
if err := coalesce(&keysBySuffix, keys, dst); err != nil {
if err := coalesce(&keysBySuffix, keys); err != nil {
return err
}
// coalesce left the keys in *dst sorted by suffix. Re-sort them by trailer.
// Update the span with the (potentially reduced) keys slice. coalesce left
// the keys in *dst sorted by suffix. Re-sort them by trailer.
*dst = keysBySuffix.keys
keyspan.SortKeysByTrailer(dst)
return nil
}

func coalesce(keysBySuffix *keysBySuffix, keys []keyspan.Key, dst *[]keyspan.Key) error {
func coalesce(keysBySuffix *keysBySuffix, keys []keyspan.Key) error {
var deleted bool
for i := 0; i < len(keys) && !deleted; i++ {
k := keys[i]
Expand Down Expand Up @@ -278,11 +282,6 @@ func coalesce(keysBySuffix *keysBySuffix, keys []keyspan.Key, dst *[]keyspan.Key
return base.CorruptionErrorf("pebble: unexpected range key kind %s", k.Kind())
}
}

// Update the span with the (potentially reduced) keys slice.
// NB: We don't re-sort by Trailer. The exported Coalesce function however
// will.
*dst = keysBySuffix.keys
return nil
}

Expand Down
27 changes: 19 additions & 8 deletions iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,21 @@ type rangeKeyBuffers struct {
internal rangekey.Buffers
}

func (b *rangeKeyBuffers) PrepareForReuse() {
const maxKeysReuse = 100
if len(b.keys) > maxKeysReuse {
b.keys = nil
}
// Avoid caching the key buf if it is overly large. The constant is
// fairly arbitrary.
if cap(b.buf) >= maxKeyBufCacheSize {
b.buf = nil
} else {
b.buf = b.buf[:0]
}
b.internal.PrepareForReuse()
}

func (i *iteratorRangeKeyState) init(cmp base.Compare, split base.Split, opts *IterOptions) {
i.cmp = cmp
i.split = split
Expand Down Expand Up @@ -1961,6 +1976,8 @@ func (i *Iterator) Error() error {
return i.err
}

const maxKeyBufCacheSize = 4 << 10 // 4 KB

// Close closes the iterator and returns any accumulated error. Exhausting
// all the key/value pairs in a table is not considered to be an error.
// It is not valid to call any method, including Close, after the iterator
Expand Down Expand Up @@ -2025,15 +2042,9 @@ func (i *Iterator) Close() error {
i.valueCloser = nil
}

const maxKeyBufCacheSize = 4 << 10 // 4 KB

if i.rangeKey != nil {
// Avoid caching the key buf if it is overly large. The constant is
// fairly arbitrary.
if cap(i.rangeKey.buf) >= maxKeyBufCacheSize {
i.rangeKey.buf = nil
}
i.rangeKey.rangeKeyBuffers.internal.PrepareForReuse()

i.rangeKey.rangeKeyBuffers.PrepareForReuse()
*i.rangeKey = iteratorRangeKeyState{
rangeKeyBuffers: i.rangeKey.rangeKeyBuffers,
}
Expand Down

0 comments on commit fcf9e40

Please sign in to comment.