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.
pull/8715/head
Jeffrey Ryan 1 year ago
parent fee4f9a076
commit 586e36c3ed

@ -39,7 +39,9 @@
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <openssl/evp.h>
@ -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<size_t> 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<bool> 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<size_t> 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)
{
LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount()));
picks.push_back(i);
return picks;
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
)
{
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<size_t> 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<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
}
}
}
}
}
return picks;

Loading…
Cancel
Save