diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb3baa8..cd0a0cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,7 +125,6 @@ jobs: with: key: msrv - run: cargo build --workspace --all-features - # ── Benchmark compile check ────────────────────────────────────────────────── bench-check: name: Benchmarks Compile @@ -137,6 +136,32 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: cargo bench --workspace --no-run --all-features + # ── Benchmark regression check (PR only, non-blocking — informational) ─────── + bench-compare: + name: Benchmark Regression Check + runs-on: ubuntu-latest + needs: [fmt, clippy] + if: github.event_name == 'pull_request' + continue-on-error: true + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + key: bench-compare + - name: Install critcmp + run: cargo install critcmp --locked + - name: Run benchmarks on PR branch + run: cargo bench -p mohu-buffer --bench ndarray_ops -- --save-baseline pr + - name: Compare against committed baseline + run: | + echo "## Benchmark comparison vs \`benches/baselines/main.json\`" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + critcmp benches/baselines/main.json pr >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "Comparisons within ±5% are noise on shared CI runners. Larger deltas warrant a closer look before merging." >> "$GITHUB_STEP_SUMMARY" + # ── Semver (PR only; non-blocking until crates are published) ──────────────── semver: name: Semver Check diff --git a/Cargo.lock b/Cargo.lock index 6a72436..0a29286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,18 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + [[package]] name = "anyhow" version = "1.0.102" @@ -260,6 +272,12 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.2.63" @@ -287,6 +305,58 @@ dependencies = [ "windows-link", ] +[[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 = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + [[package]] name = "const-random" version = "0.1.18" @@ -313,6 +383,42 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "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", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -483,6 +589,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -525,6 +637,26 @@ dependencies = [ "serde_core", ] +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -660,6 +792,7 @@ dependencies = [ name = "mohu-buffer" version = "0.1.0" dependencies = [ + "criterion", "half", "libc", "memmap2", @@ -921,12 +1054,46 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[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 = "portable-atomic" version = "1.13.1" @@ -1211,6 +1378,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[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 = "semver" version = "1.0.28" @@ -1343,6 +1519,16 @@ dependencies = [ "crunchy", ] +[[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 = "tracing" version = "0.1.44" @@ -1417,6 +1603,16 @@ dependencies = [ "libc", ] +[[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 = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1520,6 +1716,25 @@ dependencies = [ "semver", ] +[[package]] +name = "web-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + [[package]] name = "windows-core" version = "0.62.2" diff --git a/Cargo.toml b/Cargo.toml index 51f0958..e9690b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,6 +118,7 @@ tracing = "0.1" # ── Testing ─────────────────────────────────────────────────────────────────── proptest = "1" approx = "0.5" +criterion = { version = "0.5", features = ["html_reports"] } # ── Build utilities ─────────────────────────────────────────────────────────── cfg-if = "1" diff --git a/benches/README.md b/benches/README.md new file mode 100644 index 0000000..051e492 --- /dev/null +++ b/benches/README.md @@ -0,0 +1,74 @@ +# Benchmarks + +This directory contains Criterion-based benchmarks for mohu's performance-critical code paths. + +## Current scope + +`mohu-array` (the public `NdArray` type) is not yet implemented, so these +benchmarks currently exercise the `mohu-buffer` allocation layer that +`NdArray` will be built on top of. Once `NdArray` lands, these benchmarks +should be extended to cover array-level operations (creation, indexing, +elementwise arithmetic, reductions, etc). + +## Running locally + +```bash +cargo bench -p mohu-buffer --bench ndarray_ops +``` + +To save your own named baseline for comparison: + +```bash +cargo bench -p mohu-buffer --bench ndarray_ops -- --save-baseline my-branch +``` + +## Comparing against the committed baseline + +Install [critcmp](https://github.com/BurntSushi/critcmp): + +```bash +cargo install critcmp +``` + +Then compare your local run against the committed baseline: + +```bash +critcmp benches/baselines/main.json my-branch +``` + +## Current baseline + +Captured on a GitHub Codespaces cloud runner (shared vCPU, not a dedicated +benchmark machine). Absolute numbers will vary across hardware — treat this +as a relative regression baseline, not an authoritative performance claim. + +| Benchmark | Time (median) | +|-----------------------------|---------------| +| buffer_zeros_f64/100 | ~245 ns | +| buffer_zeros_f64/10000 | ~1.27 µs | +| buffer_zeros_f64/1000000 | ~6.9 µs | +| buffer_alloc_f64/100 | ~217 ns | +| buffer_alloc_f64/10000 | ~277 ns | +| buffer_alloc_f64/1000000 | ~6.5 µs | + +Full raw data: `benches/baselines/main.json` + +## Intended NumPy comparison targets (future work) + +Once `NdArray` is implemented, the intent is to benchmark these operations +against their NumPy equivalents (by name, run separately in Python — this +repo does not execute NumPy in CI): + +| mohu operation | NumPy equivalent | +|------------------------|---------------------------| +| `NdArray::zeros` | `numpy.zeros` | +| `NdArray::from_vec` | `numpy.array` | +| elementwise add/mul | `numpy.add` / `numpy.multiply` | +| `sum` / `mean` reduce | `numpy.sum` / `numpy.mean`| + +## CI regression gate + +On every pull request, CI runs these benchmarks and compares them against +`benches/baselines/main.json` using critcmp, flagging any benchmark that +regresses by more than 5% for human review. See +`.github/workflows/bench-check.yml`. \ No newline at end of file diff --git a/benches/baselines/main.json b/benches/baselines/main.json new file mode 100644 index 0000000..94b6478 --- /dev/null +++ b/benches/baselines/main.json @@ -0,0 +1,359 @@ +{ + "name": "main", + "benchmarks": { + "buffer_alloc_f64/100": { + "baseline": "main", + "fullname": "main/buffer_alloc_f64/100", + "criterion_benchmark_v1": { + "group_id": "buffer_alloc_f64", + "function_id": null, + "value_str": "100", + "throughput": null, + "full_id": "buffer_alloc_f64/100", + "directory_name": "buffer_alloc_f64/100" + }, + "criterion_estimates_v1": { + "mean": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 215.4770072844856, + "upper_bound": 227.45808499781123 + }, + "point_estimate": 221.12818935347596, + "standard_error": 3.081155280627522 + }, + "median": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 206.0101013780125, + "upper_bound": 207.50011534025376 + }, + "point_estimate": 206.35550055168457, + "standard_error": 0.3769437575831727 + }, + "median_abs_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 2.0097181304103464, + "upper_bound": 5.978719406749167 + }, + "point_estimate": 3.5166991417757343, + "standard_error": 1.1022028248558946 + }, + "slope": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 211.9912980941652, + "upper_bound": 223.4395894737038 + }, + "point_estimate": 217.1576332282198, + "standard_error": 2.933951688098023 + }, + "std_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 21.711471619641696, + "upper_bound": 39.36962758484909 + }, + "point_estimate": 30.93114770931437, + "standard_error": 4.547442141238409 + } + } + }, + "buffer_alloc_f64/10000": { + "baseline": "main", + "fullname": "main/buffer_alloc_f64/10000", + "criterion_benchmark_v1": { + "group_id": "buffer_alloc_f64", + "function_id": null, + "value_str": "10000", + "throughput": null, + "full_id": "buffer_alloc_f64/10000", + "directory_name": "buffer_alloc_f64/10000" + }, + "criterion_estimates_v1": { + "mean": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 266.63919111459825, + "upper_bound": 288.39623363109655 + }, + "point_estimate": 276.79315624738547, + "standard_error": 5.58187581182907 + }, + "median": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 250.19951249365408, + "upper_bound": 257.0373900577318 + }, + "point_estimate": 250.99569285556217, + "standard_error": 1.5026603774178682 + }, + "median_abs_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 1.7827687683920352, + "upper_bound": 11.76672215948482 + }, + "point_estimate": 4.226483395908635, + "standard_error": 3.1536563170400114 + }, + "slope": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 265.7303941393552, + "upper_bound": 289.26335295959836 + }, + "point_estimate": 276.44129714276943, + "standard_error": 6.031527483847185 + }, + "std_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 39.0111954590928, + "upper_bound": 71.58732714280117 + }, + "point_estimate": 56.1092903384509, + "standard_error": 8.400195929113455 + } + } + }, + "buffer_alloc_f64/1000000": { + "baseline": "main", + "fullname": "main/buffer_alloc_f64/1000000", + "criterion_benchmark_v1": { + "group_id": "buffer_alloc_f64", + "function_id": null, + "value_str": "1000000", + "throughput": null, + "full_id": "buffer_alloc_f64/1000000", + "directory_name": "buffer_alloc_f64/1000000" + }, + "criterion_estimates_v1": { + "mean": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 6032.571999385703, + "upper_bound": 6375.311664220887 + }, + "point_estimate": 6190.926321973251, + "standard_error": 88.00183169070316 + }, + "median": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 5852.359964726631, + "upper_bound": 5980.901666666667 + }, + "point_estimate": 5894.792993285683, + "standard_error": 36.32473667364581 + }, + "median_abs_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 162.19413771770064, + "upper_bound": 335.3801836715745 + }, + "point_estimate": 212.34504328925564, + "standard_error": 44.61462139610492 + }, + "slope": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 6117.074536055763, + "upper_bound": 6595.694264815782 + }, + "point_estimate": 6341.541326517395, + "standard_error": 122.68275872775536 + }, + "std_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 554.3121652042461, + "upper_bound": 1150.8767110675751 + }, + "point_estimate": 883.8491610125861, + "standard_error": 152.39854407890147 + } + } + }, + "buffer_zeros_f64/100": { + "baseline": "main", + "fullname": "main/buffer_zeros_f64/100", + "criterion_benchmark_v1": { + "group_id": "buffer_zeros_f64", + "function_id": null, + "value_str": "100", + "throughput": null, + "full_id": "buffer_zeros_f64/100", + "directory_name": "buffer_zeros_f64/100" + }, + "criterion_estimates_v1": { + "mean": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 229.71296018013473, + "upper_bound": 242.74839397562008 + }, + "point_estimate": 235.59658801992973, + "standard_error": 3.3367367549268314 + }, + "median": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 224.4156934083386, + "upper_bound": 225.3430554657354 + }, + "point_estimate": 224.8785277807932, + "standard_error": 0.22085871345567035 + }, + "median_abs_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 1.507853661440053, + "upper_bound": 3.242147688450977 + }, + "point_estimate": 2.1855108431912607, + "standard_error": 0.4339779548586003 + }, + "slope": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 229.02333464947023, + "upper_bound": 251.1400690098626 + }, + "point_estimate": 238.33809846985244, + "standard_error": 5.741209019146897 + }, + "std_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 18.766293739566695, + "upper_bound": 45.07457797693401 + }, + "point_estimate": 33.46839092630287, + "standard_error": 6.751052651930111 + } + } + }, + "buffer_zeros_f64/10000": { + "baseline": "main", + "fullname": "main/buffer_zeros_f64/10000", + "criterion_benchmark_v1": { + "group_id": "buffer_zeros_f64", + "function_id": null, + "value_str": "10000", + "throughput": null, + "full_id": "buffer_zeros_f64/10000", + "directory_name": "buffer_zeros_f64/10000" + }, + "criterion_estimates_v1": { + "mean": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 1241.5233433234073, + "upper_bound": 1332.564368724753 + }, + "point_estimate": 1284.210728249188, + "standard_error": 23.328202538886305 + }, + "median": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 1170.316114229707, + "upper_bound": 1184.7757658660696 + }, + "point_estimate": 1173.6390298859956, + "standard_error": 3.4612836435359093 + }, + "median_abs_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 6.614791144745011, + "upper_bound": 29.22778068571972 + }, + "point_estimate": 11.701779260850827, + "standard_error": 5.316732135980022 + }, + "slope": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 1222.0077489469377, + "upper_bound": 1335.2125016240363 + }, + "point_estimate": 1270.5769464367472, + "standard_error": 29.41467381866097 + }, + "std_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 169.74352582542423, + "upper_bound": 290.3411823875646 + }, + "point_estimate": 234.7692163058038, + "standard_error": 30.736624129461777 + } + } + }, + "buffer_zeros_f64/1000000": { + "baseline": "main", + "fullname": "main/buffer_zeros_f64/1000000", + "criterion_benchmark_v1": { + "group_id": "buffer_zeros_f64", + "function_id": null, + "value_str": "1000000", + "throughput": null, + "full_id": "buffer_zeros_f64/1000000", + "directory_name": "buffer_zeros_f64/1000000" + }, + "criterion_estimates_v1": { + "mean": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 6172.79609988723, + "upper_bound": 6753.402754901952 + }, + "point_estimate": 6441.305244104493, + "standard_error": 148.3511924714342 + }, + "median": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 5818.522021028722, + "upper_bound": 5897.632179725037 + }, + "point_estimate": 5852.957760304699, + "standard_error": 22.806516972769277 + }, + "median_abs_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 119.5999316819552, + "upper_bound": 278.2788945564611 + }, + "point_estimate": 186.6645923583325, + "standard_error": 41.80208199911697 + }, + "slope": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 6372.492546342996, + "upper_bound": 7497.997994009239 + }, + "point_estimate": 6897.646923294218, + "standard_error": 288.71347149896866 + }, + "std_dev": { + "confidence_interval": { + "confidence_level": 0.95, + "lower_bound": 924.76128195601, + "upper_bound": 1952.389451226467 + }, + "point_estimate": 1487.6850762500412, + "standard_error": 261.3535779363825 + } + } + } + } +} diff --git a/benches/ndarray_ops.rs b/benches/ndarray_ops.rs index e81ef5f..1efdc08 100644 --- a/benches/ndarray_ops.rs +++ b/benches/ndarray_ops.rs @@ -1,2 +1,34 @@ // Workspace-level benchmarks for mohu array operations. -// Run with: cargo bench +// Run with: cargo bench -p mohu-buffer +// +// These benchmarks currently exercise the `mohu-buffer` allocation layer, +// since `mohu-array` (the public NdArray type) is not yet implemented. +// Once NdArray lands, these should be extended/replaced with array-level +// benchmarks (creation, indexing, elementwise ops, etc). + +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; +use mohu_buffer::buffer::Buffer; +use mohu_dtype::dtype::DType; + +fn bench_buffer_zeros(c: &mut Criterion) { + let mut group = c.benchmark_group("buffer_zeros_f64"); + for &n in &[100usize, 10_000, 1_000_000] { + group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| { + b.iter(|| Buffer::zeros(DType::F64, &[n]).unwrap()); + }); + } + group.finish(); +} + +fn bench_buffer_alloc(c: &mut Criterion) { + let mut group = c.benchmark_group("buffer_alloc_f64"); + for &n in &[100usize, 10_000, 1_000_000] { + group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| { + b.iter(|| Buffer::alloc(DType::F64, &[n], mohu_buffer::layout::Order::C).unwrap()); + }); + } + group.finish(); +} + +criterion_group!(benches, bench_buffer_zeros, bench_buffer_alloc); +criterion_main!(benches); diff --git a/crates/mohu-buffer/Cargo.toml b/crates/mohu-buffer/Cargo.toml index 6b0ba53..bb98c8b 100644 --- a/crates/mohu-buffer/Cargo.toml +++ b/crates/mohu-buffer/Cargo.toml @@ -30,3 +30,14 @@ libc.workspace = true [dependencies.memmap2] workspace = true optional = true + + + +[dev-dependencies] +criterion.workspace = true +mohu-dtype.workspace = true + +[[bench]] +name = "ndarray_ops" +path = "../../benches/ndarray_ops.rs" +harness = false