diff --git a/.github/workflows/build-reusable.yaml b/.github/workflows/build-reusable.yaml index 4e96f454e3..96b34170ef 100644 --- a/.github/workflows/build-reusable.yaml +++ b/.github/workflows/build-reusable.yaml @@ -131,6 +131,33 @@ jobs: - name: Build tests run: make build-tests-webrtc + build-benches: + needs: [build] + timeout-minutes: 60 + runs-on: ${{ inputs.os }} + env: + RUSTFLAGS: "-C overflow-checks=off -C debug-assertions=off ${{ inputs.rustflags }}" + steps: + - name: Git checkout + uses: actions/checkout@v5 + + - name: Setup build dependencies + uses: ./.github/actions/setup-build-deps + + - name: Use shared OCaml setting up steps + uses: ./.github/actions/setup-ocaml + with: + ocaml_version: ${{ inputs.ocaml_version }} + + - name: Setup Rust + uses: ./.github/actions/setup-rust + with: + toolchain: 1.84 + cache-prefix: build-${{ inputs.os }}-${{ inputs.cache-prefix }}v0 + + - name: Build benchmarks + run: make build-benches + build-wasm: if: ${{ inputs.build-wasm }} timeout-minutes: 60 diff --git a/.github/workflows/test-docs-scripts.yaml b/.github/workflows/test-docs-scripts.yaml index 4a4d419d81..ec3d00c6fd 100644 --- a/.github/workflows/test-docs-scripts.yaml +++ b/.github/workflows/test-docs-scripts.yaml @@ -221,6 +221,29 @@ jobs: # We'll just check if it was installed via Homebrew docker --version || echo "Docker installed but not running (expected on macOS CI)" + test-benchmark-commands: + name: Verify Benchmark Commands + runs-on: ubuntu-latest + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'test-doc-scripts') + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Verify benchmark scripts and commands + run: | + # Check if scripts exist and are executable + test -x ./website/docs/developers/scripts/benchmarks/build-benchmarks.sh + test -x ./website/docs/developers/scripts/benchmarks/run-all-benchmarks.sh + test -x ./website/docs/developers/scripts/benchmarks/run-database-benchmark.sh + + # Verify the make targets referenced in scripts exist in Makefile + grep -q "^build-benches:" Makefile + grep -q "^bench:" Makefile + grep -q "^bench-database:" Makefile + + echo "SUCCESS: All benchmark commands verified" + test-update-ocaml-node-script: name: Test Update OCaml Node Script runs-on: ${{ matrix.os }} diff --git a/CHANGELOG.md b/CHANGELOG.md index c27f68c4ef..5d5de041ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#1528](https://github.com/o1-labs/mina-rust/pull/1528/)) - **Codebase**: remove unused `ledger/src/poseidon/fp.rs` file ([#1538](https://github.com/o1-labs/mina-rust/pull/1538)) +- **Ledger**: convert unused binary to proper criterion benchmarks. Benchmarks + are now built as part of the existing build workflows across all platforms, + reusing build caches for efficiency + ([#1539](https://github.com/o1-labs/mina-rust/pull/1539)) ## v0.17.0 diff --git a/Cargo.lock b/Cargo.lock index 0021fc47ef..cabc797f32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,7 +107,7 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.32", ] [[package]] @@ -157,6 +157,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "ansi_term" version = "0.12.1" @@ -1475,6 +1481,12 @@ dependencies = [ "either", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cbc" version = "0.1.2" @@ -1611,6 +1623,33 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "cipher" version = "0.3.0" @@ -1939,6 +1978,42 @@ dependencies = [ "crc", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap 4.5.20", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + [[package]] name = "critical-section" version = "1.1.2" @@ -3506,6 +3581,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54c115d4f30f52c67202f079c5f9d8b49db4691f460fdb0b4c2e838261b2ba5" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy 0.8.27", +] + [[package]] name = "hash-tool" version = "0.17.0" @@ -3661,6 +3747,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" @@ -4254,6 +4346,26 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi 0.5.2", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -5551,6 +5663,7 @@ dependencies = [ "blake2", "bs58 0.4.0", "crc32fast", + "criterion", "derive_more", "fraction", "getrandom", @@ -6253,6 +6366,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -6792,6 +6911,34 @@ version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "polling" version = "2.8.0" @@ -8077,6 +8224,15 @@ dependencies = [ "cipher 0.4.4", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.22" @@ -9278,6 +9434,16 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.8.1" @@ -10097,6 +10263,16 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -10590,6 +10766,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -11073,7 +11258,16 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ - "zerocopy-derive", + "zerocopy-derive 0.7.32", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive 0.8.27", ] [[package]] @@ -11087,6 +11281,17 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2 1.0.95", + "quote 1.0.35", + "syn 2.0.96", +] + [[package]] name = "zerofrom" version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index 69b4c4703a..848b838aef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ clap = { version = "4.5", features = ["derive"] } console = "0.15.5" console_error_panic_hook = "0.1" crc32fast = "1" +criterion = { version = "0.5", features = ["html_reports"] } crypto-bigint = { version = "0.5.5", features = [ "generic-array", "serde", diff --git a/Makefile b/Makefile index 8311529a02..1753da2892 100644 --- a/Makefile +++ b/Makefile @@ -123,6 +123,22 @@ build-wasm: ## Build WebAssembly node --out-dir pkg \ target/wasm32-unknown-unknown/release/mina_node_web.wasm +.PHONY: build-benches +build-benches: ## Build all benchmarks without running them + @cargo bench --no-run + +.PHONY: build-bench-database +build-bench-database: ## Build ledger database benchmark + @cargo bench --bench database --no-run + +.PHONY: bench +bench: ## Run all benchmarks + @cargo bench + +.PHONY: bench-database +bench-database: ## Run ledger database benchmark + @cargo bench --bench database + .PHONY: check check: ## Check code for compilation errors cargo check --all-targets diff --git a/ledger/Cargo.toml b/ledger/Cargo.toml index 868be11b27..97c7f67731 100644 --- a/ledger/Cargo.toml +++ b/ledger/Cargo.toml @@ -6,9 +6,9 @@ license = "Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[[bin]] -name = "ledger" -path = "src/bin/ledger.rs" +[[bench]] +name = "database" +harness = false [lib] crate-type = ["staticlib", "cdylib", "lib"] @@ -87,6 +87,7 @@ ocaml-interop = { workspace = true, optional = true } reqwest = { workspace = true, features = ["blocking"] } [dev-dependencies] +criterion = { workspace = true } rand_pcg = { workspace = true } rand_seeder = { workspace = true } tuple-map = { workspace = true } diff --git a/ledger/benches/database.rs b/ledger/benches/database.rs new file mode 100644 index 0000000000..62a9751ce8 --- /dev/null +++ b/ledger/benches/database.rs @@ -0,0 +1,79 @@ +//! Database benchmarks +//! +//! Run with: +//! ```sh +//! cargo bench --bench database +//! ``` +//! +//! Or from the workspace root: +//! ```sh +//! cd ledger && cargo bench --bench database +//! ``` + +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; +use mina_tree::*; + +fn benchmark_account_generation(c: &mut Criterion) { + let mut group = c.benchmark_group("account_generation"); + group.sample_size(10); + + for naccounts in [1_000, 10_000, 120_000] { + group.bench_with_input( + BenchmarkId::from_parameter(naccounts), + &naccounts, + |b, &naccounts| { + b.iter(|| { + let mut db = Database::::create(20); + let accounts = (0..naccounts).map(|_| Account::rand()).collect::>(); + + for (index, mut account) in accounts.into_iter().enumerate() { + account.token_id = TokenId::from(index as u64); + let id = account.id(); + db.get_or_create_account(id, account).unwrap(); + } + + black_box(db) + }); + }, + ); + } + + group.finish(); +} + +fn benchmark_merkle_root_computation(c: &mut Criterion) { + let mut group = c.benchmark_group("merkle_root_computation"); + group.sample_size(10); + + for naccounts in [1_000, 10_000, 120_000] { + // Prepare the database with accounts + let mut db = Database::::create(20); + let accounts = (0..naccounts).map(|_| Account::rand()).collect::>(); + + for (index, mut account) in accounts.into_iter().enumerate() { + account.token_id = TokenId::from(index as u64); + let id = account.id(); + db.get_or_create_account(id, account).unwrap(); + } + + group.bench_with_input( + BenchmarkId::from_parameter(naccounts), + &naccounts, + |b, _| { + b.iter(|| { + let root = db.merkle_root(); + black_box(root) + }); + }, + ); + } + + group.finish(); +} + +criterion_group!( + benches, + benchmark_account_generation, + benchmark_merkle_root_computation +); +criterion_main!(benches); diff --git a/ledger/src/bin/ledger.rs b/ledger/src/bin/ledger.rs deleted file mode 100644 index 779dfd7517..0000000000 --- a/ledger/src/bin/ledger.rs +++ /dev/null @@ -1,28 +0,0 @@ -use mina_tree::*; - -fn main() { - for naccounts in [1_000, 10_000, 120_000] { - println!("{:?} accounts wasmer", naccounts); - - let now = redux::Instant::now(); - - let mut db = Database::::create(20); - - let accounts = (0..naccounts).map(|_| Account::rand()).collect::>(); - - for (index, mut account) in accounts.into_iter().enumerate() { - account.token_id = TokenId::from(index as u64); - let id = account.id(); - db.get_or_create_account(id, account).unwrap(); - } - - println!("generate random accounts {:?}", now.elapsed()); - let now = redux::Instant::now(); - - assert_eq!(db.num_accounts(), naccounts as usize); - - db.merkle_root(); - - println!("compute merkle root {:?}", now.elapsed()); - } -} diff --git a/website/docs/developers/benchmarks.mdx b/website/docs/developers/benchmarks.mdx new file mode 100644 index 0000000000..70e93a69ea --- /dev/null +++ b/website/docs/developers/benchmarks.mdx @@ -0,0 +1,54 @@ +--- +title: Benchmarks +sidebar_position: 8 +description: Performance benchmarks for Mina components +--- + +import CodeBlock from "@theme/CodeBlock"; +import BuildBenchmarks from "!!raw-loader!./scripts/benchmarks/build-benchmarks.sh"; +import RunAllBenchmarks from "!!raw-loader!./scripts/benchmarks/run-all-benchmarks.sh"; +import RunDatabaseBenchmark from "!!raw-loader!./scripts/benchmarks/run-database-benchmark.sh"; + +# Benchmarks + +Performance benchmarks for Mina components using +[Criterion](https://github.com/bheisler/criterion.rs). + +## Building benchmarks + +Build all benchmarks without running them: + + + {BuildBenchmarks} + + +## Running benchmarks + +Run all benchmarks: + + + {RunAllBenchmarks} + + +## Ledger + +### Database benchmarks + +Benchmarks for ledger database operations including account generation and +merkle root computation. Tests performance with 1,000, 10,000, and 120,000 +accounts. + +Run the database benchmark: + + + {RunDatabaseBenchmark} + + +Benchmark groups: + +- **account_generation**: Measures time to generate random accounts and insert + them into the database +- **merkle_root_computation**: Measures time to compute the merkle root for a + populated database + +Results are saved to `target/criterion/` with detailed HTML reports. diff --git a/website/docs/developers/scripts/benchmarks/build-benchmarks.sh b/website/docs/developers/scripts/benchmarks/build-benchmarks.sh new file mode 100644 index 0000000000..3b8d5d7099 --- /dev/null +++ b/website/docs/developers/scripts/benchmarks/build-benchmarks.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# Build all benchmarks + +make build-benches diff --git a/website/docs/developers/scripts/benchmarks/run-all-benchmarks.sh b/website/docs/developers/scripts/benchmarks/run-all-benchmarks.sh new file mode 100644 index 0000000000..9c23e04b46 --- /dev/null +++ b/website/docs/developers/scripts/benchmarks/run-all-benchmarks.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# Run all benchmarks + +make bench diff --git a/website/docs/developers/scripts/benchmarks/run-database-benchmark.sh b/website/docs/developers/scripts/benchmarks/run-database-benchmark.sh new file mode 100644 index 0000000000..c56a321890 --- /dev/null +++ b/website/docs/developers/scripts/benchmarks/run-database-benchmark.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# Run ledger database benchmark + +make bench-database diff --git a/website/sidebars.ts b/website/sidebars.ts index 2b2411cd4a..db04a8c567 100644 --- a/website/sidebars.ts +++ b/website/sidebars.ts @@ -133,6 +133,13 @@ const sidebars: SidebarsConfig = { 'developers/testing/ocaml-node-tests', ], }, + { + type: 'category', + label: 'Performance', + items: [ + 'developers/benchmarks', + ], + }, { type: 'category', label: 'Documentation',