diff --git a/Cargo.toml b/Cargo.toml index 2feda2a..9077022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,3 +45,22 @@ criterion = {version="0.3.6", features=["html_reports"] } [features] default = [] redis=["dep:redis"] + +[[bench]] +name = "general" +harness = false + +[[bench]] +name = "deploy" +harness = false + +[[bench]] +name = "infinite_loop" +harness = false + +[[bench]] +name = "function" +harness = false + +[profile.bench] +debug = true \ No newline at end of file diff --git a/Makefile b/Makefile index 038c47c..06e62e7 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ build: test: cargo nextest run --no-fail-fast --success-output=never maturin develop --release - pytest -s --show-capture all + pytest -s bench: cargo bench clean: diff --git a/benches/complex.rs b/benches/complex.rs new file mode 100644 index 0000000..d5b10ad --- /dev/null +++ b/benches/complex.rs @@ -0,0 +1,44 @@ +use std::time::Duration; + +use criterion::{criterion_group, criterion_main, Criterion}; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +#[allow(unused)] +fn bench_call_complex_function(c: &mut Criterion) { + c.bench_function("call_complex_function", |b| { + let source = include_str!("../tests/contracts/complex_contract.hex"); + let bytecode = hex::decode(source).unwrap(); + let mut exe = tinyevm::TinyEVM::default(); + let owner = OWNER; + let deploy_to_address = Some(DEPLOY_TO_ADDRESS); + + let resp = { + exe.deploy_helper(owner, bytecode, UZERO, None, deploy_to_address) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + let fn_sig = "complexFunction()"; + b.iter(|| { + let data = hex::decode(fn_sig_to_prefix(fn_sig)).unwrap(); + + let r = exe.contract_call_helper(address, owner, data, UZERO, None); + // assert!(r.success); // this function can revert sometimes + assert!(r.gas_usage > 0); + }) + }); +} + +criterion_group!( + name = complex; + config = Criterion::default().measurement_time(Duration::from_secs(10)); + targets = bench_call_complex_function, +); + +criterion_main!(complex); diff --git a/benches/deploy.rs b/benches/deploy.rs new file mode 100644 index 0000000..45a6177 --- /dev/null +++ b/benches/deploy.rs @@ -0,0 +1,62 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use primitive_types::H256; +use revm::primitives::Address; +use ruint::aliases::U256; +use tinyevm::{TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +// Reusing can be slower because there are more data inside the instrumentation log +fn bench_contract_deterministic_deploy(c: &mut Criterion) { + c.bench_function("deploy_contract_deterministic", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let source = hex::decode(source).unwrap(); + let mut exe = TinyEVM::default(); + + b.iter(|| { + { + exe.deploy_helper(OWNER, source.clone(), UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap(); + }; + }) + }); +} + +fn bench_contract_deploy_on_different_executors(c: &mut Criterion) { + c.bench_function("deploy_contract_deploy_on_different_executors", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let source = hex::decode(source).unwrap(); + + b.iter(|| { + let mut exe = TinyEVM::default(); + { + exe.deploy_helper( + OWNER, + source.clone(), + U256::from(0), + None, + Some(DEPLOY_TO_ADDRESS), + ) + .unwrap(); + }; + }) + }); +} + +#[allow(unused)] +fn bench_random_h256(c: &mut Criterion) { + c.bench_function("call H256 random", |b| { + b.iter(|| { + let _ = H256::random(); + }) + }); +} + +criterion_group!( + name = deploy; + config = Criterion::default(); + targets = bench_random_h256, bench_contract_deterministic_deploy, bench_contract_deploy_on_different_executors +); + +criterion_main!(deploy); diff --git a/benches/function.rs b/benches/function.rs new file mode 100644 index 0000000..8f10a24 --- /dev/null +++ b/benches/function.rs @@ -0,0 +1,82 @@ +use std::{iter::repeat_with, time::Duration}; + +use criterion::{criterion_group, criterion_main, Criterion}; +use primitive_types::H256; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +#[allow(unused)] +fn bench_call_function_returning_large_string(c: &mut Criterion) { + c.bench_function("call_function_returning_large_string", |b| { + let source = include_str!("../tests/contracts/VeLogo.hex"); + let bytecode = hex::decode(source).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + let fn_sig = "tokenURI(uint256,uint256,uint256,uint256)"; + b.iter(|| { + let fn_args_hex: String = repeat_with(H256::random).take(4).map(hex::encode).collect(); + + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + + let r = exe.contract_call_helper(address, OWNER, data, UZERO, None); + assert!(r.success); + }) + }); +} + +#[allow(unused)] +// TODO this repeats most part of the previous test function, refactor +fn bench_call_function_returning_large_string_no_instrumentation(c: &mut Criterion) { + c.bench_function( + "call_function_returning_large_string_no_instrumetation", + |b| { + let source = include_str!("../tests/contracts/VeLogo.hex"); + let bytecode = hex::decode(source).unwrap(); + let mut exe = TinyEVM::default(); + exe.instrument_config_mut().enabled = false; + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + let fn_sig = "tokenURI(uint256,uint256,uint256,uint256)"; + b.iter(|| { + let fn_args_hex: String = + repeat_with(H256::random).take(4).map(hex::encode).collect(); + + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + + let r = exe.contract_call_helper(address, OWNER, data, UZERO, None); + assert!(r.success); + }) + }, + ); +} + +criterion_group!( + name = evm_benches; + config = Criterion::default().measurement_time(Duration::from_secs(10)); + targets = bench_call_function_returning_large_string, + bench_call_function_returning_large_string_no_instrumentation, +); + +criterion_main!(evm_benches); diff --git a/benches/general.rs b/benches/general.rs new file mode 100644 index 0000000..34c15c4 --- /dev/null +++ b/benches/general.rs @@ -0,0 +1,69 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +#[allow(unused)] +fn bench_call_tracing_with_shared_executor(c: &mut Criterion) { + c.bench_function("call_tracing_with_shared_executor", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test_call_success_success_failed()"; + let fn_args_hex = ""; + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + b.iter(|| { + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +#[allow(unused)] +fn bench_call_tracing_with_different_executor(c: &mut Criterion) { + c.bench_function("call_tracing_with_different_executor", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test_call_success_success_failed()"; + let fn_args_hex = ""; + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + b.iter(|| { + let mut exe = TinyEVM::default(); + let resp = exe + .deploy_helper( + OWNER, + bytecode.clone(), + UZERO, + None, + Some(DEPLOY_TO_ADDRESS), + ) + .unwrap(); + + let address = Address::from_slice(&resp.data); + + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +criterion_group!( + name = evm_benches; + config = Criterion::default(); + targets = bench_call_tracing_with_shared_executor, bench_call_tracing_with_different_executor +); + +criterion_main!(evm_benches); diff --git a/benches/infinite_loop.rs b/benches/infinite_loop.rs new file mode 100644 index 0000000..4538866 --- /dev/null +++ b/benches/infinite_loop.rs @@ -0,0 +1,67 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use primitive_types::{H160, U256}; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +// ~100ms per loop +fn bench_infinite_loop_math(c: &mut Criterion) { + c.bench_function("infinite_loop_with_simple_math", |b| { + let source = include_str!("../tests/contracts/infinite_loop_Test2.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test1(int256)"; + let fn_args_hex = format!("{:0>64x}", U256::from(0)); + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + b.iter(|| { + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +// <300ms per loop +fn bench_infinite_loop_adderss_call(c: &mut Criterion) { + c.bench_function("infinite_loop_with_address_call", |b| { + let source = include_str!("../tests/contracts/infinite_loop_Test.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test1(int256)"; + let fn_args_hex = format!("{:0>64x}", U256::from(0)); + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + b.iter(|| { + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +criterion_group!( + name = infinite_loop; + config = Criterion::default(); + targets = bench_infinite_loop_math, bench_infinite_loop_adderss_call +); + +criterion_main!(infinite_loop); diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 00f4e90..7e6579a 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -381,13 +381,21 @@ fn test_deterministic_deploy() { .deploy_helper(*OWNER, contract_deploy_bin.clone(), UZERO, None, None) .unwrap(); - assert!(c1.success, "Deploy use salt 1 should succeed: {:?}", &c1); + assert!( + c1.success, + "Deploy by initial nonce should succeed: {:?}", + &c1 + ); let c2 = vm .deploy_helper(*OWNER, contract_deploy_bin, UZERO, None, None) .unwrap(); - assert!(c2.success, "Deploy use salt 2 should succeed: {:?}", &c2); + assert!( + c2.success, + "Deploy by auto updated nonce should succeed: {:?}", + &c2 + ); assert_ne!(c1.data, c2.data, "Address of c1 and c2 should not equal"); }