Repository navigation
perf(fdbclient): reduce arena allocations in MutationRef deep-copy constructors - #13270
Conversation
|
@pchintar thank you for this contribution unfortunately I don't have appropriate access for starting the CI run. If you want I can review the work and give some suggestions. You can wait for appropriate people to have look at it and start the Ci run. :) |
|
@Ronitsabhaya75 none of the others have replied yet, so what do I do now? I'm new here that's why I'm asking |
|
@pchintar im sure they'll see this pr. Don't worry :) |
| param1 = StringRef(packed, from.param1.size()); | ||
| param2 = StringRef(packed + from.param1.size(), from.param2.size()); | ||
| } | ||
|
|
There was a problem hiding this comment.
Could we get rid of the duplicated code? A little inline function?
There was a problem hiding this comment.
Good point @saintstack I’ll factor the shared packing logic into a small helper and call it from both constructors.
There was a problem hiding this comment.
@saintstack I've just updated & also fixed formatting with help of clang-format, so can you pls check it out now & run the checks? thnx
9ceb592 to
9e438c4
Compare
| const int totalSize = a.size() + b.size(); | ||
| uint8_t* packed = totalSize ? new (to) uint8_t[totalSize] : nullptr; | ||
|
|
||
| if (a.size()) { | ||
| memcpy(packed, a.begin(), a.size()); | ||
| } | ||
|
|
||
| if (b.size()) { | ||
| memcpy(packed + a.size(), b.begin(), b.size()); | ||
| } | ||
|
|
||
| param1 = StringRef(packed, a.size()); | ||
| param2 = StringRef(packed + a.size(), b.size()); |
There was a problem hiding this comment.
Copilot worried about case where totalSIze = 0; we'd end up doing nullptr arithmetic. Suggestion below. Thanks.
| const int totalSize = a.size() + b.size(); | |
| uint8_t* packed = totalSize ? new (to) uint8_t[totalSize] : nullptr; | |
| if (a.size()) { | |
| memcpy(packed, a.begin(), a.size()); | |
| } | |
| if (b.size()) { | |
| memcpy(packed + a.size(), b.begin(), b.size()); | |
| } | |
| param1 = StringRef(packed, a.size()); | |
| param2 = StringRef(packed + a.size(), b.size()); | |
| const size_t aSize = a.size(); | |
| const size_t bSize = b.size(); | |
| const size_t totalSize = aSize + bSize; | |
| uint8_t* packed = totalSize ? new (to) uint8_t[totalSize] : nullptr; | |
| if (aSize) { | |
| memcpy(packed, a.begin(), aSize); | |
| } | |
| if (bSize) { | |
| memcpy(packed + aSize, b.begin(), bSize); | |
| } | |
| param1 = StringRef(aSize ? packed : nullptr, aSize); | |
| param2 = StringRef(bSize ? packed + aSize : nullptr, bSize); |
There was a problem hiding this comment.
@saintstack Yep, I've just fixed that, the totalSize == 0 case now explicitly uses nullptr for empty payloads.
9e438c4 to
bf177cc
Compare
Result of foundationdb-pr-clang-ide on Linux RHEL 9
|
Result of foundationdb-pr-macos-m1 on macOS Ventura 13.x
|
Result of foundationdb-pr-clang-arm on Linux CentOS 7
|
Result of foundationdb-pr-macos on macOS Ventura 13.x
|
Result of foundationdb-pr on Linux RHEL 9
|
Result of foundationdb-pr-cluster-tests on Linux RHEL 9
|
Result of foundationdb-pr-clang on Linux RHEL 9
|
|
Thank you for the contribution @pchintar |
|
@saintstack, thank you. Could you kindly check my other PR #13273 if possible? I've fixed the formatting and the CI checks have passed for it as well. |
Closes issue #13269 .
Summary
Reduce arena allocation overhead in
MutationRefdeep-copy constructors by storing copied mutation payloads in a single arena allocation instead of allocatingparam1andparam2independently.Problem
Previously, both deep-copy constructors performed two separate arena allocations:
Each
StringRef(Arena&, ...)construction allocates independently inside the arena, causing every deep-copied mutation to perform two separate allocations.This affects hot paths such as:
VectorRef<MutationRef>::emplace_back_deepVectorRef<MutationRef>::push_back_deepChanges
Both deep-copy constructors now:
param1andparam2into that shared allocationStringRefsemantics and ownership behaviorNow arena payload allocations are reduced from two allocations per mutation to one allocation per mutation. No public APIs or serialization behavior are changed.
Benchmark
Benchmarks were run using:
bin/fdbclient_bench --benchmark_filter='bench_populate' --benchmark_repetitions=10 --benchmark_report_aggregates_only=trueObserved trends:
Testing
ninja fdbclient_bench bin/fdbclient_bench --benchmark_filter='bench_populate' --benchmark_repetitions=10 --benchmark_report_aggregates_only=true@akankshamahajan15 and @neethuhaneesha could you pls run the checks if you have the access to do so and review this PR? thnx