Optimization: reuse reward vector

Profiling shows noticeable CPU spend in allocating memory for this
vector (which makes sense, since we are looping through ~1700 nodes and
building a reward vector for each one).  Avoid it by reusing a single
vector that gets cleared (but not reallocated more than a handful of
times).

This reduces batching CPU time in a debug build by about 12%; curiously
I didn't find a noticeable reduction in a release build.
This commit is contained in:
Jason Rhinelander 2022-06-18 13:45:29 -03:00
parent 230b6bdc0d
commit b5a5d01b04
No known key found for this signature in database
GPG key ID: C4992CE7A88D4262
3 changed files with 29 additions and 28 deletions

View file

@ -315,8 +315,7 @@ namespace cryptonote {
return result;
}
std::vector<cryptonote::batch_sn_payment> BlockchainSQLite::calculate_rewards(hf hf_version, uint64_t distribution_amount, service_nodes::service_node_info sn_info) {
void BlockchainSQLite::calculate_rewards(hf hf_version, uint64_t distribution_amount, const service_nodes::service_node_info& sn_info, std::vector<cryptonote::batch_sn_payment>& payments) {
LOG_PRINT_L3("BlockchainDB_SQLITE::" << __func__);
// Find out how much is due for the operator: fee_portions/PORTIONS * reward
@ -325,7 +324,7 @@ namespace cryptonote {
assert(operator_fee <= distribution_amount);
std::vector<cryptonote::batch_sn_payment> payments;
payments.clear();
// Pay the operator fee to the operator
if (operator_fee > 0)
payments.emplace_back(sn_info.operator_address, operator_fee);
@ -343,8 +342,6 @@ namespace cryptonote {
if (c_reward > 0)
payments.emplace_back(contributor.address, c_reward);
}
return payments;
}
// Calculates block rewards, then invokes either `add_sn_rewards` (if `add`) or
@ -367,6 +364,8 @@ namespace cryptonote {
uint64_t block_reward = block.reward * BATCH_REWARD_FACTOR;
uint64_t service_node_reward = cryptonote::service_node_reward_formula(0, block.major_version) * BATCH_REWARD_FACTOR;
std::vector<cryptonote::batch_sn_payment> payments;
// Step 1: Pay out the block producer their tx fees (note that, unlike the below, this applies
// even if the SN isn't currently payable).
if (block_reward < service_node_reward && m_nettype != cryptonote::network_type::FAKECHAIN)
@ -382,9 +381,9 @@ namespace cryptonote {
if (auto service_node_winner = service_nodes_state.service_nodes_infos.find(block.service_node_winner_key);
service_node_winner != service_nodes_state.service_nodes_infos.end()) {
auto tx_fee_payments = calculate_rewards(block.major_version, tx_fees, *service_node_winner->second);
calculate_rewards(block.major_version, tx_fees, *service_node_winner->second, payments);
// Takes the block producer and adds its contributors to the batching database for the transaction fees
if (!(this->*add_or_subtract)(tx_fee_payments))
if (!(this->*add_or_subtract)(payments))
return false;
}
}
@ -398,23 +397,23 @@ namespace cryptonote {
auto payable_service_node = service_nodes_state.service_nodes_infos.find(node_pubkey);
if (payable_service_node == service_nodes_state.service_nodes_infos.end())
continue;
auto snode_rewards = calculate_rewards(block.major_version, service_node_reward / total_service_nodes_payable, * payable_service_node -> second);
calculate_rewards(block.major_version, service_node_reward / total_service_nodes_payable, * payable_service_node -> second, payments);
// Takes the node and adds its contributors to the batching database
if (!(this->*add_or_subtract)(snode_rewards))
if (!(this->*add_or_subtract)(payments))
return false;
}
// Step 3: Add Governance reward to the list
if (m_nettype != cryptonote::network_type::FAKECHAIN) {
std::vector<cryptonote::batch_sn_payment> governance_rewards;
if (parsed_governance_addr.first != block.major_version) {
cryptonote::get_account_address_from_str(parsed_governance_addr.second, m_nettype,
cryptonote::get_config(m_nettype).governance_wallet_address(block.major_version));
parsed_governance_addr.first = block.major_version;
}
uint64_t foundation_reward = cryptonote::governance_reward_formula(block.major_version) * BATCH_REWARD_FACTOR;
governance_rewards.emplace_back(parsed_governance_addr.second.address, foundation_reward);
if (!(this->*add_or_subtract)(governance_rewards))
payments.clear();
payments.emplace_back(parsed_governance_addr.second.address, foundation_reward);
if (!(this->*add_or_subtract)(payments))
return false;
}

View file

@ -89,11 +89,12 @@ public:
// calculate_rewards -> takes the list of contributors from sn_info with their SN contribution
// amounts and will calculate how much of the block rewards should be the allocated to the
// contributors. The function will return a list suitable for passing to add_sn_payments
// contributors. The function will set a list suitable for passing to add_sn_payments into the
// vector (any existing values will be cleared).
//
// Note that distribution_amount here is typically passed as milli-atomic OXEN for extra
// precision.
std::vector<cryptonote::batch_sn_payment> calculate_rewards(hf hf_version, uint64_t distribution_amount, service_nodes::service_node_info sn_info);
void calculate_rewards(hf hf_version, uint64_t distribution_amount, const service_nodes::service_node_info& sn_info, std::vector<cryptonote::batch_sn_payment>& rewards);
// add/pop_block -> takes a block that contains new block rewards to be batched and added to the database
// and/or batching payments that need to be subtracted from the database, in addition it takes a reference to

View file

@ -103,7 +103,8 @@ TEST(SQLITE, CalculateRewards)
cryptonote::get_account_address_from_str(first_address, cryptonote::network_type::TESTNET, "T6TzkJb5EiASaCkcH7idBEi1HSrpSQJE1Zq3aL65ojBMPZvqHNYPTL56i3dncGVNEYCG5QG5zrBmRiVwcg6b1cRM1SRNqbp44");
single_contributor.contributors.emplace_back(0, first_address.address);
single_contributor.contributors.back().amount = block.reward;
auto rewards = sqliteDB.calculate_rewards(block.major_version, block.reward, single_contributor);
std::vector<cryptonote::batch_sn_payment> rewards;
sqliteDB.calculate_rewards(block.major_version, block.reward, single_contributor, rewards);
auto hf_version = block.major_version;
// Check that 3 contributor receives their portion of the block reward
@ -118,25 +119,25 @@ TEST(SQLITE, CalculateRewards)
cryptonote::get_account_address_from_str(third_address, cryptonote::network_type::TESTNET, "T6SkkovCyLWViVDMgeJoF7X4vFrHnKX5jXyktaoGmRuNTdoFEx1xXu1joXdmeH9mx2LLNPq998fKKcsAHwdRJWhk126SapptR");
multiple_contributors.contributors.emplace_back(0, third_address.address);
multiple_contributors.contributors.back().amount = 34;
auto multiple_rewards = sqliteDB.calculate_rewards(block.major_version, block.reward, multiple_contributors);
sqliteDB.calculate_rewards(block.major_version, block.reward, multiple_contributors, rewards);
EXPECT_EQ(multiple_rewards[0].amount, 66);
EXPECT_EQ(multiple_rewards[1].amount, 66);
EXPECT_EQ(multiple_rewards[2].amount, 68);
EXPECT_EQ(rewards[0].amount, 66);
EXPECT_EQ(rewards[1].amount, 66);
EXPECT_EQ(rewards[2].amount, 68);
// Check that 3 contributors receives their portion of the block reward when the operator takes a 10% fee
multiple_contributors.portions_for_operator = cryptonote::old::STAKING_PORTIONS/10;
multiple_contributors.operator_address = first_address.address;
block.reward = 1000;
auto multiple_rewards_with_fee = sqliteDB.calculate_rewards(block.major_version, block.reward, multiple_contributors);
sqliteDB.calculate_rewards(block.major_version, block.reward, multiple_contributors, rewards);
// Operator gets 10%
EXPECT_EQ(multiple_rewards_with_fee[0].amount, 99);
EXPECT_EQ(tools::view_guts(multiple_rewards_with_fee[0].address_info.address), tools::view_guts(first_address.address));
EXPECT_EQ(rewards[0].amount, 99);
EXPECT_EQ(tools::view_guts(rewards[0].address_info.address), tools::view_guts(first_address.address));
// Contributors (including operator) receive the balance
EXPECT_EQ(multiple_rewards_with_fee[1].amount, 297);
EXPECT_EQ(tools::view_guts(multiple_rewards_with_fee[1].address_info.address), tools::view_guts(first_address.address));
EXPECT_EQ(multiple_rewards_with_fee[2].amount, 297);
EXPECT_EQ(tools::view_guts(multiple_rewards_with_fee[2].address_info.address), tools::view_guts(second_address.address));
EXPECT_EQ(multiple_rewards_with_fee[3].amount, 306);
EXPECT_EQ(tools::view_guts(multiple_rewards_with_fee[3].address_info.address), tools::view_guts(third_address.address));
EXPECT_EQ(rewards[1].amount, 297);
EXPECT_EQ(tools::view_guts(rewards[1].address_info.address), tools::view_guts(first_address.address));
EXPECT_EQ(rewards[2].amount, 297);
EXPECT_EQ(tools::view_guts(rewards[2].address_info.address), tools::view_guts(second_address.address));
EXPECT_EQ(rewards[3].amount, 306);
EXPECT_EQ(tools::view_guts(rewards[3].address_info.address), tools::view_guts(third_address.address));
}