src: update internal data structure to boost::bimap.

startup speed up 2-5x because this PR changing
O(n^2) operation to O(nlogn).
This commit is contained in:
0xFFFC0000 2024-06-21 07:22:19 +00:00
parent cc73fe7116
commit 445319d3f1
No known key found for this signature in database
GPG Key ID: 650F7C2B7BDA3819
2 changed files with 40 additions and 26 deletions

View File

@ -29,6 +29,7 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <algorithm>
#include <boost/bimap/support/iterator_type_by.hpp>
#include <boost/filesystem.hpp>
#include <unordered_set>
#include <vector>
@ -42,6 +43,7 @@
#include "blockchain_db/blockchain_db.h"
#include "int-util.h"
#include "misc_language.h"
#include "misc_log_ex.h"
#include "warnings.h"
#include "common/perf_timer.h"
#include "crypto/hash.h"
@ -464,7 +466,7 @@ namespace cryptonote
break;
try
{
const crypto::hash &txid = it->second;
const crypto::hash &txid = it->get_right();
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(txid, meta))
{
@ -491,11 +493,11 @@ namespace cryptonote
return;
}
// remove first, in case this throws, so key images aren't removed
MINFO("Pruning tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first);
MINFO("Pruning tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->get_left().first);
m_blockchain.remove_txpool_tx(txid);
reduce_txpool_weight(meta.weight);
remove_transaction_keyimages(tx, txid);
MINFO("Pruned tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first);
MINFO("Pruned tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->get_left().first);
auto it_prev = it;
--it_prev;
@ -751,13 +753,9 @@ namespace cryptonote
m_remove_stuck_tx_interval.do_call([this](){return remove_stuck_transactions();});
}
//---------------------------------------------------------------------------------
sorted_tx_container::iterator tx_memory_pool::find_tx_in_sorted_container(const crypto::hash& id) const
sorted_tx_container::iterator tx_memory_pool::find_tx_in_sorted_container(const crypto::hash& id)
{
return std::find_if( m_txs_by_fee_and_receive_time.begin(), m_txs_by_fee_and_receive_time.end()
, [&](const sorted_tx_container::value_type& a){
return a.second == id;
}
);
return m_txs_by_fee_and_receive_time.project_up(m_txs_by_fee_and_receive_time.right.find(id));
}
//---------------------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate
@ -1644,15 +1642,15 @@ namespace cryptonote
for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it)
{
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta))
if (!m_blockchain.get_txpool_tx_meta(sorted_it->get_right(), meta))
{
static bool warned = false;
if (!warned)
MERROR(" failed to find tx meta: " << sorted_it->second << " (will only print once)");
MERROR(" failed to find tx meta: " << sorted_it->get_right() << " (will only print once)");
warned = true;
continue;
}
LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase) << ", relay method " << (unsigned)meta.get_relay_method());
LOG_PRINT_L2("Considering " << sorted_it->get_right() << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase) << ", relay method " << (unsigned)meta.get_relay_method());
if (!meta.matches(relay_category::legacy) && !(m_mine_stem_txes && meta.get_relay_method() == relay_method::stem))
{
@ -1702,7 +1700,7 @@ namespace cryptonote
}
// "local" and "stem" txes are filtered above
cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it->second, relay_category::all);
cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it->get_right(), relay_category::all);
cryptonote::transaction tx;
@ -1713,7 +1711,7 @@ namespace cryptonote
bool ready = false;
try
{
ready = is_transaction_ready_to_go(meta, sorted_it->second, txblob, tx);
ready = is_transaction_ready_to_go(meta, sorted_it->get_right(), txblob, tx);
}
catch (const std::exception &e)
{
@ -1724,7 +1722,7 @@ namespace cryptonote
{
try
{
m_blockchain.update_txpool_tx(sorted_it->second, meta);
m_blockchain.update_txpool_tx(sorted_it->get_right(), meta);
}
catch (const std::exception &e)
{
@ -1743,7 +1741,7 @@ namespace cryptonote
continue;
}
bl.tx_hashes.push_back(sorted_it->second);
bl.tx_hashes.push_back(sorted_it->get_right());
total_weight += meta.weight;
fee += meta.fee;
best_coinbase = coinbase;
@ -1854,7 +1852,11 @@ namespace cryptonote
m_txs_by_fee_and_receive_time.erase(sorted_it);
}
}
m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(fee, receive_time), txid);
auto insert_result = m_txs_by_fee_and_receive_time.insert(sorted_tx_container::value_type(std::pair<double, time_t>(fee, receive_time), txid));
if (!insert_result.second) {
MERROR("Failed to add txid " << txid << " to the m_txs_by_fee_and_receive_time");
}
// Don't check for "resurrected" txs in case of reorgs i.e. don't check in 'm_removed_txs_by_time'
// whether we have that txid there and if yes remove it; this results in possible duplicates

View File

@ -39,6 +39,9 @@
#include <queue>
#include <boost/serialization/version.hpp>
#include <boost/utility.hpp>
#include <boost/bimap.hpp>
#include <boost/bimap/set_of.hpp>
#include <boost/bimap/multiset_of.hpp>
#include "span.h"
#include "string_tools.h"
@ -62,24 +65,33 @@ namespace cryptonote
//! pair of <transaction fee, transaction hash> for organization
typedef std::pair<std::pair<double, std::time_t>, crypto::hash> tx_by_fee_and_receive_time_entry;
class txCompare
class txFeeCompare
{
public:
bool operator()(const tx_by_fee_and_receive_time_entry& a, const tx_by_fee_and_receive_time_entry& b) const
bool operator()(const std::pair<double, std::time_t>& a, const std::pair<double, std::time_t>& b) const
{
// sort by greatest first, not least
if (a.first.first > b.first.first) return true;
if (a.first.first < b.first.first) return false;
if (a.first > b.first) return true;
if (a.first < b.first) return false;
if (a.first.second < b.first.second) return true;
if (a.first.second > b.first.second) return false;
if (a.second < b.second) return true;
return false;
}
};
return memcmp(a.second.data, b.second.data, sizeof(crypto::hash)) < 0;
class hashCompare
{
public:
bool operator()(const crypto::hash& a, const crypto::hash& b) const
{
return memcmp(a.data, b.data, sizeof(crypto::hash)) < 0;
}
};
//! container for sorting transactions by fee per unit size
typedef std::set<tx_by_fee_and_receive_time_entry, txCompare> sorted_tx_container;
typedef boost::bimap<boost::bimaps::multiset_of<std::pair<double, std::time_t>, txFeeCompare>,
boost::bimaps::set_of<crypto::hash, hashCompare>> sorted_tx_container;
/**
* @brief Transaction pool, handles transactions which are not part of a block
@ -653,7 +665,7 @@ private:
*
* @return an iterator, possibly to the end of the container if not found
*/
sorted_tx_container::iterator find_tx_in_sorted_container(const crypto::hash& id) const;
sorted_tx_container::iterator find_tx_in_sorted_container(const crypto::hash& id);
//! cache/call Blockchain::check_tx_inputs results
bool check_tx_inputs(const std::function<cryptonote::transaction&(void)> &get_tx, const crypto::hash &txid, uint64_t &max_used_block_height, crypto::hash &max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false) const;