diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs index 5350a530c6..5bb2cfaf2a 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -236,6 +236,27 @@ impl<'a> InvokeContext<'a> { } } + pub fn new_mock_with_sysvars_and_features( + transaction_context: &'a mut TransactionContext, + sysvar_cache: &'a SysvarCache, + feature_set: Arc, + ) -> Self { + Self::new( + transaction_context, + Rent::default(), + &[], + Cow::Borrowed(sysvar_cache), + Some(LogCollector::new_ref()), + ComputeBudget::default(), + Rc::new(RefCell::new(Executors::default())), + None, + feature_set, + Hash::default(), + 0, + 0, + ) + } + pub fn new_mock( transaction_context: &'a mut TransactionContext, builtin_programs: &'a [BuiltinProgram], diff --git a/programs/vote/benches/process_vote.rs b/programs/vote/benches/process_vote.rs new file mode 100644 index 0000000000..322e25985a --- /dev/null +++ b/programs/vote/benches/process_vote.rs @@ -0,0 +1,169 @@ +#![feature(test)] + +extern crate test; + +use { + solana_program_runtime::{invoke_context::InvokeContext, sysvar_cache::SysvarCache}, + solana_sdk::{ + account::{create_account_for_test, Account, AccountSharedData}, + clock::{Clock, Slot}, + feature_set::FeatureSet, + hash::Hash, + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + slot_hashes::{SlotHashes, MAX_ENTRIES}, + sysvar, + transaction_context::{InstructionAccount, TransactionContext}, + }, + solana_vote_program::{ + vote_instruction::VoteInstruction, + vote_state::{Vote, VoteInit, VoteState, VoteStateVersions, MAX_LOCKOUT_HISTORY}, + }, + std::sync::Arc, + test::Bencher, +}; + +/// `feature` can be used to change vote program behavior per bench run. +fn do_bench(bencher: &mut Bencher, feature: Option) { + // vote accounts are usually almost full of votes in normal operation + let num_initial_votes = MAX_LOCKOUT_HISTORY; + let num_vote_slots: usize = 4; + let last_vote_slot = num_initial_votes + .saturating_add(num_vote_slots) + .saturating_sub(1); + let last_vote_hash = Hash::new_unique(); + + let clock = Clock::default(); + let mut slot_hashes = SlotHashes::new(&[]); + for i in 0..MAX_ENTRIES { + // slot hashes is full in normal operation + slot_hashes.add( + i as Slot, + if i == last_vote_slot { + last_vote_hash + } else { + Hash::default() + }, + ); + } + + let vote_pubkey = Pubkey::new_unique(); + let authority_pubkey = Pubkey::new_unique(); + let vote_account = { + let mut vote_state = VoteState::new( + &VoteInit { + node_pubkey: authority_pubkey, + authorized_voter: authority_pubkey, + authorized_withdrawer: authority_pubkey, + commission: 0, + }, + &clock, + ); + + for next_vote_slot in 0..num_initial_votes as u64 { + vote_state.process_next_vote_slot(next_vote_slot, 0); + } + + let mut vote_account_data: Vec = vec![0; VoteState::size_of()]; + let versioned = VoteStateVersions::new_current(vote_state); + VoteState::serialize(&versioned, &mut vote_account_data).unwrap(); + + Account { + lamports: 1, + data: vote_account_data, + owner: solana_vote_program::id(), + executable: false, + rent_epoch: 0, + } + }; + let slot_hashes_account = create_account_for_test(&slot_hashes); + let clock_account = create_account_for_test(&clock); + let authority_account = Account::default(); + + let mut sysvar_cache = SysvarCache::default(); + sysvar_cache.set_clock(clock); + sysvar_cache.set_slot_hashes(slot_hashes); + + let mut feature_set = FeatureSet::all_enabled(); + if let Some(feature) = feature { + feature_set.activate(&feature, 0); + } + let feature_set = Arc::new(feature_set); + + let vote_ix_data = bincode::serialize(&VoteInstruction::Vote(Vote::new( + (num_initial_votes as u64..).take(num_vote_slots).collect(), + last_vote_hash, + ))) + .unwrap(); + + let instruction = Instruction { + program_id: solana_vote_program::id(), + data: vote_ix_data, + accounts: vec![ + AccountMeta::new(vote_pubkey, false), + AccountMeta::new_readonly(sysvar::slot_hashes::id(), false), + AccountMeta::new_readonly(sysvar::clock::id(), false), + AccountMeta::new_readonly(authority_pubkey, true), + ], + }; + + let program_indices = vec![4]; + let instruction_accounts = instruction + .accounts + .iter() + .enumerate() + .map(|(index_in_transaction, account_meta)| InstructionAccount { + index_in_transaction, + index_in_caller: index_in_transaction, + is_signer: account_meta.is_signer, + is_writable: account_meta.is_writable, + }) + .collect::>(); + + bencher.iter(|| { + let mut transaction_context = TransactionContext::new( + vec![ + (vote_pubkey, AccountSharedData::from(vote_account.clone())), + ( + sysvar::slot_hashes::id(), + AccountSharedData::from(slot_hashes_account.clone()), + ), + ( + sysvar::clock::id(), + AccountSharedData::from(clock_account.clone()), + ), + ( + authority_pubkey, + AccountSharedData::from(authority_account.clone()), + ), + (solana_vote_program::id(), AccountSharedData::default()), + ], + 1, + ); + + let mut invoke_context = InvokeContext::new_mock_with_sysvars_and_features( + &mut transaction_context, + &sysvar_cache, + feature_set.clone(), + ); + + invoke_context + .push(&instruction_accounts, &program_indices, &[]) + .unwrap(); + + let first_instruction_account = 1; + assert_eq!( + solana_vote_program::vote_instruction::process_instruction( + first_instruction_account, + &instruction.data, + &mut invoke_context + ), + Ok(()) + ); + }); +} + +#[bench] +fn bench_process_vote_instruction(bencher: &mut Bencher) { + do_bench(bencher, None); +} diff --git a/runtime/benches/bank.rs b/runtime/benches/bank.rs index f680bde5fa..cacb43e45b 100644 --- a/runtime/benches/bank.rs +++ b/runtime/benches/bank.rs @@ -17,10 +17,6 @@ use { signature::{Keypair, Signer}, transaction::Transaction, }, - solana_vote_program::{ - vote_instruction, - vote_state::{Vote, VoteInit}, - }, std::{sync::Arc, thread::sleep, time::Duration}, test::Bencher, }; @@ -66,52 +62,6 @@ pub fn create_builtin_transactions( .collect() } -pub fn create_vote_transactions( - bank_client: &BankClient, - mint_keypair: &Keypair, -) -> Vec { - let blockhash = bank_client.get_latest_blockhash().unwrap(); - (0..4096) - .map(|_| { - // Seed the signer account - let payer_keypair = Keypair::new(); - bank_client - .transfer_and_confirm(27_000_000, mint_keypair, &payer_keypair.pubkey()) - .unwrap_or_else(|_| panic!("{}:{}", line!(), file!())); - - // Setup vote - let vote_keypair = Keypair::new(); - let instructions = vote_instruction::create_account( - &payer_keypair.pubkey(), - &vote_keypair.pubkey(), - &VoteInit { - node_pubkey: payer_keypair.pubkey(), - authorized_voter: payer_keypair.pubkey(), - authorized_withdrawer: payer_keypair.pubkey(), - commission: 100u8, - }, - 26_858_640, - ); - let message = Message::new(&instructions, Some(&payer_keypair.pubkey())); - bank_client - .send_and_confirm_message(&[&payer_keypair, &vote_keypair], message) - .unwrap(); - - let vote_ix = vote_instruction::vote( - &vote_keypair.pubkey(), - &payer_keypair.pubkey(), - Vote { - slots: vec![0], - hash: blockhash, - timestamp: None, - }, - ); - let message = Message::new(&[vote_ix], Some(&payer_keypair.pubkey())); - Transaction::new(&[&payer_keypair], message, blockhash) - }) - .collect() -} - pub fn create_native_loader_transactions( bank_client: &BankClient, mint_keypair: &Keypair, @@ -221,12 +171,6 @@ 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_sync_process_vote_transactions(bencher: &mut Bencher) { - do_bench_transactions(bencher, &sync_bencher, &create_vote_transactions); -} - #[bench] #[ignore] fn bench_bank_async_process_builtin_transactions(bencher: &mut Bencher) {