diff --git a/.github/workflows/arbitrator-ci.yml b/.github/workflows/arbitrator-ci.yml index 51c0617f3e..2d72775df5 100644 --- a/.github/workflows/arbitrator-ci.yml +++ b/.github/workflows/arbitrator-ci.yml @@ -167,6 +167,9 @@ jobs: - name: Run rust tests run: cargo test -p arbutil -p prover -p jit -p stylus --release --manifest-path arbitrator/prover/Cargo.toml + - name: Check stylus_bechmark + run: cargo check --manifest-path arbitrator/tools/stylus_benchmark/Cargo.toml + - name: Rustfmt run: cargo fmt -p arbutil -p prover -p jit -p stylus --manifest-path arbitrator/Cargo.toml -- --check diff --git a/arbitrator/jit/src/program.rs b/arbitrator/jit/src/program.rs index d80b3771c6..abb498cf55 100644 --- a/arbitrator/jit/src/program.rs +++ b/arbitrator/jit/src/program.rs @@ -139,10 +139,10 @@ pub fn new_program( ))); }; - exec_program(exec, module, calldata, config, evm_data, gas) + launch_program_thread(exec, module, calldata, config, evm_data, gas) } -pub fn exec_program( +pub fn launch_program_thread( exec: &mut WasmEnv, module: Arc<[u8]>, calldata: Vec, diff --git a/arbitrator/tools/stylus_benchmark/Cargo.lock b/arbitrator/tools/stylus_benchmark/Cargo.lock index fe3ff5e908..44a838fd15 100644 --- a/arbitrator/tools/stylus_benchmark/Cargo.lock +++ b/arbitrator/tools/stylus_benchmark/Cargo.lock @@ -1665,9 +1665,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" @@ -1701,9 +1701,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -1721,9 +1721,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -1964,6 +1964,7 @@ dependencies = [ "eyre", "jit", "prover", + "rand", "strum", "strum_macros", "stylus", diff --git a/arbitrator/tools/stylus_benchmark/Cargo.toml b/arbitrator/tools/stylus_benchmark/Cargo.toml index e193fc0ca8..20d169e3e5 100644 --- a/arbitrator/tools/stylus_benchmark/Cargo.toml +++ b/arbitrator/tools/stylus_benchmark/Cargo.toml @@ -14,3 +14,4 @@ stylus = { path = "../../stylus/", default-features = false } clap = { version = "4.4.8", features = ["derive"] } strum = "0.26" strum_macros = "0.26" +rand = "0.8.5" diff --git a/arbitrator/tools/stylus_benchmark/src/benchmark.rs b/arbitrator/tools/stylus_benchmark/src/benchmark.rs index 43f7b7553a..40ea15ad0b 100644 --- a/arbitrator/tools/stylus_benchmark/src/benchmark.rs +++ b/arbitrator/tools/stylus_benchmark/src/benchmark.rs @@ -1,4 +1,4 @@ -// Copyright 2021-2024, Offchain Labs, Inc. +// Copyright 2021-2025, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE use arbutil::evm::{api::Ink, EvmData}; @@ -45,7 +45,7 @@ fn run(compiled_module: Vec) -> (Duration, Ink) { let exec = &mut WasmEnv::default(); - let module = jit::program::exec_program( + let module = jit::program::launch_program_thread( exec, compiled_module.into(), calldata, @@ -94,11 +94,11 @@ pub fn benchmark(wat: Vec) -> eyre::Result<()> { durations = durations[l..r].to_vec(); let avg_duration = durations.iter().sum::() / (r - l) as u32; - let avg_ink_spent_per_micro_second = ink_spent.0 / avg_duration.as_micros() as u64; + let avg_ink_spent_per_nano_second = ink_spent.0 / avg_duration.as_nanos() as u64; println!("After discarding top and bottom runs: "); println!( - "avg_duration: {:?}, avg_ink_spent_per_micro_second: {:?}", - avg_duration, avg_ink_spent_per_micro_second + "avg_duration: {:?}, avg_ink_spent_per_nano_second: {:?}", + avg_duration, avg_ink_spent_per_nano_second ); Ok(()) diff --git a/arbitrator/tools/stylus_benchmark/src/main.rs b/arbitrator/tools/stylus_benchmark/src/main.rs index 4b8971ecab..db5ff2e5c1 100644 --- a/arbitrator/tools/stylus_benchmark/src/main.rs +++ b/arbitrator/tools/stylus_benchmark/src/main.rs @@ -1,13 +1,13 @@ -// Copyright 2021-2024, Offchain Labs, Inc. +// Copyright 2021-2025, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE mod benchmark; mod scenario; +mod scenarios; -use clap::Parser; +use clap::{Parser, ValueEnum}; use scenario::Scenario; use std::path::PathBuf; -use strum::IntoEnumIterator; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] @@ -20,7 +20,7 @@ struct Args { } fn handle_scenario(scenario: Scenario, output_wat_dir_path: Option) -> eyre::Result<()> { - println!("Benchmarking {}", scenario); + println!("Benchmarking {:?}", scenario); let wat = scenario::generate_wat(scenario, output_wat_dir_path); benchmark::benchmark(wat) } @@ -32,8 +32,8 @@ fn main() -> eyre::Result<()> { Some(scenario) => handle_scenario(scenario, args.output_wat_dir_path), None => { println!("No scenario specified, benchmarking all scenarios\n"); - for scenario in Scenario::iter() { - let benchmark_result = handle_scenario(scenario, args.output_wat_dir_path.clone()); + for scenario in Scenario::value_variants() { + let benchmark_result = handle_scenario(*scenario, args.output_wat_dir_path.clone()); if let Err(err) = benchmark_result { return Err(err); } diff --git a/arbitrator/tools/stylus_benchmark/src/scenario.rs b/arbitrator/tools/stylus_benchmark/src/scenario.rs index 348678ed69..8856080d05 100644 --- a/arbitrator/tools/stylus_benchmark/src/scenario.rs +++ b/arbitrator/tools/stylus_benchmark/src/scenario.rs @@ -1,17 +1,523 @@ -// Copyright 2021-2024, Offchain Labs, Inc. +// Copyright 2021-2025, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE +use crate::scenarios::{ + br, br_if, br_table, call, call_indirect, convert, data_type::DataType, global_get, global_set, + if_op, instruction_with_1_arg_1_return, instruction_with_2_args_1_return, load, local_get, + local_set, local_tee, select, store, +}; +use clap::ValueEnum; use std::fs::File; use std::io::Write; use std::path::PathBuf; -use strum_macros::{Display, EnumIter, EnumString}; -#[derive(Copy, Clone, PartialEq, Eq, Debug, EnumString, Display, EnumIter)] +#[derive(ValueEnum, Copy, Clone, PartialEq, Eq, Debug)] +#[clap(rename_all = "PascalCase")] pub enum Scenario { - #[strum(serialize = "add_i32")] - AddI32, - #[strum(serialize = "xor_i32")] - XorI32, + I32Add, + I32And, + I32Clz, + I32Ctz, + I32DivS, + I32DivU, + I32Eq, + I32Eqz, + I32GeS, + I32GeU, + I32GtU, + I32GtS, + I32LeU, + I32LeS, + I32Load, + I32LtU, + I32LtS, + I32Mul, + I32Ne, + I32Or, + I32Popcnt, + I32RemS, + I32RemU, + I32Rotl, + I32Rotr, + I32Shl, + I32ShrS, + I32ShrU, + I32Store, + I32Sub, + I32WrapI64, + I32Xor, + I64Add, + I64And, + I64Clz, + I64Ctz, + I64DivS, + I64DivU, + I64Eq, + I64Eqz, + I64GeS, + I64GeU, + I64GtU, + I64GtS, + I64LeU, + I64LeS, + I64Load, + I64LtU, + I64LtS, + I64Mul, + I64Ne, + I64Or, + I64Popcnt, + I64RemS, + I64RemU, + I64Rotl, + I64Rotr, + I64Shl, + I64ShrS, + I64ShrU, + I64Store, + I64Sub, + I64Xor, + I64ExtendI32U, + I64ExtendI32S, + Br, + BrIf, + BrTable, + Call, + CallIndirect, + GlobalGet, + GlobalSet, + If, + LocalGet, + LocalSet, + LocalTee, + Select, +} + +trait ScenarioWatGenerator { + fn write_specific_wat_beginning(&self, wat: &mut Vec); + fn write_specific_exported_func_beginning(&self, wat: &mut Vec); + fn write_wat_ops(&self, wat: &mut Vec, number_of_ops_per_loop_iteration: usize); +} + +impl ScenarioWatGenerator for Scenario { + fn write_specific_wat_beginning(&self, wat: &mut Vec) { + match self { + Scenario::Call => call::write_specific_wat_beginning(wat), + Scenario::CallIndirect => call_indirect::write_specific_wat_beginning(wat), + Scenario::GlobalGet => global_get::write_specific_wat_beginning(wat), + Scenario::GlobalSet => global_set::write_specific_wat_beginning(wat), + _ => {} + } + } + + fn write_specific_exported_func_beginning(&self, wat: &mut Vec) { + match self { + Scenario::LocalGet => local_get::write_specific_exported_func_beginning(wat), + Scenario::LocalSet => local_set::write_specific_exported_func_beginning(wat), + Scenario::LocalTee => local_tee::write_specific_exported_func_beginning(wat), + _ => {} + } + } + + fn write_wat_ops(&self, wat: &mut Vec, number_of_ops_per_loop_iteration: usize) { + match self { + Scenario::Br => br::write_wat_ops(wat, number_of_ops_per_loop_iteration), + Scenario::BrIf => br_if::write_wat_ops(wat, number_of_ops_per_loop_iteration), + Scenario::BrTable => br_table::write_wat_ops(wat, number_of_ops_per_loop_iteration, 5), + Scenario::Call => call::write_wat_ops(wat, number_of_ops_per_loop_iteration), + Scenario::CallIndirect => { + call_indirect::write_wat_ops(wat, number_of_ops_per_loop_iteration) + } + Scenario::GlobalGet => global_get::write_wat_ops(wat, number_of_ops_per_loop_iteration), + Scenario::GlobalSet => global_set::write_wat_ops(wat, number_of_ops_per_loop_iteration), + Scenario::I32Add => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "add", + ), + Scenario::I32And => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "and", + ), + Scenario::I32Clz => instruction_with_1_arg_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "clz", + ), + Scenario::I32Ctz => instruction_with_1_arg_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "ctz", + ), + Scenario::I32DivS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "div_s", + ), + Scenario::I32DivU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "div_u", + ), + Scenario::I32Eq => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "eq", + ), + Scenario::I32Eqz => instruction_with_1_arg_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "eqz", + ), + Scenario::I32GeS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "ge_s", + ), + Scenario::I32GeU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "ge_u", + ), + Scenario::I32GtU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "gt_u", + ), + Scenario::I32GtS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "gt_s", + ), + Scenario::I32LeU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "le_u", + ), + Scenario::I32LeS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "le_s", + ), + Scenario::I32Load => { + load::write_wat_ops(wat, number_of_ops_per_loop_iteration, DataType::I32) + } + Scenario::I32LtU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "lt_u", + ), + Scenario::I32LtS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "lt_s", + ), + Scenario::I32Mul => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "mul", + ), + Scenario::I32Ne => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "ne", + ), + Scenario::I32Or => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "or", + ), + Scenario::I32Popcnt => instruction_with_1_arg_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "popcnt", + ), + Scenario::I32RemS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "rem_s", + ), + Scenario::I32RemU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "rem_u", + ), + Scenario::I32Rotl => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "rotl", + ), + Scenario::I32Rotr => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "rotr", + ), + Scenario::I32Shl => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "shl", + ), + Scenario::I32ShrS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "shr_s", + ), + Scenario::I32ShrU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "shr_u", + ), + Scenario::I32Store => { + store::write_wat_ops(wat, number_of_ops_per_loop_iteration, DataType::I32) + } + Scenario::I32Sub => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "sub", + ), + Scenario::I32WrapI64 => convert::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + DataType::I32, + "wrap_i64", + ), + Scenario::I32Xor => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + "xor", + ), + Scenario::I64Add => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "add", + ), + Scenario::I64And => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "and", + ), + Scenario::I64Clz => instruction_with_1_arg_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "clz", + ), + Scenario::I64Ctz => instruction_with_1_arg_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "ctz", + ), + Scenario::I64DivS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "div_s", + ), + Scenario::I64DivU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "div_u", + ), + Scenario::I64Eq => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "eq", + ), + Scenario::I64Eqz => instruction_with_1_arg_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "eqz", + ), + Scenario::I64GeS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "ge_s", + ), + Scenario::I64GeU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "ge_u", + ), + Scenario::I64GtU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "gt_u", + ), + Scenario::I64GtS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "gt_s", + ), + Scenario::I64LeU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "le_u", + ), + Scenario::I64LeS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "le_s", + ), + Scenario::I64Load => { + load::write_wat_ops(wat, number_of_ops_per_loop_iteration, DataType::I64) + } + Scenario::I64LtU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "lt_u", + ), + Scenario::I64LtS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "lt_s", + ), + Scenario::I64Mul => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "mul", + ), + Scenario::I64Ne => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "ne", + ), + Scenario::I64Or => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "or", + ), + Scenario::I64Popcnt => instruction_with_1_arg_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "popcnt", + ), + Scenario::I64RemS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "rem_s", + ), + Scenario::I64RemU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "rem_u", + ), + Scenario::I64Rotl => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "rotl", + ), + Scenario::I64Rotr => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "rotr", + ), + Scenario::I64Shl => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "shl", + ), + Scenario::I64ShrS => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "shr_s", + ), + Scenario::I64ShrU => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "shr_u", + ), + Scenario::I64Store => { + store::write_wat_ops(wat, number_of_ops_per_loop_iteration, DataType::I64) + } + Scenario::I64Sub => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "sub", + ), + Scenario::I64Xor => instruction_with_2_args_1_return::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I64, + "xor", + ), + Scenario::I64ExtendI32U => convert::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + DataType::I64, + "extend_i32_u", + ), + Scenario::I64ExtendI32S => convert::write_wat_ops( + wat, + number_of_ops_per_loop_iteration, + DataType::I32, + DataType::I64, + "extend_i32_s", + ), + Scenario::If => if_op::write_wat_ops(wat, number_of_ops_per_loop_iteration), + Scenario::LocalGet => local_get::write_wat_ops(wat, number_of_ops_per_loop_iteration), + Scenario::LocalSet => local_set::write_wat_ops(wat, number_of_ops_per_loop_iteration), + Scenario::LocalTee => local_tee::write_wat_ops(wat, number_of_ops_per_loop_iteration), + Scenario::Select => select::write_wat_ops(wat, number_of_ops_per_loop_iteration), + } + } } // Programs to be benchmarked have a loop in which several similar operations are executed. @@ -19,21 +525,28 @@ pub enum Scenario { // but not too large to avoid a big program size. // Keeping a small program size is important to better use CPU cache, trying to keep the code in the cache. -fn write_wat_beginning(wat: &mut Vec) { +fn write_common_wat_beginning(wat: &mut Vec) { wat.write_all(b"(module\n").unwrap(); wat.write_all(b" (import \"debug\" \"start_benchmark\" (func $start_benchmark))\n") .unwrap(); wat.write_all(b" (import \"debug\" \"end_benchmark\" (func $end_benchmark))\n") .unwrap(); - wat.write_all(b" (memory (export \"memory\") 0 0)\n") + wat.write_all(b" (import \"vm_hooks\" \"pay_for_memory_grow\" (func (param i32)))\n") + .unwrap(); + wat.write_all(b" (memory $memory 1)\n").unwrap(); + wat.write_all(b" (export \"memory\" (memory $memory))\n") .unwrap(); wat.write_all(b" (global $ops_counter (mut i32) (i32.const 0))\n") .unwrap(); +} + +fn write_exported_func_beginning(wat: &mut Vec) { wat.write_all(b" (func (export \"user_entrypoint\") (param i32) (result i32)\n") .unwrap(); +} +fn write_loop_beginning(wat: &mut Vec) { wat.write_all(b" call $start_benchmark\n").unwrap(); - wat.write_all(b" (loop $loop\n").unwrap(); } @@ -73,15 +586,20 @@ fn write_wat_end( wat.write_all(b")").unwrap(); } -fn wat(write_wat_ops: fn(&mut Vec, usize)) -> Vec { +pub fn generate_wat(scenario: Scenario, output_wat_dir_path: Option) -> Vec { let number_of_loop_iterations = 200_000; let number_of_ops_per_loop_iteration = 2000; let mut wat = Vec::new(); - write_wat_beginning(&mut wat); + write_common_wat_beginning(&mut wat); + scenario.write_specific_wat_beginning(&mut wat); - write_wat_ops(&mut wat, number_of_ops_per_loop_iteration); + write_exported_func_beginning(&mut wat); + scenario.write_specific_exported_func_beginning(&mut wat); + write_loop_beginning(&mut wat); + + scenario.write_wat_ops(&mut wat, number_of_ops_per_loop_iteration); write_wat_end( &mut wat, @@ -89,37 +607,10 @@ fn wat(write_wat_ops: fn(&mut Vec, usize)) -> Vec { number_of_ops_per_loop_iteration, ); - wat.to_vec() -} - -fn write_add_i32_wat_ops(wat: &mut Vec, number_of_ops_per_loop_iteration: usize) { - wat.write_all(b" i32.const 0\n").unwrap(); - for _ in 0..number_of_ops_per_loop_iteration { - wat.write_all(b" i32.const 1\n").unwrap(); - wat.write_all(b" i32.add\n").unwrap(); - } - wat.write_all(b" drop\n").unwrap(); -} - -fn write_xor_i32_wat_ops(wat: &mut Vec, number_of_ops_per_loop_iteration: usize) { - wat.write_all(b" i32.const 1231\n").unwrap(); - for _ in 0..number_of_ops_per_loop_iteration { - wat.write_all(b" i32.const 12312313\n").unwrap(); - wat.write_all(b" i32.xor\n").unwrap(); - } - wat.write_all(b" drop\n").unwrap(); -} - -pub fn generate_wat(scenario: Scenario, output_wat_dir_path: Option) -> Vec { - let wat = match scenario { - Scenario::AddI32 => wat(write_add_i32_wat_ops), - Scenario::XorI32 => wat(write_xor_i32_wat_ops), - }; - // print wat to file if needed if let Some(output_wat_dir_path) = output_wat_dir_path { let mut output_wat_path = output_wat_dir_path; - output_wat_path.push(format!("{}.wat", scenario)); + output_wat_path.push(format!("{:?}.wat", scenario)); let mut file = File::create(output_wat_path).unwrap(); file.write_all(&wat).unwrap(); } diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/br.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/br.rs new file mode 100644 index 0000000000..84f3dfbac1 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/br.rs @@ -0,0 +1,16 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::io::Write; + +pub fn write_wat_ops(wat: &mut Vec, number_of_ops_per_loop_iteration: usize) { + for _ in 0..number_of_ops_per_loop_iteration { + wat.write_all(b" (block\n").unwrap(); + wat.write_all(b" (block \n").unwrap(); + wat.write_all(b" (block \n").unwrap(); + wat.write_all(b" br 2\n").unwrap(); // it will jump to the end of the first block + wat.write_all(b" )\n").unwrap(); + wat.write_all(b" )\n").unwrap(); + wat.write_all(b" )\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/br_if.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/br_if.rs new file mode 100644 index 0000000000..fe7509b646 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/br_if.rs @@ -0,0 +1,18 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::io::Write; + +pub fn write_wat_ops(wat: &mut Vec, number_of_ops_per_loop_iteration: usize) { + for _ in 0..number_of_ops_per_loop_iteration { + wat.write_all(b" (block\n").unwrap(); + wat.write_all(b" (block \n").unwrap(); + wat.write_all(b" (block \n").unwrap(); + wat.write_all(b" i32.const 1\n") + .unwrap(); + wat.write_all(b" br_if 2\n").unwrap(); // it will jump to the end of the first block + wat.write_all(b" )\n").unwrap(); + wat.write_all(b" )\n").unwrap(); + wat.write_all(b" )\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/br_table.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/br_table.rs new file mode 100644 index 0000000000..e3a0e00815 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/br_table.rs @@ -0,0 +1,41 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::io::Write; + +fn rm_identation(s: &mut String) { + for _ in 0..4 { + s.pop(); + } +} + +pub fn write_wat_ops( + wat: &mut Vec, + number_of_ops_per_loop_iteration: usize, + table_size: usize, +) { + for _ in 0..number_of_ops_per_loop_iteration { + let mut identation = String::from(" "); + for _ in 0..table_size { + wat.write_all(format!("{}(block\n", identation).as_bytes()) + .unwrap(); + identation.push_str(" "); + } + // it will jump to the end of the first block + wat.write_all(format!("{}i32.const {}\n", identation, table_size - 1).as_bytes()) + .unwrap(); + + let mut br_table = String::from("br_table"); + for i in 0..table_size { + br_table.push_str(&format!(" {}", i)); + } + wat.write_all(format!("{}{}\n", identation, br_table).as_bytes()) + .unwrap(); + + for _ in 0..table_size { + rm_identation(&mut identation); + wat.write_all(format!("{})\n", identation).as_bytes()) + .unwrap(); + } + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/call.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/call.rs new file mode 100644 index 0000000000..8a79ba79f0 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/call.rs @@ -0,0 +1,14 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::io::Write; + +pub fn write_specific_wat_beginning(wat: &mut Vec) { + wat.write_all(b" (func $nop nop)\n").unwrap(); +} + +pub fn write_wat_ops(wat: &mut Vec, number_of_loop_iterations: usize) { + for _ in 0..number_of_loop_iterations { + wat.write_all(b" call $nop\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/call_indirect.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/call_indirect.rs new file mode 100644 index 0000000000..6dbe6cbe6f --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/call_indirect.rs @@ -0,0 +1,21 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::io::Write; + +pub fn write_specific_wat_beginning(wat: &mut Vec) { + wat.write_all(b" (type $nop_func_type (func))\n") + .unwrap(); + wat.write_all(b" (func $nop nop)\n").unwrap(); + wat.write_all(b" (table 1 funcref)\n").unwrap(); + wat.write_all(b" (elem (i32.const 0) $nop)\n") + .unwrap(); +} + +pub fn write_wat_ops(wat: &mut Vec, number_of_loop_iterations: usize) { + for _ in 0..number_of_loop_iterations { + wat.write_all(b" i32.const 0\n").unwrap(); + wat.write_all(b" call_indirect (type $nop_func_type)\n") + .unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/convert.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/convert.rs new file mode 100644 index 0000000000..63b8829c44 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/convert.rs @@ -0,0 +1,28 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::scenarios::data_type::{DataType, Rand}; +use std::io::Write; + +pub fn write_wat_ops( + wat: &mut Vec, + number_of_ops_per_loop_iteration: usize, + source_data_type: DataType, + dest_data_type: DataType, + instruction: &str, +) { + for _ in 0..number_of_ops_per_loop_iteration { + wat.write_all( + format!( + " {}.const {}\n", + source_data_type, + DataType::I32.gen() + ) + .as_bytes(), + ) + .unwrap(); + wat.write_all(format!(" {}.{}\n", dest_data_type, instruction).as_bytes()) + .unwrap(); + wat.write_all(b" drop\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/data_type.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/data_type.rs new file mode 100644 index 0000000000..f7af36fdd5 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/data_type.rs @@ -0,0 +1,26 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use rand::Rng; +use strum_macros::{Display, EnumString}; + +#[derive(Debug, Display, EnumString)] +#[strum(serialize_all = "lowercase")] +pub enum DataType { + I32, + I64, +} + +pub trait Rand { + fn gen(&self) -> usize; +} + +impl Rand for DataType { + fn gen(&self) -> usize { + let mut rng = rand::thread_rng(); + match self { + DataType::I32 => rng.gen_range(0..i32::MAX).try_into().unwrap(), + DataType::I64 => rng.gen_range(0..i64::MAX).try_into().unwrap(), + } + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/global_get.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/global_get.rs new file mode 100644 index 0000000000..f2fa065bc5 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/global_get.rs @@ -0,0 +1,16 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::io::Write; + +pub fn write_specific_wat_beginning(wat: &mut Vec) { + wat.write_all(b" (global $var i32 (i32.const 10))\n") + .unwrap(); +} + +pub fn write_wat_ops(wat: &mut Vec, number_of_loop_iterations: usize) { + for _ in 0..number_of_loop_iterations { + wat.write_all(b" global.get $var\n").unwrap(); + wat.write_all(b" drop\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/global_set.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/global_set.rs new file mode 100644 index 0000000000..4781a9b276 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/global_set.rs @@ -0,0 +1,16 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::io::Write; + +pub fn write_specific_wat_beginning(wat: &mut Vec) { + wat.write_all(b" (global $var (mut i32) (i32.const 0))\n") + .unwrap(); +} + +pub fn write_wat_ops(wat: &mut Vec, number_of_loop_iterations: usize) { + for _ in 0..number_of_loop_iterations { + wat.write_all(b" i32.const 10\n").unwrap(); + wat.write_all(b" global.set $var\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/if_op.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/if_op.rs new file mode 100644 index 0000000000..1c076a6700 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/if_op.rs @@ -0,0 +1,15 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::io::Write; + +pub fn write_wat_ops(wat: &mut Vec, number_of_ops_per_loop_iteration: usize) { + for _ in 0..number_of_ops_per_loop_iteration { + wat.write_all(b" i32.const 0\n").unwrap(); + wat.write_all(b" (if\n").unwrap(); + wat.write_all(b" (then\n").unwrap(); + wat.write_all(b" nop\n").unwrap(); + wat.write_all(b" )\n").unwrap(); + wat.write_all(b" )\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/instruction_with_1_arg_1_return.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/instruction_with_1_arg_1_return.rs new file mode 100644 index 0000000000..06c13c541f --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/instruction_with_1_arg_1_return.rs @@ -0,0 +1,20 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::scenarios::data_type::{DataType, Rand}; +use std::io::Write; + +pub fn write_wat_ops( + wat: &mut Vec, + number_of_ops_per_loop_iteration: usize, + data_type: DataType, + instruction: &str, +) { + for _ in 0..number_of_ops_per_loop_iteration { + wat.write_all(format!(" {}.const {}\n", data_type, data_type.gen()).as_bytes()) + .unwrap(); + wat.write_all(format!(" {}.{}\n", data_type, instruction).as_bytes()) + .unwrap(); + wat.write_all(b" drop\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/instruction_with_2_args_1_return.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/instruction_with_2_args_1_return.rs new file mode 100644 index 0000000000..dd4f20c542 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/instruction_with_2_args_1_return.rs @@ -0,0 +1,22 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::scenarios::data_type::{DataType, Rand}; +use std::io::Write; + +pub fn write_wat_ops( + wat: &mut Vec, + number_of_ops_per_loop_iteration: usize, + data_type: DataType, + instruction: &str, +) { + for _ in 0..number_of_ops_per_loop_iteration { + wat.write_all(format!(" {}.const {}\n", data_type, data_type.gen()).as_bytes()) + .unwrap(); + wat.write_all(format!(" {}.const {}\n", data_type, data_type.gen()).as_bytes()) + .unwrap(); + wat.write_all(format!(" {}.{}\n", data_type, instruction).as_bytes()) + .unwrap(); + wat.write_all(b" drop\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/load.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/load.rs new file mode 100644 index 0000000000..50094bf19d --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/load.rs @@ -0,0 +1,19 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::scenarios::data_type::DataType; +use std::io::Write; + +pub fn write_wat_ops( + wat: &mut Vec, + number_of_ops_per_loop_iteration: usize, + data_type: DataType, +) { + for _ in 0..number_of_ops_per_loop_iteration { + wat.write_all(format!(" i32.const 0\n").as_bytes()) + .unwrap(); + wat.write_all(format!(" {}.load\n", data_type).as_bytes()) + .unwrap(); + wat.write_all(b" drop\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/local_get.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/local_get.rs new file mode 100644 index 0000000000..bd89fe6925 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/local_get.rs @@ -0,0 +1,17 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::io::Write; + +pub fn write_specific_exported_func_beginning(wat: &mut Vec) { + wat.write_all(b" (local $var i32)\n").unwrap(); + wat.write_all(b" (local.set $var (i32.const 10))\n") + .unwrap(); +} + +pub fn write_wat_ops(wat: &mut Vec, number_of_loop_iterations: usize) { + for _ in 0..number_of_loop_iterations { + wat.write_all(b" local.get $var\n").unwrap(); + wat.write_all(b" drop\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/local_set.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/local_set.rs new file mode 100644 index 0000000000..6333a84c06 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/local_set.rs @@ -0,0 +1,22 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::scenarios::data_type::{DataType, Rand}; +use std::io::Write; + +pub fn write_specific_exported_func_beginning(wat: &mut Vec) { + wat.write_all(b" (local $var i32)\n").unwrap(); +} + +pub fn write_wat_ops(wat: &mut Vec, number_of_loop_iterations: usize) { + for _ in 0..number_of_loop_iterations { + wat.write_all( + format!( + " (local.set $var (i32.const {}))\n", + DataType::I32.gen() + ) + .as_bytes(), + ) + .unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/local_tee.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/local_tee.rs new file mode 100644 index 0000000000..ed81647cd5 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/local_tee.rs @@ -0,0 +1,17 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::io::Write; + +pub fn write_specific_exported_func_beginning(wat: &mut Vec) { + wat.write_all(b" (local $var i32)\n").unwrap(); +} + +pub fn write_wat_ops(wat: &mut Vec, number_of_loop_iterations: usize) { + wat.write_all(b" (i32.const 1073741823)\n") + .unwrap(); + for _ in 0..number_of_loop_iterations { + wat.write_all(b" local.tee $var\n").unwrap(); + } + wat.write_all(b" drop\n").unwrap(); +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/mod.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/mod.rs new file mode 100644 index 0000000000..6cf7d499ce --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +pub mod br; +pub mod br_if; +pub mod br_table; +pub mod call; +pub mod call_indirect; +pub mod convert; +pub mod data_type; +pub mod global_get; +pub mod global_set; +pub mod if_op; +pub mod instruction_with_1_arg_1_return; +pub mod instruction_with_2_args_1_return; +pub mod load; +pub mod local_get; +pub mod local_set; +pub mod local_tee; +pub mod select; +pub mod store; diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/select.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/select.rs new file mode 100644 index 0000000000..7b744625a1 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/select.rs @@ -0,0 +1,17 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::scenarios::data_type::{DataType, Rand}; +use std::io::Write; + +pub fn write_wat_ops(wat: &mut Vec, number_of_ops_per_loop_iteration: usize) { + for _ in 0..number_of_ops_per_loop_iteration { + wat.write_all(format!(" i32.const {}\n", DataType::I32.gen()).as_bytes()) + .unwrap(); + wat.write_all(format!(" i32.const {}\n", DataType::I32.gen()).as_bytes()) + .unwrap(); + wat.write_all(b" i32.const 0\n").unwrap(); + wat.write_all(b" select\n").unwrap(); + wat.write_all(b" drop\n").unwrap(); + } +} diff --git a/arbitrator/tools/stylus_benchmark/src/scenarios/store.rs b/arbitrator/tools/stylus_benchmark/src/scenarios/store.rs new file mode 100644 index 0000000000..78b3067870 --- /dev/null +++ b/arbitrator/tools/stylus_benchmark/src/scenarios/store.rs @@ -0,0 +1,20 @@ +// Copyright 2021-2025, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::scenarios::data_type::{DataType, Rand}; +use std::io::Write; + +pub fn write_wat_ops( + wat: &mut Vec, + number_of_ops_per_loop_iteration: usize, + data_type: DataType, +) { + for _ in 0..number_of_ops_per_loop_iteration { + wat.write_all(format!(" i32.const 0\n").as_bytes()) + .unwrap(); + wat.write_all(format!(" {}.const {}\n", data_type, data_type.gen()).as_bytes()) + .unwrap(); + wat.write_all(format!(" {}.store\n", data_type).as_bytes()) + .unwrap(); + } +}