Skip to content

Conversation

martinus
Copy link
Contributor

@martinus martinus commented May 4, 2018

Some simple optimizations to SelectCoinsBnB. In total these changes sum up to a speedup of a factor of about 2.4 on my machine:

BnBExhaustion, 5, 650, 2.08837, 0.000634196, 0.000660664, 0.000638457  # before
BnBExhaustion, 5, 2300, 4.89576, 0.000422629, 0.000429628, 0.000424571 # std::vector<char>
BnBExhaustion, 5, 2300, 3.48065, 0.000299667, 0.000308361, 0.000301751 # with curr_selection_size
BnBExhaustion, 5, 2300, 3.08375, 0.000266867, 0.000270109, 0.00026751  # [] instead of .at

image

These changes are minor code refactoring that should not change the behaviour of the algorithm in any way.

martinus added 3 commits May 4, 2018 16:37
std::vector<bool> is unfortunately very slow. This change replaces it with std::vector<char>
like it is done in ApproximateBestSubset. Also updates number of iterations for the benchmark.

The performance difference is quite significant:

BnBExhaustion, 5, 650, 2.08837, 0.000634196, 0.000660664, 0.000638457  # before
BnBExhaustion, 5, 2300, 4.89576, 0.000422629, 0.000429628, 0.000424571 # after

This changes speeds up the benchmark by a factor of about 1.5.
Using a preallocated std::vector<char> with indexing instead of push_back and
pop_back further speeds up the algorithm:

BnBExhaustion, 5, 2300, 4.89576, 0.000422629, 0.000429628, 0.000424571 # std::vector<const>
BnBExhaustion, 5, 2300, 3.48065, 0.000299667, 0.000308361, 0.000301751 # with indexing

Speedup is a factor of 1.4 on my machine.
.at() performs out of bounds checks, which are not necessary because
curr_available_value is used as a stopping criteria anyways. Another speedup:

BnBExhaustion, 5, 2300, 3.48065, 0.000299667, 0.000308361, 0.000301751 # with curr_selection_size
BnBExhaustion, 5, 2300, 3.08375, 0.000266867, 0.000270109, 0.00026751  # [] instead of .at

Speeds up by another factor of 1.13.
@fanquake fanquake added the Wallet label May 4, 2018
@fanquake fanquake requested a review from achow101 May 5, 2018 04:00
Copy link
Member

@achow101 achow101 left a comment

Choose a reason for hiding this comment

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

Mostly looks good, just a few concerns about accidentally including outputs which aren't actually part of the best selection but were in a different selection iteration.

@@ -102,7 +102,6 @@ bool SelectCoinsBnB(std::vector<CInputCoin>& utxo_pool, const CAmount& target_va
// explore any more UTXOs to avoid burning money like that.
if (curr_waste <= best_waste) {
best_selection = curr_selection;
best_selection.resize(utxo_pool.size());
Copy link
Member

Choose a reason for hiding this comment

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

I think you will need to also record curr_selection_size since copying curr_selection may also copy outputs that were selected in previous iterations that are not actually part of the current selection (i.e. selected previously but are at indexes past curr_selection_size).

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 think this is not necessary. If I understand it correctly, what's right from curr_selection_size will always be 0. The only place where the size goes back is in line 114-117, the loop stops when it finds true then sets it to false. So there is no way that any value right from curr_selection_size will ever be true

Copy link
Member

Choose a reason for hiding this comment

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

Indeed, that is the case. Ignore my other comments then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for reviewing!

@@ -155,9 +155,9 @@ bool SelectCoinsBnB(std::vector<CInputCoin>& utxo_pool, const CAmount& target_va
// Set output set
value_ret = 0;
for (size_t i = 0; i < best_selection.size(); ++i) {
Copy link
Member

Choose a reason for hiding this comment

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

This should go up to a tracked best_selection_size. Otherwise we may accidentally include outputs which are not part of the best selection as they are at locations past the size of the actual best selection.

@@ -112,38 +111,39 @@ bool SelectCoinsBnB(std::vector<CInputCoin>& utxo_pool, const CAmount& target_va
// Backtracking, moving backwards
if (backtrack) {
// Walk backwards to find the last included UTXO that still needs to have its omission branch traversed.
while (!curr_selection.empty() && !curr_selection.back()) {
curr_selection.pop_back();
Copy link
Member

Choose a reason for hiding this comment

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

As an extra belt-and-suspenders check, walking backwards should reset each selection choice to the default state of false.

@achow101
Copy link
Member

achow101 commented May 5, 2018

utACK d339f1f

@DrahtBot
Copy link
Contributor

Needs rebase

@DrahtBot
Copy link
Contributor

There hasn't been much activity lately and the patch still needs rebase, so I am closing this for now. Please let me know when you want to continue working on this, so the pull request can be re-opened.

@DrahtBot DrahtBot closed this Oct 28, 2018
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Dec 16, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants