| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- #![feature(test)]
- #![cfg(feature = "sbf_c")]
- #![allow(clippy::uninlined_format_args)]
- #![allow(clippy::arithmetic_side_effects)]
- #![cfg_attr(
- any(target_os = "windows", not(target_arch = "x86_64")),
- allow(dead_code, unused_imports)
- )]
- use {solana_keypair::Keypair, std::slice};
- extern crate test;
- use {
- byteorder::{ByteOrder, LittleEndian, WriteBytesExt},
- solana_account::AccountSharedData,
- solana_bpf_loader_program::{create_vm, syscalls::create_program_runtime_environment_v1},
- solana_client_traits::SyncClient,
- solana_instruction::{AccountMeta, Instruction},
- solana_measure::measure::Measure,
- solana_message::Message,
- solana_program_entrypoint::SUCCESS,
- solana_program_runtime::{
- execution_budget::SVMTransactionExecutionBudget, invoke_context::InvokeContext,
- serialization::serialize_parameters,
- },
- solana_pubkey::Pubkey,
- solana_runtime::{
- bank::Bank,
- bank_client::BankClient,
- genesis_utils::{create_genesis_config, GenesisConfigInfo},
- loader_utils::{load_program_from_file, load_program_of_loader_v4},
- },
- solana_sbpf::{
- ebpf::MM_INPUT_START, elf::Executable, memory_region::MemoryRegion,
- verifier::RequisiteVerifier, vm::ContextObject,
- },
- solana_sdk_ids::{bpf_loader, native_loader},
- solana_signer::Signer,
- solana_svm_feature_set::SVMFeatureSet,
- solana_transaction_context::InstructionAccount,
- std::{mem, sync::Arc},
- test::Bencher,
- };
- const ARMSTRONG_LIMIT: u64 = 500;
- const ARMSTRONG_EXPECTED: u64 = 5;
- macro_rules! with_mock_invoke_context {
- ($invoke_context:ident, $loader_id:expr, $account_size:expr) => {
- let program_key = Pubkey::new_unique();
- let transaction_accounts = vec![
- (
- $loader_id,
- AccountSharedData::new(0, 0, &native_loader::id()),
- ),
- (program_key, AccountSharedData::new(1, 0, &$loader_id)),
- (
- Pubkey::new_unique(),
- AccountSharedData::new(2, $account_size, &program_key),
- ),
- ];
- let instruction_accounts = vec![InstructionAccount {
- index_in_transaction: 2,
- index_in_caller: 2,
- index_in_callee: 0,
- is_signer: false,
- is_writable: true,
- }];
- solana_program_runtime::with_mock_invoke_context!(
- $invoke_context,
- transaction_context,
- transaction_accounts
- );
- $invoke_context
- .transaction_context
- .get_next_instruction_context()
- .unwrap()
- .configure(&[0, 1], &instruction_accounts, &[]);
- $invoke_context.push().unwrap();
- };
- }
- #[bench]
- fn bench_program_create_executable(bencher: &mut Bencher) {
- let elf = load_program_from_file("bench_alu");
- let program_runtime_environment = create_program_runtime_environment_v1(
- &SVMFeatureSet::default(),
- &SVMTransactionExecutionBudget::default(),
- true,
- false,
- );
- let program_runtime_environment = Arc::new(program_runtime_environment.unwrap());
- bencher.iter(|| {
- let _ = Executable::<InvokeContext>::from_elf(&elf, program_runtime_environment.clone())
- .unwrap();
- });
- }
- #[bench]
- #[cfg(all(not(target_os = "windows"), target_arch = "x86_64"))]
- fn bench_program_alu(bencher: &mut Bencher) {
- let ns_per_s = 1000000000;
- let one_million = 1000000;
- let mut inner_iter = vec![];
- inner_iter
- .write_u64::<LittleEndian>(ARMSTRONG_LIMIT)
- .unwrap();
- inner_iter.write_u64::<LittleEndian>(0).unwrap();
- let elf = load_program_from_file("bench_alu");
- with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001);
- let program_runtime_environment = create_program_runtime_environment_v1(
- invoke_context.get_feature_set(),
- &SVMTransactionExecutionBudget::default(),
- true,
- false,
- );
- let mut executable =
- Executable::<InvokeContext>::from_elf(&elf, Arc::new(program_runtime_environment.unwrap()))
- .unwrap();
- executable.verify::<RequisiteVerifier>().unwrap();
- executable.jit_compile().unwrap();
- create_vm!(
- vm,
- &executable,
- vec![MemoryRegion::new_writable(&mut inner_iter, MM_INPUT_START)],
- vec![],
- &mut invoke_context,
- );
- let (mut vm, _, _) = vm.unwrap();
- println!("Interpreted:");
- vm.context_object_pointer
- .mock_set_remaining(i64::MAX as u64);
- let (instructions, result) = vm.execute_program(&executable, true);
- assert_eq!(SUCCESS, result.unwrap());
- assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
- assert_eq!(
- ARMSTRONG_EXPECTED,
- LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
- );
- bencher.iter(|| {
- vm.context_object_pointer
- .mock_set_remaining(i64::MAX as u64);
- vm.execute_program(&executable, true).1.unwrap();
- });
- let summary = bencher.bench(|_bencher| Ok(())).unwrap().unwrap();
- println!(" {:?} instructions", instructions);
- println!(" {:?} ns/iter median", summary.median as u64);
- assert!(0f64 != summary.median);
- let mips = (instructions * (ns_per_s / summary.median as u64)) / one_million;
- println!(" {:?} MIPS", mips);
- println!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_interpreted_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips);
- println!("JIT to native:");
- assert_eq!(SUCCESS, vm.execute_program(&executable, false).1.unwrap());
- assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
- assert_eq!(
- ARMSTRONG_EXPECTED,
- LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
- );
- bencher.iter(|| {
- vm.context_object_pointer
- .mock_set_remaining(i64::MAX as u64);
- vm.execute_program(&executable, false).1.unwrap();
- });
- let summary = bencher.bench(|_bencher| Ok(())).unwrap().unwrap();
- println!(" {:?} instructions", instructions);
- println!(" {:?} ns/iter median", summary.median as u64);
- assert!(0f64 != summary.median);
- let mips = (instructions * (ns_per_s / summary.median as u64)) / one_million;
- println!(" {:?} MIPS", mips);
- println!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_jit_to_native_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips);
- }
- #[bench]
- fn bench_program_execute_noop(bencher: &mut Bencher) {
- let GenesisConfigInfo {
- genesis_config,
- mint_keypair,
- ..
- } = create_genesis_config(50);
- let bank = Bank::new_for_benches(&genesis_config);
- let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
- let mut bank_client = BankClient::new_shared(bank.clone());
- let authority_keypair = Keypair::new();
- let mint_pubkey = mint_keypair.pubkey();
- let (_bank, invoke_program_id) = load_program_of_loader_v4(
- &mut bank_client,
- &bank_forks,
- &mint_keypair,
- &authority_keypair,
- "noop",
- );
- let account_metas = vec![AccountMeta::new(mint_pubkey, true)];
- let instruction =
- Instruction::new_with_bincode(invoke_program_id, &[u8::MAX, 0, 0, 0], account_metas);
- let message = Message::new(&[instruction], Some(&mint_pubkey));
- bank_client
- .send_and_confirm_message(&[&mint_keypair], message.clone())
- .unwrap();
- bencher.iter(|| {
- bank.clear_signatures();
- bank_client
- .send_and_confirm_message(&[&mint_keypair], message.clone())
- .unwrap();
- });
- }
- #[bench]
- fn bench_create_vm(bencher: &mut Bencher) {
- let elf = load_program_from_file("noop");
- with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001);
- const BUDGET: u64 = 200_000;
- invoke_context.mock_set_remaining(BUDGET);
- let direct_mapping = invoke_context
- .get_feature_set()
- .bpf_account_data_direct_mapping;
- let program_runtime_environment = create_program_runtime_environment_v1(
- invoke_context.get_feature_set(),
- &SVMTransactionExecutionBudget::default(),
- true,
- false,
- );
- let executable =
- Executable::<InvokeContext>::from_elf(&elf, Arc::new(program_runtime_environment.unwrap()))
- .unwrap();
- executable.verify::<RequisiteVerifier>().unwrap();
- // Serialize account data
- let (_serialized, regions, account_lengths) = serialize_parameters(
- invoke_context.transaction_context,
- invoke_context
- .transaction_context
- .get_current_instruction_context()
- .unwrap(),
- !direct_mapping, // copy_account_data
- true, // mask_out_rent_epoch_in_vm_serialization
- )
- .unwrap();
- bencher.iter(|| {
- create_vm!(
- vm,
- &executable,
- clone_regions(®ions),
- account_lengths.clone(),
- &mut invoke_context,
- );
- vm.unwrap();
- });
- }
- #[bench]
- fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
- let elf = load_program_from_file("tuner");
- with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001);
- const BUDGET: u64 = 200_000;
- invoke_context.mock_set_remaining(BUDGET);
- let direct_mapping = invoke_context
- .get_feature_set()
- .bpf_account_data_direct_mapping;
- // Serialize account data
- let (_serialized, regions, account_lengths) = serialize_parameters(
- invoke_context.transaction_context,
- invoke_context
- .transaction_context
- .get_current_instruction_context()
- .unwrap(),
- !direct_mapping, // copy_account_data
- true, // mask_out_rent_epoch_in_vm_serialization
- )
- .unwrap();
- let program_runtime_environment = create_program_runtime_environment_v1(
- invoke_context.get_feature_set(),
- &SVMTransactionExecutionBudget::default(),
- true,
- false,
- );
- let executable =
- Executable::<InvokeContext>::from_elf(&elf, Arc::new(program_runtime_environment.unwrap()))
- .unwrap();
- executable.verify::<RequisiteVerifier>().unwrap();
- create_vm!(
- vm,
- &executable,
- regions,
- account_lengths,
- &mut invoke_context,
- );
- let (mut vm, _, _) = vm.unwrap();
- let mut measure = Measure::start("tune");
- let (instructions, _result) = vm.execute_program(&executable, true);
- measure.stop();
- assert_eq!(
- 0,
- vm.context_object_pointer.get_remaining(),
- "Tuner must consume the whole budget"
- );
- println!(
- "{:?} compute units took {:?} us ({:?} instructions)",
- BUDGET - vm.context_object_pointer.get_remaining(),
- measure.as_us(),
- instructions,
- );
- }
- fn clone_regions(regions: &[MemoryRegion]) -> Vec<MemoryRegion> {
- unsafe {
- regions
- .iter()
- .map(|region| {
- let mut new_region = if region.writable.get() {
- MemoryRegion::new_writable(
- slice::from_raw_parts_mut(
- region.host_addr.get() as *mut _,
- region.len as usize,
- ),
- region.vm_addr,
- )
- } else {
- MemoryRegion::new_readonly(
- slice::from_raw_parts(
- region.host_addr.get() as *const _,
- region.len as usize,
- ),
- region.vm_addr,
- )
- };
- new_region.cow_callback_payload = region.cow_callback_payload;
- new_region
- })
- .collect()
- }
- }
|