From c545e812d0ffcb3ac1a1035da7c08b2c6630a80e Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 29 Apr 2019 13:09:11 -0700 Subject: [PATCH] Expand bank benches to include async/sync and native_loader (#4026) --- ci/test-bench.sh | 5 +- runtime/benches/bank.rs | 184 ++++++++++++++++++++++++++++--------- runtime/src/bank_client.rs | 7 +- 3 files changed, 148 insertions(+), 48 deletions(-) diff --git a/ci/test-bench.sh b/ci/test-bench.sh index 589fcbd73..c990c2090 100755 --- a/ci/test-bench.sh +++ b/ci/test-bench.sh @@ -40,7 +40,10 @@ fi BENCH_FILE=bench_output.log BENCH_ARTIFACT=current_bench_results.log -# First remove "BENCH_FILE", if it exists so that the following commands can append +# Ensure all dependencies are built +_ cargo +$rust_nightly build --all --release + +# Remove "BENCH_FILE", if it exists so that the following commands can append rm -f "$BENCH_FILE" # Run sdk benches diff --git a/runtime/benches/bank.rs b/runtime/benches/bank.rs index 5a88cf0d3..5207a5a82 100644 --- a/runtime/benches/bank.rs +++ b/runtime/benches/bank.rs @@ -2,75 +2,169 @@ extern crate test; +use log::*; use solana_runtime::bank::*; use solana_runtime::bank_client::BankClient; +use solana_runtime::loader_utils::{create_invoke_instruction, load_program}; +use solana_sdk::account::KeyedAccount; use solana_sdk::client::AsyncClient; +use solana_sdk::client::SyncClient; use solana_sdk::genesis_block::GenesisBlock; -use solana_sdk::hash::hash; +use solana_sdk::instruction::InstructionError; +use solana_sdk::native_loader; +use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil}; -use solana_sdk::system_transaction; -use solana_sdk::timing::{DEFAULT_TICKS_PER_SLOT, MAX_RECENT_BLOCKHASHES}; use solana_sdk::transaction::Transaction; +use std::sync::Arc; +use std::thread::sleep; +use std::time::Duration; use test::Bencher; -// Create transactions between unrelated parties. -pub fn create_sample_transactions(bank: &Bank, mint_keypair: &Keypair) -> Vec { +const BUILTIN_PROGRAM_ID: [u8; 32] = [ + 098, 117, 105, 108, 116, 105, 110, 095, 112, 114, 111, 103, 114, 097, 109, 095, 105, 100, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +fn process_instruction( + _program_id: &Pubkey, + _keyed_accounts: &mut [KeyedAccount], + _data: &[u8], + _tick_height: u64, +) -> Result<(), InstructionError> { + Ok(()) +} + +pub fn create_builtin_transactions( + bank_client: &BankClient, + mint_keypair: &Keypair, +) -> Vec { + let program_id = Pubkey::new(&BUILTIN_PROGRAM_ID); + (0..4096) .into_iter() .map(|_| { - // Seed the 'from' account. + // Seed the signer account let rando0 = Keypair::new(); - let tx = system_transaction::transfer( - &mint_keypair, - &rando0.pubkey(), - 10_000, - bank.last_blockhash(), - 0, - ); - assert_eq!(bank.process_transaction(&tx), Ok(())); + bank_client + .transfer(10_000, &mint_keypair, &rando0.pubkey()) + .expect(&format!("{}:{}", line!(), file!())); - // Seed the 'to' account and a cell for its signature. - let rando1 = Keypair::new(); - system_transaction::transfer(&rando0, &rando1.pubkey(), 1, bank.last_blockhash(), 0) + let instruction = create_invoke_instruction(rando0.pubkey(), program_id, &1u8); + Transaction::new_signed_instructions( + &[&rando0], + vec![instruction], + bank_client.get_recent_blockhash().unwrap(), + ) }) .collect() } -#[bench] -fn bench_process_transaction(bencher: &mut Bencher) { - let (genesis_block, mint_keypair) = GenesisBlock::new(100_000_000); - let bank = Bank::new(&genesis_block); - let transactions = create_sample_transactions(&bank, &mint_keypair); +pub fn create_native_loader_transactions( + bank_client: &BankClient, + mint_keypair: &Keypair, +) -> Vec { + let program = "solana_noop_program".as_bytes().to_vec(); + let program_id = load_program(&bank_client, &mint_keypair, &native_loader::id(), program); - // Run once to create all the 'to' accounts. + (0..4096) + .into_iter() + .map(|_| { + // Seed the signer account©41 + let rando0 = Keypair::new(); + bank_client + .transfer(10_000, &mint_keypair, &rando0.pubkey()) + .expect(&format!("{}:{}", line!(), file!())); + + let instruction = create_invoke_instruction(rando0.pubkey(), program_id, &1u8); + Transaction::new_signed_instructions( + &[&rando0], + vec![instruction], + bank_client.get_recent_blockhash().unwrap(), + ) + }) + .collect() +} + +fn sync_bencher(bank: &Arc, _bank_client: &BankClient, transactions: &Vec) { + let results = bank.process_transactions(&transactions); + assert!(results.iter().all(Result::is_ok)); +} + +fn async_bencher(bank: &Arc, bank_client: &BankClient, transactions: &Vec) { + for transaction in transactions.clone() { + bank_client.async_send_transaction(transaction).unwrap(); + } + for _ in 0..1_000_000_000_u64 { + if bank + .get_signature_status(&transactions.last().unwrap().signatures.get(0).unwrap()) + .is_some() + { + break; + } + sleep(Duration::from_nanos(1)); + } + if !bank + .get_signature_status(&transactions.last().unwrap().signatures.get(0).unwrap()) + .unwrap() + .is_ok() + { + error!( + "transaction failed: {:?}", + bank.get_signature_status(&transactions.last().unwrap().signatures.get(0).unwrap()) + .unwrap() + ); + assert!(false); + } +} + +fn do_bench_transactions( + bencher: &mut Bencher, + bench_work: &Fn(&Arc, &BankClient, &Vec), + create_transactions: &Fn(&BankClient, &Keypair) -> Vec, +) { + solana_logger::setup(); + let ns_per_s = 1_000_000_000; + let (genesis_block, mint_keypair) = GenesisBlock::new(100_000_000); + let mut bank = Bank::new(&genesis_block); + bank.add_instruction_processor(Pubkey::new(&BUILTIN_PROGRAM_ID), process_instruction); + let bank = Arc::new(bank); + let bank_client = BankClient::new_shared(&bank); + let transactions = create_transactions(&bank_client, &mint_keypair); + + // Do once to fund accounts, load modules, etc... let results = bank.process_transactions(&transactions); assert!(results.iter().all(Result::is_ok)); - let mut id = bank.last_blockhash(); - for _ in 0..(MAX_RECENT_BLOCKHASHES * DEFAULT_TICKS_PER_SLOT as usize) { - bank.register_tick(&id); - id = hash(&id.as_ref()) - } - bencher.iter(|| { - // Since benchmarker runs this multiple times, we need to clear the signatures. + // Since bencher runs this multiple times, we need to clear the signatures. bank.clear_signatures(); - let results = bank.process_transactions(&transactions); - assert!(results.iter().all(Result::is_ok)); - }) + bench_work(&bank, &bank_client, &transactions); + }); + + let summary = bencher.bench(|_bencher| {}).unwrap(); + info!(" {:?} transactions", transactions.len()); + info!(" {:?} ns/iter median", summary.median as u64); + assert!(0f64 != summary.median); + let tps = transactions.len() as u64 * (ns_per_s / summary.median as u64); + info!(" {:?} TPS", tps); } #[bench] -fn bench_bank_client(bencher: &mut Bencher) { - let (genesis_block, mint_keypair) = GenesisBlock::new(100_000_000); - let bank = Bank::new(&genesis_block); - let transactions = create_sample_transactions(&bank, &mint_keypair); - - bencher.iter(|| { - let bank = Bank::new(&genesis_block); - let bank_client = BankClient::new(bank); - for transaction in transactions.clone() { - bank_client.async_send_transaction(transaction).unwrap(); - } - }) +fn bench_bank_sync_process_builtin_transactions(bencher: &mut Bencher) { + do_bench_transactions(bencher, &sync_bencher, &create_builtin_transactions); +} + +#[bench] +fn bench_bank_sync_process_native_loader_transactions(bencher: &mut Bencher) { + do_bench_transactions(bencher, &sync_bencher, &create_native_loader_transactions); +} + +#[bench] +fn bench_bank_async_process_builtin_transactions(bencher: &mut Bencher) { + do_bench_transactions(bencher, &async_bencher, &create_builtin_transactions); +} + +#[bench] +fn bench_bank_async_process_native_loader_transactions(bencher: &mut Bencher) { + do_bench_transactions(bencher, &async_bencher, &create_native_loader_transactions); } diff --git a/runtime/src/bank_client.rs b/runtime/src/bank_client.rs index c75dd7708..96bbf7a26 100644 --- a/runtime/src/bank_client.rs +++ b/runtime/src/bank_client.rs @@ -191,8 +191,7 @@ impl BankClient { } } - pub fn new(bank: Bank) -> Self { - let bank = Arc::new(bank); + pub fn new_shared(bank: &Arc) -> Self { let (transaction_sender, transaction_receiver) = channel(); let transaction_sender = Mutex::new(transaction_sender); let thread_bank = bank.clone(); @@ -206,6 +205,10 @@ impl BankClient { transaction_sender, } } + + pub fn new(bank: Bank) -> Self { + Self::new_shared(&Arc::new(bank)) + } } #[cfg(test)]