* Refactoring to match rbpf-v0.2.1 and add JIT compilation caching. * Removes obsolete bpf-trace.py which has been replaced by the rbpf CLI tool.
		
			
				
	
	
		
			254 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
#![feature(test)]
 | 
						|
#![cfg(feature = "bpf_c")]
 | 
						|
 | 
						|
extern crate test;
 | 
						|
#[macro_use]
 | 
						|
extern crate solana_bpf_loader_program;
 | 
						|
 | 
						|
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
 | 
						|
use solana_bpf_loader_program::{
 | 
						|
    create_vm, serialization::serialize_parameters, syscalls::register_syscalls, BPFError,
 | 
						|
    ThisInstructionMeter,
 | 
						|
};
 | 
						|
use solana_measure::measure::Measure;
 | 
						|
use solana_rbpf::vm::{Config, Executable, InstructionMeter};
 | 
						|
use solana_runtime::{
 | 
						|
    bank::Bank,
 | 
						|
    bank_client::BankClient,
 | 
						|
    genesis_utils::{create_genesis_config, GenesisConfigInfo},
 | 
						|
    loader_utils::load_program,
 | 
						|
};
 | 
						|
use solana_sdk::{
 | 
						|
    account::Account,
 | 
						|
    bpf_loader,
 | 
						|
    client::SyncClient,
 | 
						|
    entrypoint::SUCCESS,
 | 
						|
    instruction::{AccountMeta, Instruction},
 | 
						|
    message::Message,
 | 
						|
    process_instruction::{InvokeContext, MockInvokeContext},
 | 
						|
    pubkey::Pubkey,
 | 
						|
    signature::{Keypair, Signer},
 | 
						|
};
 | 
						|
use std::{cell::RefCell, env, fs::File, io::Read, mem, path::PathBuf, sync::Arc};
 | 
						|
use test::Bencher;
 | 
						|
 | 
						|
/// BPF program file extension
 | 
						|
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
 | 
						|
/// 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 load_elf(name: &str) -> Result<Vec<u8>, std::io::Error> {
 | 
						|
    let path = create_bpf_path(name);
 | 
						|
    let mut file = File::open(&path).expect(&format!("Unable to open {:?}", path));
 | 
						|
    let mut elf = Vec::new();
 | 
						|
    file.read_to_end(&mut elf).unwrap();
 | 
						|
    Ok(elf)
 | 
						|
}
 | 
						|
 | 
						|
fn load_bpf_program(
 | 
						|
    bank_client: &BankClient,
 | 
						|
    loader_id: &Pubkey,
 | 
						|
    payer_keypair: &Keypair,
 | 
						|
    name: &str,
 | 
						|
) -> Pubkey {
 | 
						|
    let path = create_bpf_path(name);
 | 
						|
    let mut file = File::open(path).unwrap();
 | 
						|
    let mut elf = Vec::new();
 | 
						|
    file.read_to_end(&mut elf).unwrap();
 | 
						|
    load_program(bank_client, payer_keypair, loader_id, elf)
 | 
						|
}
 | 
						|
 | 
						|
const ARMSTRONG_LIMIT: u64 = 500;
 | 
						|
const ARMSTRONG_EXPECTED: u64 = 5;
 | 
						|
 | 
						|
#[bench]
 | 
						|
fn bench_program_create_executable(bencher: &mut Bencher) {
 | 
						|
    let elf = load_elf("bench_alu").unwrap();
 | 
						|
 | 
						|
    bencher.iter(|| {
 | 
						|
        let _ =
 | 
						|
            Executable::<BPFError, ThisInstructionMeter>::from_elf(&elf, None, Config::default())
 | 
						|
                .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::<LittleEndian>(ARMSTRONG_LIMIT)
 | 
						|
        .unwrap();
 | 
						|
    inner_iter.write_u64::<LittleEndian>(0).unwrap();
 | 
						|
    let loader_id = bpf_loader::id();
 | 
						|
    let mut invoke_context = MockInvokeContext::default();
 | 
						|
 | 
						|
    let elf = load_elf("bench_alu").unwrap();
 | 
						|
    let mut executable =
 | 
						|
        Executable::<BPFError, ThisInstructionMeter>::from_elf(&elf, None, Config::default())
 | 
						|
            .unwrap();
 | 
						|
    executable.set_syscall_registry(register_syscalls(&mut invoke_context).unwrap());
 | 
						|
    executable.jit_compile().unwrap();
 | 
						|
    let compute_meter = invoke_context.get_compute_meter();
 | 
						|
    let mut instruction_meter = ThisInstructionMeter { compute_meter };
 | 
						|
    let mut vm = create_vm(
 | 
						|
        &loader_id,
 | 
						|
        executable.as_ref(),
 | 
						|
        &mut inner_iter,
 | 
						|
        &[],
 | 
						|
        &mut invoke_context,
 | 
						|
    )
 | 
						|
    .unwrap();
 | 
						|
 | 
						|
    println!("Interpreted:");
 | 
						|
    assert_eq!(
 | 
						|
        SUCCESS,
 | 
						|
        vm.execute_program_interpreted(&mut instruction_meter)
 | 
						|
            .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.execute_program_interpreted(&mut instruction_meter)
 | 
						|
            .unwrap();
 | 
						|
    });
 | 
						|
    let instructions = vm.get_total_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!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_interpreted_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips);
 | 
						|
 | 
						|
    println!("JIT to native:");
 | 
						|
    assert_eq!(
 | 
						|
        SUCCESS,
 | 
						|
        vm.execute_program_jit(&mut instruction_meter).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.execute_program_jit(&mut instruction_meter).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);
 | 
						|
    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 mut bank = Bank::new(&genesis_config);
 | 
						|
    let (name, id, entrypoint) = solana_bpf_loader_program!();
 | 
						|
    bank.add_builtin(&name, id, entrypoint);
 | 
						|
    let bank = Arc::new(bank);
 | 
						|
    let bank_client = BankClient::new_shared(&bank);
 | 
						|
 | 
						|
    let invoke_program_id =
 | 
						|
        load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, "noop");
 | 
						|
 | 
						|
    let mint_pubkey = mint_keypair.pubkey();
 | 
						|
    let account_metas = vec![AccountMeta::new(mint_pubkey, true)];
 | 
						|
 | 
						|
    let instruction = Instruction::new(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_instruction_count_tuner(_bencher: &mut Bencher) {
 | 
						|
    const BUDGET: u64 = 200_000;
 | 
						|
    let loader_id = bpf_loader::id();
 | 
						|
    let mut invoke_context = MockInvokeContext::default();
 | 
						|
    invoke_context.compute_meter.remaining = BUDGET;
 | 
						|
 | 
						|
    let accounts = [RefCell::new(Account::new(
 | 
						|
        1,
 | 
						|
        10000001,
 | 
						|
        &solana_sdk::pubkey::new_rand(),
 | 
						|
    ))];
 | 
						|
    let keys = [solana_sdk::pubkey::new_rand()];
 | 
						|
    let keyed_accounts: Vec<_> = keys
 | 
						|
        .iter()
 | 
						|
        .zip(&accounts)
 | 
						|
        .map(|(key, account)| solana_sdk::keyed_account::KeyedAccount::new(&key, false, &account))
 | 
						|
        .collect();
 | 
						|
    let instruction_data = vec![0u8];
 | 
						|
 | 
						|
    // Serialize account data
 | 
						|
    let mut serialized = serialize_parameters(
 | 
						|
        &bpf_loader::id(),
 | 
						|
        &solana_sdk::pubkey::new_rand(),
 | 
						|
        &keyed_accounts,
 | 
						|
        &instruction_data,
 | 
						|
    )
 | 
						|
    .unwrap();
 | 
						|
 | 
						|
    let elf = load_elf("tuner").unwrap();
 | 
						|
    let mut executable =
 | 
						|
        Executable::<BPFError, ThisInstructionMeter>::from_elf(&elf, None, Config::default())
 | 
						|
            .unwrap();
 | 
						|
    executable.set_syscall_registry(register_syscalls(&mut invoke_context).unwrap());
 | 
						|
    let compute_meter = invoke_context.get_compute_meter();
 | 
						|
    let mut instruction_meter = ThisInstructionMeter { compute_meter };
 | 
						|
    let mut vm = create_vm(
 | 
						|
        &loader_id,
 | 
						|
        executable.as_ref(),
 | 
						|
        &mut serialized,
 | 
						|
        &[],
 | 
						|
        &mut invoke_context,
 | 
						|
    )
 | 
						|
    .unwrap();
 | 
						|
 | 
						|
    let mut measure = Measure::start("tune");
 | 
						|
    let _ = vm.execute_program_interpreted(&mut instruction_meter);
 | 
						|
    measure.stop();
 | 
						|
 | 
						|
    assert_eq!(
 | 
						|
        0,
 | 
						|
        instruction_meter.get_remaining(),
 | 
						|
        "Tuner must consume the whole budget"
 | 
						|
    );
 | 
						|
    println!(
 | 
						|
        "{:?} compute units took {:?} us ({:?} instructions)",
 | 
						|
        BUDGET - instruction_meter.get_remaining(),
 | 
						|
        measure.as_us(),
 | 
						|
        vm.get_total_instruction_count(),
 | 
						|
    );
 | 
						|
}
 |