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

refactor: reserve memory allocation for transaction outputs #30093

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

paplorinc
Copy link
Contributor

@paplorinc paplorinc commented May 13, 2024

Reserved memory for the transaction inputs and outputs.

Split out of https://github.com/bitcoin/bitcoin/pull/30050/files#r1597631104

@DrahtBot
Copy link
Contributor

DrahtBot commented May 13, 2024

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage

For detailed information about the code coverage, see the test coverage report.

Reviews

See the guideline for information on the review process.

Type Reviewers
Concept ACK theuni

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #30050 (refactor, wallet: get serialized size of CRecipients directly by josibake)
  • #24128 (wallet: BIP 326 sequence based anti-fee-snipe for taproot inputs by maflcko)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@paplorinc paplorinc changed the title Optimize memory allocation for transaction outputs refactor: optimize memory allocation for transaction outputs May 13, 2024
@paplorinc paplorinc changed the title refactor: optimize memory allocation for transaction outputs refactor: reserve memory allocation for transaction outputs May 13, 2024
Copy link
Member

@theuni theuni left a comment

Choose a reason for hiding this comment

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

Concept ACK

@@ -1088,6 +1088,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
coin_selection_params.tx_noinputs_size = 10 + GetSizeOfCompactSize(vecSend.size()); // bytes for output count

// vouts to the payees
txNew.vout.reserve(txNew.vout.size() + vecSend.size());
Copy link
Member

Choose a reason for hiding this comment

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

A few suggestions:

  • Looks to me like txNew.vout is always empty here, so I'm not sure why we'd bother to add txNew.vout.size() unless I'm missing something ?
  • There's an additional likely insert for change. Since the reserved size here is exact, that could trigger a reallocation up to the next power-of-two. Makes sense to make this vecSend.size()+1 just in case.
  • I think the txouts could be std::moved (in both places) instead to save a copy similar to rpc: move UniValue in blockToJSON #30094?
  • Any reason not .reserve() for the txNew.vin as well?

Copy link
Contributor Author

@paplorinc paplorinc May 13, 2024

Choose a reason for hiding this comment

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

Hey @theuni, excellent review, thanks!

  1. yes, it's always empty, leaving it out is indeed more readable - and if people modify it in the future, they should just pay attention to this part
  2. nice, haven't noticed it, added the +1 (added you as co-author)
  3. To be consistent with txNew.vin, I've changed it to emplace_back instead, the performance should be comparable - do you agree?
  4. wanted to be focused only on the issue discovered during code review, but this is much better, thanks for the comment, pushed!

@paplorinc paplorinc force-pushed the paplorinc/spend_preallocation branch from aaed2db to a1c3d58 Compare May 13, 2024 20:08
@@ -1098,7 +1099,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
if (IsDust(txout, wallet.chain().relayDustFee())) {
return util::Error{_("Transaction amount too small")};
}
txNew.vout.push_back(txout);
txNew.vout.emplace_back(txout);
Copy link
Member

Choose a reason for hiding this comment

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

This will just be forwarded to the copy ctor. push_back(std::move(txout)) would be more correct (and clear).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added benchmarks to verify:

|               ns/op |                op/s |    err% |     total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
|            2,089.91 |          478,489.15 |    0.1% |      1.10 | `BenchmarkAddOutputsEmplaceBackReserve`
|            1,257.81 |          795,031.71 |    0.1% |      1.10 | `BenchmarkAddOutputsInPlaceEmplaceBackReserve`
|            2,594.12 |          385,487.12 |    0.2% |      1.10 | `BenchmarkAddOutputsPushBack`
|            1,667.85 |          599,573.52 |    0.1% |      1.10 | `BenchmarkAddOutputsPushBackMoveReserve`
|            2,089.21 |          478,649.26 |    0.1% |      1.10 | `BenchmarkAddOutputsPushBackReserve`

ended up adding it directly to txNew.vout which is both faster and simpler - what do you think?

@paplorinc paplorinc force-pushed the paplorinc/spend_preallocation branch 3 times, most recently from 794db03 to 1cdecda Compare May 14, 2024 08:05
paplorinc and others added 3 commits May 16, 2024 21:59
Accommodating possible later insert as well

Co-authored-by: Cory Fields <cory-nospam-@coryfields.com>
Co-authored-by: Cory Fields <cory-nospam-@coryfields.com>
Benchmark results show that in place `emplace_back` outperforms constructing `CTxOut` and then `push_back` or `emplace_back` and even `push_back` with `move`.

This simplifies and optimizes the code at the same time.

> make && ./src/bench/bench_bitcoin --filter='BenchmarkAddOutputsPushBack|BenchmarkAddOutputsPushBackReserve|BenchmarkAddOutputsPushBackMoveReserve|BenchmarkAddOutputsEmplaceBackReserve|BenchmarkAddOutputsInPlaceEmplaceBackReserve' --min-time=1000

```
|               ns/op |                op/s |    err% |     total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
|            2,089.91 |          478,489.15 |    0.1% |      1.10 | `BenchmarkAddOutputsEmplaceBackReserve`
|            1,257.81 |          795,031.71 |    0.1% |      1.10 | `BenchmarkAddOutputsInPlaceEmplaceBackReserve`
|            2,594.12 |          385,487.12 |    0.2% |      1.10 | `BenchmarkAddOutputsPushBack`
|            1,667.85 |          599,573.52 |    0.1% |      1.10 | `BenchmarkAddOutputsPushBackMoveReserve`
|            2,089.21 |          478,649.26 |    0.1% |      1.10 | `BenchmarkAddOutputsPushBackReserve`
```

Co-authored-by: Cory Fields <cory-nospam-@coryfields.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants