bpf_loader.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. #![feature(test)]
  2. #![cfg(feature = "sbf_c")]
  3. #![allow(clippy::uninlined_format_args)]
  4. #![allow(clippy::arithmetic_side_effects)]
  5. #![cfg_attr(
  6. any(target_os = "windows", not(target_arch = "x86_64")),
  7. allow(dead_code, unused_imports)
  8. )]
  9. use {solana_keypair::Keypair, std::slice};
  10. extern crate test;
  11. use {
  12. agave_syscalls::create_program_runtime_environment_v1,
  13. byteorder::{ByteOrder, LittleEndian, WriteBytesExt},
  14. solana_account::AccountSharedData,
  15. solana_bpf_loader_program::create_vm,
  16. solana_client_traits::SyncClient,
  17. solana_instruction::{AccountMeta, Instruction},
  18. solana_measure::measure::Measure,
  19. solana_message::Message,
  20. solana_program_entrypoint::SUCCESS,
  21. solana_program_runtime::{
  22. execution_budget::SVMTransactionExecutionBudget, invoke_context::InvokeContext,
  23. serialization::serialize_parameters,
  24. },
  25. solana_pubkey::Pubkey,
  26. solana_runtime::{
  27. bank::Bank,
  28. bank_client::BankClient,
  29. genesis_utils::{create_genesis_config, GenesisConfigInfo},
  30. loader_utils::{load_program_from_file, load_program_of_loader_v4},
  31. },
  32. solana_sbpf::{
  33. ebpf::MM_INPUT_START, elf::Executable, memory_region::MemoryRegion,
  34. verifier::RequisiteVerifier, vm::ContextObject,
  35. },
  36. solana_sdk_ids::{bpf_loader, native_loader},
  37. solana_signer::Signer,
  38. solana_svm_feature_set::SVMFeatureSet,
  39. solana_transaction_context::InstructionAccount,
  40. std::{mem, sync::Arc},
  41. test::Bencher,
  42. };
  43. const ARMSTRONG_LIMIT: u64 = 500;
  44. const ARMSTRONG_EXPECTED: u64 = 5;
  45. macro_rules! with_mock_invoke_context {
  46. ($invoke_context:ident, $loader_id:expr, $account_size:expr) => {
  47. let program_key = Pubkey::new_unique();
  48. let transaction_accounts = vec![
  49. (
  50. $loader_id,
  51. AccountSharedData::new(0, 0, &native_loader::id()),
  52. ),
  53. (program_key, AccountSharedData::new(1, 0, &$loader_id)),
  54. (
  55. Pubkey::new_unique(),
  56. AccountSharedData::new(2, $account_size, &program_key),
  57. ),
  58. ];
  59. let instruction_accounts = vec![InstructionAccount::new(2, false, true)];
  60. solana_program_runtime::with_mock_invoke_context!(
  61. $invoke_context,
  62. transaction_context,
  63. transaction_accounts
  64. );
  65. $invoke_context
  66. .transaction_context
  67. .configure_next_instruction_for_tests(1, instruction_accounts, &[])
  68. .unwrap();
  69. $invoke_context.push().unwrap();
  70. };
  71. }
  72. #[bench]
  73. fn bench_program_create_executable(bencher: &mut Bencher) {
  74. let elf = load_program_from_file("bench_alu");
  75. let feature_set = SVMFeatureSet::default();
  76. let program_runtime_environment = create_program_runtime_environment_v1(
  77. &feature_set,
  78. &SVMTransactionExecutionBudget::new_with_defaults(feature_set.raise_cpi_nesting_limit_to_8),
  79. true,
  80. false,
  81. );
  82. let program_runtime_environment = Arc::new(program_runtime_environment.unwrap());
  83. bencher.iter(|| {
  84. let _ = Executable::<InvokeContext>::from_elf(&elf, program_runtime_environment.clone())
  85. .unwrap();
  86. });
  87. }
  88. #[bench]
  89. #[cfg(all(not(target_os = "windows"), target_arch = "x86_64"))]
  90. fn bench_program_alu(bencher: &mut Bencher) {
  91. let ns_per_s = 1000000000;
  92. let one_million = 1000000;
  93. let mut inner_iter = vec![];
  94. inner_iter
  95. .write_u64::<LittleEndian>(ARMSTRONG_LIMIT)
  96. .unwrap();
  97. inner_iter.write_u64::<LittleEndian>(0).unwrap();
  98. let elf = load_program_from_file("bench_alu");
  99. with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001);
  100. let feature_set = invoke_context.get_feature_set();
  101. let program_runtime_environment = create_program_runtime_environment_v1(
  102. feature_set,
  103. &SVMTransactionExecutionBudget::new_with_defaults(feature_set.raise_cpi_nesting_limit_to_8),
  104. true,
  105. false,
  106. );
  107. let mut executable =
  108. Executable::<InvokeContext>::from_elf(&elf, Arc::new(program_runtime_environment.unwrap()))
  109. .unwrap();
  110. executable.verify::<RequisiteVerifier>().unwrap();
  111. executable.jit_compile().unwrap();
  112. create_vm!(
  113. vm,
  114. &executable,
  115. vec![MemoryRegion::new_writable(&mut inner_iter, MM_INPUT_START)],
  116. vec![],
  117. &mut invoke_context,
  118. );
  119. let (mut vm, _, _) = vm.unwrap();
  120. println!("Interpreted:");
  121. vm.context_object_pointer
  122. .mock_set_remaining(i64::MAX as u64);
  123. let (instructions, result) = vm.execute_program(&executable, true);
  124. assert_eq!(SUCCESS, result.unwrap());
  125. assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
  126. assert_eq!(
  127. ARMSTRONG_EXPECTED,
  128. LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
  129. );
  130. bencher.iter(|| {
  131. vm.context_object_pointer
  132. .mock_set_remaining(i64::MAX as u64);
  133. vm.execute_program(&executable, true).1.unwrap();
  134. });
  135. let summary = bencher.bench(|_bencher| Ok(())).unwrap().unwrap();
  136. println!(" {:?} instructions", instructions);
  137. println!(" {:?} ns/iter median", summary.median as u64);
  138. assert!(0f64 != summary.median);
  139. let mips = (instructions * (ns_per_s / summary.median as u64)) / one_million;
  140. println!(" {:?} MIPS", mips);
  141. println!(
  142. "{{ \"type\": \"bench\", \"name\": \"bench_program_alu_interpreted_mips\", \"median\": \
  143. {mips:?}, \"deviation\": 0 }}",
  144. );
  145. println!("JIT to native:");
  146. assert_eq!(SUCCESS, vm.execute_program(&executable, false).1.unwrap());
  147. assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
  148. assert_eq!(
  149. ARMSTRONG_EXPECTED,
  150. LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
  151. );
  152. bencher.iter(|| {
  153. vm.context_object_pointer
  154. .mock_set_remaining(i64::MAX as u64);
  155. vm.execute_program(&executable, false).1.unwrap();
  156. });
  157. let summary = bencher.bench(|_bencher| Ok(())).unwrap().unwrap();
  158. println!(" {:?} instructions", instructions);
  159. println!(" {:?} ns/iter median", summary.median as u64);
  160. assert!(0f64 != summary.median);
  161. let mips = (instructions * (ns_per_s / summary.median as u64)) / one_million;
  162. println!(" {:?} MIPS", mips);
  163. println!(
  164. "{{ \"type\": \"bench\", \"name\": \"bench_program_alu_jit_to_native_mips\", \"median\": \
  165. {mips:?}, \"deviation\": 0 }}",
  166. );
  167. }
  168. #[bench]
  169. fn bench_program_execute_noop(bencher: &mut Bencher) {
  170. let GenesisConfigInfo {
  171. genesis_config,
  172. mint_keypair,
  173. ..
  174. } = create_genesis_config(50);
  175. let bank = Bank::new_for_benches(&genesis_config);
  176. let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
  177. let mut bank_client = BankClient::new_shared(bank.clone());
  178. let authority_keypair = Keypair::new();
  179. let mint_pubkey = mint_keypair.pubkey();
  180. let (_bank, invoke_program_id) = load_program_of_loader_v4(
  181. &mut bank_client,
  182. &bank_forks,
  183. &mint_keypair,
  184. &authority_keypair,
  185. "noop",
  186. );
  187. let account_metas = vec![AccountMeta::new(mint_pubkey, true)];
  188. let instruction =
  189. Instruction::new_with_bincode(invoke_program_id, &[u8::MAX, 0, 0, 0], account_metas);
  190. let message = Message::new(&[instruction], Some(&mint_pubkey));
  191. bank_client
  192. .send_and_confirm_message(&[&mint_keypair], message.clone())
  193. .unwrap();
  194. bencher.iter(|| {
  195. bank.clear_signatures();
  196. bank_client
  197. .send_and_confirm_message(&[&mint_keypair], message.clone())
  198. .unwrap();
  199. });
  200. }
  201. #[bench]
  202. fn bench_create_vm(bencher: &mut Bencher) {
  203. let elf = load_program_from_file("noop");
  204. with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001);
  205. const BUDGET: u64 = 200_000;
  206. invoke_context.mock_set_remaining(BUDGET);
  207. let stricter_abi_and_runtime_constraints = invoke_context
  208. .get_feature_set()
  209. .stricter_abi_and_runtime_constraints;
  210. let raise_cpi_nesting_limit_to_8 = invoke_context
  211. .get_feature_set()
  212. .raise_cpi_nesting_limit_to_8;
  213. let program_runtime_environment = create_program_runtime_environment_v1(
  214. invoke_context.get_feature_set(),
  215. &SVMTransactionExecutionBudget::new_with_defaults(raise_cpi_nesting_limit_to_8),
  216. true,
  217. false,
  218. );
  219. let executable =
  220. Executable::<InvokeContext>::from_elf(&elf, Arc::new(program_runtime_environment.unwrap()))
  221. .unwrap();
  222. executable.verify::<RequisiteVerifier>().unwrap();
  223. // Serialize account data
  224. let (_serialized, regions, account_lengths) = serialize_parameters(
  225. &invoke_context
  226. .transaction_context
  227. .get_current_instruction_context()
  228. .unwrap(),
  229. stricter_abi_and_runtime_constraints,
  230. false, // account_data_direct_mapping
  231. true, // mask_out_rent_epoch_in_vm_serialization
  232. )
  233. .unwrap();
  234. bencher.iter(|| {
  235. create_vm!(
  236. vm,
  237. &executable,
  238. clone_regions(&regions),
  239. account_lengths.clone(),
  240. &mut invoke_context,
  241. );
  242. vm.unwrap();
  243. });
  244. }
  245. #[bench]
  246. fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
  247. let elf = load_program_from_file("tuner");
  248. with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001);
  249. const BUDGET: u64 = 200_000;
  250. invoke_context.mock_set_remaining(BUDGET);
  251. let stricter_abi_and_runtime_constraints = invoke_context
  252. .get_feature_set()
  253. .stricter_abi_and_runtime_constraints;
  254. // Serialize account data
  255. let (_serialized, regions, account_lengths) = serialize_parameters(
  256. &invoke_context
  257. .transaction_context
  258. .get_current_instruction_context()
  259. .unwrap(),
  260. stricter_abi_and_runtime_constraints,
  261. false, // account_data_direct_mapping
  262. true, // mask_out_rent_epoch_in_vm_serialization
  263. )
  264. .unwrap();
  265. let feature_set = invoke_context.get_feature_set();
  266. let program_runtime_environment = create_program_runtime_environment_v1(
  267. feature_set,
  268. &SVMTransactionExecutionBudget::new_with_defaults(feature_set.raise_cpi_nesting_limit_to_8),
  269. true,
  270. false,
  271. );
  272. let executable =
  273. Executable::<InvokeContext>::from_elf(&elf, Arc::new(program_runtime_environment.unwrap()))
  274. .unwrap();
  275. executable.verify::<RequisiteVerifier>().unwrap();
  276. create_vm!(
  277. vm,
  278. &executable,
  279. regions,
  280. account_lengths,
  281. &mut invoke_context,
  282. );
  283. let (mut vm, _, _) = vm.unwrap();
  284. let mut measure = Measure::start("tune");
  285. let (instructions, _result) = vm.execute_program(&executable, true);
  286. measure.stop();
  287. assert_eq!(
  288. 0,
  289. vm.context_object_pointer.get_remaining(),
  290. "Tuner must consume the whole budget"
  291. );
  292. println!(
  293. "{:?} compute units took {:?} us ({:?} instructions)",
  294. BUDGET - vm.context_object_pointer.get_remaining(),
  295. measure.as_us(),
  296. instructions,
  297. );
  298. }
  299. fn clone_regions(regions: &[MemoryRegion]) -> Vec<MemoryRegion> {
  300. unsafe {
  301. regions
  302. .iter()
  303. .map(|region| {
  304. let mut new_region = if region.writable {
  305. MemoryRegion::new_writable(
  306. slice::from_raw_parts_mut(region.host_addr as *mut _, region.len as usize),
  307. region.vm_addr,
  308. )
  309. } else {
  310. MemoryRegion::new_readonly(
  311. slice::from_raw_parts(region.host_addr as *const _, region.len as usize),
  312. region.vm_addr,
  313. )
  314. };
  315. new_region.access_violation_handler_payload =
  316. region.access_violation_handler_payload;
  317. new_region
  318. })
  319. .collect()
  320. }
  321. }