Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

logstore: pool MVCCStats to avoid hot path allocs #113742

Merged
merged 1 commit into from
Nov 3, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions pkg/kv/kvserver/logstore/logstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,13 @@ var nonBlockingSyncWaiterCallbackPool = sync.Pool{
New: func() interface{} { return new(nonBlockingSyncWaiterCallback) },
}

var valPool = sync.Pool{
New: func() interface{} { return &roachpb.Value{} },
var logAppendPool = sync.Pool{
New: func() interface{} {
return new(struct {
roachpb.Value
enginepb.MVCCStats
})
Comment on lines +365 to +368
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: may as well give this type a name, but don't really care either way.

Copy link
Collaborator Author

@pav-kv pav-kv Nov 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meh, I think that's one of the cases where it's somewhat beneficial to not introduce a type explicitly (so that there is zero encouragement to use it elsewhere). There is exactly one caller/user of this type, and it's tied to the pool, and the pool is tied to a single function (also a reason why I renamed the pool after the function's name).

},
}

// logAppend adds the given entries to the raft log. Takes the previous log
Expand All @@ -382,13 +387,21 @@ func logAppend(
if len(entries) == 0 {
return prev, nil
}
var diff enginepb.MVCCStats
opts := storage.MVCCWriteOptions{
Stats: &diff,
}
value := valPool.Get().(*roachpb.Value)

// NB: the Value and MVCCStats lifetime is this function, so we coalesce their
// allocation into the same pool.
// TODO(pavelkalinnikov): figure out why they escape into the heap, and find a
// way to avoid the pool.
v := logAppendPool.Get().(*struct {
roachpb.Value
enginepb.MVCCStats
})
defer logAppendPool.Put(v)
value, diff := &v.Value, &v.MVCCStats
value.RawBytes = value.RawBytes[:0]
defer valPool.Put(value)
diff.Reset()

opts := storage.MVCCWriteOptions{Stats: diff}
for i := range entries {
ent := &entries[i]
key := keys.RaftLogKeyFromPrefix(raftLogPrefix, kvpb.RaftIndex(ent.Index))
Expand Down