GODRIVER-4018 Reduce allocations when building write batches#2476
Open
paveon wants to merge 1 commit into
Open
GODRIVER-4018 Reduce allocations when building write batches#2476paveon wants to merge 1 commit into
paveon wants to merge 1 commit into
Conversation
Reserve the output buffer once before appending documents in AppendBatchSequence and AppendBatchArray, instead of letting the per-document append grow it by repeated reallocation. A first pass computes how many documents fit within the maxCount/totalSize limits and their combined size; dst is grown a single time, then the documents are appended. When dst already has capacity the reservation is a no-op. AppendBatchArray additionally wrote each element key with strconv.Itoa, allocating a string per document; it now writes the numeric key in place with strconv.AppendInt via a helper that composes the same bsoncore primitives, producing byte-identical output. Behavior is unchanged: same batch count, byte-identical output, same maxCount/totalSize handling, and the same empty-result early return. Benchmark (1000 x 256-byte docs, fresh buffer): AppendBatchSequence: 24 -> 3 allocs/op, 1.19MB -> 262KB, ~130us -> ~29us AppendBatchArray: 925 -> 3 allocs/op, 1.19MB -> 262KB, ~166us -> ~38us
d277f19 to
d6bf08d
Compare
Contributor
|
@paveon Thanks for the improvement! The Go Driver team is at capacity right now, but we've scheduled reviewing your PR after some pressing work. Someone from the team will review it in about a month. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GODRIVER-4018
Summary
Pre-size the output buffer once in
Batches.AppendBatchSequenceandAppendBatchArrayinstead of letting the per-documentappendgrow it byrepeated reallocation. A first pass totals the documents that fit within the
maxCount/totalSizelimits,dstis grown a single time, then the documentsare appended; when
dstalready has capacity the reservation is a no-op.AppendBatchArrayalso now writes each element's numeric key in place withstrconv.AppendIntrather than allocating a string viastrconv.Itoa.Behavior is unchanged: same number of batches appended, byte-identical output,
same limit handling, and the same empty-result early return.
Background & Motivation
Both methods build the write payload for write operations (
BulkWrite,InsertMany, etc.) by appending each encoded document todstin a loop with nopre-sizing, so
appendgrowsdstgeometrically — each growth reallocates andcopies the whole buffer accumulated so far. On large batches this is significant,
avoidable allocation churn and GC pressure, and it showed up as a top allocation
site in a profile of a write-heavy service. Document sizes are known up front, so
the capacity can be reserved in one shot; the limit-checking logic is unchanged.
The array path additionally allocated one key string per document. Minimum Go is
1.19, so
slices.Growis not used.Benchmark (1000 × 256-byte documents, appending into a fresh buffer):
AppendBatchSequenceAppendBatchArray