From 586e36c3edf9209ca68453d42d09bc142b224114 Mon Sep 17 00:00:00 2001 From: Jeffrey Ryan Date: Thu, 19 Jan 2023 14:20:40 -0600 Subject: [PATCH] wallet2: reduce complexity of pick_preferred_rct_inputs Inside of `pick_preferred_rct_inputs`, when we are looking for 1 outputs which is spendable and fulfills the amount requirement, we create a local vector which stores the indices of only the "spendable" transfers (regardless of amounts). Then when we look for a 2 output combination, we iterate through said list instead of the whole transfers list. This 1) reduces branching and 2) reduces the complexity of the algorithm from `O(T^2)` to `O(max(T, S^2))` where `T = # of total transfers` and `S = # of spendable transfers`. In the case where a large portion of the outputs are already spent, this can be speed up the output picking by quite a bit. --- src/wallet/wallet2.cpp | 57 ++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 5817d3b4c..be40a51c7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -39,7 +39,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -64,7 +66,6 @@ using namespace epee; #include "multisig/multisig_account.h" #include "multisig/multisig_kex_msg.h" #include "multisig/multisig_tx_builder_ringct.h" -#include "common/boost_serialization_helper.h" #include "common/command_line.h" #include "common/threadpool.h" #include "int-util.h" @@ -9332,25 +9333,38 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui LOG_PRINT_L2("pick_preferred_rct_inputs: needed_money " << print_money(needed_money)); - // pre-cache a "is this output to be considered" array to speed up tests later - std::vector spendable(m_transfers.size(), false); - for (size_t i = 0; i < m_transfers.size(); ++i) - { - const transfer_details& td = m_transfers[i]; - if (!is_spent(td, false) && !td.m_frozen && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1 && !td.m_key_image_partial) - if (td.amount() <= m_ignore_outputs_above && td.amount() >= m_ignore_outputs_below) - spendable[i] = true; - } + // This vector will hold only indices of spendable transfers to speed up double output checks + std::vector spendable_transfer_indices; + spendable_transfer_indices.reserve(m_transfers.size()); - // try to find a rct input of enough size + // try to find a single rct input of enough size or collect otherwise spendable transfers for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; - if (spendable[i] && td.amount() >= needed_money) + if + ( + !is_spent(td, false) && + !td.m_frozen && + td.is_rct() && + is_transfer_unlocked(td) && + td.m_subaddr_index.major == subaddr_account && + subaddr_indices.count(td.m_subaddr_index.minor) == 1 && + !td.m_key_image_partial && + td.amount() <= m_ignore_outputs_above && + td.amount() >= m_ignore_outputs_below + ) { - LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount())); - picks.push_back(i); - return picks; + if (td.amount() >= needed_money) + { + LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount())); + picks.push_back(i); + return picks; + } + else + { + // Amount is not enough on its own, but it may be enough in addition to another + spendable_transfer_indices.push_back(i); + } } } @@ -9358,16 +9372,16 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui // this could be made better by picking one of the outputs to be a small one, since those // are less useful since often below the needed money, so if one can be used in a pair, // it gets rid of it for the future - for (size_t i = 0; i < m_transfers.size(); ++i) + for (size_t si = 0; si < spendable_transfer_indices.size(); ++si) { - const transfer_details& td = m_transfers[i]; - if (spendable[i]) - { + const size_t i = spendable_transfer_indices[si]; + const transfer_details& td = m_transfers[i]; LOG_PRINT_L2("Considering input " << i << ", " << print_money(td.amount())); - for (size_t j = i + 1; j < m_transfers.size(); ++j) + for (size_t sj = si + 1; sj < spendable_transfer_indices.size(); ++sj) { + const size_t j = spendable_transfer_indices[sj]; const transfer_details& td2 = m_transfers[j]; - if (spendable[j] && td.amount() + td2.amount() >= needed_money && td2.m_subaddr_index == td.m_subaddr_index) + if (td.amount() + td2.amount() >= needed_money && td2.m_subaddr_index == td.m_subaddr_index) { // update our picks if those outputs are less related than any we // already found. If the same, don't update, and oldest suitable outputs @@ -9389,7 +9403,6 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui } } } - } } return picks;