Add ComputeBudget tuner (#12476)

This commit is contained in:
Jack May 2020-09-25 09:01:22 -07:00 committed by GitHub
parent b8c4b88188
commit d326512121
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 115 additions and 5 deletions

View File

@ -1755,6 +1755,7 @@ dependencies = [
"elf",
"solana-bpf-loader-program",
"solana-logger",
"solana-measure",
"solana-runtime",
"solana-sdk",
"solana_rbpf",

View File

@ -24,6 +24,7 @@ byteorder = "1.3.2"
elf = "0.0.10"
solana-bpf-loader-program = { path = "../bpf_loader", version = "1.4.0" }
solana-logger = { path = "../../logger", version = "1.4.0" }
solana-measure = { path = "../../measure", version = "1.4.0" }
solana-runtime = { path = "../../runtime", version = "1.4.0" }
solana-sdk = { path = "../../sdk", version = "1.4.0" }
solana_rbpf = "=0.1.31"

View File

@ -6,7 +6,9 @@ extern crate test;
extern crate solana_bpf_loader_program;
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use solana_rbpf::vm::EbpfVm;
use solana_bpf_loader_program::syscalls::SyscallError;
use solana_measure::measure::Measure;
use solana_rbpf::vm::{EbpfVm, InstructionMeter};
use solana_runtime::{
bank::Bank,
bank_client::BankClient,
@ -186,11 +188,48 @@ fn bench_program_execute_noop(bencher: &mut Bencher) {
});
}
#[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.borrow_mut().remaining = BUDGET;
let compute_meter = invoke_context.get_compute_meter();
let elf = load_elf("tuner").unwrap();
let executable =
EbpfVm::<solana_bpf_loader_program::BPFError>::create_executable_from_elf(&elf, None)
.unwrap();
let (mut vm, _) = solana_bpf_loader_program::create_vm(
&loader_id,
executable.as_ref(),
&[],
&mut invoke_context,
)
.unwrap();
let instruction_meter = MockInstructionMeter { compute_meter };
let mut measure = Measure::start("tune");
let _ = vm.execute_program_metered(&mut [0], &[], &[], instruction_meter.clone());
measure.stop();
assert_eq!(
0,
instruction_meter.get_remaining(),
"Tuner must consume the whole budget"
);
println!(
"{:?} Consumed compute budget took {:?} us ({:?} instructions)",
BUDGET - instruction_meter.get_remaining(),
measure.as_us(),
vm.get_total_instruction_count(),
);
}
#[derive(Debug, Default)]
pub struct MockInvokeContext {
key: Pubkey,
mock_logger: MockLogger,
mock_compute_meter: MockComputeMeter,
logger: MockLogger,
compute_meter: Rc<RefCell<MockComputeMeter>>,
}
impl InvokeContext for MockInvokeContext {
fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> {
@ -212,7 +251,7 @@ impl InvokeContext for MockInvokeContext {
&[]
}
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
Rc::new(RefCell::new(self.mock_logger.clone()))
Rc::new(RefCell::new(self.logger.clone()))
}
fn is_cross_program_supported(&self) -> bool {
true
@ -221,7 +260,7 @@ impl InvokeContext for MockInvokeContext {
ComputeBudget::default()
}
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
Rc::new(RefCell::new(self.mock_compute_meter.clone()))
self.compute_meter.clone()
}
fn add_executor(&mut self, _pubkey: &Pubkey, _executor: Arc<dyn Executor>) {}
fn get_executor(&mut self, _pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
@ -257,3 +296,19 @@ impl ComputeMeter for MockComputeMeter {
self.remaining
}
}
/// Passed to the VM to enforce the compute budget
#[derive(Clone)]
struct MockInstructionMeter {
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
}
impl InstructionMeter for MockInstructionMeter {
fn consume(&mut self, amount: u64) {
// 1 to 1 instruction to compute unit mapping
// ignore error, Ebpf will bail if exceeded
let _ = self.compute_meter.borrow_mut().consume(amount);
}
fn get_remaining(&self) -> u64 {
self.compute_meter.borrow().get_remaining()
}
}

View File

@ -0,0 +1,27 @@
/**
* @brief Example C++-based BPF program that prints out the parameters
* passed to it
*/
#include <solana_sdk.h>
/**
* Custom error for when input serialization fails
*/
#define INVALID_INPUT 1
extern uint64_t entrypoint(const uint8_t *input) {
SolAccountInfo ka[1];
SolParameters params = (SolParameters) { .ka = ka };
sol_log(__FILE__);
if (!sol_deserialize(input, &params, SOL_ARRAY_SIZE(ka))) {
return ERROR_INVALID_ARGUMENT;
}
// Log the provided input parameters. In the case of the no-op
// program, no account keys or input data are expected but real
// programs will have specific requirements so they can do their work.
sol_log_params(&params);
return SUCCESS;
}

View File

@ -0,0 +1,23 @@
/**
* @brief Compute budget tuner program. Spins in a loop consuming the entire
* budget, used by the tuner bench test to tune the compute budget costs.
*
* Care should be taken because the compiler might optimize out the mechanism
* you are trying to tune.
*/
#include <solana_sdk.h>
extern uint64_t entrypoint(const uint8_t *input) {
uint8_t *val = (uint8_t *)input;
for (uint64_t i = 0; i < UINT64_MAX; i++) {
// Uncomment for raw compute
{
if (*val != 0) {
*val = *val + 1;
}
}
}
return *val;
}

View File

@ -134,6 +134,8 @@ fn test_program_bpf_sanity() {
("noop++", true),
("panic", false),
("relative_call", true),
("sanity", true),
("sanity++", true),
("struct_pass", true),
("struct_ret", true),
]);
@ -150,6 +152,7 @@ fn test_program_bpf_sanity() {
("solana_bpf_rust_noop", true),
("solana_bpf_rust_panic", false),
("solana_bpf_rust_param_passing", true),
("solana_bpf_rust_sanity", true),
("solana_bpf_rust_sysval", true),
]);
}