Skip to content

Commit

Permalink
TransactionCost - hold transaction reference (solana-labs#3162)
Browse files Browse the repository at this point in the history
  • Loading branch information
apfitzge authored Oct 16, 2024
1 parent a8aef04 commit 6a70b6c
Show file tree
Hide file tree
Showing 9 changed files with 433 additions and 355 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ serde_json = { workspace = true }
serial_test = { workspace = true }
# See order-crates-for-publishing.py for using this unusual `path = "."`
solana-core = { path = ".", features = ["dev-context-only-utils"] }
solana-cost-model = { workspace = true, features = ["dev-context-only-utils"] }
solana-ledger = { workspace = true, features = ["dev-context-only-utils"] }
solana-logger = { workspace = true }
solana-poh = { workspace = true, features = ["dev-context-only-utils"] }
Expand All @@ -123,9 +124,7 @@ test-case = { workspace = true }
sysctl = { workspace = true }

[features]
dev-context-only-utils = [
"solana-runtime/dev-context-only-utils",
]
dev-context-only-utils = ["solana-runtime/dev-context-only-utils"]
frozen-abi = [
"dep:solana-frozen-abi",
"dep:solana-frozen-abi-macro",
Expand Down
36 changes: 28 additions & 8 deletions core/src/banking_stage/forward_packet_batches_by_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use {
solana_feature_set::FeatureSet,
solana_perf::packet::Packet,
solana_sdk::transaction::SanitizedTransaction,
solana_svm_transaction::svm_message::SVMMessage,
std::sync::Arc,
};

Expand Down Expand Up @@ -146,7 +147,7 @@ impl ForwardPacketBatchesByAccounts {
// put into batch #3 to satisfy all batch limits.
fn get_batch_index_by_updated_costs(
&self,
tx_cost: &TransactionCost,
tx_cost: &TransactionCost<impl SVMMessage>,
updated_costs: &UpdatedCosts,
) -> usize {
let Some(batch_index_by_block_limit) =
Expand All @@ -170,11 +171,14 @@ mod tests {
use {
super::*,
crate::banking_stage::unprocessed_packet_batches::DeserializedPacket,
solana_cost_model::transaction_cost::UsageCostDetails,
solana_cost_model::transaction_cost::{UsageCostDetails, WritableKeysTransaction},
solana_feature_set::FeatureSet,
solana_sdk::{
compute_budget::ComputeBudgetInstruction, message::Message, pubkey::Pubkey,
system_instruction, transaction::Transaction,
compute_budget::ComputeBudgetInstruction,
message::{Message, TransactionSignatureDetails},
pubkey::Pubkey,
system_instruction,
transaction::Transaction,
},
};

Expand Down Expand Up @@ -206,6 +210,21 @@ mod tests {
(sanitized_transaction, deserialized_packet, limit_ratio)
}

fn zero_transaction_cost() -> TransactionCost<'static, WritableKeysTransaction> {
static DUMMY_TRANSACTION: WritableKeysTransaction = WritableKeysTransaction(vec![]);

TransactionCost::Transaction(UsageCostDetails {
transaction: &DUMMY_TRANSACTION,
signature_cost: 0,
write_lock_cost: 0,
data_bytes_cost: 0,
programs_execution_cost: 0,
loaded_accounts_data_size_cost: 0,
allocated_accounts_data_size: 0,
signature_details: TransactionSignatureDetails::new(0, 0, 0),
})
}

#[test]
fn test_try_add_packet_to_multiple_batches() {
// setup two transactions, one has high priority that writes to hot account, the
Expand Down Expand Up @@ -351,8 +370,9 @@ mod tests {
ForwardPacketBatchesByAccounts::new_with_default_batch_limits();
forward_packet_batches_by_accounts.batch_vote_limit = test_cost + 1;

let dummy_transaction = WritableKeysTransaction(vec![]);
let transaction_cost = TransactionCost::SimpleVote {
writable_accounts: vec![],
transaction: &dummy_transaction,
};
assert_eq!(
0,
Expand Down Expand Up @@ -382,7 +402,7 @@ mod tests {
ForwardPacketBatchesByAccounts::new_with_default_batch_limits();
forward_packet_batches_by_accounts.batch_block_limit = test_cost + 1;

let transaction_cost = TransactionCost::Transaction(UsageCostDetails::default());
let transaction_cost = zero_transaction_cost();
assert_eq!(
0,
forward_packet_batches_by_accounts.get_batch_index_by_updated_costs(
Expand Down Expand Up @@ -411,7 +431,7 @@ mod tests {
ForwardPacketBatchesByAccounts::new_with_default_batch_limits();
forward_packet_batches_by_accounts.batch_account_limit = test_cost + 1;

let transaction_cost = TransactionCost::Transaction(UsageCostDetails::default());
let transaction_cost = zero_transaction_cost();
assert_eq!(
0,
forward_packet_batches_by_accounts.get_batch_index_by_updated_costs(
Expand Down Expand Up @@ -445,7 +465,7 @@ mod tests {
forward_packet_batches_by_accounts.batch_vote_limit = test_cost / 2 + 1;
forward_packet_batches_by_accounts.batch_account_limit = test_cost / 3 + 1;

let transaction_cost = TransactionCost::Transaction(UsageCostDetails::default());
let transaction_cost = zero_transaction_cost();
assert_eq!(
2,
forward_packet_batches_by_accounts.get_batch_index_by_updated_costs(
Expand Down
46 changes: 33 additions & 13 deletions core/src/banking_stage/qos_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use {
saturating_add_assign,
transaction::{self, SanitizedTransaction, TransactionError},
},
solana_svm_transaction::svm_message::SVMMessage,
std::sync::atomic::{AtomicU64, Ordering},
};

Expand All @@ -39,12 +40,15 @@ impl QosService {
/// include in the slot, and accumulate costs in the cost tracker.
/// Returns a vector of results containing selected transaction costs, and the number of
/// transactions that were *NOT* selected.
pub fn select_and_accumulate_transaction_costs(
pub fn select_and_accumulate_transaction_costs<'a>(
&self,
bank: &Bank,
transactions: &[SanitizedTransaction],
transactions: &'a [SanitizedTransaction],
pre_results: impl Iterator<Item = transaction::Result<()>>,
) -> (Vec<transaction::Result<TransactionCost>>, u64) {
) -> (
Vec<transaction::Result<TransactionCost<'a, SanitizedTransaction>>>,
u64,
) {
let transaction_costs =
self.compute_transaction_costs(&bank.feature_set, transactions.iter(), pre_results);
let (transactions_qos_cost_results, num_included) = self.select_transactions_per_cost(
Expand All @@ -71,7 +75,7 @@ impl QosService {
feature_set: &FeatureSet,
transactions: impl Iterator<Item = &'a SanitizedTransaction>,
pre_results: impl Iterator<Item = transaction::Result<()>>,
) -> Vec<transaction::Result<TransactionCost>> {
) -> Vec<transaction::Result<TransactionCost<'a, SanitizedTransaction>>> {
let mut compute_cost_time = Measure::start("compute_cost_time");
let txs_costs: Vec<_> = transactions
.zip(pre_results)
Expand All @@ -95,9 +99,14 @@ impl QosService {
fn select_transactions_per_cost<'a>(
&self,
transactions: impl Iterator<Item = &'a SanitizedTransaction>,
transactions_costs: impl Iterator<Item = transaction::Result<TransactionCost>>,
transactions_costs: impl Iterator<
Item = transaction::Result<TransactionCost<'a, SanitizedTransaction>>,
>,
bank: &Bank,
) -> (Vec<transaction::Result<TransactionCost>>, usize) {
) -> (
Vec<transaction::Result<TransactionCost<'a, SanitizedTransaction>>>,
usize,
) {
let mut cost_tracking_time = Measure::start("cost_tracking_time");
let mut cost_tracker = bank.write_cost_tracker().unwrap();
let mut num_included = 0;
Expand Down Expand Up @@ -153,7 +162,9 @@ impl QosService {
/// Removes transaction costs from the cost tracker if not committed or recorded, or
/// updates the transaction costs for committed transactions.
pub fn remove_or_update_costs<'a>(
transaction_cost_results: impl Iterator<Item = &'a transaction::Result<TransactionCost>>,
transaction_cost_results: impl Iterator<
Item = &'a transaction::Result<TransactionCost<'a, SanitizedTransaction>>,
>,
transaction_committed_status: Option<&Vec<CommitTransactionDetails>>,
bank: &Bank,
) {
Expand All @@ -172,7 +183,9 @@ impl QosService {
/// For recorded transactions, remove units reserved by uncommitted transaction, or update
/// units for committed transactions.
fn remove_or_update_recorded_transaction_costs<'a>(
transaction_cost_results: impl Iterator<Item = &'a transaction::Result<TransactionCost>>,
transaction_cost_results: impl Iterator<
Item = &'a transaction::Result<TransactionCost<'a, SanitizedTransaction>>,
>,
transaction_committed_status: &Vec<CommitTransactionDetails>,
bank: &Bank,
) {
Expand Down Expand Up @@ -210,7 +223,9 @@ impl QosService {

/// Remove reserved units for transaction batch that unsuccessfully recorded.
fn remove_unrecorded_transaction_costs<'a>(
transaction_cost_results: impl Iterator<Item = &'a transaction::Result<TransactionCost>>,
transaction_cost_results: impl Iterator<
Item = &'a transaction::Result<TransactionCost<'a, SanitizedTransaction>>,
>,
bank: &Bank,
) {
let mut cost_tracker = bank.write_cost_tracker().unwrap();
Expand Down Expand Up @@ -326,8 +341,8 @@ impl QosService {

// rollup transaction cost details, eg signature_cost, write_lock_cost, data_bytes_cost and
// execution_cost from the batch of transactions selected for block.
fn accumulate_batched_transaction_costs<'a>(
transactions_costs: impl Iterator<Item = &'a transaction::Result<TransactionCost>>,
fn accumulate_batched_transaction_costs<'a, Tx: SVMMessage + 'a>(
transactions_costs: impl Iterator<Item = &'a transaction::Result<TransactionCost<'a, Tx>>>,
) -> BatchedTransactionDetails {
let mut batched_transaction_details = BatchedTransactionDetails::default();
transactions_costs.for_each(|cost| match cost {
Expand Down Expand Up @@ -609,10 +624,11 @@ mod tests {
use {
super::*,
itertools::Itertools,
solana_cost_model::transaction_cost::UsageCostDetails,
solana_cost_model::transaction_cost::{UsageCostDetails, WritableKeysTransaction},
solana_runtime::genesis_utils::{create_genesis_config, GenesisConfigInfo},
solana_sdk::{
hash::Hash,
message::TransactionSignatureDetails,
signature::{Keypair, Signer},
system_transaction,
},
Expand Down Expand Up @@ -935,15 +951,19 @@ mod tests {
let programs_execution_cost = 10;
let num_txs = 4;

let dummy_transaction = WritableKeysTransaction(vec![]);
let tx_cost_results: Vec<_> = (0..num_txs)
.map(|n| {
if n % 2 == 0 {
Ok(TransactionCost::Transaction(UsageCostDetails {
transaction: &dummy_transaction,
signature_cost,
write_lock_cost,
data_bytes_cost,
programs_execution_cost,
..UsageCostDetails::default()
loaded_accounts_data_size_cost: 0,
allocated_accounts_data_size: 0,
signature_details: TransactionSignatureDetails::new(0, 0, 0),
}))
} else {
Err(TransactionError::WouldExceedMaxBlockCostLimit)
Expand Down
3 changes: 3 additions & 0 deletions cost-model/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ name = "solana_cost_model"
[dev-dependencies]
itertools = { workspace = true }
rand = "0.8.5"
# See order-crates-for-publishing.py for using this unusual `path = "."`
solana-cost-model = { path = ".", features = ["dev-context-only-utils"] }
solana-logger = { workspace = true }
solana-sdk = { workspace = true, features = ["dev-context-only-utils"] }
solana-system-program = { workspace = true }
Expand All @@ -45,6 +47,7 @@ test-case = { workspace = true }
targets = ["x86_64-unknown-linux-gnu"]

[features]
dev-context-only-utils = []
frozen-abi = [
"dep:solana-frozen-abi",
"dep:solana-frozen-abi-macro",
Expand Down
45 changes: 32 additions & 13 deletions cost-model/benches/cost_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ use {
itertools::Itertools,
solana_cost_model::{
cost_tracker::CostTracker,
transaction_cost::{TransactionCost, UsageCostDetails},
transaction_cost::{TransactionCost, UsageCostDetails, WritableKeysTransaction},
},
solana_sdk::pubkey::Pubkey,
solana_sdk::{message::TransactionSignatureDetails, pubkey::Pubkey},
test::Bencher,
};

struct BenchSetup {
cost_tracker: CostTracker,
tx_costs: Vec<TransactionCost>,
transactions: Vec<WritableKeysTransaction>,
}

fn setup(num_transactions: usize, contentious_transactions: bool) -> BenchSetup {
Expand All @@ -22,36 +22,54 @@ fn setup(num_transactions: usize, contentious_transactions: bool) -> BenchSetup

let max_accounts_per_tx = 128;
let pubkey = Pubkey::new_unique();
let tx_costs = (0..num_transactions)
let transactions = (0..num_transactions)
.map(|_| {
let mut usage_cost_details = UsageCostDetails::default();
let mut writable_accounts = Vec::with_capacity(max_accounts_per_tx);
(0..max_accounts_per_tx).for_each(|_| {
let writable_account_key = if contentious_transactions {
pubkey
} else {
Pubkey::new_unique()
};
usage_cost_details
.writable_accounts
.push(writable_account_key)
writable_accounts.push(writable_account_key)
});
usage_cost_details.programs_execution_cost = 9999;
TransactionCost::Transaction(usage_cost_details)
WritableKeysTransaction(writable_accounts)
})
.collect_vec();

BenchSetup {
cost_tracker,
tx_costs,
transactions,
}
}

fn get_costs(
transactions: &[WritableKeysTransaction],
) -> Vec<TransactionCost<WritableKeysTransaction>> {
transactions
.iter()
.map(|transaction| {
TransactionCost::Transaction(UsageCostDetails {
transaction,
signature_cost: 0,
write_lock_cost: 0,
data_bytes_cost: 0,
programs_execution_cost: 9999,
loaded_accounts_data_size_cost: 0,
allocated_accounts_data_size: 0,
signature_details: TransactionSignatureDetails::new(0, 0, 0),
})
})
.collect_vec()
}

#[bench]
fn bench_cost_tracker_non_contentious_transaction(bencher: &mut Bencher) {
let BenchSetup {
mut cost_tracker,
tx_costs,
transactions,
} = setup(1024, false);
let tx_costs = get_costs(&transactions);

bencher.iter(|| {
for tx_cost in tx_costs.iter() {
Expand All @@ -67,8 +85,9 @@ fn bench_cost_tracker_non_contentious_transaction(bencher: &mut Bencher) {
fn bench_cost_tracker_contentious_transaction(bencher: &mut Bencher) {
let BenchSetup {
mut cost_tracker,
tx_costs,
transactions,
} = setup(1024, true);
let tx_costs = get_costs(&transactions);

bencher.iter(|| {
for tx_cost in tx_costs.iter() {
Expand Down
Loading

0 comments on commit 6a70b6c

Please sign in to comment.