bpf_loader.rs 12 KB

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