#![feature(test)] #![allow(clippy::integer_arithmetic)] extern crate test; use { log::*, solana_program_runtime::invoke_context::InvokeContext, solana_runtime::{ bank::{test_utils::goto_end_of_slot, *}, bank_client::BankClient, loader_utils::create_invoke_instruction, }, solana_sdk::{ client::{AsyncClient, SyncClient}, clock::MAX_RECENT_BLOCKHASHES, genesis_config::create_genesis_config, instruction::InstructionError, message::Message, pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction, }, std::{sync::Arc, thread::sleep, time::Duration}, test::Bencher, }; const BUILTIN_PROGRAM_ID: [u8; 32] = [ 98, 117, 105, 108, 116, 105, 110, 95, 112, 114, 111, 103, 114, 97, 109, 95, 105, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; const NOOP_PROGRAM_ID: [u8; 32] = [ 98, 117, 105, 108, 116, 105, 110, 95, 112, 114, 111, 103, 114, 97, 109, 95, 105, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ]; #[allow(clippy::unnecessary_wraps)] fn process_instruction( _first_instruction_account: usize, _invoke_context: &mut InvokeContext, ) -> Result<(), InstructionError> { Ok(()) } pub fn create_builtin_transactions( bank_client: &BankClient, mint_keypair: &Keypair, ) -> Vec { let program_id = Pubkey::from(BUILTIN_PROGRAM_ID); (0..4096) .map(|_| { // Seed the signer account let rando0 = Keypair::new(); bank_client .transfer_and_confirm(10_000, mint_keypair, &rando0.pubkey()) .unwrap_or_else(|_| panic!("{}:{}", line!(), file!())); let instruction = create_invoke_instruction(rando0.pubkey(), program_id, &1u8); let blockhash = bank_client.get_latest_blockhash().unwrap(); let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); Transaction::new(&[&rando0], message, blockhash) }) .collect() } pub fn create_native_loader_transactions( bank_client: &BankClient, mint_keypair: &Keypair, ) -> Vec { let program_id = Pubkey::from(NOOP_PROGRAM_ID); (0..4096) .map(|_| { // Seed the signer account©41 let rando0 = Keypair::new(); bank_client .transfer_and_confirm(10_000, mint_keypair, &rando0.pubkey()) .unwrap_or_else(|_| panic!("{}:{}", line!(), file!())); let instruction = create_invoke_instruction(rando0.pubkey(), program_id, &1u8); let blockhash = bank_client.get_latest_blockhash().unwrap(); let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); Transaction::new(&[&rando0], message, blockhash) }) .collect() } fn sync_bencher(bank: &Arc, _bank_client: &BankClient, transactions: &[Transaction]) { let results = bank.process_transactions(transactions.iter()); assert!(results.iter().all(Result::is_ok)); } fn async_bencher(bank: &Arc, bank_client: &BankClient, transactions: &[Transaction]) { for transaction in transactions.iter().cloned() { 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_err() { error!( "transaction failed: {:?}", bank.get_signature_status(transactions.last().unwrap().signatures.get(0).unwrap()) .unwrap() ); panic!(); } } #[allow(clippy::type_complexity)] fn do_bench_transactions( bencher: &mut Bencher, bench_work: &dyn Fn(&Arc, &BankClient, &[Transaction]), create_transactions: &dyn Fn(&BankClient, &Keypair) -> Vec, ) { solana_logger::setup(); let ns_per_s = 1_000_000_000; let (mut genesis_config, mint_keypair) = create_genesis_config(100_000_000_000_000); genesis_config.ticks_per_slot = 100; let bank = Bank::new_for_benches(&genesis_config); // freeze bank so that slot hashes is populated bank.freeze(); let mut bank = Bank::new_from_parent(&Arc::new(bank), &Pubkey::default(), 1); bank.add_builtin( "builtin_program", &Pubkey::from(BUILTIN_PROGRAM_ID), process_instruction, ); bank.add_builtin_account("solana_noop_program", &Pubkey::from(NOOP_PROGRAM_ID), false); 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.iter()); assert!(results.iter().all(Result::is_ok)); bencher.iter(|| { // Since bencher runs this multiple times, we need to clear the signatures. bank.clear_signatures(); 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] #[ignore] fn bench_bank_sync_process_builtin_transactions(bencher: &mut Bencher) { do_bench_transactions(bencher, &sync_bencher, &create_builtin_transactions); } #[bench] #[ignore] fn bench_bank_sync_process_native_loader_transactions(bencher: &mut Bencher) { do_bench_transactions(bencher, &sync_bencher, &create_native_loader_transactions); } #[bench] #[ignore] fn bench_bank_async_process_builtin_transactions(bencher: &mut Bencher) { do_bench_transactions(bencher, &async_bencher, &create_builtin_transactions); } #[bench] #[ignore] fn bench_bank_async_process_native_loader_transactions(bencher: &mut Bencher) { do_bench_transactions(bencher, &async_bencher, &create_native_loader_transactions); } #[bench] #[ignore] fn bench_bank_update_recent_blockhashes(bencher: &mut Bencher) { let (genesis_config, _mint_keypair) = create_genesis_config(100); let mut bank = Arc::new(Bank::new_for_benches(&genesis_config)); goto_end_of_slot(Arc::get_mut(&mut bank).unwrap()); let genesis_hash = bank.last_blockhash(); // Prime blockhash_queue for i in 0..(MAX_RECENT_BLOCKHASHES + 1) { bank = Arc::new(Bank::new_from_parent( &bank, &Pubkey::default(), (i + 1) as u64, )); goto_end_of_slot(Arc::get_mut(&mut bank).unwrap()); } // Verify blockhash_queue is full (genesis hash has been kicked out) assert!(!bank.is_hash_valid_for_age(&genesis_hash, MAX_RECENT_BLOCKHASHES)); bencher.iter(|| { bank.update_recent_blockhashes(); }); }