diff --git a/external-crates/move/Cargo.toml b/external-crates/move/Cargo.toml index 8bac472e296d6..4a13e5700a41a 100644 --- a/external-crates/move/Cargo.toml +++ b/external-crates/move/Cargo.toml @@ -39,7 +39,7 @@ clap = { version = "4", features = ["derive"] } codespan = "0.11.1" codespan-reporting = "0.11.1" colored = "2.0.0" -criterion = "0.3.4" +criterion = { version = "0.3.4", features = ["html_reports"]} criterion-cpu-time = "0.1.0" crossbeam = "0.8" crossbeam-channel = "0.5.0" diff --git a/external-crates/move/crates/language-benchmarks/Cargo.toml b/external-crates/move/crates/language-benchmarks/Cargo.toml index 34dc23bcf4880..457e0fd869bb6 100644 --- a/external-crates/move/crates/language-benchmarks/Cargo.toml +++ b/external-crates/move/crates/language-benchmarks/Cargo.toml @@ -23,6 +23,9 @@ move-binary-format.workspace = true move-stdlib.workspace = true move-stdlib-natives.workspace = true +[lib] +bench = false + [[bench]] -name = "vm_benches" +name = "criterion" harness = false diff --git a/external-crates/move/crates/language-benchmarks/benches/criterion.rs b/external-crates/move/crates/language-benchmarks/benches/criterion.rs new file mode 100644 index 0000000000000..2e1c66f000939 --- /dev/null +++ b/external-crates/move/crates/language-benchmarks/benches/criterion.rs @@ -0,0 +1,58 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use criterion::{criterion_group, criterion_main, measurement::Measurement, Criterion}; +use language_benchmarks::{measurement::cpu_time_measurement, move_vm::bench}; + +// +// MoveVM benchmarks +// + +fn arith(c: &mut Criterion) { + bench(c, "arith.move"); +} + +fn arith_2(c: &mut Criterion) { + bench(c, "arith_2.move"); +} + +fn basic_alloc(c: &mut Criterion) { + bench(c, "basic_alloc.move"); +} + +fn call(c: &mut Criterion) { + bench(c, "call.move"); +} + +fn call_2(c: &mut Criterion) { + bench(c, "call_2.move"); +} + +fn natives(c: &mut Criterion) { + bench(c, "natives.move"); +} + +fn transfers(c: &mut Criterion) { + bench(c, "transfers.move"); +} + +fn vector(c: &mut Criterion) { + bench(c, "vector.move"); +} + +criterion_group!( + name = vm_benches; + config = cpu_time_measurement(); + targets = + arith, + arith_2, + basic_alloc, + call, + call_2, + natives, + transfers, + vector, +); + +criterion_main!(vm_benches); diff --git a/external-crates/move/crates/language-benchmarks/benches/vm_benches.rs b/external-crates/move/crates/language-benchmarks/benches/vm_benches.rs deleted file mode 100644 index 3482e15c0a3b8..0000000000000 --- a/external-crates/move/crates/language-benchmarks/benches/vm_benches.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -use criterion::{criterion_group, criterion_main, measurement::Measurement, Criterion}; -use language_benchmarks::{measurement::cpu_time_measurement, move_vm::bench}; - -// -// MoveVM benchmarks -// - -fn arith(c: &mut Criterion) { - bench(c, "arith"); -} - -fn call(c: &mut Criterion) { - bench(c, "call"); -} - -fn natives(c: &mut Criterion) { - bench(c, "natives"); -} - -criterion_group!( - name = vm_benches; - config = cpu_time_measurement(); - targets = arith, - call, - natives -); - -criterion_main!(vm_benches); diff --git a/external-crates/move/crates/language-benchmarks/src/measurement.rs b/external-crates/move/crates/language-benchmarks/src/measurement.rs index 83b10dda9418e..8713a4a04eca4 100644 --- a/external-crates/move/crates/language-benchmarks/src/measurement.rs +++ b/external-crates/move/crates/language-benchmarks/src/measurement.rs @@ -4,9 +4,16 @@ use criterion::Criterion; use criterion_cpu_time::PosixTime; +use std::time::Duration; pub fn cpu_time_measurement() -> Criterion { - Criterion::default().with_measurement(PosixTime::UserAndSystemTime) + Criterion::default() + .with_measurement(PosixTime::UserAndSystemTime) + .without_plots() + .noise_threshold(0.20) + .confidence_level(0.9) + .warm_up_time(Duration::from_secs(10)) // Warm-up time before measurements start + .measurement_time(Duration::from_secs(30)) // Measurement time of 30 seconds } pub fn wall_time_measurement() -> Criterion { diff --git a/external-crates/move/crates/language-benchmarks/src/move_vm.rs b/external-crates/move/crates/language-benchmarks/src/move_vm.rs index 68cdeca91dc0f..2a6cd7c0a6d7d 100644 --- a/external-crates/move/crates/language-benchmarks/src/move_vm.rs +++ b/external-crates/move/crates/language-benchmarks/src/move_vm.rs @@ -4,46 +4,68 @@ use criterion::{measurement::Measurement, Criterion}; use move_binary_format::CompiledModule; -use move_compiler::Compiler; +use move_compiler::{editions::Edition, shared::PackagePaths, Compiler, FullyCompiledProgram}; use move_core_types::{ account_address::AccountAddress, - identifier::{IdentStr, Identifier}, + identifier::Identifier, language_storage::{ModuleId, CORE_CODE_ADDRESS}, }; + use move_vm_runtime::move_vm::MoveVM; use move_vm_test_utils::BlankStorage; use move_vm_types::gas::UnmeteredGasMeter; use once_cell::sync::Lazy; -use std::path::PathBuf; +use std::{path::PathBuf, sync::Arc}; -static MOVE_BENCH_SRC_PATH: Lazy = Lazy::new(|| { - vec![env!("CARGO_MANIFEST_DIR"), "src", "bench.move"] - .into_iter() - .collect() +static PRECOMPILED_MOVE_STDLIB: Lazy = Lazy::new(|| { + let program_res = move_compiler::construct_pre_compiled_lib( + vec![PackagePaths { + name: None, + paths: move_stdlib::move_stdlib_files(), + named_address_map: move_stdlib::move_stdlib_named_addresses(), + }], + None, + move_compiler::Flags::empty(), + None, + ) + .unwrap(); + match program_res { + Ok(stdlib) => stdlib, + Err((files, errors)) => { + eprintln!("!!!Standard library failed to compile!!!"); + move_compiler::diagnostics::report_diagnostics(&files, errors) + } + } }); /// Entry point for the bench, provide a function name to invoke in Module Bench in bench.move. -pub fn bench(c: &mut Criterion, fun: &str) { - let modules = compile_modules(); - let move_vm = MoveVM::new(move_stdlib_natives::all_natives( - AccountAddress::from_hex_literal("0x1").unwrap(), - move_stdlib_natives::GasParameters::zeros(), - /* silent debug */ true, - )) - .unwrap(); - execute(c, &move_vm, modules, fun); +pub fn bench(c: &mut Criterion, filename: &str) { + let modules = compile_modules(filename); + let move_vm = create_vm(); + execute(c, &move_vm, modules, filename); +} + +fn make_path(file: &str) -> PathBuf { + vec![env!("CARGO_MANIFEST_DIR"), "tests", file] + .into_iter() + .collect() } // Compile `bench.move` and its dependencies -fn compile_modules() -> Vec { - let mut src_files = move_stdlib::move_stdlib_files(); - src_files.push(MOVE_BENCH_SRC_PATH.to_str().unwrap().to_owned()); +pub fn compile_modules(filename: &str) -> Vec { + let src_files = vec![make_path(filename).to_str().unwrap().to_owned()]; + let pkg_config = move_compiler::shared::PackageConfig { + edition: Edition::E2024_BETA, + ..Default::default() + }; let (_files, compiled_units) = Compiler::from_files( None, src_files, vec![], move_stdlib::move_stdlib_named_addresses(), ) + .set_pre_compiled_lib(Arc::new(PRECOMPILED_MOVE_STDLIB.clone())) + .set_default_config(pkg_config) .build_and_report() .expect("Error compiling..."); compiled_units @@ -52,12 +74,21 @@ fn compile_modules() -> Vec { .collect() } +fn create_vm() -> MoveVM { + MoveVM::new(move_stdlib_natives::all_natives( + AccountAddress::from_hex_literal("0x1").unwrap(), + move_stdlib_natives::GasParameters::zeros(), + /* silent debug */ true, + )) + .unwrap() +} + // execute a given function in the Bench module fn execute( c: &mut Criterion, move_vm: &MoveVM, modules: Vec, - fun: &str, + file: &str, ) { // establish running context let storage = BlankStorage::new(); @@ -77,21 +108,23 @@ fn execute( } // module and function to call - let module_id = ModuleId::new(sender, Identifier::new("Bench").unwrap()); - let fun_name = IdentStr::new(fun).unwrap_or_else(|_| panic!("Invalid identifier name {}", fun)); + let module_id = ModuleId::new(sender, Identifier::new("bench").unwrap()); + let fun_name = Identifier::new("bench").unwrap(); // benchmark - c.bench_function(fun, |b| { - b.iter(|| { + c.bench_function(file, |b| { + b.iter_with_large_drop(|| { session .execute_function_bypass_visibility( &module_id, - fun_name, + &fun_name, vec![], Vec::>::new(), &mut UnmeteredGasMeter, ) - .unwrap_or_else(|err| panic!("{:?}::{} failed with {:?}", &module_id, fun, err)) + .unwrap_or_else(|err| { + panic!("{:?}::bench in {file} failed with {:?}", &module_id, err) + }) }) }); } diff --git a/external-crates/move/crates/language-benchmarks/tests/arith.move b/external-crates/move/crates/language-benchmarks/tests/arith.move new file mode 100644 index 0000000000000..23e06cedfc33d --- /dev/null +++ b/external-crates/move/crates/language-benchmarks/tests/arith.move @@ -0,0 +1,22 @@ +module 0x1::bench { + fun check(check: bool, code: u64) { + if (check) () else abort code + } + + public fun bench() { + let mut i = 0; + // 10000 is the number of loops to make the benchmark run for a couple of minutes, + // which is an eternity. + // Adjust according to your needs, it's just a reference + while (i < 10000) { + 1; + 10 + 3; + 10; + 7 + 5; + let x = 1; + let y = x + 3; + check(x + y == 5, 10); + i = i + 1; + }; + } +} diff --git a/external-crates/move/crates/language-benchmarks/tests/arith_2.move b/external-crates/move/crates/language-benchmarks/tests/arith_2.move new file mode 100644 index 0000000000000..bba7bbce51d96 --- /dev/null +++ b/external-crates/move/crates/language-benchmarks/tests/arith_2.move @@ -0,0 +1,12 @@ +module 0x1::bench { + const COUNT: u64 = 10_000u64; + + public fun bench() { + let mut sum = 0; + let mut i = 0; + while (i < COUNT) { + sum = sum + i; + i = i + 1; + } + } +} diff --git a/external-crates/move/crates/language-benchmarks/tests/basic_alloc.move b/external-crates/move/crates/language-benchmarks/tests/basic_alloc.move new file mode 100644 index 0000000000000..8fad1143855f2 --- /dev/null +++ b/external-crates/move/crates/language-benchmarks/tests/basic_alloc.move @@ -0,0 +1,22 @@ +module 0x1::bench { + const COUNT: u64 = 10_000u64; + + public struct LargeStruct has drop { + a: u64, b: u64, c: u64, d: u64, e: u64, f: u64, g: u64, h: u64 + } + + fun bench_inner(): LargeStruct { + let mut i = 0; + let mut alloc = LargeStruct { a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, h: 0 }; + while (i < COUNT) { + alloc = LargeStruct { a: i, b: i, c: i, d: i, e: i, f: i, g: i, h: i }; + i = i + 1; + }; + alloc + } + + public fun bench() { + let LargeStruct { a: _, b: _, c: _, d: _, e: _, f: _, g: _, h: i } = bench_inner(); + let _i = i; + } +} diff --git a/external-crates/move/crates/language-benchmarks/src/bench.move b/external-crates/move/crates/language-benchmarks/tests/bench.move similarity index 100% rename from external-crates/move/crates/language-benchmarks/src/bench.move rename to external-crates/move/crates/language-benchmarks/tests/bench.move diff --git a/external-crates/move/crates/language-benchmarks/tests/call.move b/external-crates/move/crates/language-benchmarks/tests/call.move new file mode 100644 index 0000000000000..dd2f54f302a2b --- /dev/null +++ b/external-crates/move/crates/language-benchmarks/tests/call.move @@ -0,0 +1,47 @@ +module 0x1::bench { + // + // Global helpers + // + fun check(check: bool, code: u64) { + if (check) () else abort code + } + + public fun bench() { + let mut i = 0; + // 3000 is the number of loops to make the benchmark run for a couple of minutes, + // which is an eternity. + // Adjust according to your needs, it's just a reference + while (i < 3000) { + let b = call_1(@0x0, 128); + call_2(b); + i = i + 1; + }; + } + + fun call_1(addr: address, val: u64): bool { + let b = call_1_1(&addr); + call_1_2(val, val); + b + } + + fun call_1_1(_addr: &address): bool { + true + } + + fun call_1_2(val1: u64, val2: u64): bool { + val1 == val2 + } + + fun call_2(b: bool) { + call_2_1(b); + check(call_2_2() == 400, 200); + } + + fun call_2_1(b: bool) { + check(b == b, 100) + } + + fun call_2_2(): u64 { + 100 + 300 + } +} diff --git a/external-crates/move/crates/language-benchmarks/tests/call_2.move b/external-crates/move/crates/language-benchmarks/tests/call_2.move new file mode 100644 index 0000000000000..9503929d98631 --- /dev/null +++ b/external-crates/move/crates/language-benchmarks/tests/call_2.move @@ -0,0 +1,16 @@ +module 0x1::bench { + + const COUNT: u64 = 10_000u64; + + public fun bench(): u64 { + let mut i = 0; + while (i < COUNT) { + i = i + call_empty_function(); + }; + i + } + + public fun call_empty_function(): u64 { + 1 + } +} diff --git a/external-crates/move/crates/language-benchmarks/tests/natives.move b/external-crates/move/crates/language-benchmarks/tests/natives.move new file mode 100644 index 0000000000000..1c5ab67e40f86 --- /dev/null +++ b/external-crates/move/crates/language-benchmarks/tests/natives.move @@ -0,0 +1,48 @@ +module 0x1::bench { + + // + // Global helpers + // + fun check(check: bool, code: u64) { + if (check) () else abort code + } + + // + // `natives` benchmark + // + fun test_vector_ops(mut x1: T, mut x2: T): (T, T) { + let mut v: vector = vector::empty(); + check(v.length() == 0, 100); + v.push_back(x1); + check(v.length() == 1, 101); + v.push_back(x2); + check(v.length() == 2, 102); + v.swap(0, 1); + x1 = v.pop_back(); + check(v.length() == 1, 103); + x2 = v.pop_back(); + check(v.length() == 0, 104); + v.destroy_empty(); + (x1, x2) + } + + fun test_vector() { + test_vector_ops(1u8, 2u8); + test_vector_ops(1u64, 2u64); + test_vector_ops(1u128, 2u128); + test_vector_ops(true, false); + test_vector_ops
(@0x1, @0x2); + test_vector_ops>(vector::empty(), vector::empty()); + } + + public fun bench() { + let mut i = 0; + // 300 is the number of loops to make the benchmark run for a couple of minutes, + // which is an eternity. + // Adjust according to your needs, it's just a reference + while (i < 300) { + test_vector(); + i = i + 1; + } + } +} diff --git a/external-crates/move/crates/language-benchmarks/tests/transfers.move b/external-crates/move/crates/language-benchmarks/tests/transfers.move new file mode 100644 index 0000000000000..cc214c07be6e2 --- /dev/null +++ b/external-crates/move/crates/language-benchmarks/tests/transfers.move @@ -0,0 +1,46 @@ +module 0x1::bench { + + const COUNT: u64 = 10_000u64; + + public struct Account { balance: u64 } + + fun destroy_account(account: Account) { + let Account { balance: _ } = account; + } + + public fun bench() { + let mut accounts = vector::empty(); + let num_accounts = COUNT; + let transfer_amount = 10; + + // Initialize accounts with a balance of 1000 each + let mut i = 0; + while (i < num_accounts) { + accounts.push_back(Account { balance: 1000 }); + i = i + 1; + }; + + // Perform transfers + let mut j = 0; + while (j < num_accounts / 2) { + let sender_index = j; + let receiver_index = num_accounts - j - 1; + + let sender = accounts.borrow_mut(sender_index); + if (sender.balance >= transfer_amount) { + sender.balance = sender.balance - transfer_amount; + let receiver = accounts.borrow_mut(receiver_index); + receiver.balance = receiver.balance + transfer_amount; + }; + + j = j + 1; + }; + + let mut i = 0; + while (i < num_accounts) { + destroy_account(accounts.pop_back()); + i = i + 1; + }; + accounts.destroy_empty(); + } +} diff --git a/external-crates/move/crates/language-benchmarks/tests/vector.move b/external-crates/move/crates/language-benchmarks/tests/vector.move new file mode 100644 index 0000000000000..24c249a11686d --- /dev/null +++ b/external-crates/move/crates/language-benchmarks/tests/vector.move @@ -0,0 +1,17 @@ +module 0x1::bench { + const COUNT: u64 = 100_000u64; + + public fun bench() { + let mut v = vector::empty(); + let mut i = 0; + while (i < COUNT) { + v.push_back(i); + i = i + 1; + }; + i = 0; + while (i < COUNT) { + v.pop_back(); + i = i + 1; + }; + } +} diff --git a/external-crates/move/crates/move-transactional-test-runner/src/vm_test_harness.rs b/external-crates/move/crates/move-transactional-test-runner/src/vm_test_harness.rs index bfa9734d737e8..f04769afce091 100644 --- a/external-crates/move/crates/move-transactional-test-runner/src/vm_test_harness.rs +++ b/external-crates/move/crates/move-transactional-test-runner/src/vm_test_harness.rs @@ -300,7 +300,7 @@ impl SimpleVMTestAdapter { } } -static PRECOMPILED_MOVE_STDLIB: Lazy = Lazy::new(|| { +pub static PRECOMPILED_MOVE_STDLIB: Lazy = Lazy::new(|| { let program_res = move_compiler::construct_pre_compiled_lib( vec![PackagePaths { name: None,