diff --git a/Cargo.toml b/Cargo.toml index 17900bda0..b8367bf29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ path = "src/bin/wallet.rs" codecov = { repository = "solana-labs/solana", branch = "master", service = "github" } [features] -bpf_c = [] +bpf_c = ["solana-bpfloader/bpf_c"] chacha = [] cuda = [] erasure = [] diff --git a/build.rs b/build.rs index e3870c27e..ec692364b 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,5 @@ use std::env; use std::fs; -use std::process::Command; fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -14,30 +13,10 @@ fn main() { } }); - let bpf_c = !env::var("CARGO_FEATURE_BPF_C").is_err(); let chacha = !env::var("CARGO_FEATURE_CHACHA").is_err(); let cuda = !env::var("CARGO_FEATURE_CUDA").is_err(); let erasure = !env::var("CARGO_FEATURE_ERASURE").is_err(); - if bpf_c { - let out_dir = "OUT_DIR=../../../target/".to_string() - + &env::var("PROFILE").unwrap() - + &"/bpf".to_string(); - - println!("cargo:rerun-if-changed=programs/bpf/c/sdk/bpf.mk"); - println!("cargo:rerun-if-changed=programs/bpf/c/sdk/inc/solana_sdk.h"); - println!("cargo:rerun-if-changed=programs/bpf/c/makefile"); - println!("cargo:rerun-if-changed=programs/bpf/c/src/move_funds.c"); - println!("cargo:rerun-if-changed=programs/bpf/c/src/noop.c"); - println!("cargo:warning=(not a warning) Compiling C-based BPF programs"); - let status = Command::new("make") - .current_dir("programs/bpf/c") - .arg("all") - .arg(&out_dir) - .status() - .expect("Failed to build C-based BPF programs"); - assert!(status.success()); - } if chacha || cuda || erasure { println!("cargo:rerun-if-changed=target/perf-libs"); println!("cargo:rustc-link-search=native=target/perf-libs"); diff --git a/programs/bpf/c/src/bench_alu.c b/programs/bpf/c/src/bench_alu.c new file mode 100644 index 000000000..1099a65f1 --- /dev/null +++ b/programs/bpf/c/src/bench_alu.c @@ -0,0 +1,27 @@ +/** + * @brief Benchmark program that does work + * + * Counts Armstrong Numbers between 1 and x + */ + +#include + +extern bool entrypoint(const uint8_t *input) { + uint64_t x = *(uint64_t *)input; + uint64_t count = 0; + + for (int i = 1; i <= x; i++) { + uint64_t temp = i; + uint64_t num = 0; + while (temp != 0) { + uint64_t rem = (temp % 10); + num += rem * rem * rem; + temp /= 10; + } + if (i == num) { + count++; + } + } + + return count; +} diff --git a/programs/native/bpf_loader/Cargo.toml b/programs/native/bpf_loader/Cargo.toml index efce46e6f..2e0b687af 100644 --- a/programs/native/bpf_loader/Cargo.toml +++ b/programs/native/bpf_loader/Cargo.toml @@ -6,6 +6,9 @@ authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" +[features] +bpf_c = [] + [dependencies] bincode = "1.0.0" byteorder = "1.2.1" @@ -13,12 +16,15 @@ elf = "0.0.10" env_logger = "0.5.12" libc = "0.2.43" log = "0.4.2" -solana_rbpf = "=0.1.3" +solana_rbpf = "=0.1.4" serde = "1.0.27" serde_derive = "1.0.27" solana-sdk = { path = "../../../sdk", version = "0.11.0" } [lib] name = "solana_bpf_loader" -crate-type = ["cdylib"] +crate-type = ["lib", "cdylib"] + +[[bench]] +name = "bpf_loader" diff --git a/programs/native/bpf_loader/benches/bpf_loader.rs b/programs/native/bpf_loader/benches/bpf_loader.rs new file mode 100644 index 000000000..aaffc5215 --- /dev/null +++ b/programs/native/bpf_loader/benches/bpf_loader.rs @@ -0,0 +1,115 @@ +#![feature(test)] +#![cfg(feature = "bpf_c")] +extern crate byteorder; +extern crate solana_bpf_loader; +extern crate solana_rbpf; +extern crate test; + +use byteorder::{LittleEndian, WriteBytesExt}; +use solana_rbpf::EbpfVmRaw; +use std::env; +use std::fs::File; +use std::io::Error; +use std::io::Read; +use std::path::PathBuf; +use test::Bencher; + +/// BPF program file extension +const PLATFORM_FILE_EXTENSION_BPF: &str = "o"; +/// Create a BPF program file name +fn create_bpf_path(name: &str) -> PathBuf { + let mut pathbuf = { + let current_exe = env::current_exe().unwrap(); + PathBuf::from(current_exe.parent().unwrap().parent().unwrap()) + }; + pathbuf.push("bpf/"); + pathbuf.push(name); + pathbuf.set_extension(PLATFORM_FILE_EXTENSION_BPF); + pathbuf +} + +fn empty_check(_prog: &[u8]) -> Result<(), Error> { + Ok(()) +} + +const ARMSTRONG_LIMIT: u64 = 500; +const ARMSTRONG_EXPECTED: u64 = 5; + +#[bench] +fn bench_program_load_elf(bencher: &mut Bencher) { + let mut file = File::open(create_bpf_path("bench_alu")).expect("file open failed"); + let mut elf = Vec::new(); + file.read_to_end(&mut elf).unwrap(); + + let mut vm = EbpfVmRaw::new(None).unwrap(); + vm.set_verifier(empty_check).unwrap(); + + bencher.iter(|| { + vm.set_elf(&elf).unwrap(); + }); +} + +#[bench] +fn bench_program_verify(bencher: &mut Bencher) { + let mut file = File::open(create_bpf_path("bench_alu")).expect("file open failed"); + let mut elf = Vec::new(); + file.read_to_end(&mut elf).unwrap(); + + let mut vm = EbpfVmRaw::new(None).unwrap(); + vm.set_verifier(empty_check).unwrap(); + vm.set_elf(&elf).unwrap(); + + bencher.iter(|| { + vm.set_verifier(solana_bpf_loader::bpf_verifier::check) + .unwrap(); + }); +} + +#[bench] +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::(ARMSTRONG_LIMIT) + .unwrap(); + + let mut file = File::open(create_bpf_path("bench_alu")).expect("file open failed"); + let mut elf = Vec::new(); + file.read_to_end(&mut elf).unwrap(); + let mut vm = solana_bpf_loader::create_vm(&elf).unwrap(); + + println!("Interpreted:"); + assert_eq!( + ARMSTRONG_EXPECTED, + vm.execute_program(&mut inner_iter).unwrap() + ); + bencher.iter(|| { + vm.execute_program(&mut inner_iter).unwrap(); + }); + let instructions = vm.get_last_instruction_count(); + let summary = bencher.bench(|_bencher| {}).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!("JIT to native:"); + vm.jit_compile().unwrap(); + unsafe { + assert_eq!( + ARMSTRONG_EXPECTED, + vm.execute_program_jit(&mut inner_iter).unwrap() + ); + } + bencher.iter(|| unsafe { + vm.execute_program_jit(&mut inner_iter).unwrap(); + }); + let summary = bencher.bench(|_bencher| {}).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); +} diff --git a/programs/native/bpf_loader/build.rs b/programs/native/bpf_loader/build.rs new file mode 100644 index 000000000..aa700a140 --- /dev/null +++ b/programs/native/bpf_loader/build.rs @@ -0,0 +1,29 @@ +use std::env; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let bpf_c = !env::var("CARGO_FEATURE_BPF_C").is_err(); + + if bpf_c { + let out_dir = "OUT_DIR=../../../target/".to_string() + + &env::var("PROFILE").unwrap() + + &"/bpf".to_string(); + + println!("cargo:rerun-if-changed=../../bpf/c/sdk/bpf.mk"); + println!("cargo:rerun-if-changed=../../bpf/c/sdk/inc/solana_sdk.h"); + println!("cargo:rerun-if-changed=../../bpf/c/makefile"); + println!("cargo:rerun-if-changed=../../bpf/c/src/bernch_alu.c"); + println!("cargo:rerun-if-changed=../../bpf/c/src/move_funds.c"); + println!("cargo:rerun-if-changed=../../bpf/c/src/noop.c"); + println!("cargo:warning=(not a warning) Compiling C-based BPF programs"); + let status = Command::new("make") + .current_dir("../../bpf/c") + .arg("all") + .arg(&out_dir) + .status() + .expect("Failed to build C-based BPF programs"); + assert!(status.success()); + } +} diff --git a/programs/native/bpf_loader/src/lib.rs b/programs/native/bpf_loader/src/lib.rs index 8dc87a259..ba0ec9c34 100644 --- a/programs/native/bpf_loader/src/lib.rs +++ b/programs/native/bpf_loader/src/lib.rs @@ -88,7 +88,7 @@ pub fn helper_sol_log_u64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) 0 } -fn create_vm(prog: &[u8]) -> Result { +pub fn create_vm(prog: &[u8]) -> Result { let mut vm = EbpfVmRaw::new(None)?; vm.set_verifier(bpf_verifier::check)?; vm.set_max_instruction_count(36000)?; // 36000 is a wag, need to tune