-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[mono-move] Add gas benchmarks #19470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,3 +51,7 @@ harness = false | |
| [[bench]] | ||
| name = "bst" | ||
| harness = false | ||
|
|
||
| [[bench]] | ||
| name = "match_sum" | ||
| harness = false | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| // Copyright (c) Aptos Foundation | ||
| // Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE | ||
|
|
||
| //! Shared bench helpers. Included via `#[path = "helpers.rs"] mod helpers;` | ||
| //! in each bench binary. Not listed as a `[[bench]]` target, so it is never | ||
| //! compiled standalone. | ||
|
|
||
| use mono_move_alloc::{ExecutableArena, ExecutableArenaPtr}; | ||
| use mono_move_core::{FrameLayoutInfo, Function, MicroOpGasSchedule, SortedSafePointEntries}; | ||
| use mono_move_gas::GasInstrumentor; | ||
|
|
||
| /// Build a gas-instrumented copy of `func_ptrs`, re-allocated in a fresh arena. | ||
| /// | ||
| /// The caller should build the program fresh (without calling | ||
| /// `Function::resolve_calls`) before passing it here, so the code still | ||
| /// contains `CallFunc` (index-based) ops. After this call, invoke | ||
| /// `Function::resolve_calls` on the returned table to patch those ops to | ||
| /// direct pointers into the instrumented arena. | ||
| /// | ||
| /// Frame layouts are re-created as empty; these benchmark programs do not | ||
| /// trigger GC, so the omission has no effect on execution. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// Each pointer in `func_ptrs` must be valid (the owning arena must outlive | ||
| /// this call). | ||
| pub unsafe fn gas_instrument( | ||
| func_ptrs: &[Option<ExecutableArenaPtr<Function>>], | ||
| ) -> (Vec<Option<ExecutableArenaPtr<Function>>>, ExecutableArena) { | ||
| let arena = ExecutableArena::new(); | ||
| let instrumentor = GasInstrumentor::new(MicroOpGasSchedule); | ||
| let new_fns = func_ptrs | ||
| .iter() | ||
| .map(|f| { | ||
| let fp = (*f)?; | ||
| // SAFETY: caller guarantees the pointer is valid. | ||
| let func = unsafe { fp.as_ref_unchecked() }; | ||
| let raw = unsafe { func.code.as_ref_unchecked() }; | ||
| let instrumented = instrumentor.run(raw.to_vec()); | ||
| let code = arena.alloc_slice_copy(&instrumented); | ||
| Some(arena.alloc(Function { | ||
| name: func.name, | ||
| code, | ||
| args_size: func.args_size, | ||
| args_and_locals_size: func.args_and_locals_size, | ||
| extended_frame_size: func.extended_frame_size, | ||
| zero_frame: func.zero_frame, | ||
| frame_layout: FrameLayoutInfo::empty(&arena), | ||
| safe_point_layouts: SortedSafePointEntries::empty(&arena), | ||
| })) | ||
| }) | ||
| .collect(); | ||
| (new_fns, arena) | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [LOW] |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| // Copyright (c) Aptos Foundation | ||
| // Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE | ||
|
|
||
| use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; | ||
|
|
||
| #[path = "helpers.rs"] | ||
| mod helpers; | ||
|
|
||
| const N: u64 = 1_000_000; | ||
|
|
||
| fn bench_match_sum(c: &mut Criterion) { | ||
| use mono_move_gas::{NoOpGasMeter, SimpleGasMeter}; | ||
| use mono_move_programs::{ | ||
| match_sum::{micro_op_match_sum, move_bytecode_match_sum, native_match_sum}, | ||
| testing, | ||
| }; | ||
| use mono_move_runtime::InterpreterContext; | ||
|
|
||
| // -- native & micro_op ------------------------------------------------- | ||
| { | ||
| let mut group = c.benchmark_group("match_sum"); | ||
| group | ||
| .warm_up_time(std::time::Duration::from_secs(1)) | ||
| .measurement_time(std::time::Duration::from_secs(3)); | ||
|
|
||
| group.bench_function("native", |b| { | ||
| b.iter(|| black_box(native_match_sum(N))); | ||
| }); | ||
|
|
||
| // plain (no gas instrumentation) | ||
| let (functions, descriptors, _arena) = micro_op_match_sum(); | ||
| group.bench_function("micro_op", |b| { | ||
| b.iter_batched( | ||
| || { | ||
| let mut ctx = InterpreterContext::new(&descriptors, NoOpGasMeter, unsafe { | ||
| functions[0].as_ref_unchecked() | ||
| }); | ||
| ctx.set_root_arg(0, &N.to_le_bytes()); | ||
| ctx | ||
| }, | ||
| |mut ctx| { | ||
| ctx.run().unwrap(); | ||
| black_box(ctx.root_result()) | ||
| }, | ||
| BatchSize::SmallInput, | ||
| ); | ||
| }); | ||
|
|
||
| // with gas instrumentation | ||
| let (functions, _, _arena) = micro_op_match_sum(); | ||
| let wrapped = functions.iter().map(|f| Some(*f)).collect::<Vec<_>>(); | ||
| // SAFETY: Exclusive access during bench setup; arena is alive. | ||
| let (functions_gas, _arena) = unsafe { helpers::gas_instrument(&wrapped) }; | ||
| group.bench_function("micro_op/gas", |b| { | ||
| b.iter_batched( | ||
| || { | ||
| let gas_meter = SimpleGasMeter::new(u64::MAX); | ||
| let mut ctx = InterpreterContext::new(&descriptors, gas_meter, unsafe { | ||
| functions_gas[0].unwrap().as_ref_unchecked() | ||
| }); | ||
| ctx.set_root_arg(0, &N.to_le_bytes()); | ||
| ctx | ||
| }, | ||
| |mut ctx| { | ||
| ctx.run().unwrap(); | ||
| black_box(ctx.root_result()) | ||
| }, | ||
| BatchSize::SmallInput, | ||
| ); | ||
| }); | ||
|
|
||
| group.finish(); | ||
| } | ||
|
|
||
| // -- move_vm ----------------------------------------------------------- | ||
| { | ||
| let mut group = c.benchmark_group("match_sum"); | ||
| group | ||
| .sample_size(10) | ||
| .warm_up_time(std::time::Duration::from_secs(1)) | ||
| .measurement_time(std::time::Duration::from_secs(3)); | ||
|
|
||
| let module = move_bytecode_match_sum(); | ||
| testing::with_loaded_move_function(&module, "match_sum", |env| { | ||
| group.bench_function("move_vm", |b| { | ||
| b.iter(|| { | ||
| let result = env.run(vec![testing::arg_u64(N)]); | ||
| black_box(testing::return_u64(&result)) | ||
| }); | ||
| }); | ||
| }); | ||
| group.finish(); | ||
| } | ||
| } | ||
|
|
||
| criterion_group!(benches, bench_match_sum); | ||
| criterion_main!(benches); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[LOW]
NoOpGasMeteris exported as a top-levelpub structwith no#[cfg(test)]or feature gate. SinceInterpreterContextis generic overG: GasMeter, this type is silently usable in any execution context. The doc comment says "for testing" but there is no compile-time enforcement. Consider#[cfg(any(test, feature = "testing"))], consistent with the pattern already used inprograms/src/lib.rs.