Add ComputeBudget tuner (#12476)
This commit is contained in:
parent
b8c4b88188
commit
d326512121
1
programs/bpf/Cargo.lock
generated
1
programs/bpf/Cargo.lock
generated
@ -1755,6 +1755,7 @@ dependencies = [
|
||||
"elf",
|
||||
"solana-bpf-loader-program",
|
||||
"solana-logger",
|
||||
"solana-measure",
|
||||
"solana-runtime",
|
||||
"solana-sdk",
|
||||
"solana_rbpf",
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
27
programs/bpf/c/src/sanity++/sanity++.cc
Normal file
27
programs/bpf/c/src/sanity++/sanity++.cc
Normal 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, ¶ms, 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(¶ms);
|
||||
return SUCCESS;
|
||||
}
|
23
programs/bpf/c/src/tuner/tuner.c
Normal file
23
programs/bpf/c/src/tuner/tuner.c
Normal 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;
|
||||
}
|
@ -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),
|
||||
]);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user