Skip to content

Commit

Permalink
Add benchmark for simple transaction (#577)
Browse files Browse the repository at this point in the history
* feat: add benchmark for default transaction

* refactor: use trace instead of emit, create wrapper for host

* refactor: improve cycles print

* refactor: add inline comments, change trace ids

* chore: ignore the test untill it become a binary

* refactor: organise cycles storing, add traces for each note

* feat: create a simple cli for benchmark binary package

* feat: create cargo-make script for benchmarking, move benchmarks back to the miden-tx

* refactor: move TransactionProgress to the TransactionHost

* feat: implement bench for P2ID note, add NoteId to the output

* refactor: move last note id obtaining to the separate function

* refactor: fix no-std build

* refactor: update comments in main.masm file to be consistent with api.masm

* feat: write benchmark results to the json file

* refactor: move benchmarks to their own crate

* refactor: add ability to run all benches

* refactor: update bin name, dependencies; remove benches except all

* chore: fix no-std build

* refactor: rework TransactionProgress serialization

* refactor: exclude bench-tx from no-std build, update dependencies

* refactor: improve imports formatting

* refactor: rework writing to json

* chore: change visibility of miden-tx MockDataStore

* chore: update CHANGELOG, create README for bench-tx
  • Loading branch information
Fumuran authored and bobbinth committed Apr 26, 2024
1 parent 39585b4 commit 20f8f6d
Show file tree
Hide file tree
Showing 18 changed files with 887 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* [BREAKING] Removed the transaction script root output from the transaction kernel (#608).
* [BREAKING] Refactored account update details, moved `Block` to `miden-objects` (#618, #621).
* Introduce the `miden-bench-tx` crate used for transactions benchmarking (#577).

## 0.2.3 (2024-04-26) - `miden-tx` crate only

Expand Down
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = [
members = [
"bench-tx",
"miden-lib",
"miden-tx",
"mock",
Expand Down
11 changes: 9 additions & 2 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,16 @@ args = ["build", "--release"]
[tasks.build-no-std]
description = "Build using no-std"
command = "cargo"
args = ["build", "--no-default-features", "--target", "wasm32-unknown-unknown", "--workspace", "--exclude", "miden-mock"]
args = ["build", "--no-default-features", "--target", "wasm32-unknown-unknown", "--workspace", "--exclude", "miden-mock", "--exclude", "miden-bench-tx"]

# --- utilities ----------------------------------------------------------------------------------------
# --- benchmarking --------------------------------------------------------------------------------
[tasks.bench-tx]
description = "Run all available transaction benchmarks"
workspace = false
command = "cargo"
args = ["run", "--bin", "bench-tx"]

# --- utilities -----------------------------------------------------------------------------------
[tasks.watch]
description = "Watch for changes and rebuild"
workspace = false
Expand Down
23 changes: 23 additions & 0 deletions bench-tx/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "miden-bench-tx"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
homepage.workspace = true
repository.workspace = true
exclude.workspace = true

[[bin]]
name = "bench-tx"
path = "src/main.rs"

[dependencies]
miden-lib = { package = "miden-lib", path = "../miden-lib", version = "0.3" }
miden-objects = { package = "miden-objects", path = "../objects", version = "0.3" }
miden-tx = { package = "miden-tx", path = "../miden-tx", version = "0.3" }
mock = { package = "miden-mock", path = "../mock" }
serde = { package = "serde", version = "1.0" }
serde_json = { package = "serde_json", version = "1.0", features = ["preserve_order"] }
vm-processor = { workspace = true }
24 changes: 24 additions & 0 deletions bench-tx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Miden transactions benchmark

This crate contains an executable used for benchmarking transactions.

For each transaction, data is collected on the number of cycles required to complete:
- Prologue
- All notes processing
- Each note execution
- Transaction script processing
- Epilogue

## Usage

To run the benchmark you can use [cargo-make](https://github.com/sagiegurari/cargo-make) with the following command present in our [Makefile.toml](Makefile.toml):

```shell
cargo make bench-tx
```

Results of the benchmark are stored in the [bench-tx.json](bench-tx.json) file.

## License

This project is [MIT licensed](../LICENSE).
21 changes: 21 additions & 0 deletions bench-tx/bench-tx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"simple": {
"prologue": 3644,
"notes_processing": 1151,
"note_execution": {
"0x47b2fbff8a3f09e40343238e7b15c8918d7c63e570fd1b3c904ada458c4d74bd": 392,
"0x8a55c3531cdd5725aa805475093ed3006c6773b71a008e8ca840da8364a67cd6": 715
},
"tx_script_processing": 32,
"epilogue": 2222
},
"p2id": {
"prologue": 2004,
"notes_processing": 920,
"note_execution": {
"0xb9fa30eb43d80d579be02dc004338e06b5ad565e81e0bac11a94ab01abfdd40a": 883
},
"tx_script_processing": 88209,
"epilogue": 272
}
}
163 changes: 163 additions & 0 deletions bench-tx/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use core::fmt;
use std::{
fs::{read_to_string, write, File},
io::Write,
path::Path,
};

use miden_lib::{
notes::create_p2id_note, transaction::ToTransactionKernelInputs, utils::Serializable,
};
use miden_objects::{
accounts::AccountId,
assembly::ProgramAst,
assets::{Asset, FungibleAsset},
crypto::{dsa::rpo_falcon512::SecretKey, rand::RpoRandomCoin},
notes::NoteType,
transaction::TransactionArgs,
Felt,
};
use miden_tx::{TransactionExecutor, TransactionHost, TransactionProgress};
use vm_processor::{ExecutionOptions, RecAdviceProvider, Word};

mod utils;
use utils::{
get_account_with_default_account_code, write_bench_results_to_json, MockDataStore, String,
ToString, Vec, ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN,
ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN, ACCOUNT_ID_SENDER, DEFAULT_AUTH_SCRIPT,
};

pub enum Benchmark {
Simple,
P2ID,
}

impl fmt::Display for Benchmark {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Benchmark::Simple => write!(f, "simple"),
Benchmark::P2ID => write!(f, "p2id"),
}
}
}

fn main() -> Result<(), String> {
// create a template file for benchmark results
let path = Path::new("bench-tx/bench-tx.json");
let mut file = File::create(path).map_err(|e| e.to_string())?;
file.write_all(b"{}").map_err(|e| e.to_string())?;

// run all available benchmarks
let benchmark_results = vec![
(Benchmark::Simple, benchmark_default_tx()?),
(Benchmark::P2ID, benchmark_p2id()?),
];

// store benchmark results in the JSON file
write_bench_results_to_json(path, benchmark_results)?;

Ok(())
}

// BENCHMARKS
// ================================================================================================

/// Runs the default transaction with empty transaction script and two default notes.
pub fn benchmark_default_tx() -> Result<TransactionProgress, String> {
let data_store = MockDataStore::default();
let mut executor = TransactionExecutor::new(data_store.clone()).with_tracing();

let account_id = data_store.account.id();
executor.load_account(account_id).map_err(|e| e.to_string())?;

let block_ref = data_store.block_header.block_num();
let note_ids = data_store.notes.iter().map(|note| note.id()).collect::<Vec<_>>();

let transaction = executor
.prepare_transaction(account_id, block_ref, &note_ids, data_store.tx_args().clone())
.map_err(|e| e.to_string())?;

let (stack_inputs, advice_inputs) = transaction.get_kernel_inputs();
let advice_recorder: RecAdviceProvider = advice_inputs.into();
let mut host = TransactionHost::new(transaction.account().into(), advice_recorder);

vm_processor::execute(
transaction.program(),
stack_inputs,
&mut host,
ExecutionOptions::default().with_tracing(),
)
.map_err(|e| e.to_string())?;

Ok(host.tx_progress().clone())
}

/// Runs the transaction which consumes a P2ID note into a basic wallet.
pub fn benchmark_p2id() -> Result<TransactionProgress, String> {
// Create assets
let faucet_id = AccountId::try_from(ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN).unwrap();
let fungible_asset: Asset = FungibleAsset::new(faucet_id, 100).unwrap().into();

// Create sender and target account
let sender_account_id = AccountId::try_from(ACCOUNT_ID_SENDER).unwrap();

let target_account_id =
AccountId::try_from(ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN).unwrap();
let sec_key = SecretKey::new();
let target_pub_key: Word = sec_key.public_key().into();
let mut pk_sk_bytes = sec_key.to_bytes();
pk_sk_bytes.append(&mut target_pub_key.to_bytes());
let target_sk_pk_felt: Vec<Felt> =
pk_sk_bytes.iter().map(|a| Felt::new(*a as u64)).collect::<Vec<Felt>>();
let target_account =
get_account_with_default_account_code(target_account_id, target_pub_key, None);

// Create the note
let note = create_p2id_note(
sender_account_id,
target_account_id,
vec![fungible_asset],
NoteType::Public,
RpoRandomCoin::new([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]),
)
.unwrap();

let data_store =
MockDataStore::with_existing(Some(target_account.clone()), Some(vec![note.clone()]));

let mut executor = TransactionExecutor::new(data_store.clone()).with_tracing();
executor.load_account(target_account_id).unwrap();

let block_ref = data_store.block_header.block_num();
let note_ids = data_store.notes.iter().map(|note| note.id()).collect::<Vec<_>>();

let tx_script_code = ProgramAst::parse(DEFAULT_AUTH_SCRIPT).unwrap();

let tx_script_target = executor
.compile_tx_script(
tx_script_code.clone(),
vec![(target_pub_key, target_sk_pk_felt)],
vec![],
)
.unwrap();
let tx_args_target = TransactionArgs::with_tx_script(tx_script_target);

// execute transaction
let transaction = executor
.prepare_transaction(target_account_id, block_ref, &note_ids, tx_args_target)
.map_err(|e| e.to_string())?;

let (stack_inputs, advice_inputs) = transaction.get_kernel_inputs();
let advice_recorder: RecAdviceProvider = advice_inputs.into();
let mut host = TransactionHost::new(transaction.account().into(), advice_recorder);

vm_processor::execute(
transaction.program(),
stack_inputs,
&mut host,
ExecutionOptions::default().with_tracing(),
)
.map_err(|e| e.to_string())?;

Ok(host.tx_progress().clone())
}
Loading

0 comments on commit 20f8f6d

Please sign in to comment.