From 04e1f10d35459f076dce5bd8210bd09ecf475132 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Wed, 26 Jun 2024 15:43:05 -0400 Subject: [PATCH 01/33] add readme --- benches/README.md | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/benches/README.md b/benches/README.md index 464090415..cbf86119a 100644 --- a/benches/README.md +++ b/benches/README.md @@ -1 +1,44 @@ -# TODO +# Benches +This directory contains 3 sub-directories: + +| Sub-directory | Purpose | +|---------------|---------| +| `micro/` | Micro-benchmarks for crates (e.g. timings for a single function) +| `macro/` | Macro-benchmarks for whole crates or sub-systems (using Cuprate's custom benchmarking harness) +| `harness/` | Cuprate's custom benchmarking harness + +## Harness +The harness is just another crate (that happens to be for benchmarking). + +Conceptually, it's purpose is very simple: +1. Set-up the benchmark +1. Start timer +1. Run benchmark +1. Output data + +This single harness runs the benchmarks found in `macro/`. + +The way benchmarks "plug-in" to the harness is simply by implementing `trait Benchmark`. + +See `cuprate-harness`' crate documentation for a user-guide: +```bash +cargo doc --open --package cuprate-harness +``` + +## Macro +Each sub-directory in here is a crate that plugs into the harness. + +Benchmarks in `macro/` are for testing sub-systems and/or sections of a sub-system, e.g. the block downloader, the RPC server, the database, etc. + + +See `macro/cuprate-database` for an example. + + +## Micro +Each sub-directory in here is a crate that uses [Criterion](https://bheisler.github.io/criterion.rs/book) for timing single functions, groups of functions. + +They are generally be small in scope. + + +See `macro/cuprate-json-rpc` for an example. + \ No newline at end of file From b538272f4d14c873d1fc4c2871ee12f4761117b5 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Wed, 26 Jun 2024 16:25:55 -0400 Subject: [PATCH 02/33] readme, basic examples --- Cargo.lock | 266 ++++++++++++++++++ Cargo.toml | 31 +- benches/README.md | 85 ++++-- benches/criterion/cuprate-json-rpc/Cargo.toml | 21 ++ .../cuprate-json-rpc/benches/main.rs | 81 ++++++ .../cuprate-json-rpc/benches/response.rs | 94 +++++++ benches/criterion/cuprate-json-rpc/src/lib.rs | 77 +++++ benches/harness/Cargo.toml | 23 ++ benches/harness/harness-test/Cargo.toml | 14 + benches/harness/harness-test/src/lib.rs | 16 ++ benches/harness/lib/Cargo.toml | 15 + benches/harness/lib/src/benchmark.rs | 16 ++ benches/harness/lib/src/lib.rs | 97 +++++++ benches/harness/src/main.rs | 120 ++++++++ 14 files changed, 921 insertions(+), 35 deletions(-) create mode 100644 benches/criterion/cuprate-json-rpc/Cargo.toml create mode 100644 benches/criterion/cuprate-json-rpc/benches/main.rs create mode 100644 benches/criterion/cuprate-json-rpc/benches/response.rs create mode 100644 benches/criterion/cuprate-json-rpc/src/lib.rs create mode 100644 benches/harness/Cargo.toml create mode 100644 benches/harness/harness-test/Cargo.toml create mode 100644 benches/harness/harness-test/src/lib.rs create mode 100644 benches/harness/lib/Cargo.toml create mode 100644 benches/harness/lib/src/benchmark.rs create mode 100644 benches/harness/lib/src/lib.rs create mode 100644 benches/harness/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 079972889..fa6ebcc94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -50,6 +59,12 @@ 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.7" @@ -275,6 +290,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.99" @@ -305,6 +326,33 @@ dependencies = [ "windows-targets 0.52.5", ] +[[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.5.7" @@ -377,6 +425,42 @@ dependencies = [ "libc", ] +[[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" version = "0.8.4" @@ -569,6 +653,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "cuprate-criterion-json-rpc" +version = "0.0.0" +dependencies = [ + "criterion", + "cuprate-json-rpc", + "function_name", + "serde_json", + "tempfile", +] + [[package]] name = "cuprate-cryptonight" version = "0.1.0" @@ -637,6 +732,27 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cuprate-harness" +version = "0.0.0" +dependencies = [ + "cfg-if", + "cuprate-database", + "cuprate-harness-lib", + "cuprate-harness-test", +] + +[[package]] +name = "cuprate-harness-lib" +version = "0.0.0" + +[[package]] +name = "cuprate-harness-test" +version = "0.0.0" +dependencies = [ + "cuprate-harness-lib", +] + [[package]] name = "cuprate-helper" version = "0.1.0" @@ -1006,6 +1122,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "function_name" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1ab577a896d09940b5fe12ec5ae71f9d8211fff62c919c03a3750a9901e98a7" +dependencies = [ + "function_name-proc-macro", +] + +[[package]] +name = "function_name-proc-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673464e1e314dd67a0fd9544abc99e8eb28d0c7e3b69b033bcff9b2d00b87333" + [[package]] name = "funty" version = "2.0.0" @@ -1127,6 +1258,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1374,6 +1515,26 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[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.11" @@ -1601,6 +1762,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "openssl-probe" version = "0.1.5" @@ -1761,6 +1928,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "plotters" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" + +[[package]] +name = "plotters-svg" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +dependencies = [ + "plotters-backend", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1998,6 +2193,29 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + [[package]] name = "regex-syntax" version = "0.8.4" @@ -2125,6 +2343,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[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.23" @@ -2395,6 +2622,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 = "tinyvec" version = "1.6.0" @@ -2655,6 +2892,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 = "want" version = "0.3.1" @@ -2724,6 +2971,16 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2740,6 +2997,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 8891b83bf..5d77862e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,30 +1,40 @@ [workspace] resolver = "2" - members = [ + # Benchmarks + "benches/harness", + "benches/harness/lib", + "benches/harness/harness-test", + "benches/criterion/cuprate-json-rpc", + # Consensus "consensus", "consensus/fast-sync", "consensus/rules", - "cryptonight", - "helper", + # Net "net/epee-encoding", "net/fixed-bytes", "net/levin", "net/wire", + # P2P "p2p/p2p", "p2p/p2p-core", "p2p/dandelion-tower", "p2p/async-buffer", "p2p/address-book", + # Storage "storage/blockchain", "storage/txpool", "storage/database", - "pruning", - "test-utils", - "types", + # RPC "rpc/json-rpc", "rpc/rpc-types", "rpc/rpc-interface", + # Misc + "cryptonight", + "helper", + "pruning", + "test-utils", + "types", ] [profile.release] @@ -85,6 +95,8 @@ tracing-subscriber = { version = "0.3.17", default-features = false } tracing = { version = "0.1.40", default-features = false } ## workspace.dev-dependencies +criterion = { version = "0.5.1" } +function_name = { version = "0.3.0" } tempfile = { version = "3" } pretty_assertions = { version = "1.4.0" } proptest = { version = "1" } @@ -99,9 +111,4 @@ tokio-test = { version = "0.4.4" } # once_cell = { version = "1.18.0" } # Lazy/one-time initialization | https://github.com/matklad/once_cell # open = { version = "5.0.0" } # Open PATH/URL, probably for binaries | https://github.com/byron/open-rs # regex = { version = "1.10.2" } # Regular expressions | https://github.com/rust-lang/regex -# ryu = { version = "1.0.15" } # Fast float to string formatting | https://github.com/dtolnay/ryu - -# Maybe one day. -# disk = { version = "*" } # (De)serialization to/from disk with various file formats | https://github.com/hinto-janai/disk -# readable = { version = "*" } # Stack-based string formatting utilities | https://github.com/hinto-janai/readable -# json-rpc = { git = "https://github.com/hinto-janai/json-rpc" } # JSON-RPC 2.0 types +# ryu = { version = "1.0.15" } # Fast float to string formatting | https://github.com/dtolnay/ryu \ No newline at end of file diff --git a/benches/README.md b/benches/README.md index cbf86119a..c5c676717 100644 --- a/benches/README.md +++ b/benches/README.md @@ -1,44 +1,83 @@ # Benches -This directory contains 3 sub-directories: +This directory contains Cuprate's benchmarks and benchmarking utilities. + +- [1. File layout and purpose](#1-file-layout-and-purpose) +- [2. Harness](#2-harness) + - [2.1 Creating a harness benchmark](#21-creating-a-harness-benchmark) + - [2.2 Running a harness benchmark](#22-running-a-harness-benchmark) +- [3. Criterion](#3-criterion) + - [2.1 Creating a Criterion benchmark](#21-creating-a-criterion-benchmark) + - [2.2 Running a Criterion benchmark](#22-running-a-criterion-benchmark) + +## 1. File layout and purpose +This directory is sorted into 4 important categories: | Sub-directory | Purpose | |---------------|---------| -| `micro/` | Micro-benchmarks for crates (e.g. timings for a single function) -| `macro/` | Macro-benchmarks for whole crates or sub-systems (using Cuprate's custom benchmarking harness) -| `harness/` | Cuprate's custom benchmarking harness +| `harness/src` | Cuprate's custom benchmarking harness **binary** +| `harness/lib` | Cuprate's custom benchmarking harness **library** +| `harness/*` | Macro-benchmarks for whole crates or sub-systems (using Cuprate's custom benchmarking harness) +| `criterion/*` | Micro-benchmarks for crates (e.g. timings for a single function) -## Harness -The harness is just another crate (that happens to be for benchmarking). +## 2. Harness +The harness is: +- `cuprate-harness`; the actual binary crate ran +- `cuprate-harness-lib`; the library that other crates hook into -Conceptually, it's purpose is very simple: +The purpose of the harness is very simple: 1. Set-up the benchmark 1. Start timer 1. Run benchmark 1. Output data -This single harness runs the benchmarks found in `macro/`. +The harness runs the benchmarks found in `harness/`. -The way benchmarks "plug-in" to the harness is simply by implementing `trait Benchmark`. +The way benchmarks "plug-in" to the harness is simply by implementing `cuprate_harness_lib::Benchmark`. -See `cuprate-harness`' crate documentation for a user-guide: +See `cuprate-harness-lib` crate documentation for a user-guide: ```bash -cargo doc --open --package cuprate-harness +cargo doc --open --package cuprate-harness-lib ``` -## Macro -Each sub-directory in here is a crate that plugs into the harness. +### 2.1 Creating a harness benchmark +1. Create a new crate inside `benches/harness` (consider copying `benches/harness/test` as a base) +2. Pull in `cuprate_harness_lib` as a dependency +3. Implement `cuprate_harness_lib::Benchmark` +4. Add a feature inside `cuprate_harness` for your benchmark -Benchmarks in `macro/` are for testing sub-systems and/or sections of a sub-system, e.g. the block downloader, the RPC server, the database, etc. - - -See `macro/cuprate-database` for an example. - +### 2.2 Running a harness benchmark +After your benchmark is implemented, run this command: +```bash +cargo run --release --package cuprate-harness --features $YOUR_BENCHMARK_CRATE_FEATURE +``` +For example, to run the test benchmark: +```bash +cargo run --release --package cuprate-harness --features test +``` -## Micro -Each sub-directory in here is a crate that uses [Criterion](https://bheisler.github.io/criterion.rs/book) for timing single functions, groups of functions. +## 3. Criterion +Each sub-directory in here is a crate that uses [Criterion](https://bheisler.github.io/criterion.rs/book) for timing single functions and/or groups of functions. They are generally be small in scope. - -See `macro/cuprate-json-rpc` for an example. - \ No newline at end of file +See [`criterion/cuprate-json-rpc`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-json-rpc) for an example. + +### 3.1 Creating a Criterion benchmark +1. Copy [`criterion/test`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion) as base +2. Read the `Getting Started` section of +3. Get started + +### 3.1 Running a Criterion benchmark +To run all Criterion benchmarks, run this from the repository root: +```bash +cargo bench +``` + +To run specific package(s), use: +```bash +cargo bench --package $CRITERION_BENCHMARK_CRATE_NAME +``` +For example: +```bash +cargo bench --package cuprate-criterion-json-rpc +``` \ No newline at end of file diff --git a/benches/criterion/cuprate-json-rpc/Cargo.toml b/benches/criterion/cuprate-json-rpc/Cargo.toml new file mode 100644 index 000000000..eaa264708 --- /dev/null +++ b/benches/criterion/cuprate-json-rpc/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "cuprate-criterion-json-rpc" +version = "0.0.0" +edition = "2021" +description = "Criterion benchmarking for cuprate-json-rpc" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/micro/cuprate-json-rpc" +keywords = ["cuprate", "json-rpc", "criterion", "benchmark"] + +[dependencies] +criterion = { workspace = true } +function_name = { workspace = true } +serde_json = { workspace = true, features = ["default"] } +tempfile = { workspace = true } + +cuprate-json-rpc = { path = "../../../rpc/json-rpc" } + +[[bench]] +name = "main" +harness = false \ No newline at end of file diff --git a/benches/criterion/cuprate-json-rpc/benches/main.rs b/benches/criterion/cuprate-json-rpc/benches/main.rs new file mode 100644 index 000000000..84585ee99 --- /dev/null +++ b/benches/criterion/cuprate-json-rpc/benches/main.rs @@ -0,0 +1,81 @@ +//! TODO + +//---------------------------------------------------------------------------------------------------- Lints +// Forbid lints. +// Our code, and code generated (e.g macros) cannot overrule these. +#![forbid( + // `unsafe` is allowed but it _must_ be + // commented with `SAFETY: reason`. + clippy::undocumented_unsafe_blocks, + // Never. + unused_unsafe, + redundant_semicolons, + unused_allocation, + coherence_leak_check, + while_true, + clippy::missing_docs_in_private_items, + // Maybe can be put into `#[deny]`. + unconditional_recursion, + for_loops_over_fallibles, + unused_braces, + unused_doc_comments, + unused_labels, + keyword_idents, + non_ascii_idents, + variant_size_differences, + single_use_lifetimes, + // Probably can be put into `#[deny]`. + future_incompatible, + let_underscore, + break_with_label_and_loop, + duplicate_macro_attributes, + exported_private_dependencies, + large_assignments, + overlapping_range_endpoints, + semicolon_in_expressions_from_macros, + noop_method_call, + unreachable_pub, +)] +// Deny lints. +// Some of these are `#[allow]`'ed on a per-case basis. +#![deny( + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, + clippy::cargo, + unused_mut, + missing_docs, + deprecated, + unused_comparisons, + nonstandard_style +)] +#![allow(unreachable_code, unused_variables, dead_code, unused_imports)] // TODO: remove +#![allow( + // FIXME: this lint affects crates outside of + // `database/` for some reason, allow for now. + clippy::cargo_common_metadata, + // FIXME: adding `#[must_use]` onto everything + // might just be more annoying than useful... + // although it is sometimes nice. + clippy::must_use_candidate, + // TODO: should be removed after all `todo!()`'s are gone. + clippy::diverging_sub_expression, + clippy::module_name_repetitions, + clippy::module_inception, + clippy::redundant_pub_crate, + clippy::option_if_let_else, + clippy::significant_drop_tightening, +)] +// Allow some lints when running in debug mode. +#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] + +mod response; + +criterion::criterion_main! { + response::benches, +} diff --git a/benches/criterion/cuprate-json-rpc/benches/response.rs b/benches/criterion/cuprate-json-rpc/benches/response.rs new file mode 100644 index 000000000..93e209af9 --- /dev/null +++ b/benches/criterion/cuprate-json-rpc/benches/response.rs @@ -0,0 +1,94 @@ +//! `trait Storable` benchmarks. + +//---------------------------------------------------------------------------------------------------- Import +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use function_name::named; +use serde_json::{from_str, to_string_pretty}; + +use cuprate_json_rpc::{Id, Response}; + +//---------------------------------------------------------------------------------------------------- Criterion +criterion_group! { + benches, + response_from_str_u8, + response_from_str_u64, + response_from_str_string_5_len, + response_from_str_string_10_len, + response_from_str_string_100_len, + response_from_str_string_500_len, + response_to_string_pretty_u8, + response_to_string_pretty_u64, + response_to_string_pretty_string_5_len, + response_to_string_pretty_string_10_len, + response_to_string_pretty_string_100_len, + response_to_string_pretty_string_500_len, +} +criterion_main!(benches); + +//---------------------------------------------------------------------------------------------------- Deserialization +/// TODO +macro_rules! impl_from_str_benchmark { + ( + $( + $fn_name:ident => $request_type:ty => $request_string:literal, + )* + ) => { + $( + /// TODO + #[named] + fn $fn_name(c: &mut Criterion) { + let request_string = $request_string; + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let _r = from_str::>( + black_box(request_string) + ); + }); + }); + } + )* + }; +} + +impl_from_str_benchmark! { + response_from_str_u8 => u8 => r#"{"jsonrpc":"2.0","id":123,"result":0}"#, + response_from_str_u64 => u64 => r#"{"jsonrpc":"2.0","id":123,"result":0}"#, + response_from_str_string_5_len => String => r#"{"jsonrpc":"2.0","id":123,"result":"hello"}"#, + response_from_str_string_10_len => String => r#"{"jsonrpc":"2.0","id":123,"result":"hellohello"}"#, + response_from_str_string_100_len => String => r#"{"jsonrpc":"2.0","id":123,"result":"helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld"}"#, + response_from_str_string_500_len => String => r#"{"jsonrpc":"2.0","id":123,"result":"helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld"}"#, +} + +//---------------------------------------------------------------------------------------------------- Deserialization +/// TODO +macro_rules! impl_to_string_pretty_benchmark { + ( + $( + $fn_name:ident => $request_constructor:expr, + )* + ) => { + $( + /// TODO + #[named] + fn $fn_name(c: &mut Criterion) { + let request = $request_constructor; + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let _s = to_string_pretty(black_box(&request)).unwrap(); + }); + }); + } + )* + }; +} + +impl_to_string_pretty_benchmark! { + response_to_string_pretty_u8 => Response::::ok(Id::Null, 0), + response_to_string_pretty_u64 => Response::::ok(Id::Null, 0), + response_to_string_pretty_string_5_len => Response::ok(Id::Null, String::from("hello")), + response_to_string_pretty_string_10_len => Response::ok(Id::Null, String::from("hellohello")), + response_to_string_pretty_string_100_len => Response::ok(Id::Null, String::from("helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld")), + response_to_string_pretty_string_500_len => Response::ok(Id::Null, String::from("helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld")), +} diff --git a/benches/criterion/cuprate-json-rpc/src/lib.rs b/benches/criterion/cuprate-json-rpc/src/lib.rs new file mode 100644 index 000000000..00561eee1 --- /dev/null +++ b/benches/criterion/cuprate-json-rpc/src/lib.rs @@ -0,0 +1,77 @@ +//! TODO + +//---------------------------------------------------------------------------------------------------- Lints +// Forbid lints. +// Our code, and code generated (e.g macros) cannot overrule these. +#![forbid( + // `unsafe` is allowed but it _must_ be + // commented with `SAFETY: reason`. + clippy::undocumented_unsafe_blocks, + // Never. + unused_unsafe, + redundant_semicolons, + unused_allocation, + coherence_leak_check, + while_true, + clippy::missing_docs_in_private_items, + // Maybe can be put into `#[deny]`. + unconditional_recursion, + for_loops_over_fallibles, + unused_braces, + unused_doc_comments, + unused_labels, + keyword_idents, + non_ascii_idents, + variant_size_differences, + single_use_lifetimes, + // Probably can be put into `#[deny]`. + future_incompatible, + let_underscore, + break_with_label_and_loop, + duplicate_macro_attributes, + exported_private_dependencies, + large_assignments, + overlapping_range_endpoints, + semicolon_in_expressions_from_macros, + noop_method_call, + unreachable_pub, +)] +// Deny lints. +// Some of these are `#[allow]`'ed on a per-case basis. +#![deny( + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, + clippy::cargo, + unused_mut, + missing_docs, + deprecated, + unused_comparisons, + nonstandard_style +)] +#![allow(unreachable_code, unused_variables, dead_code, unused_imports)] // TODO: remove +#![allow( + // FIXME: this lint affects crates outside of + // `database/` for some reason, allow for now. + clippy::cargo_common_metadata, + // FIXME: adding `#[must_use]` onto everything + // might just be more annoying than useful... + // although it is sometimes nice. + clippy::must_use_candidate, + // TODO: should be removed after all `todo!()`'s are gone. + clippy::diverging_sub_expression, + clippy::module_name_repetitions, + clippy::module_inception, + clippy::redundant_pub_crate, + clippy::option_if_let_else, + clippy::significant_drop_tightening, +)] +// Allow some lints when running in debug mode. +#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] + +//---------------------------------------------------------------------------------------------------- Modules diff --git a/benches/harness/Cargo.toml b/benches/harness/Cargo.toml new file mode 100644 index 000000000..30a852cb8 --- /dev/null +++ b/benches/harness/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cuprate-harness" +version = "0.0.0" +edition = "2021" +description = "Cuprate's benchmarking harness binary" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/harness" +keywords = ["cuprate", "benchmarking", "harness", "binary"] + +[features] +default = ["test"] +test = ["dep:cuprate-harness-test"] +database = ["dep:cuprate-database"] + +[dependencies] +cuprate-harness-lib = { path = "lib" } +cuprate-harness-test = { path = "harness-test", optional = true } +cuprate-database = { path = "../../storage/database", optional = true } + +cfg-if = { workspace = true } + +[dev-dependencies] \ No newline at end of file diff --git a/benches/harness/harness-test/Cargo.toml b/benches/harness/harness-test/Cargo.toml new file mode 100644 index 000000000..278ddd64c --- /dev/null +++ b/benches/harness/harness-test/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "cuprate-harness-test" +version = "0.0.0" +edition = "2021" +description = "Test for Cuprate's benchmarking harness" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/macro/harness-test" +keywords = ["cuprate", "benchmarking", "harness", "test"] + +[dependencies] +cuprate-harness-lib = { path = "../lib" } + +[dev-dependencies] \ No newline at end of file diff --git a/benches/harness/harness-test/src/lib.rs b/benches/harness/harness-test/src/lib.rs new file mode 100644 index 000000000..beb360fd7 --- /dev/null +++ b/benches/harness/harness-test/src/lib.rs @@ -0,0 +1,16 @@ +//! TODO + +/// TODO +pub struct BenchmarkHarnessTest; + +// TODO +impl cuprate_harness_lib::Benchmark for BenchmarkHarnessTest { + /// TODO + type Input = (); + + /// TODO + const SETUP: fn() -> Self::Input = || {}; + + /// TODO + const MAIN: fn(Self::Input) = |_| {}; +} diff --git a/benches/harness/lib/Cargo.toml b/benches/harness/lib/Cargo.toml new file mode 100644 index 000000000..965117ee1 --- /dev/null +++ b/benches/harness/lib/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "cuprate-harness-lib" +version = "0.0.0" +edition = "2021" +description = "Cuprate's benchmarking harness library" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/harness/core" +keywords = ["cuprate", "benchmarking", "harness", "library"] + +[features] + +[dependencies] + +[dev-dependencies] \ No newline at end of file diff --git a/benches/harness/lib/src/benchmark.rs b/benches/harness/lib/src/benchmark.rs new file mode 100644 index 000000000..3f46ff66f --- /dev/null +++ b/benches/harness/lib/src/benchmark.rs @@ -0,0 +1,16 @@ +//! TODO + +//---------------------------------------------------------------------------------------------------- Use + +//---------------------------------------------------------------------------------------------------- trait Benchmark +/// A benchmarking function and its inputs. +pub trait Benchmark { + /// Input to the main benchmarking function. + type Input; + + /// Setup function to generate the input. + const SETUP: fn() -> Self::Input; + + /// The main function to benchmark. + const MAIN: fn(Self::Input); +} diff --git a/benches/harness/lib/src/lib.rs b/benches/harness/lib/src/lib.rs new file mode 100644 index 000000000..1f8edeeb9 --- /dev/null +++ b/benches/harness/lib/src/lib.rs @@ -0,0 +1,97 @@ +//! TODO + +//---------------------------------------------------------------------------------------------------- Lints +// Forbid lints. +// Our code, and code generated (e.g macros) cannot overrule these. +#![forbid( + // `unsafe` is allowed but it _must_ be + // commented with `SAFETY: reason`. + clippy::undocumented_unsafe_blocks, + + // Never. + unused_unsafe, + redundant_semicolons, + unused_allocation, + coherence_leak_check, + while_true, + clippy::missing_docs_in_private_items, + + // Maybe can be put into `#[deny]`. + unconditional_recursion, + for_loops_over_fallibles, + unused_braces, + unused_labels, + keyword_idents, + non_ascii_idents, + variant_size_differences, + single_use_lifetimes, + + // Probably can be put into `#[deny]`. + future_incompatible, + let_underscore, + break_with_label_and_loop, + duplicate_macro_attributes, + exported_private_dependencies, + large_assignments, + overlapping_range_endpoints, + semicolon_in_expressions_from_macros, + noop_method_call, + unreachable_pub, +)] +// Deny lints. +// Some of these are `#[allow]`'ed on a per-case basis. +#![deny( + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, + clippy::cargo, + unused_doc_comments, + unused_mut, + missing_docs, + deprecated, + unused_comparisons, + nonstandard_style +)] +#![allow( + // FIXME: this lint affects crates outside of + // `database/` for some reason, allow for now. + clippy::cargo_common_metadata, + + // FIXME: adding `#[must_use]` onto everything + // might just be more annoying than useful... + // although it is sometimes nice. + clippy::must_use_candidate, + + // FIXME: good lint but too many false positives + // with our `Env` + `RwLock` setup. + clippy::significant_drop_tightening, + + // FIXME: good lint but is less clear in most cases. + clippy::items_after_statements, + + clippy::module_name_repetitions, + clippy::module_inception, + clippy::redundant_pub_crate, + clippy::option_if_let_else, +)] +// Allow some lints when running in debug mode. +#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] +// Allow some lints in tests. +#![cfg_attr( + test, + allow( + clippy::cognitive_complexity, + clippy::needless_pass_by_value, + clippy::cast_possible_truncation, + clippy::too_many_lines + ) +)] + +//---------------------------------------------------------------------------------------------------- Modules +mod benchmark; +pub use benchmark::Benchmark; diff --git a/benches/harness/src/main.rs b/benches/harness/src/main.rs new file mode 100644 index 000000000..2987b7243 --- /dev/null +++ b/benches/harness/src/main.rs @@ -0,0 +1,120 @@ +//! TODO + +//---------------------------------------------------------------------------------------------------- Lints +// Forbid lints. +// Our code, and code generated (e.g macros) cannot overrule these. +#![forbid( + // `unsafe` is allowed but it _must_ be + // commented with `SAFETY: reason`. + clippy::undocumented_unsafe_blocks, + + // Never. + unused_unsafe, + redundant_semicolons, + unused_allocation, + coherence_leak_check, + while_true, + clippy::missing_docs_in_private_items, + + // Maybe can be put into `#[deny]`. + unconditional_recursion, + for_loops_over_fallibles, + unused_braces, + unused_labels, + keyword_idents, + non_ascii_idents, + variant_size_differences, + single_use_lifetimes, + + // Probably can be put into `#[deny]`. + future_incompatible, + let_underscore, + break_with_label_and_loop, + duplicate_macro_attributes, + exported_private_dependencies, + large_assignments, + overlapping_range_endpoints, + semicolon_in_expressions_from_macros, + noop_method_call, + unreachable_pub, +)] +// Deny lints. +// Some of these are `#[allow]`'ed on a per-case basis. +#![deny( + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, + clippy::cargo, + unused_doc_comments, + unused_mut, + missing_docs, + deprecated, + unused_comparisons, + nonstandard_style +)] +#![allow( + // FIXME: this lint affects crates outside of + // `database/` for some reason, allow for now. + clippy::cargo_common_metadata, + + // FIXME: adding `#[must_use]` onto everything + // might just be more annoying than useful... + // although it is sometimes nice. + clippy::must_use_candidate, + + // FIXME: good lint but too many false positives + // with our `Env` + `RwLock` setup. + clippy::significant_drop_tightening, + + // FIXME: good lint but is less clear in most cases. + clippy::items_after_statements, + + clippy::module_name_repetitions, + clippy::module_inception, + clippy::redundant_pub_crate, + clippy::option_if_let_else, +)] +// Allow some lints when running in debug mode. +#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] +// Allow some lints in tests. +#![cfg_attr( + test, + allow( + clippy::cognitive_complexity, + clippy::needless_pass_by_value, + clippy::cast_possible_truncation, + clippy::too_many_lines + ) +)] + +//---------------------------------------------------------------------------------------------------- Modules +cfg_if::cfg_if! { + if #[cfg(feature = "database")] { + use cuprate_harness_database::BenchmarkDatabase as B; + } else if #[cfg(feature = "test")] { + use cuprate_harness_test::BenchmarkHarnessTest as B; + } else { + compile_error!("cuprate_harness: no feature specified. Use `--features $YOUR_CRATE_BENCHMARK_NAME` when building."); + } +} + +//---------------------------------------------------------------------------------------------------- Main +use cuprate_harness_lib::Benchmark; + +//---------------------------------------------------------------------------------------------------- Main + +#[allow(clippy::let_unit_value)] +fn main() { + let input = B::SETUP(); + let name = std::any::type_name::(); + println!("[Cuprate Harness] {name}"); + + let now = std::time::Instant::now(); + B::MAIN(input); + println!("{}", now.elapsed().as_secs_f32()); +} From 7bac741d5f023bdaa1bf783edaa77345a45a4ff4 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 3 Oct 2024 21:36:32 -0400 Subject: [PATCH 03/33] name changes, bin impl --- Cargo.lock | 42 +++--- Cargo.toml | 6 +- benches/benchmark/bin/Cargo.toml | 28 ++++ benches/benchmark/bin/README.md | 1 + benches/benchmark/bin/src/main.rs | 66 ++++++++++ benches/benchmark/example/Cargo.toml | 17 +++ benches/benchmark/example/README.md | 1 + benches/benchmark/example/src/lib.rs | 10 ++ benches/benchmark/lib/Cargo.toml | 18 +++ benches/benchmark/lib/README.md | 1 + .../lib/src/benchmark.rs | 11 +- benches/benchmark/lib/src/lib.rs | 5 + benches/harness/Cargo.toml | 23 ---- benches/harness/harness-test/Cargo.toml | 14 -- benches/harness/harness-test/src/lib.rs | 16 --- benches/harness/lib/Cargo.toml | 15 --- benches/harness/lib/src/lib.rs | 97 -------------- benches/harness/src/main.rs | 120 ------------------ 18 files changed, 179 insertions(+), 312 deletions(-) create mode 100644 benches/benchmark/bin/Cargo.toml create mode 100644 benches/benchmark/bin/README.md create mode 100644 benches/benchmark/bin/src/main.rs create mode 100644 benches/benchmark/example/Cargo.toml create mode 100644 benches/benchmark/example/README.md create mode 100644 benches/benchmark/example/src/lib.rs create mode 100644 benches/benchmark/lib/Cargo.toml create mode 100644 benches/benchmark/lib/README.md rename benches/{harness => benchmark}/lib/src/benchmark.rs (56%) create mode 100644 benches/benchmark/lib/src/lib.rs delete mode 100644 benches/harness/Cargo.toml delete mode 100644 benches/harness/harness-test/Cargo.toml delete mode 100644 benches/harness/harness-test/src/lib.rs delete mode 100644 benches/harness/lib/Cargo.toml delete mode 100644 benches/harness/lib/src/lib.rs delete mode 100644 benches/harness/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 82fbf8dfb..ac74f503b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -632,6 +632,27 @@ dependencies = [ "tokio", ] +[[package]] +name = "cuprate-benchmark" +version = "0.0.0" +dependencies = [ + "cfg-if", + "cuprate-benchmark-example", + "cuprate-benchmark-lib", + "cuprate-database", +] + +[[package]] +name = "cuprate-benchmark-example" +version = "0.0.0" +dependencies = [ + "cuprate-benchmark-lib", +] + +[[package]] +name = "cuprate-benchmark-lib" +version = "0.0.0" + [[package]] name = "cuprate-blockchain" version = "0.0.0" @@ -821,27 +842,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cuprate-harness" -version = "0.0.0" -dependencies = [ - "cfg-if", - "cuprate-database", - "cuprate-harness-lib", - "cuprate-harness-test", -] - -[[package]] -name = "cuprate-harness-lib" -version = "0.0.0" - -[[package]] -name = "cuprate-harness-test" -version = "0.0.0" -dependencies = [ - "cuprate-harness-lib", -] - [[package]] name = "cuprate-helper" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0d93a285f..08fbf3f07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,9 @@ members = [ # Binaries "binaries/cuprated", # Benchmarks - "benches/harness", - "benches/harness/lib", - "benches/harness/harness-test", + "benches/benchmark/bin", + "benches/benchmark/lib", + "benches/benchmark/example", "benches/criterion/cuprate-json-rpc", # Consensus "consensus", diff --git a/benches/benchmark/bin/Cargo.toml b/benches/benchmark/bin/Cargo.toml new file mode 100644 index 000000000..bee6745d2 --- /dev/null +++ b/benches/benchmark/bin/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "cuprate-benchmark" +version = "0.0.0" +edition = "2021" +description = "Cuprate's benchmarking binary" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/bin" +keywords = ["cuprate", "benchmarking", "binary"] + +[features] +default = ["example"] +example = ["dep:cuprate-benchmark-example"] +database = ["dep:cuprate-database"] + +[dependencies] +cuprate-benchmark-lib = { path = "../lib" } +cuprate-benchmark-example = { path = "../example", optional = true } +cuprate-database = { path = "../../../storage/database", optional = true } + +cfg-if = { workspace = true } +# serde = { workspace = true, features = ["derive"] } +# serde_json = { workspace = true, features = ["std"] } + +[dev-dependencies] + +[lints] +workspace = true \ No newline at end of file diff --git a/benches/benchmark/bin/README.md b/benches/benchmark/bin/README.md new file mode 100644 index 000000000..562330cd9 --- /dev/null +++ b/benches/benchmark/bin/README.md @@ -0,0 +1 @@ +## `cuprate-benchmark` diff --git a/benches/benchmark/bin/src/main.rs b/benches/benchmark/bin/src/main.rs new file mode 100644 index 000000000..fb635c365 --- /dev/null +++ b/benches/benchmark/bin/src/main.rs @@ -0,0 +1,66 @@ +#![doc = include_str!("../README.md")] +#![allow( + unused_crate_dependencies, + reason = "this crate imports many potentially unused dependencies" +)] + +use std::{collections::HashMap, io::Write}; + +use cfg_if::cfg_if; + +use cuprate_benchmark_lib::Benchmark; + +fn main() { + let mut timings = HashMap::new(); + + cfg_if! { + if #[cfg(not(any(feature = "database", feature = "example")))] { + compile_error!("[cuprate_benchmark]: no feature specified. Use `--features $BENCHMARK_FEATURE` when building."); + } + } + + cfg_if! { + if #[cfg(feature = "database")] { + run_benchmark::(&mut timings); + } + } + + cfg_if! { + if #[cfg(feature = "example")] { + run_benchmark::(&mut timings); + } + } + + print_timings(&timings); +} + +fn run_benchmark(timings: &mut HashMap<&'static str, f32>) { + let name = std::any::type_name::(); + + print!("{name:>34} ... "); + std::io::stdout().flush().unwrap(); + + let input = B::SETUP(); + + let now = std::time::Instant::now(); + B::MAIN(input); + let time = now.elapsed().as_secs_f32(); + + println!("{time}"); + assert!( + timings.insert(name, time).is_none(), + "[cuprate_benchmark]: there were 2 benchmarks with the same name - this collides the final output: {name}", + ); +} + +fn print_timings(timings: &HashMap<&'static str, f32>) { + let mut s = String::new(); + s.push_str("| Benchmark | Time (seconds) |\n"); + s.push_str("|------------------------------------|----------------|"); + #[expect(clippy::iter_over_hash_type)] + for (k, v) in timings { + s += &format!("\n| {k:<34} | {v:<14} |"); + } + + println!("\n{s}"); +} diff --git a/benches/benchmark/example/Cargo.toml b/benches/benchmark/example/Cargo.toml new file mode 100644 index 000000000..5728bcd4b --- /dev/null +++ b/benches/benchmark/example/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cuprate-benchmark-example" +version = "0.0.0" +edition = "2021" +description = "Example showcasing Cuprate's benchmarking harness" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/example" +keywords = ["cuprate", "benchmarking", "example"] + +[dependencies] +cuprate-benchmark-lib = { path = "../lib" } + +[dev-dependencies] + +[lints] +workspace = true \ No newline at end of file diff --git a/benches/benchmark/example/README.md b/benches/benchmark/example/README.md new file mode 100644 index 000000000..65177df97 --- /dev/null +++ b/benches/benchmark/example/README.md @@ -0,0 +1 @@ +## `cuprate-benchmark-example` diff --git a/benches/benchmark/example/src/lib.rs b/benches/benchmark/example/src/lib.rs new file mode 100644 index 000000000..6a60beb6e --- /dev/null +++ b/benches/benchmark/example/src/lib.rs @@ -0,0 +1,10 @@ +#![doc = include_str!("../README.md")] + +/// TODO +pub struct Example; + +impl cuprate_benchmark_lib::Benchmark for Example { + type Input = (); + const SETUP: fn() -> Self::Input = || {}; + const MAIN: fn(Self::Input) = |()| {}; +} diff --git a/benches/benchmark/lib/Cargo.toml b/benches/benchmark/lib/Cargo.toml new file mode 100644 index 000000000..b0771f09f --- /dev/null +++ b/benches/benchmark/lib/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "cuprate-benchmark-lib" +version = "0.0.0" +edition = "2021" +description = "Cuprate's benchmarking library" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/lib" +keywords = ["cuprate", "benchmarking", "library"] + +[features] + +[dependencies] + +[dev-dependencies] + +[lints] +workspace = true \ No newline at end of file diff --git a/benches/benchmark/lib/README.md b/benches/benchmark/lib/README.md new file mode 100644 index 000000000..5ad92b704 --- /dev/null +++ b/benches/benchmark/lib/README.md @@ -0,0 +1 @@ +## `cuprate-benchmark-lib` diff --git a/benches/harness/lib/src/benchmark.rs b/benches/benchmark/lib/src/benchmark.rs similarity index 56% rename from benches/harness/lib/src/benchmark.rs rename to benches/benchmark/lib/src/benchmark.rs index 3f46ff66f..bbd6ae611 100644 --- a/benches/harness/lib/src/benchmark.rs +++ b/benches/benchmark/lib/src/benchmark.rs @@ -1,16 +1,21 @@ //! TODO -//---------------------------------------------------------------------------------------------------- Use - -//---------------------------------------------------------------------------------------------------- trait Benchmark /// A benchmarking function and its inputs. pub trait Benchmark { /// Input to the main benchmarking function. + /// + /// This is passed to [`Self::MAIN`]. type Input; /// Setup function to generate the input. + /// + /// This function is not timed. const SETUP: fn() -> Self::Input; /// The main function to benchmark. + /// + /// The start of the timer begins right before + /// this function is called and ends after the + /// function returns. const MAIN: fn(Self::Input); } diff --git a/benches/benchmark/lib/src/lib.rs b/benches/benchmark/lib/src/lib.rs new file mode 100644 index 000000000..a6bec8208 --- /dev/null +++ b/benches/benchmark/lib/src/lib.rs @@ -0,0 +1,5 @@ +#![doc = include_str!("../README.md")] + +mod benchmark; + +pub use benchmark::Benchmark; diff --git a/benches/harness/Cargo.toml b/benches/harness/Cargo.toml deleted file mode 100644 index 30a852cb8..000000000 --- a/benches/harness/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "cuprate-harness" -version = "0.0.0" -edition = "2021" -description = "Cuprate's benchmarking harness binary" -license = "MIT" -authors = ["hinto-janai"] -repository = "https://github.com/Cuprate/cuprate/tree/main/benches/harness" -keywords = ["cuprate", "benchmarking", "harness", "binary"] - -[features] -default = ["test"] -test = ["dep:cuprate-harness-test"] -database = ["dep:cuprate-database"] - -[dependencies] -cuprate-harness-lib = { path = "lib" } -cuprate-harness-test = { path = "harness-test", optional = true } -cuprate-database = { path = "../../storage/database", optional = true } - -cfg-if = { workspace = true } - -[dev-dependencies] \ No newline at end of file diff --git a/benches/harness/harness-test/Cargo.toml b/benches/harness/harness-test/Cargo.toml deleted file mode 100644 index 278ddd64c..000000000 --- a/benches/harness/harness-test/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "cuprate-harness-test" -version = "0.0.0" -edition = "2021" -description = "Test for Cuprate's benchmarking harness" -license = "MIT" -authors = ["hinto-janai"] -repository = "https://github.com/Cuprate/cuprate/tree/main/benches/macro/harness-test" -keywords = ["cuprate", "benchmarking", "harness", "test"] - -[dependencies] -cuprate-harness-lib = { path = "../lib" } - -[dev-dependencies] \ No newline at end of file diff --git a/benches/harness/harness-test/src/lib.rs b/benches/harness/harness-test/src/lib.rs deleted file mode 100644 index beb360fd7..000000000 --- a/benches/harness/harness-test/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! TODO - -/// TODO -pub struct BenchmarkHarnessTest; - -// TODO -impl cuprate_harness_lib::Benchmark for BenchmarkHarnessTest { - /// TODO - type Input = (); - - /// TODO - const SETUP: fn() -> Self::Input = || {}; - - /// TODO - const MAIN: fn(Self::Input) = |_| {}; -} diff --git a/benches/harness/lib/Cargo.toml b/benches/harness/lib/Cargo.toml deleted file mode 100644 index 965117ee1..000000000 --- a/benches/harness/lib/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "cuprate-harness-lib" -version = "0.0.0" -edition = "2021" -description = "Cuprate's benchmarking harness library" -license = "MIT" -authors = ["hinto-janai"] -repository = "https://github.com/Cuprate/cuprate/tree/main/benches/harness/core" -keywords = ["cuprate", "benchmarking", "harness", "library"] - -[features] - -[dependencies] - -[dev-dependencies] \ No newline at end of file diff --git a/benches/harness/lib/src/lib.rs b/benches/harness/lib/src/lib.rs deleted file mode 100644 index 1f8edeeb9..000000000 --- a/benches/harness/lib/src/lib.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! TODO - -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - clippy::missing_docs_in_private_items, - - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, - unreachable_pub, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_doc_comments, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style -)] -#![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - - // FIXME: good lint but too many false positives - // with our `Env` + `RwLock` setup. - clippy::significant_drop_tightening, - - // FIXME: good lint but is less clear in most cases. - clippy::items_after_statements, - - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, -)] -// Allow some lints when running in debug mode. -#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] -// Allow some lints in tests. -#![cfg_attr( - test, - allow( - clippy::cognitive_complexity, - clippy::needless_pass_by_value, - clippy::cast_possible_truncation, - clippy::too_many_lines - ) -)] - -//---------------------------------------------------------------------------------------------------- Modules -mod benchmark; -pub use benchmark::Benchmark; diff --git a/benches/harness/src/main.rs b/benches/harness/src/main.rs deleted file mode 100644 index 2987b7243..000000000 --- a/benches/harness/src/main.rs +++ /dev/null @@ -1,120 +0,0 @@ -//! TODO - -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - clippy::missing_docs_in_private_items, - - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, - unreachable_pub, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_doc_comments, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style -)] -#![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - - // FIXME: good lint but too many false positives - // with our `Env` + `RwLock` setup. - clippy::significant_drop_tightening, - - // FIXME: good lint but is less clear in most cases. - clippy::items_after_statements, - - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, -)] -// Allow some lints when running in debug mode. -#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] -// Allow some lints in tests. -#![cfg_attr( - test, - allow( - clippy::cognitive_complexity, - clippy::needless_pass_by_value, - clippy::cast_possible_truncation, - clippy::too_many_lines - ) -)] - -//---------------------------------------------------------------------------------------------------- Modules -cfg_if::cfg_if! { - if #[cfg(feature = "database")] { - use cuprate_harness_database::BenchmarkDatabase as B; - } else if #[cfg(feature = "test")] { - use cuprate_harness_test::BenchmarkHarnessTest as B; - } else { - compile_error!("cuprate_harness: no feature specified. Use `--features $YOUR_CRATE_BENCHMARK_NAME` when building."); - } -} - -//---------------------------------------------------------------------------------------------------- Main -use cuprate_harness_lib::Benchmark; - -//---------------------------------------------------------------------------------------------------- Main - -#[allow(clippy::let_unit_value)] -fn main() { - let input = B::SETUP(); - let name = std::any::type_name::(); - println!("[Cuprate Harness] {name}"); - - let now = std::time::Instant::now(); - B::MAIN(input); - println!("{}", now.elapsed().as_secs_f32()); -} From 24d176ff147d27b82c32b4673efdb34b4bb1ab54 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Fri, 4 Oct 2024 17:37:38 -0400 Subject: [PATCH 04/33] example, docs --- Cargo.lock | 2 ++ benches/benchmark/bin/Cargo.toml | 5 +-- benches/benchmark/bin/README.md | 18 ++++++++++ benches/benchmark/bin/src/main.rs | 49 ++++++-------------------- benches/benchmark/bin/src/print.rs | 35 ++++++++++++++++++ benches/benchmark/bin/src/run.rs | 34 ++++++++++++++++++ benches/benchmark/bin/src/timings.rs | 5 +++ benches/benchmark/example/README.md | 2 ++ benches/benchmark/example/src/lib.rs | 42 +++++++++++++++++++--- benches/benchmark/lib/src/benchmark.rs | 15 +++++++- 10 files changed, 161 insertions(+), 46 deletions(-) create mode 100644 benches/benchmark/bin/src/print.rs create mode 100644 benches/benchmark/bin/src/run.rs create mode 100644 benches/benchmark/bin/src/timings.rs diff --git a/Cargo.lock b/Cargo.lock index ac74f503b..f7b24b83b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -640,6 +640,8 @@ dependencies = [ "cuprate-benchmark-example", "cuprate-benchmark-lib", "cuprate-database", + "serde", + "serde_json", ] [[package]] diff --git a/benches/benchmark/bin/Cargo.toml b/benches/benchmark/bin/Cargo.toml index bee6745d2..e7267d4c3 100644 --- a/benches/benchmark/bin/Cargo.toml +++ b/benches/benchmark/bin/Cargo.toml @@ -10,6 +10,7 @@ keywords = ["cuprate", "benchmarking", "binary"] [features] default = ["example"] +json = [] example = ["dep:cuprate-benchmark-example"] database = ["dep:cuprate-database"] @@ -19,8 +20,8 @@ cuprate-benchmark-example = { path = "../example", optional = true } cuprate-database = { path = "../../../storage/database", optional = true } cfg-if = { workspace = true } -# serde = { workspace = true, features = ["derive"] } -# serde_json = { workspace = true, features = ["std"] } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true, features = ["std"] } [dev-dependencies] diff --git a/benches/benchmark/bin/README.md b/benches/benchmark/bin/README.md index 562330cd9..515f5bb17 100644 --- a/benches/benchmark/bin/README.md +++ b/benches/benchmark/bin/README.md @@ -1 +1,19 @@ ## `cuprate-benchmark` +This crate links all benchmarks together into a single binary that can be run as: `cuprate-benchmark`. + +`cuprate-benchmark` will run all enabled benchmarks sequentially and print data at the end. + +## Benchmarks +Benchmarks are opt-in and enabled via features. + +| Feature | Enables which benchmark crate? | +|----------|--------------------------------| +| example | cuprate-benchmark-example | +| database | cuprate-benchmark-database | + +## Features +These are features that aren't for enabling benchmarks, but rather for other things. + +| Features | Does what | +|----------|-----------| +| json | Prints JSON timings instead of a markdown table \ No newline at end of file diff --git a/benches/benchmark/bin/src/main.rs b/benches/benchmark/bin/src/main.rs index fb635c365..03c9696ee 100644 --- a/benches/benchmark/bin/src/main.rs +++ b/benches/benchmark/bin/src/main.rs @@ -4,14 +4,18 @@ reason = "this crate imports many potentially unused dependencies" )] -use std::{collections::HashMap, io::Write}; +mod print; +mod run; +mod timings; use cfg_if::cfg_if; -use cuprate_benchmark_lib::Benchmark; - +/// What `main()` does: +/// 1. Run all enabled benchmarks +/// 2. Record benchmark timings +/// 3. Print timing data fn main() { - let mut timings = HashMap::new(); + let mut timings = timings::Timings::new(); cfg_if! { if #[cfg(not(any(feature = "database", feature = "example")))] { @@ -21,46 +25,15 @@ fn main() { cfg_if! { if #[cfg(feature = "database")] { - run_benchmark::(&mut timings); + run::run_benchmark::(&mut timings); } } cfg_if! { if #[cfg(feature = "example")] { - run_benchmark::(&mut timings); + run::run_benchmark::(&mut timings); } } - print_timings(&timings); -} - -fn run_benchmark(timings: &mut HashMap<&'static str, f32>) { - let name = std::any::type_name::(); - - print!("{name:>34} ... "); - std::io::stdout().flush().unwrap(); - - let input = B::SETUP(); - - let now = std::time::Instant::now(); - B::MAIN(input); - let time = now.elapsed().as_secs_f32(); - - println!("{time}"); - assert!( - timings.insert(name, time).is_none(), - "[cuprate_benchmark]: there were 2 benchmarks with the same name - this collides the final output: {name}", - ); -} - -fn print_timings(timings: &HashMap<&'static str, f32>) { - let mut s = String::new(); - s.push_str("| Benchmark | Time (seconds) |\n"); - s.push_str("|------------------------------------|----------------|"); - #[expect(clippy::iter_over_hash_type)] - for (k, v) in timings { - s += &format!("\n| {k:<34} | {v:<14} |"); - } - - println!("\n{s}"); + print::print_timings(&timings); } diff --git a/benches/benchmark/bin/src/print.rs b/benches/benchmark/bin/src/print.rs new file mode 100644 index 000000000..070d2575d --- /dev/null +++ b/benches/benchmark/bin/src/print.rs @@ -0,0 +1,35 @@ +use cfg_if::cfg_if; + +use crate::timings::Timings; + +/// Print the final the final markdown table of benchmark timings. +pub(crate) fn print_timings(timings: &Timings) { + cfg_if! { + if #[cfg(feature = "json")] { + print_timings_json(timings); + } else { + print_timings_markdown(timings); + } + } +} + +/// Default timing formatting. +pub(crate) fn print_timings_markdown(timings: &Timings) { + let mut s = String::new(); + s.push_str("| Benchmark | Time (seconds) |\n"); + s.push_str("|------------------------------------|----------------|"); + + #[expect(clippy::iter_over_hash_type)] + for (k, v) in timings { + s += &format!("\n| {k:<34} | {v:<14} |"); + } + + println!("\n{s}"); +} + +/// Enabled via `json` feature. +#[expect(dead_code)] +pub(crate) fn print_timings_json(timings: &Timings) { + let json = serde_json::to_string_pretty(timings).unwrap(); + println!("\n{json}"); +} diff --git a/benches/benchmark/bin/src/run.rs b/benches/benchmark/bin/src/run.rs new file mode 100644 index 000000000..8119048d2 --- /dev/null +++ b/benches/benchmark/bin/src/run.rs @@ -0,0 +1,34 @@ +use std::io::Write; + +use cuprate_benchmark_lib::Benchmark; + +use crate::timings::Timings; + +/// Run a [`Benchmark`] and record its timing. +pub(crate) fn run_benchmark(timings: &mut Timings) { + // Print the benchmark name. + let name = std::any::type_name::(); + print!("{name:>34} ... "); + std::io::stdout().flush().unwrap(); + + // Setup the benchmark input. + let input = B::SETUP(); + + // Sleep before running the benchmark. + std::thread::sleep(B::PRE_SLEEP_DURATION); + + // Run/time the benchmark. + let now = std::time::Instant::now(); + B::MAIN(input); + let time = now.elapsed().as_secs_f32(); + + // Print the benchmark timings. + println!("{time}"); + assert!( + timings.insert(name, time).is_none(), + "[cuprate_benchmark]: there were 2 benchmarks with the same name - this collides the final output: {name}", + ); + + // Sleep for a cooldown period after the benchmark run. + std::thread::sleep(B::POST_SLEEP_DURATION); +} diff --git a/benches/benchmark/bin/src/timings.rs b/benches/benchmark/bin/src/timings.rs new file mode 100644 index 000000000..34a07952b --- /dev/null +++ b/benches/benchmark/bin/src/timings.rs @@ -0,0 +1,5 @@ +/// Benchmark timing data. +/// +/// - Key = benchmark name +/// - Value = benchmark time in seconds +pub(crate) type Timings = std::collections::HashMap<&'static str, f32>; diff --git a/benches/benchmark/example/README.md b/benches/benchmark/example/README.md index 65177df97..be6b71679 100644 --- a/benches/benchmark/example/README.md +++ b/benches/benchmark/example/README.md @@ -1 +1,3 @@ ## `cuprate-benchmark-example` +This crate contains a short example benchmark that shows how to implement and use +`cuprate-benchmark-lib` so that it can be ran by `cuprate-benchmark`. \ No newline at end of file diff --git a/benches/benchmark/example/src/lib.rs b/benches/benchmark/example/src/lib.rs index 6a60beb6e..cc704a7f4 100644 --- a/benches/benchmark/example/src/lib.rs +++ b/benches/benchmark/example/src/lib.rs @@ -1,10 +1,42 @@ #![doc = include_str!("../README.md")] -/// TODO +use std::hint::black_box; + +use cuprate_benchmark_lib::Benchmark; + +/// Marker struct that implements [`Benchmark`] pub struct Example; -impl cuprate_benchmark_lib::Benchmark for Example { - type Input = (); - const SETUP: fn() -> Self::Input = || {}; - const MAIN: fn(Self::Input) = |()| {}; +/// The input to our benchmark function. +pub type ExampleBenchmarkInput = u64; + +/// The setup function that creates the input. +pub const fn example_benchmark_setup() -> ExampleBenchmarkInput { + 1 +} + +/// The main benchmarking function. +#[expect(clippy::unit_arg)] +pub fn example_benchmark_main(input: ExampleBenchmarkInput) { + // In this case, we're simply benchmarking the + // performance of simple arithmetic on the input data. + + fn math(input: ExampleBenchmarkInput, number: u64) { + let x = input; + let x = black_box(x * number); + let x = black_box(x / number); + let x = black_box(x + number); + let _ = black_box(x - number); + } + + for number in 1..100_000_000 { + black_box(math(input, number)); + } +} + +// This implementation will be run by `cuprate-benchmark`. +impl Benchmark for Example { + type Input = ExampleBenchmarkInput; + const SETUP: fn() -> Self::Input = example_benchmark_setup; + const MAIN: fn(Self::Input) = example_benchmark_main; } diff --git a/benches/benchmark/lib/src/benchmark.rs b/benches/benchmark/lib/src/benchmark.rs index bbd6ae611..3e99964ab 100644 --- a/benches/benchmark/lib/src/benchmark.rs +++ b/benches/benchmark/lib/src/benchmark.rs @@ -1,4 +1,6 @@ -//! TODO +//! Benchmarking trait. + +use std::time::Duration; /// A benchmarking function and its inputs. pub trait Benchmark { @@ -18,4 +20,15 @@ pub trait Benchmark { /// this function is called and ends after the /// function returns. const MAIN: fn(Self::Input); + + /// `cuprate-benchmark` will sleep for this [`Duration`] after + /// creating the [`Self::Input`], but before starting [`Self::Main`]. + /// + /// 1 second by default. + const PRE_SLEEP_DURATION: Duration = Duration::from_secs(1); + + /// `cuprate-benchmark` will sleep for this [`Duration`] after [`Self::Main`]. + /// + /// 1 second by default. + const POST_SLEEP_DURATION: Duration = Duration::from_secs(1); } From 65253077444c2ea4a54c4eb80e2abeeedfc8eea7 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Mon, 7 Oct 2024 20:33:44 -0400 Subject: [PATCH 05/33] book --- Cargo.lock | 2 - benches/README.md | 82 +------------------ benches/benchmark/bin/Cargo.toml | 7 +- benches/benchmark/bin/src/main.rs | 13 ++- benches/criterion/cuprate-json-rpc/Cargo.toml | 6 +- .../cuprate-json-rpc/benches/main.rs | 77 +---------------- .../cuprate-json-rpc/benches/response.rs | 11 +-- benches/criterion/cuprate-json-rpc/src/lib.rs | 77 +---------------- books/architecture/src/SUMMARY.md | 13 ++- .../src/benchmarking/criterion.md | 1 - .../src/benchmarking/criterion/creating.md | 10 +++ .../src/benchmarking/criterion/intro.md | 6 ++ .../src/benchmarking/criterion/running.md | 15 ++++ .../src/benchmarking/cuprate/creating.md | 42 ++++++++++ .../src/benchmarking/cuprate/intro.md | 12 +++ .../src/benchmarking/cuprate/running.md | 16 ++++ .../architecture/src/benchmarking/harness.md | 1 - books/architecture/src/benchmarking/intro.md | 23 +++++- 18 files changed, 156 insertions(+), 258 deletions(-) delete mode 100644 books/architecture/src/benchmarking/criterion.md create mode 100644 books/architecture/src/benchmarking/criterion/creating.md create mode 100644 books/architecture/src/benchmarking/criterion/intro.md create mode 100644 books/architecture/src/benchmarking/criterion/running.md create mode 100644 books/architecture/src/benchmarking/cuprate/creating.md create mode 100644 books/architecture/src/benchmarking/cuprate/intro.md create mode 100644 books/architecture/src/benchmarking/cuprate/running.md delete mode 100644 books/architecture/src/benchmarking/harness.md diff --git a/Cargo.lock b/Cargo.lock index f7b24b83b..8842f5b2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -639,7 +639,6 @@ dependencies = [ "cfg-if", "cuprate-benchmark-example", "cuprate-benchmark-lib", - "cuprate-database", "serde", "serde_json", ] @@ -746,7 +745,6 @@ dependencies = [ "cuprate-json-rpc", "function_name", "serde_json", - "tempfile", ] [[package]] diff --git a/benches/README.md b/benches/README.md index c5c676717..af6bb932f 100644 --- a/benches/README.md +++ b/benches/README.md @@ -1,83 +1,5 @@ # Benches This directory contains Cuprate's benchmarks and benchmarking utilities. -- [1. File layout and purpose](#1-file-layout-and-purpose) -- [2. Harness](#2-harness) - - [2.1 Creating a harness benchmark](#21-creating-a-harness-benchmark) - - [2.2 Running a harness benchmark](#22-running-a-harness-benchmark) -- [3. Criterion](#3-criterion) - - [2.1 Creating a Criterion benchmark](#21-creating-a-criterion-benchmark) - - [2.2 Running a Criterion benchmark](#22-running-a-criterion-benchmark) - -## 1. File layout and purpose -This directory is sorted into 4 important categories: - -| Sub-directory | Purpose | -|---------------|---------| -| `harness/src` | Cuprate's custom benchmarking harness **binary** -| `harness/lib` | Cuprate's custom benchmarking harness **library** -| `harness/*` | Macro-benchmarks for whole crates or sub-systems (using Cuprate's custom benchmarking harness) -| `criterion/*` | Micro-benchmarks for crates (e.g. timings for a single function) - -## 2. Harness -The harness is: -- `cuprate-harness`; the actual binary crate ran -- `cuprate-harness-lib`; the library that other crates hook into - -The purpose of the harness is very simple: -1. Set-up the benchmark -1. Start timer -1. Run benchmark -1. Output data - -The harness runs the benchmarks found in `harness/`. - -The way benchmarks "plug-in" to the harness is simply by implementing `cuprate_harness_lib::Benchmark`. - -See `cuprate-harness-lib` crate documentation for a user-guide: -```bash -cargo doc --open --package cuprate-harness-lib -``` - -### 2.1 Creating a harness benchmark -1. Create a new crate inside `benches/harness` (consider copying `benches/harness/test` as a base) -2. Pull in `cuprate_harness_lib` as a dependency -3. Implement `cuprate_harness_lib::Benchmark` -4. Add a feature inside `cuprate_harness` for your benchmark - -### 2.2 Running a harness benchmark -After your benchmark is implemented, run this command: -```bash -cargo run --release --package cuprate-harness --features $YOUR_BENCHMARK_CRATE_FEATURE -``` -For example, to run the test benchmark: -```bash -cargo run --release --package cuprate-harness --features test -``` - -## 3. Criterion -Each sub-directory in here is a crate that uses [Criterion](https://bheisler.github.io/criterion.rs/book) for timing single functions and/or groups of functions. - -They are generally be small in scope. - -See [`criterion/cuprate-json-rpc`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-json-rpc) for an example. - -### 3.1 Creating a Criterion benchmark -1. Copy [`criterion/test`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion) as base -2. Read the `Getting Started` section of -3. Get started - -### 3.1 Running a Criterion benchmark -To run all Criterion benchmarks, run this from the repository root: -```bash -cargo bench -``` - -To run specific package(s), use: -```bash -cargo bench --package $CRITERION_BENCHMARK_CRATE_NAME -``` -For example: -```bash -cargo bench --package cuprate-criterion-json-rpc -``` \ No newline at end of file +See the [`Benchmarking` section in the Architecture book](https://architecture.cuprate.org/benchmarking/intro.html) +to see how to create and run these benchmarks. \ No newline at end of file diff --git a/benches/benchmark/bin/Cargo.toml b/benches/benchmark/bin/Cargo.toml index e7267d4c3..86558d163 100644 --- a/benches/benchmark/bin/Cargo.toml +++ b/benches/benchmark/bin/Cargo.toml @@ -9,15 +9,16 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/bi keywords = ["cuprate", "benchmarking", "binary"] [features] -default = ["example"] +# All new benchmarks should be added here! +all = ["example"] + +default = [] json = [] example = ["dep:cuprate-benchmark-example"] -database = ["dep:cuprate-database"] [dependencies] cuprate-benchmark-lib = { path = "../lib" } cuprate-benchmark-example = { path = "../example", optional = true } -cuprate-database = { path = "../../../storage/database", optional = true } cfg-if = { workspace = true } serde = { workspace = true, features = ["derive"] } diff --git a/benches/benchmark/bin/src/main.rs b/benches/benchmark/bin/src/main.rs index 03c9696ee..3c0946da0 100644 --- a/benches/benchmark/bin/src/main.rs +++ b/benches/benchmark/bin/src/main.rs @@ -14,21 +14,20 @@ use cfg_if::cfg_if; /// 1. Run all enabled benchmarks /// 2. Record benchmark timings /// 3. Print timing data +/// +/// To add a new benchmark to be ran here: +/// 1. Copy + paste a `cfg_if` block +/// 2. Change it to your benchmark's feature flag +/// 3. Change it to your benchmark's type fn main() { let mut timings = timings::Timings::new(); cfg_if! { - if #[cfg(not(any(feature = "database", feature = "example")))] { + if #[cfg(not(any(feature = "example")))] { compile_error!("[cuprate_benchmark]: no feature specified. Use `--features $BENCHMARK_FEATURE` when building."); } } - cfg_if! { - if #[cfg(feature = "database")] { - run::run_benchmark::(&mut timings); - } - } - cfg_if! { if #[cfg(feature = "example")] { run::run_benchmark::(&mut timings); diff --git a/benches/criterion/cuprate-json-rpc/Cargo.toml b/benches/criterion/cuprate-json-rpc/Cargo.toml index eaa264708..5d47e3ad7 100644 --- a/benches/criterion/cuprate-json-rpc/Cargo.toml +++ b/benches/criterion/cuprate-json-rpc/Cargo.toml @@ -12,10 +12,12 @@ keywords = ["cuprate", "json-rpc", "criterion", "benchmark"] criterion = { workspace = true } function_name = { workspace = true } serde_json = { workspace = true, features = ["default"] } -tempfile = { workspace = true } cuprate-json-rpc = { path = "../../../rpc/json-rpc" } [[bench]] name = "main" -harness = false \ No newline at end of file +harness = false + +[lints] +workspace = true \ No newline at end of file diff --git a/benches/criterion/cuprate-json-rpc/benches/main.rs b/benches/criterion/cuprate-json-rpc/benches/main.rs index 84585ee99..578dd8ed6 100644 --- a/benches/criterion/cuprate-json-rpc/benches/main.rs +++ b/benches/criterion/cuprate-json-rpc/benches/main.rs @@ -1,78 +1,5 @@ -//! TODO - -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - clippy::missing_docs_in_private_items, - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_doc_comments, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, - unreachable_pub, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style -)] -#![allow(unreachable_code, unused_variables, dead_code, unused_imports)] // TODO: remove -#![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - // TODO: should be removed after all `todo!()`'s are gone. - clippy::diverging_sub_expression, - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, - clippy::significant_drop_tightening, -)] -// Allow some lints when running in debug mode. -#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] +//! Benchmarks for `cuprate-json-rpc`. +#![allow(unused_crate_dependencies)] mod response; diff --git a/benches/criterion/cuprate-json-rpc/benches/response.rs b/benches/criterion/cuprate-json-rpc/benches/response.rs index 93e209af9..c1a134119 100644 --- a/benches/criterion/cuprate-json-rpc/benches/response.rs +++ b/benches/criterion/cuprate-json-rpc/benches/response.rs @@ -1,13 +1,12 @@ -//! `trait Storable` benchmarks. +//! Benchmarks for [`Response`]. +#![allow(unused_attributes, unused_crate_dependencies)] -//---------------------------------------------------------------------------------------------------- Import use criterion::{black_box, criterion_group, criterion_main, Criterion}; use function_name::named; use serde_json::{from_str, to_string_pretty}; use cuprate_json_rpc::{Id, Response}; -//---------------------------------------------------------------------------------------------------- Criterion criterion_group! { benches, response_from_str_u8, @@ -25,8 +24,7 @@ criterion_group! { } criterion_main!(benches); -//---------------------------------------------------------------------------------------------------- Deserialization -/// TODO +/// Generate `from_str` deserialization benchmark functions for [`Response`]. macro_rules! impl_from_str_benchmark { ( $( @@ -60,8 +58,7 @@ impl_from_str_benchmark! { response_from_str_string_500_len => String => r#"{"jsonrpc":"2.0","id":123,"result":"helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld"}"#, } -//---------------------------------------------------------------------------------------------------- Deserialization -/// TODO +/// Generate `to_string_pretty` serialization benchmark functions for [`Response`]. macro_rules! impl_to_string_pretty_benchmark { ( $( diff --git a/benches/criterion/cuprate-json-rpc/src/lib.rs b/benches/criterion/cuprate-json-rpc/src/lib.rs index 00561eee1..8a892fcb5 100644 --- a/benches/criterion/cuprate-json-rpc/src/lib.rs +++ b/benches/criterion/cuprate-json-rpc/src/lib.rs @@ -1,77 +1,2 @@ //! TODO - -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - clippy::missing_docs_in_private_items, - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_doc_comments, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, - unreachable_pub, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style -)] -#![allow(unreachable_code, unused_variables, dead_code, unused_imports)] // TODO: remove -#![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - // TODO: should be removed after all `todo!()`'s are gone. - clippy::diverging_sub_expression, - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, - clippy::significant_drop_tightening, -)] -// Allow some lints when running in debug mode. -#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] - -//---------------------------------------------------------------------------------------------------- Modules +#![allow(unused_crate_dependencies, reason = "used in benchmarks")] diff --git a/books/architecture/src/SUMMARY.md b/books/architecture/src/SUMMARY.md index bf6686097..ee4795bfd 100644 --- a/books/architecture/src/SUMMARY.md +++ b/books/architecture/src/SUMMARY.md @@ -143,9 +143,16 @@ --- -- [⚪️ Benchmarking](benchmarking/intro.md) - - [⚪️ Criterion](benchmarking/criterion.md) - - [⚪️ Harness](benchmarking/harness.md) +- [🟢 Benchmarking](benchmarking/intro.md) + - [🟢 Criterion](benchmarking/criterion/intro.md) + - [🟢 Creating](benchmarking/criterion/creating.md) + - [🟢 Running](benchmarking/criterion/running.md) + - [🟢 `cuprate-benchmark`](benchmarking/cuprate/intro.md) + - [🟢 Creating](benchmarking/cuprate/creating.md) + - [🟢 Running](benchmarking/cuprate/running.md) + +--- + - [⚪️ Testing](testing/intro.md) - [⚪️ Monero data](testing/monero-data.md) - [⚪️ RPC client](testing/rpc-client.md) diff --git a/books/architecture/src/benchmarking/criterion.md b/books/architecture/src/benchmarking/criterion.md deleted file mode 100644 index e9d61e657..000000000 --- a/books/architecture/src/benchmarking/criterion.md +++ /dev/null @@ -1 +0,0 @@ -# ⚪️ Criterion diff --git a/books/architecture/src/benchmarking/criterion/creating.md b/books/architecture/src/benchmarking/criterion/creating.md new file mode 100644 index 000000000..faf78bbf6 --- /dev/null +++ b/books/architecture/src/benchmarking/criterion/creating.md @@ -0,0 +1,10 @@ +# Creating +Creating a new Criterion-based benchmarking crate for one of Cuprate's crates is relatively simple, +although, it requires knowledge of how to use Criterion first: + +1. Read the `Getting Started` section of +2. Copy [`benches/criterion/example`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion/example) as base +3. Get started + +For a real example, see: +[`cuprate-criterion-json-rpc`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-json-rpc). \ No newline at end of file diff --git a/books/architecture/src/benchmarking/criterion/intro.md b/books/architecture/src/benchmarking/criterion/intro.md new file mode 100644 index 000000000..ec9ef802e --- /dev/null +++ b/books/architecture/src/benchmarking/criterion/intro.md @@ -0,0 +1,6 @@ +# Criterion +Each sub-directory in [`benches/criterion/`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion) is a crate that uses [Criterion](https://bheisler.github.io/criterion.rs/book) for timing single functions and/or groups of functions. + +They are generally be small in scope. + +See [`benches/criterion/cuprate-json-rpc`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-json-rpc) for an example. diff --git a/books/architecture/src/benchmarking/criterion/running.md b/books/architecture/src/benchmarking/criterion/running.md new file mode 100644 index 000000000..14067f67e --- /dev/null +++ b/books/architecture/src/benchmarking/criterion/running.md @@ -0,0 +1,15 @@ +# Running +To run all Criterion benchmarks, run this from the repository root: +```bash +cargo bench +``` + +To run specific package(s), use: +```bash +cargo bench --package $CRITERION_BENCHMARK_CRATE_NAME +``` + +For example: +```bash +cargo bench --package cuprate-criterion-json-rpc +``` \ No newline at end of file diff --git a/books/architecture/src/benchmarking/cuprate/creating.md b/books/architecture/src/benchmarking/cuprate/creating.md new file mode 100644 index 000000000..a8820849e --- /dev/null +++ b/books/architecture/src/benchmarking/cuprate/creating.md @@ -0,0 +1,42 @@ +# Creating +New benchmarks are plugged into `cuprate-benchmark` by: +1. Implementing `cuprate_benchmark_lib::Benchmark` +1. Registering the benchmark in the `cuprate_benchmark` binary + +See [`benches/benchmark/example`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/example) +for an example. For a real example, see: +[`cuprate-benchmark-database`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/cuprate-database). + +## Creating the benchmark crate +Before plugging into `cuprate-benchmark`, your actual benchmark crate must be created: + +1. Create a new crate inside `benches/benchmark` (consider copying `benches/benchmark/example` as a base) +1. Pull in `cuprate_benchmark_lib` as a dependency +1. Create a benchmark +1. Implement `cuprate_benchmark_lib::Benchmark` + +## `cuprate_benchmark_lib::Benchmark` +This is the trait that standarizes all benchmarks ran under `cuprate-benchmark`. + +It must be implemented by your benchmarking crate. + +See `cuprate-benchmark-lib` crate documentation for a user-guide: . + +## Adding a feature to `cuprate-benchmark` +After your benchmark's behavior is defined, it must be registered +in the binary that is actually ran: `cuprate-benchmark`. + +If your benchmark is new, add a new crate feature to [`cuprate-benchmark`'s Cargo.toml file](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/bin/Cargo.toml) with an optional dependency to your benchmarking crate. + +## Adding to `cuprate-benchmark`'s `main()` +After adding your crate's feature, add a conditional line that run the benchmark +if the feature is enabled to the `main()` function: + +For example, if your crate's name is `egg`: +```rust +cfg_if! { + if #[cfg(feature = "egg")] { + run::run_benchmark::(&mut timings); + } +} +``` \ No newline at end of file diff --git a/books/architecture/src/benchmarking/cuprate/intro.md b/books/architecture/src/benchmarking/cuprate/intro.md new file mode 100644 index 000000000..7a31c48bd --- /dev/null +++ b/books/architecture/src/benchmarking/cuprate/intro.md @@ -0,0 +1,12 @@ +# cuprate-benchmark +Cuprate has 2 custom crates for macro benchmarking: +- `cuprate-benchmark`; the actual binary crate ran +- `cuprate-benchmark-lib`; the library that other crates hook into + +The purpose of `cuprate-benchmark` is very simple: +1. Set-up the benchmark +1. Start timer +1. Run benchmark +1. Output data + +`cuprate-benchmark` runs the benchmarks found in [`benches/benchmark/cuprate-*`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark). diff --git a/books/architecture/src/benchmarking/cuprate/running.md b/books/architecture/src/benchmarking/cuprate/running.md new file mode 100644 index 000000000..22ee9a696 --- /dev/null +++ b/books/architecture/src/benchmarking/cuprate/running.md @@ -0,0 +1,16 @@ +# Running +`cuprate-benchmark` benchmarks are ran with this command: +```bash +cargo run --release --package cuprate-benchmark --features $YOUR_BENCHMARK_CRATE_FEATURE +``` + +For example, to run the example benchmark: +```bash +cargo run --release --package cuprate-benchmark --features example +``` + +Use the `all` feature to run all benchmarks: +```bash +# Run all benchmarks +cargo run --release --package cuprate-benchmark --features all +``` diff --git a/books/architecture/src/benchmarking/harness.md b/books/architecture/src/benchmarking/harness.md deleted file mode 100644 index 6f82b523e..000000000 --- a/books/architecture/src/benchmarking/harness.md +++ /dev/null @@ -1 +0,0 @@ -# ⚪️ Harness diff --git a/books/architecture/src/benchmarking/intro.md b/books/architecture/src/benchmarking/intro.md index f043a0bac..77ad7e58b 100644 --- a/books/architecture/src/benchmarking/intro.md +++ b/books/architecture/src/benchmarking/intro.md @@ -1 +1,22 @@ -# ⚪️ Benchmarking +# Benchmarking +Cuprate has 2 types of benchmarks: +- Criterion benchmarks +- `cuprate-benchmark` benchmarks + +[Criterion](https://bheisler.github.io/criterion.rs/book/user_guide/advanced_configuration.html) is used for micro benchmarks; they time single functions, groups of functions, and generally are small in scope. + +`cuprate-benchmark` and `cuprate-benchmark-lib` are custom in-house crates Cuprate uses for macro benchmarks; these test sub-systems, sections of a sub-system, or otherwise larger or more complicated code that isn't suited for micro benchmarks. + +## File layout and purpose +All benchmarking related files are in the [`benches/`](https://github.com/Cuprate/cuprate/tree/main/benches) folder. + +This directory is organized like such: + +| Directory | Purpose | +|-------------------------------|---------| +| `benches/criterion/` | Criterion (micro) benchmarks +| `benches/criterion/cuprate-*` | Criterion benchmarks for the crate with the same name +| `benches/benchmark/` | Cuprate's custom benchmarking files +| `benches/benchmark/bin` | The `cuprate-benchmark` crate; the actual binary run that links all benchmarks +| `benches/benchmark/lib` | The `cuprate-benchmark-lib` crate; the benchmarking framework all benchmarks plug into +| `benches/benchmark/cuprate-*` | `cuprate-benchmark` benchmarks for the crate with the same name From 185c2ee25a79414f7df6c841591d2ed9d84ab42e Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Tue, 8 Oct 2024 17:42:21 -0400 Subject: [PATCH 06/33] add `cuprate-criterion-example` --- Cargo.lock | 9 ++++ Cargo.toml | 1 + benches/criterion/cuprate-json-rpc/Cargo.toml | 2 +- benches/criterion/example/Cargo.toml | 21 ++++++++ benches/criterion/example/README.md | 14 ++++++ benches/criterion/example/benches/example.rs | 48 +++++++++++++++++++ benches/criterion/example/benches/main.rs | 10 ++++ benches/criterion/example/src/lib.rs | 13 +++++ .../src/benchmarking/cuprate/creating.md | 2 +- 9 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 benches/criterion/example/Cargo.toml create mode 100644 benches/criterion/example/README.md create mode 100644 benches/criterion/example/benches/example.rs create mode 100644 benches/criterion/example/benches/main.rs create mode 100644 benches/criterion/example/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index aa7054b02..ea83921be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -737,6 +737,15 @@ dependencies = [ name = "cuprate-constants" version = "0.1.0" +[[package]] +name = "cuprate-criterion-example" +version = "0.0.0" +dependencies = [ + "criterion", + "function_name", + "serde_json", +] + [[package]] name = "cuprate-criterion-json-rpc" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 08fbf3f07..a402cbdc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "benches/benchmark/bin", "benches/benchmark/lib", "benches/benchmark/example", + "benches/criterion/example", "benches/criterion/cuprate-json-rpc", # Consensus "consensus", diff --git a/benches/criterion/cuprate-json-rpc/Cargo.toml b/benches/criterion/cuprate-json-rpc/Cargo.toml index 5d47e3ad7..08383255c 100644 --- a/benches/criterion/cuprate-json-rpc/Cargo.toml +++ b/benches/criterion/cuprate-json-rpc/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" description = "Criterion benchmarking for cuprate-json-rpc" license = "MIT" authors = ["hinto-janai"] -repository = "https://github.com/Cuprate/cuprate/tree/main/benches/micro/cuprate-json-rpc" +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-json-rpc" keywords = ["cuprate", "json-rpc", "criterion", "benchmark"] [dependencies] diff --git a/benches/criterion/example/Cargo.toml b/benches/criterion/example/Cargo.toml new file mode 100644 index 000000000..43e601191 --- /dev/null +++ b/benches/criterion/example/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "cuprate-criterion-example" +version = "0.0.0" +edition = "2021" +description = "Criterion benchmarking example for Cuprate" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/example" +keywords = ["cuprate", "criterion", "benchmark", "example"] + +[dependencies] +criterion = { workspace = true } +function_name = { workspace = true } +serde_json = { workspace = true, features = ["default"] } + +[[bench]] +name = "main" +harness = false + +[lints] +workspace = true \ No newline at end of file diff --git a/benches/criterion/example/README.md b/benches/criterion/example/README.md new file mode 100644 index 000000000..cf1983ffc --- /dev/null +++ b/benches/criterion/example/README.md @@ -0,0 +1,14 @@ +## `cuprate-criterion-example` +An example of using Criterion for benchmarking Cuprate crates. + +Consider copy+pasting this crate to use as a base when creating new Criterion benchmark crates. + +## `src/` +Benchmark crates have a `benches/` ran by `cargo bench`, but they are also crates themselves, +as in, they have a `src` folder that `benches/` can pull code from. + +The `src` directories in these benchmarking crates are usually filled with +helper functions, types, etc, that are used repeatedly in the benchmarks. + +## `benches/` +These are the actual benchmarks ran by `cargo bench`. diff --git a/benches/criterion/example/benches/example.rs b/benches/criterion/example/benches/example.rs new file mode 100644 index 000000000..7ea8e9a1d --- /dev/null +++ b/benches/criterion/example/benches/example.rs @@ -0,0 +1,48 @@ +//! Benchmarks. +#![allow(unused_attributes, unused_crate_dependencies)] + +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; +use function_name::named; + +use cuprate_criterion_example::SomeHardToCreateObject; + +// This is how you register criterion benchmarks. +criterion_group! { + name = benches; + config = Criterion::default(); + targets = benchmark_1, benchmark_range, +} +criterion_main!(benches); + +/// Benchmark a single input. +/// +/// +#[named] +fn benchmark_1(c: &mut Criterion) { + // It is recommended to use `function_name!()` as a benchmark + // identifier instead of manually re-typing the function name. + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(SomeHardToCreateObject::from(1)); + }); + }); +} + +/// Benchmark a range of inputs. +/// +/// +#[named] +fn benchmark_range(c: &mut Criterion) { + let mut group = c.benchmark_group(function_name!()); + + for i in 0..4 { + group.throughput(Throughput::Elements(i)); + group.bench_with_input(BenchmarkId::from_parameter(i), &i, |b, &i| { + b.iter(|| { + black_box(SomeHardToCreateObject::from(i)); + }); + }); + } + + group.finish(); +} diff --git a/benches/criterion/example/benches/main.rs b/benches/criterion/example/benches/main.rs new file mode 100644 index 000000000..d4f0bf80c --- /dev/null +++ b/benches/criterion/example/benches/main.rs @@ -0,0 +1,10 @@ +//! Benchmarks examples. +#![allow(unused_crate_dependencies)] + +// All modules within `benches/` are `mod`ed here. +mod example; + +// And all the Criterion benchmarks are registered like so: +criterion::criterion_main! { + example::benches, +} diff --git a/benches/criterion/example/src/lib.rs b/benches/criterion/example/src/lib.rs new file mode 100644 index 000000000..0f732a47d --- /dev/null +++ b/benches/criterion/example/src/lib.rs @@ -0,0 +1,13 @@ +#![doc = include_str!("../README.md")] // See the README for crate documentation. +#![allow(unused_crate_dependencies, reason = "used in benchmarks")] + +/// Shared type that all benchmarks can use. +#[expect(dead_code)] +pub struct SomeHardToCreateObject(u64); + +impl From for SomeHardToCreateObject { + /// Shared function that all benchmarks can use. + fn from(value: u64) -> Self { + Self(value) + } +} diff --git a/books/architecture/src/benchmarking/cuprate/creating.md b/books/architecture/src/benchmarking/cuprate/creating.md index a8820849e..3f1e91ff9 100644 --- a/books/architecture/src/benchmarking/cuprate/creating.md +++ b/books/architecture/src/benchmarking/cuprate/creating.md @@ -16,7 +16,7 @@ Before plugging into `cuprate-benchmark`, your actual benchmark crate must be cr 1. Implement `cuprate_benchmark_lib::Benchmark` ## `cuprate_benchmark_lib::Benchmark` -This is the trait that standarizes all benchmarks ran under `cuprate-benchmark`. +This is the trait that standardizes all benchmarks ran under `cuprate-benchmark`. It must be implemented by your benchmarking crate. From 9a0b6138b5ce7b58f6ce167bcb3703c0a4b80cbc Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Tue, 8 Oct 2024 20:19:22 -0400 Subject: [PATCH 07/33] docs, tracing --- Cargo.lock | 38 +++++++++++++++++-- benches/benchmark/bin/Cargo.toml | 25 +++++++++--- benches/benchmark/bin/README.md | 10 ++++- benches/benchmark/bin/src/log.rs | 29 ++++++++++++++ benches/benchmark/bin/src/main.rs | 5 ++- benches/benchmark/bin/src/print.rs | 2 + benches/benchmark/bin/src/run.rs | 16 +++++--- benches/benchmark/lib/src/benchmark.rs | 12 +++++- .../src/benchmarking/criterion/intro.md | 4 +- .../src/benchmarking/cuprate/creating.md | 3 ++ .../src/benchmarking/cuprate/intro.md | 29 +++++++++++++- .../src/benchmarking/cuprate/running.md | 2 +- books/architecture/src/benchmarking/intro.md | 14 +++---- 13 files changed, 156 insertions(+), 33 deletions(-) create mode 100644 benches/benchmark/bin/src/log.rs diff --git a/Cargo.lock b/Cargo.lock index ea83921be..655d12748 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -641,6 +641,8 @@ dependencies = [ "cuprate-benchmark-lib", "serde", "serde_json", + "tracing", + "tracing-subscriber", ] [[package]] @@ -1862,6 +1864,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -2362,7 +2373,7 @@ dependencies = [ "rand", "rand_chacha", "rand_xorshift", - "regex-syntax", + "regex-syntax 0.8.4", "rusty-fork", "tempfile", "unarray", @@ -2536,8 +2547,17 @@ checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -2548,9 +2568,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.4", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.4" @@ -3257,10 +3283,14 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] diff --git a/benches/benchmark/bin/Cargo.toml b/benches/benchmark/bin/Cargo.toml index 86558d163..7f8c8e5f9 100644 --- a/benches/benchmark/bin/Cargo.toml +++ b/benches/benchmark/bin/Cargo.toml @@ -12,17 +12,30 @@ keywords = ["cuprate", "benchmarking", "binary"] # All new benchmarks should be added here! all = ["example"] -default = [] -json = [] -example = ["dep:cuprate-benchmark-example"] +# Non-benchmark features. +default = [] +json = [] +trace = [] +debug = [] +warn = [] +info = [] +error = [] + +# Benchmark features. +# New benchmarks should be added here! +example = [ + "dep:cuprate-benchmark-example" +] [dependencies] cuprate-benchmark-lib = { path = "../lib" } cuprate-benchmark-example = { path = "../example", optional = true } -cfg-if = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true, features = ["std"] } +cfg-if = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true, features = ["std"] } +tracing = { workspace = true, features = ["std", "attributes"] } +tracing-subscriber = { workspace = true, features = ["fmt", "std", "env-filter"] } [dev-dependencies] diff --git a/benches/benchmark/bin/README.md b/benches/benchmark/bin/README.md index 515f5bb17..ad0700fcc 100644 --- a/benches/benchmark/bin/README.md +++ b/benches/benchmark/bin/README.md @@ -14,6 +14,14 @@ Benchmarks are opt-in and enabled via features. ## Features These are features that aren't for enabling benchmarks, but rather for other things. +Since `cuprate-benchmark` is built right before it is ran, +these features almost act like command line arguments. + | Features | Does what | |----------|-----------| -| json | Prints JSON timings instead of a markdown table \ No newline at end of file +| json | Prints JSON timings instead of a markdown table +| trace | Use the `trace` log-level +| debug | Use the `debug` log-level +| warn | Use the `warn` log-level +| info | Use the `info` log-level (default) +| error | Use the `error` log-level \ No newline at end of file diff --git a/benches/benchmark/bin/src/log.rs b/benches/benchmark/bin/src/log.rs new file mode 100644 index 000000000..455f13091 --- /dev/null +++ b/benches/benchmark/bin/src/log.rs @@ -0,0 +1,29 @@ +use cfg_if::cfg_if; +use tracing::{info, instrument, Level}; +use tracing_subscriber::FmtSubscriber; + +/// Initializes the `tracing` logger. +#[instrument] +pub(crate) fn init_logger() { + const LOG_LEVEL: Level = { + cfg_if! { + if #[cfg(feature = "trace")] { + Level::TRACE + } else if #[cfg(feature = "debug")] { + Level::DEBUG + } else if #[cfg(feature = "warn")] { + Level::WARN + } else if #[cfg(feature = "info")] { + Level::INFO + } else if #[cfg(feature = "error")] { + Level::ERROR + } else { + Level::INFO + } + } + }; + + FmtSubscriber::builder().with_max_level(LOG_LEVEL).init(); + + info!("Log level: {LOG_LEVEL}"); +} diff --git a/benches/benchmark/bin/src/main.rs b/benches/benchmark/bin/src/main.rs index 3c0946da0..2e6272094 100644 --- a/benches/benchmark/bin/src/main.rs +++ b/benches/benchmark/bin/src/main.rs @@ -4,6 +4,7 @@ reason = "this crate imports many potentially unused dependencies" )] +mod log; mod print; mod run; mod timings; @@ -20,11 +21,13 @@ use cfg_if::cfg_if; /// 2. Change it to your benchmark's feature flag /// 3. Change it to your benchmark's type fn main() { + log::init_logger(); + let mut timings = timings::Timings::new(); cfg_if! { if #[cfg(not(any(feature = "example")))] { - compile_error!("[cuprate_benchmark]: no feature specified. Use `--features $BENCHMARK_FEATURE` when building."); + compile_error!("No feature specified. Use `--features $BENCHMARK_FEATURE` when building."); } } diff --git a/benches/benchmark/bin/src/print.rs b/benches/benchmark/bin/src/print.rs index 070d2575d..8fb489d2e 100644 --- a/benches/benchmark/bin/src/print.rs +++ b/benches/benchmark/bin/src/print.rs @@ -4,6 +4,8 @@ use crate::timings::Timings; /// Print the final the final markdown table of benchmark timings. pub(crate) fn print_timings(timings: &Timings) { + println!("\nFinished all benchmarks, printing results:"); + cfg_if! { if #[cfg(feature = "json")] { print_timings_json(timings); diff --git a/benches/benchmark/bin/src/run.rs b/benches/benchmark/bin/src/run.rs index 8119048d2..28ffc3583 100644 --- a/benches/benchmark/bin/src/run.rs +++ b/benches/benchmark/bin/src/run.rs @@ -1,20 +1,23 @@ use std::io::Write; +use tracing::{info, instrument, trace}; + use cuprate_benchmark_lib::Benchmark; use crate::timings::Timings; /// Run a [`Benchmark`] and record its timing. +#[instrument(skip_all)] pub(crate) fn run_benchmark(timings: &mut Timings) { - // Print the benchmark name. - let name = std::any::type_name::(); - print!("{name:>34} ... "); - std::io::stdout().flush().unwrap(); + // Get the benchmark name. + let name = B::name(); + trace!("Running benchmark: {name}"); // Setup the benchmark input. let input = B::SETUP(); // Sleep before running the benchmark. + trace!("Pre-benchmark, sleeping for: {:?}", B::POST_SLEEP_DURATION); std::thread::sleep(B::PRE_SLEEP_DURATION); // Run/time the benchmark. @@ -23,12 +26,13 @@ pub(crate) fn run_benchmark(timings: &mut Timings) { let time = now.elapsed().as_secs_f32(); // Print the benchmark timings. - println!("{time}"); + info!("{name:>34} ... {time}"); assert!( timings.insert(name, time).is_none(), - "[cuprate_benchmark]: there were 2 benchmarks with the same name - this collides the final output: {name}", + "There were 2 benchmarks with the same name - this collides the final output: {name}", ); // Sleep for a cooldown period after the benchmark run. + trace!("Post-benchmark, sleeping for: {:?}", B::POST_SLEEP_DURATION); std::thread::sleep(B::POST_SLEEP_DURATION); } diff --git a/benches/benchmark/lib/src/benchmark.rs b/benches/benchmark/lib/src/benchmark.rs index 3e99964ab..031767317 100644 --- a/benches/benchmark/lib/src/benchmark.rs +++ b/benches/benchmark/lib/src/benchmark.rs @@ -4,6 +4,14 @@ use std::time::Duration; /// A benchmarking function and its inputs. pub trait Benchmark { + /// The benchmark's name. + /// + /// This is automatically implemented + /// as the name of the [`Self`] type. + fn name() -> &'static str { + std::any::type_name::() + } + /// Input to the main benchmarking function. /// /// This is passed to [`Self::MAIN`]. @@ -22,12 +30,12 @@ pub trait Benchmark { const MAIN: fn(Self::Input); /// `cuprate-benchmark` will sleep for this [`Duration`] after - /// creating the [`Self::Input`], but before starting [`Self::Main`]. + /// creating the [`Self::Input`], but before starting [`Self::MAIN`]. /// /// 1 second by default. const PRE_SLEEP_DURATION: Duration = Duration::from_secs(1); - /// `cuprate-benchmark` will sleep for this [`Duration`] after [`Self::Main`]. + /// `cuprate-benchmark` will sleep for this [`Duration`] after [`Self::MAIN`]. /// /// 1 second by default. const POST_SLEEP_DURATION: Duration = Duration::from_secs(1); diff --git a/books/architecture/src/benchmarking/criterion/intro.md b/books/architecture/src/benchmarking/criterion/intro.md index ec9ef802e..b7a79b21a 100644 --- a/books/architecture/src/benchmarking/criterion/intro.md +++ b/books/architecture/src/benchmarking/criterion/intro.md @@ -1,6 +1,4 @@ # Criterion Each sub-directory in [`benches/criterion/`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion) is a crate that uses [Criterion](https://bheisler.github.io/criterion.rs/book) for timing single functions and/or groups of functions. -They are generally be small in scope. - -See [`benches/criterion/cuprate-json-rpc`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-json-rpc) for an example. +They are generally be small in scope. \ No newline at end of file diff --git a/books/architecture/src/benchmarking/cuprate/creating.md b/books/architecture/src/benchmarking/cuprate/creating.md index 3f1e91ff9..805c576af 100644 --- a/books/architecture/src/benchmarking/cuprate/creating.md +++ b/books/architecture/src/benchmarking/cuprate/creating.md @@ -28,6 +28,9 @@ in the binary that is actually ran: `cuprate-benchmark`. If your benchmark is new, add a new crate feature to [`cuprate-benchmark`'s Cargo.toml file](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/bin/Cargo.toml) with an optional dependency to your benchmarking crate. +Please remember to edit the feature table in the +[`README.md`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/bin/README.md) as well! + ## Adding to `cuprate-benchmark`'s `main()` After adding your crate's feature, add a conditional line that run the benchmark if the feature is enabled to the `main()` function: diff --git a/books/architecture/src/benchmarking/cuprate/intro.md b/books/architecture/src/benchmarking/cuprate/intro.md index 7a31c48bd..25efb4608 100644 --- a/books/architecture/src/benchmarking/cuprate/intro.md +++ b/books/architecture/src/benchmarking/cuprate/intro.md @@ -1,12 +1,37 @@ # cuprate-benchmark -Cuprate has 2 custom crates for macro benchmarking: +Cuprate has 2 custom crates for general benchmarking: - `cuprate-benchmark`; the actual binary crate ran - `cuprate-benchmark-lib`; the library that other crates hook into -The purpose of `cuprate-benchmark` is very simple: +The abstract purpose of `cuprate-benchmark` is very simple: 1. Set-up the benchmark 1. Start timer 1. Run benchmark 1. Output data `cuprate-benchmark` runs the benchmarks found in [`benches/benchmark/cuprate-*`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark). + +`cuprate-benchmark-lib` defines the `Benchmark` trait that all +benchmark crates implement to "plug-in" to the benchmarking harness. + +## Diagram +A diagram displaying the relation between `cuprate-benchmark` and related crates. + +``` + ┌─────────────────────┐ + │ cuprate_benchmark │ + │ (actual binary ran) │ + └──────────┬──────────┘ + ┌──────────────────┴───────────────────┐ + │ cuprate_benchmark_lib │ + │ ┌───────────────────────────────────┐│ + │ │ trait Benchmark ││ + │ └───────────────────────────────────┘│ + └──────────────────┬───────────────────┘ +┌───────────────────────────┐ │ ┌───────────────────────────┐ +│ cuprate_benchmark_example ├──┼───┤ cuprate_benchmark_* │ +└───────────────────────────┘ │ └───────────────────────────┘ +┌───────────────────────────┐ │ ┌───────────────────────────┐ +│ cuprate_benchmark_* ├──┴───┤ cuprate_benchmark_* │ +└───────────────────────────┘ └───────────────────────────┘ +``` \ No newline at end of file diff --git a/books/architecture/src/benchmarking/cuprate/running.md b/books/architecture/src/benchmarking/cuprate/running.md index 22ee9a696..b776163ed 100644 --- a/books/architecture/src/benchmarking/cuprate/running.md +++ b/books/architecture/src/benchmarking/cuprate/running.md @@ -1,7 +1,7 @@ # Running `cuprate-benchmark` benchmarks are ran with this command: ```bash -cargo run --release --package cuprate-benchmark --features $YOUR_BENCHMARK_CRATE_FEATURE +cargo run --release --package cuprate-benchmark --features $BENCHMARK_CRATE_FEATURE ``` For example, to run the example benchmark: diff --git a/books/architecture/src/benchmarking/intro.md b/books/architecture/src/benchmarking/intro.md index 77ad7e58b..e6ab6b12d 100644 --- a/books/architecture/src/benchmarking/intro.md +++ b/books/architecture/src/benchmarking/intro.md @@ -1,11 +1,11 @@ # Benchmarking Cuprate has 2 types of benchmarks: -- Criterion benchmarks +- [Criterion](https://bheisler.github.io/criterion.rs/book/user_guide/advanced_configuration.html) benchmarks - `cuprate-benchmark` benchmarks -[Criterion](https://bheisler.github.io/criterion.rs/book/user_guide/advanced_configuration.html) is used for micro benchmarks; they time single functions, groups of functions, and generally are small in scope. +Criterion is used for micro benchmarks; they time single functions, groups of functions, and generally are small in scope. -`cuprate-benchmark` and `cuprate-benchmark-lib` are custom in-house crates Cuprate uses for macro benchmarks; these test sub-systems, sections of a sub-system, or otherwise larger or more complicated code that isn't suited for micro benchmarks. +`cuprate-benchmark` and [`cuprate-benchmark-lib`](https://doc.cuprate.org/cuprate_benchmark_lib) are custom in-house crates Cuprate uses for macro benchmarks; these test sub-systems, sections of a sub-system, or otherwise larger or more complicated code that isn't well-suited for micro benchmarks. ## File layout and purpose All benchmarking related files are in the [`benches/`](https://github.com/Cuprate/cuprate/tree/main/benches) folder. @@ -14,9 +14,9 @@ This directory is organized like such: | Directory | Purpose | |-------------------------------|---------| -| `benches/criterion/` | Criterion (micro) benchmarks +| [`benches/criterion/`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion) | Criterion (micro) benchmarks | `benches/criterion/cuprate-*` | Criterion benchmarks for the crate with the same name -| `benches/benchmark/` | Cuprate's custom benchmarking files -| `benches/benchmark/bin` | The `cuprate-benchmark` crate; the actual binary run that links all benchmarks -| `benches/benchmark/lib` | The `cuprate-benchmark-lib` crate; the benchmarking framework all benchmarks plug into +| [`benches/benchmark/`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark) | Cuprate's custom benchmarking files +| [`benches/benchmark/bin`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/bin) | The `cuprate-benchmark` crate; the actual binary run that links all benchmarks +| [`benches/benchmark/lib`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/lib) | The `cuprate-benchmark-lib` crate; the benchmarking framework all benchmarks plug into | `benches/benchmark/cuprate-*` | `cuprate-benchmark` benchmarks for the crate with the same name From 8c9d159124fa3eb2e40fde158c2d1f9da62f6f73 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Tue, 8 Oct 2024 20:35:00 -0400 Subject: [PATCH 08/33] fix clippy --- benches/benchmark/bin/src/print.rs | 3 ++- benches/benchmark/bin/src/run.rs | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/benches/benchmark/bin/src/print.rs b/benches/benchmark/bin/src/print.rs index 8fb489d2e..36a5f05a6 100644 --- a/benches/benchmark/bin/src/print.rs +++ b/benches/benchmark/bin/src/print.rs @@ -1,3 +1,5 @@ +#![expect(dead_code, reason = "code hidden behind feature flags")] + use cfg_if::cfg_if; use crate::timings::Timings; @@ -30,7 +32,6 @@ pub(crate) fn print_timings_markdown(timings: &Timings) { } /// Enabled via `json` feature. -#[expect(dead_code)] pub(crate) fn print_timings_json(timings: &Timings) { let json = serde_json::to_string_pretty(timings).unwrap(); println!("\n{json}"); diff --git a/benches/benchmark/bin/src/run.rs b/benches/benchmark/bin/src/run.rs index 28ffc3583..05a220f93 100644 --- a/benches/benchmark/bin/src/run.rs +++ b/benches/benchmark/bin/src/run.rs @@ -1,5 +1,3 @@ -use std::io::Write; - use tracing::{info, instrument, trace}; use cuprate_benchmark_lib::Benchmark; From 828cbbac27e55ae3e2976534a204fbf1ae83dd6e Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Tue, 8 Oct 2024 20:49:13 -0400 Subject: [PATCH 09/33] docs --- Cargo.toml | 2 +- .../criterion/cuprate-json-rpc/benches/main.rs | 4 +++- .../cuprate-json-rpc/benches/response.rs | 7 +++++-- benches/criterion/cuprate-json-rpc/src/lib.rs | 2 +- .../src/benchmarking/criterion/creating.md | 13 ++++++++++++- .../src/benchmarking/cuprate/creating.md | 18 +++++++++++++++--- 6 files changed, 37 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a402cbdc7..d756b08a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,7 +106,7 @@ tempfile = { version = "3.13.0" } monero-rpc = { git = "https://github.com/Cuprate/serai.git", rev = "d5205ce" } monero-simple-request-rpc = { git = "https://github.com/Cuprate/serai.git", rev = "d5205ce" } pretty_assertions = { version = "1.4.1" } -proptest = { version = "1" } +proptest = { version = "1.5.0" } proptest-derive = { version = "0.4.0" } tokio-test = { version = "0.4.4" } diff --git a/benches/criterion/cuprate-json-rpc/benches/main.rs b/benches/criterion/cuprate-json-rpc/benches/main.rs index 578dd8ed6..8bdc5f70a 100644 --- a/benches/criterion/cuprate-json-rpc/benches/main.rs +++ b/benches/criterion/cuprate-json-rpc/benches/main.rs @@ -1,8 +1,10 @@ //! Benchmarks for `cuprate-json-rpc`. +//! +//! TODO: this crate is not finished. #![allow(unused_crate_dependencies)] mod response; criterion::criterion_main! { - response::benches, + response::serde, } diff --git a/benches/criterion/cuprate-json-rpc/benches/response.rs b/benches/criterion/cuprate-json-rpc/benches/response.rs index c1a134119..3ac995318 100644 --- a/benches/criterion/cuprate-json-rpc/benches/response.rs +++ b/benches/criterion/cuprate-json-rpc/benches/response.rs @@ -7,8 +7,11 @@ use serde_json::{from_str, to_string_pretty}; use cuprate_json_rpc::{Id, Response}; +// `serde` benchmarks on `Response`. criterion_group! { - benches, + name = serde; + config = Criterion::default(); + targets = response_from_str_u8, response_from_str_u64, response_from_str_string_5_len, @@ -22,7 +25,7 @@ criterion_group! { response_to_string_pretty_string_100_len, response_to_string_pretty_string_500_len, } -criterion_main!(benches); +criterion_main!(serde); /// Generate `from_str` deserialization benchmark functions for [`Response`]. macro_rules! impl_from_str_benchmark { diff --git a/benches/criterion/cuprate-json-rpc/src/lib.rs b/benches/criterion/cuprate-json-rpc/src/lib.rs index 8a892fcb5..cf9ea54fa 100644 --- a/benches/criterion/cuprate-json-rpc/src/lib.rs +++ b/benches/criterion/cuprate-json-rpc/src/lib.rs @@ -1,2 +1,2 @@ -//! TODO +//! TODO: this crate is not finished. #![allow(unused_crate_dependencies, reason = "used in benchmarks")] diff --git a/books/architecture/src/benchmarking/criterion/creating.md b/books/architecture/src/benchmarking/criterion/creating.md index faf78bbf6..010090429 100644 --- a/books/architecture/src/benchmarking/criterion/creating.md +++ b/books/architecture/src/benchmarking/criterion/creating.md @@ -6,5 +6,16 @@ although, it requires knowledge of how to use Criterion first: 2. Copy [`benches/criterion/example`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion/example) as base 3. Get started +## Naming +New benchmark crates using Criterion should: +- Be in [`benches/criterion/`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion/) +- Be in the `cuprate-criterion-$CRATE_NAME` format + For a real example, see: -[`cuprate-criterion-json-rpc`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-json-rpc). \ No newline at end of file +[`cuprate-criterion-json-rpc`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-json-rpc). + +## Workspace +Finally, make sure to add the benchmark crate to the workspace +[`Cargo.toml`](https://github.com/Cuprate/cuprate/blob/main/Cargo.toml) file. + +Your benchmark is now ready to be ran. \ No newline at end of file diff --git a/books/architecture/src/benchmarking/cuprate/creating.md b/books/architecture/src/benchmarking/cuprate/creating.md index 805c576af..76eab7890 100644 --- a/books/architecture/src/benchmarking/cuprate/creating.md +++ b/books/architecture/src/benchmarking/cuprate/creating.md @@ -4,8 +4,7 @@ New benchmarks are plugged into `cuprate-benchmark` by: 1. Registering the benchmark in the `cuprate_benchmark` binary See [`benches/benchmark/example`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/example) -for an example. For a real example, see: -[`cuprate-benchmark-database`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/cuprate-database). +for an example. ## Creating the benchmark crate Before plugging into `cuprate-benchmark`, your actual benchmark crate must be created: @@ -15,6 +14,13 @@ Before plugging into `cuprate-benchmark`, your actual benchmark crate must be cr 1. Create a benchmark 1. Implement `cuprate_benchmark_lib::Benchmark` +New benchmark crates using `cuprate-database` should: +- Be in [`benches/benchmark/`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/) +- Be in the `cuprate-benchmark-$CRATE_NAME` format + +For a real example, see: +[`cuprate-benchmark-database`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/cuprate-database). + ## `cuprate_benchmark_lib::Benchmark` This is the trait that standardizes all benchmarks ran under `cuprate-benchmark`. @@ -42,4 +48,10 @@ cfg_if! { run::run_benchmark::(&mut timings); } } -``` \ No newline at end of file +``` + +## Workspace +Finally, make sure to add the benchmark crate to the workspace +[`Cargo.toml`](https://github.com/Cuprate/cuprate/blob/main/Cargo.toml) file. + +Your benchmark is now ready to be ran. \ No newline at end of file From cab201161303cba849d14c7b8a808fea0d2d6b6d Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Wed, 9 Oct 2024 17:23:32 -0400 Subject: [PATCH 10/33] lib readme --- benches/benchmark/lib/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/benches/benchmark/lib/README.md b/benches/benchmark/lib/README.md index 5ad92b704..6abce6b01 100644 --- a/benches/benchmark/lib/README.md +++ b/benches/benchmark/lib/README.md @@ -1 +1,16 @@ ## `cuprate-benchmark-lib` +This crate is the glue between +[`cuprate-benchmark`](https://github.com/Cuprate/cuprate/tree/benches/benches/benchmark/bin) +and all the benchmark crates. + +It defines the [`cuprate_benchmark_lib::Benchmark`] trait, +which is the behavior of all benchmarks. + +See the [`cuprate-benchmark-example`](https://github.com/Cuprate/cuprate/tree/benches/benches/benchmark/example) +crate to see an example implementation of this trait. + +After implementing this trait, a few steps must +be done such that the `cuprate-benchmark` binary +can actually run your benchmark crate; see the +[`Benchmarking` section in the Architecture book](https://architecture.cuprate.org/benchmarking/intro.html) +to see how to do this. \ No newline at end of file From eb617b30f885afb43c2b4c35da45cbbd95b954b7 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Wed, 9 Oct 2024 17:42:56 -0400 Subject: [PATCH 11/33] json-rpc benchmarks --- .../cuprate-json-rpc/benches/main.rs | 2 -- .../cuprate-json-rpc/benches/response.rs | 20 +++++++++++++++++-- benches/criterion/cuprate-json-rpc/src/lib.rs | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/benches/criterion/cuprate-json-rpc/benches/main.rs b/benches/criterion/cuprate-json-rpc/benches/main.rs index 8bdc5f70a..a72494302 100644 --- a/benches/criterion/cuprate-json-rpc/benches/main.rs +++ b/benches/criterion/cuprate-json-rpc/benches/main.rs @@ -1,6 +1,4 @@ //! Benchmarks for `cuprate-json-rpc`. -//! -//! TODO: this crate is not finished. #![allow(unused_crate_dependencies)] mod response; diff --git a/benches/criterion/cuprate-json-rpc/benches/response.rs b/benches/criterion/cuprate-json-rpc/benches/response.rs index 3ac995318..908a9f49b 100644 --- a/benches/criterion/cuprate-json-rpc/benches/response.rs +++ b/benches/criterion/cuprate-json-rpc/benches/response.rs @@ -8,6 +8,8 @@ use serde_json::{from_str, to_string_pretty}; use cuprate_json_rpc::{Id, Response}; // `serde` benchmarks on `Response`. +// +// These are benchmarked as `Response` has a custom serde implementation. criterion_group! { name = serde; config = Criterion::default(); @@ -24,6 +26,11 @@ criterion_group! { response_to_string_pretty_string_10_len, response_to_string_pretty_string_100_len, response_to_string_pretty_string_500_len, + response_from_str_bad_field_1, + response_from_str_bad_field_5, + response_from_str_bad_field_10, + response_from_str_bad_field_100, + response_from_str_missing_field, } criterion_main!(serde); @@ -35,7 +42,6 @@ macro_rules! impl_from_str_benchmark { )* ) => { $( - /// TODO #[named] fn $fn_name(c: &mut Criterion) { let request_string = $request_string; @@ -59,6 +65,17 @@ impl_from_str_benchmark! { response_from_str_string_10_len => String => r#"{"jsonrpc":"2.0","id":123,"result":"hellohello"}"#, response_from_str_string_100_len => String => r#"{"jsonrpc":"2.0","id":123,"result":"helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld"}"#, response_from_str_string_500_len => String => r#"{"jsonrpc":"2.0","id":123,"result":"helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworld"}"#, + + // The custom serde currently looks at all fields. + // These are for testing the performance if the serde + // has to parse through a bunch of unrelated fields. + response_from_str_bad_field_1 => u8 => r#"{"bad_field":0,"jsonrpc":"2.0","id":123,"result":0}"#, + response_from_str_bad_field_5 => u8 => r#"{"bad_field_1":0,"bad_field_2":0,"bad_field_3":0,"bad_field_4":0,"bad_field_5":0,"jsonrpc":"2.0","id":123,"result":0}"#, + response_from_str_bad_field_10 => u8 => r#"{"bad_field_1":0,"bad_field_2":0,"bad_field_3":0,"bad_field_4":0,"bad_field_5":0,"bad_field_6":0,"bad_field_7":0,"bad_field_8":0,"bad_field_9":0,"bad_field_10":0,"jsonrpc":"2.0","id":123,"result":0}"#, + response_from_str_bad_field_100 => u8 => r#"{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"jsonrpc":"2.0","id":123,"result":0}"#, + + // These are missing the `jsonrpc` field. + response_from_str_missing_field => u8 => r#"{"id":123,"result":0}"#, } /// Generate `to_string_pretty` serialization benchmark functions for [`Response`]. @@ -69,7 +86,6 @@ macro_rules! impl_to_string_pretty_benchmark { )* ) => { $( - /// TODO #[named] fn $fn_name(c: &mut Criterion) { let request = $request_constructor; diff --git a/benches/criterion/cuprate-json-rpc/src/lib.rs b/benches/criterion/cuprate-json-rpc/src/lib.rs index cf9ea54fa..b29887aa7 100644 --- a/benches/criterion/cuprate-json-rpc/src/lib.rs +++ b/benches/criterion/cuprate-json-rpc/src/lib.rs @@ -1,2 +1,2 @@ -//! TODO: this crate is not finished. +//! Benchmark lib for `cuprate-json-rpc`. #![allow(unused_crate_dependencies, reason = "used in benchmarks")] From 2e3e36c840e697830cd95207f77d6d84f6298fed Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Wed, 9 Oct 2024 19:46:13 -0400 Subject: [PATCH 12/33] add to crates.md --- benches/benchmark/lib/README.md | 3 +-- books/architecture/src/appendix/crates.md | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/benches/benchmark/lib/README.md b/benches/benchmark/lib/README.md index 6abce6b01..9ea79ae4e 100644 --- a/benches/benchmark/lib/README.md +++ b/benches/benchmark/lib/README.md @@ -3,8 +3,7 @@ This crate is the glue between [`cuprate-benchmark`](https://github.com/Cuprate/cuprate/tree/benches/benches/benchmark/bin) and all the benchmark crates. -It defines the [`cuprate_benchmark_lib::Benchmark`] trait, -which is the behavior of all benchmarks. +It defines the [`crate::Benchmark`] trait, which is the behavior of all benchmarks. See the [`cuprate-benchmark-example`](https://github.com/Cuprate/cuprate/tree/benches/benches/benchmark/example) crate to see an example implementation of this trait. diff --git a/books/architecture/src/appendix/crates.md b/books/architecture/src/appendix/crates.md index 1993c47e7..c792250e4 100644 --- a/books/architecture/src/appendix/crates.md +++ b/books/architecture/src/appendix/crates.md @@ -61,3 +61,11 @@ cargo doc --open --package cuprate-blockchain | [`cuprate-helper`](https://doc.cuprate.org/cuprate_helper) | [`helper/`](https://github.com/Cuprate/cuprate/tree/main/helper) | Kitchen-sink helper crate for Cuprate | [`cuprate-test-utils`](https://doc.cuprate.org/cuprate_test_utils) | [`test-utils/`](https://github.com/Cuprate/cuprate/tree/main/test-utils) | Testing utilities for Cuprate | [`cuprate-types`](https://doc.cuprate.org/cuprate_types) | [`types/`](https://github.com/Cuprate/cuprate/tree/main/types) | Shared types across Cuprate + +## Benchmarks +| Crate | In-tree path | Purpose | +|-------|--------------|---------| +| [`cuprate-benchmark`](https://doc.cuprate.org/cuprate_benchmark) | [`benches/benchmark/bin/`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/bin) | Cuprate benchmarking binary +| [`cuprate-benchmark-lib`](https://doc.cuprate.org/cuprate_benchmark_lib) | [`benches/benchmark/lib/`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/lib) | Cuprate benchmarking library +| `cuprate-benchmark-*` | [`benches/benchmark/cuprate-*`](https://github.com/Cuprate/cuprate/tree/main/benches/benchmark/) | Benchmark for a Cuprate crate that uses `cuprate-benchmark` +| `cuprate-criterion-*` | [`benches/criterion/cuprate-*`](https://github.com/Cuprate/cuprate/tree/main/benches/criterion) | Benchmark for a Cuprate crate that uses [Criterion](https://bheisler.github.io/criterion.rs/book) \ No newline at end of file From 1c462e3c957e1d01f07f8b304618a8087fbcd40b Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Wed, 9 Oct 2024 20:17:58 -0400 Subject: [PATCH 13/33] add `fixme` --- benches/benchmark/lib/src/benchmark.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/benches/benchmark/lib/src/benchmark.rs b/benches/benchmark/lib/src/benchmark.rs index 031767317..4dca55045 100644 --- a/benches/benchmark/lib/src/benchmark.rs +++ b/benches/benchmark/lib/src/benchmark.rs @@ -8,6 +8,9 @@ pub trait Benchmark { /// /// This is automatically implemented /// as the name of the [`Self`] type. + // + // FIXME: use `const` instead of `fn` when stable + // fn name() -> &'static str { std::any::type_name::() } From e1a6f82e92d12b1c85a235445bbb7b8213bdbe35 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Fri, 18 Oct 2024 20:11:47 -0400 Subject: [PATCH 14/33] add `benches/criterion/cuprate-helper` --- Cargo.lock | 13 ++++ Cargo.toml | 1 + benches/criterion/cuprate-helper/Cargo.toml | 26 +++++++ .../criterion/cuprate-helper/benches/cast.rs | 40 +++++++++++ .../criterion/cuprate-helper/benches/main.rs | 14 ++++ .../criterion/cuprate-helper/benches/map.rs | 70 +++++++++++++++++++ .../criterion/cuprate-helper/benches/num.rs | 64 +++++++++++++++++ .../criterion/cuprate-helper/benches/tx.rs | 28 ++++++++ benches/criterion/cuprate-helper/src/lib.rs | 1 + 9 files changed, 257 insertions(+) create mode 100644 benches/criterion/cuprate-helper/Cargo.toml create mode 100644 benches/criterion/cuprate-helper/benches/cast.rs create mode 100644 benches/criterion/cuprate-helper/benches/main.rs create mode 100644 benches/criterion/cuprate-helper/benches/map.rs create mode 100644 benches/criterion/cuprate-helper/benches/num.rs create mode 100644 benches/criterion/cuprate-helper/benches/tx.rs create mode 100644 benches/criterion/cuprate-helper/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 655d12748..9875b7818 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -748,6 +748,19 @@ dependencies = [ "serde_json", ] +[[package]] +name = "cuprate-criterion-helper" +version = "0.0.0" +dependencies = [ + "criterion", + "cuprate-constants", + "cuprate-helper", + "cuprate-test-utils", + "function_name", + "monero-serai", + "serde_json", +] + [[package]] name = "cuprate-criterion-json-rpc" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index d756b08a1..6d78005dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "benches/benchmark/example", "benches/criterion/example", "benches/criterion/cuprate-json-rpc", + "benches/criterion/cuprate-helper", # Consensus "consensus", "consensus/fast-sync", diff --git a/benches/criterion/cuprate-helper/Cargo.toml b/benches/criterion/cuprate-helper/Cargo.toml new file mode 100644 index 000000000..c9d0c8d30 --- /dev/null +++ b/benches/criterion/cuprate-helper/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "cuprate-criterion-helper" +version = "0.0.0" +edition = "2021" +description = "Criterion benchmarks for cuprate-helper" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/example" +keywords = ["cuprate", "helper", "criterion", "benchmark"] + +[dependencies] +cuprate-constants = { path = "../../../constants", features = ["block"] } +cuprate-helper = { path = "../../../helper", features = ["cast", "map", "num", "tx"] } +cuprate-test-utils = { path = "../../../test-utils" } + +criterion = { workspace = true } +function_name = { workspace = true } +monero-serai = { workspace = true } +serde_json = { workspace = true, features = ["default"] } + +[[bench]] +name = "main" +harness = false + +[lints] +workspace = true \ No newline at end of file diff --git a/benches/criterion/cuprate-helper/benches/cast.rs b/benches/criterion/cuprate-helper/benches/cast.rs new file mode 100644 index 000000000..256f43d5c --- /dev/null +++ b/benches/criterion/cuprate-helper/benches/cast.rs @@ -0,0 +1,40 @@ +//! Benchmarks for `cuprate_helper::cast`. +#![allow(unused_attributes, unused_crate_dependencies)] + +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use function_name::named; + +use cuprate_helper::cast::{ + i32_to_isize, i64_to_isize, isize_to_i64, u32_to_usize, u64_to_usize, usize_to_u64, +}; + +criterion_group! { + name = benches; + config = Criterion::default(); + targets = integer, unsigned, +} +criterion_main!(benches); + +/// Benchmark integer casts. +#[named] +fn integer(c: &mut Criterion) { + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(i32_to_isize(black_box(0))); + black_box(i64_to_isize(black_box(0))); + black_box(isize_to_i64(black_box(0))); + }); + }); +} + +/// Benchmark unsigned integer casts. +#[named] +fn unsigned(c: &mut Criterion) { + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(u32_to_usize(black_box(0))); + black_box(u64_to_usize(black_box(0))); + black_box(usize_to_u64(black_box(0))); + }); + }); +} diff --git a/benches/criterion/cuprate-helper/benches/main.rs b/benches/criterion/cuprate-helper/benches/main.rs new file mode 100644 index 000000000..aa325357d --- /dev/null +++ b/benches/criterion/cuprate-helper/benches/main.rs @@ -0,0 +1,14 @@ +//! `cuprate_helper` benchmarks. +#![allow(unused_crate_dependencies)] + +mod cast; +mod map; +mod num; +mod tx; + +criterion::criterion_main! { + cast::benches, + map::benches, + num::benches, + tx::benches, +} diff --git a/benches/criterion/cuprate-helper/benches/map.rs b/benches/criterion/cuprate-helper/benches/map.rs new file mode 100644 index 000000000..7b2f34453 --- /dev/null +++ b/benches/criterion/cuprate-helper/benches/map.rs @@ -0,0 +1,70 @@ +//! Benchmarks for `cuprate_helper::cast`. +#![allow(unused_attributes, unused_crate_dependencies)] + +use criterion::black_box as b; +use criterion::{criterion_group, criterion_main, Criterion}; +use function_name::named; + +use cuprate_constants::block::MAX_BLOCK_HEIGHT; +use cuprate_helper::map; + +criterion_group! { + name = benches; + config = Criterion::default(); + targets = + combine_low_high_bits_to_u128, + split_u128_into_low_high_bits, + timelock_to_u64, + u64_to_timelock, +} +criterion_main!(benches); + +/// Benchmark [`curpate_helper::map::combine_low_high_bits_to_u128`]. +#[named] +fn combine_low_high_bits_to_u128(c: &mut Criterion) { + c.bench_function(function_name!(), |bench| { + bench.iter(|| { + b(map::combine_low_high_bits_to_u128(b(0), b(0))); + }); + }); +} + +/// Benchmark [`curpate_helper::map::split_u128_into_low_high_bits`]. +#[named] +fn split_u128_into_low_high_bits(c: &mut Criterion) { + c.bench_function(function_name!(), |bench| { + bench.iter(|| { + b(map::split_u128_into_low_high_bits(b(0))); + }); + }); +} + +/// Benchmark [`curpate_helper::map::timelock_to_u64`]. +#[named] +fn timelock_to_u64(c: &mut Criterion) { + c.bench_function(function_name!(), |bench| { + bench.iter(|| { + b(map::timelock_to_u64(b( + monero_serai::transaction::Timelock::None, + ))); + b(map::timelock_to_u64(b( + monero_serai::transaction::Timelock::Time(0), + ))); + b(map::timelock_to_u64(b( + monero_serai::transaction::Timelock::Block(0), + ))); + }); + }); +} + +/// Benchmark [`curpate_helper::map::u64_to_timelock`]. +#[named] +fn u64_to_timelock(c: &mut Criterion) { + c.bench_function(function_name!(), |bench| { + bench.iter(|| { + b(map::u64_to_timelock(b(0))); + b(map::u64_to_timelock(b(MAX_BLOCK_HEIGHT))); + b(map::u64_to_timelock(b(MAX_BLOCK_HEIGHT + 1))); + }); + }); +} diff --git a/benches/criterion/cuprate-helper/benches/num.rs b/benches/criterion/cuprate-helper/benches/num.rs new file mode 100644 index 000000000..070951411 --- /dev/null +++ b/benches/criterion/cuprate-helper/benches/num.rs @@ -0,0 +1,64 @@ +//! Benchmarks for `cuprate_helper::cast`. +#![allow(unused_attributes, unused_crate_dependencies)] + +use criterion::black_box as b; +use criterion::{criterion_group, criterion_main, Criterion}; +use function_name::named; + +use cuprate_helper::num; + +criterion_group! { + name = benches; + config = Criterion::default(); + targets = + cmp_float, + cmp_float_nan, + get_mid, + median, +} +criterion_main!(benches); + +/// Benchmark [`curpate_helper::num::cmp_float`]. +#[named] +fn cmp_float(c: &mut Criterion) { + c.bench_function(function_name!(), |bench| { + bench.iter(|| { + b(num::cmp_float(b(0.0), b(0.0))); + }); + }); +} + +/// Benchmark [`curpate_helper::num::cmp_float_nan`]. +#[named] +fn cmp_float_nan(c: &mut Criterion) { + c.bench_function(function_name!(), |bench| { + bench.iter(|| { + b(num::cmp_float_nan(b(0.0), b(0.0))); + }); + }); +} + +/// Benchmark [`curpate_helper::num::get_mid`]. +#[named] +fn get_mid(c: &mut Criterion) { + c.bench_function(function_name!(), |bench| { + bench.iter(|| { + b(num::get_mid(b(0_u8), b(0_u8))); + b(num::get_mid(b(1_i64), b(10_i64))); + b(num::get_mid(b(0.0_f32), b(0.0_f32))); + b(num::get_mid(b(0.0_f64), b(10.0_f64))); + }); + }); +} + +/// Benchmark [`curpate_helper::num::median`]. +#[named] +fn median(c: &mut Criterion) { + c.bench_function(function_name!(), |bench| { + bench.iter(|| { + b(num::median(b(vec![0_u8, 1, 2, 3, 4, 5]))); + b(num::median(b(vec![0.0_f32, 1.0, 2.0, 3.0, 4.0, 5.0]))); + b(num::median(b(vec![0_u64; 100]))); + }); + }); +} diff --git a/benches/criterion/cuprate-helper/benches/tx.rs b/benches/criterion/cuprate-helper/benches/tx.rs new file mode 100644 index 000000000..0b2dc2d0c --- /dev/null +++ b/benches/criterion/cuprate-helper/benches/tx.rs @@ -0,0 +1,28 @@ +//! Benchmarks for `cuprate_helper::cast`. +#![allow(unused_attributes, unused_crate_dependencies)] + +use criterion::black_box as b; +use criterion::{criterion_group, criterion_main, Criterion}; +use function_name::named; + +use cuprate_helper::tx; +use cuprate_test_utils::data::{TX_V1_SIG0, TX_V1_SIG2, TX_V2_RCT3}; + +criterion_group! { + name = benches; + config = Criterion::default(); + targets = tx_fee, +} +criterion_main!(benches); + +/// Benchmark [`curpate_helper::tx::tx_fee`]. +#[named] +fn tx_fee(c: &mut Criterion) { + c.bench_function(function_name!(), |bench| { + bench.iter(|| { + b(tx::tx_fee(b(&TX_V1_SIG0.tx))); + b(tx::tx_fee(b(&TX_V1_SIG2.tx))); + b(tx::tx_fee(b(&TX_V2_RCT3.tx))); + }); + }); +} diff --git a/benches/criterion/cuprate-helper/src/lib.rs b/benches/criterion/cuprate-helper/src/lib.rs new file mode 100644 index 000000000..5a529588c --- /dev/null +++ b/benches/criterion/cuprate-helper/src/lib.rs @@ -0,0 +1 @@ +#![allow(unused_crate_dependencies, reason = "used in benchmarks")] From 7a94952e144ace471f733afe039741663ee4f981 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Mon, 21 Oct 2024 17:01:11 -0400 Subject: [PATCH 15/33] copy #98 --- benches/criterion/cuprate-database/Cargo.toml | 24 + benches/criterion/cuprate-database/README.md | 48 ++ .../criterion/cuprate-database/benches/db.rs | 583 +++++++++++++++++ .../benches/db_multi_thread.rs | 584 ++++++++++++++++++ .../criterion/cuprate-database/benches/env.rs | 161 +++++ .../benches/env_multi_thread.rs | 162 +++++ .../cuprate-database/benches/main.rs | 91 +++ .../cuprate-database/benches/service.rs | 25 + .../cuprate-database/benches/storable.rs | 81 +++ .../criterion/cuprate-database/src/helper.rs | 30 + benches/criterion/cuprate-database/src/lib.rs | 83 +++ 11 files changed, 1872 insertions(+) create mode 100644 benches/criterion/cuprate-database/Cargo.toml create mode 100644 benches/criterion/cuprate-database/README.md create mode 100644 benches/criterion/cuprate-database/benches/db.rs create mode 100644 benches/criterion/cuprate-database/benches/db_multi_thread.rs create mode 100644 benches/criterion/cuprate-database/benches/env.rs create mode 100644 benches/criterion/cuprate-database/benches/env_multi_thread.rs create mode 100644 benches/criterion/cuprate-database/benches/main.rs create mode 100644 benches/criterion/cuprate-database/benches/service.rs create mode 100644 benches/criterion/cuprate-database/benches/storable.rs create mode 100644 benches/criterion/cuprate-database/src/helper.rs create mode 100644 benches/criterion/cuprate-database/src/lib.rs diff --git a/benches/criterion/cuprate-database/Cargo.toml b/benches/criterion/cuprate-database/Cargo.toml new file mode 100644 index 000000000..c37d265b9 --- /dev/null +++ b/benches/criterion/cuprate-database/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cuprate-database-benchmark" +version = "0.0.0" +edition = "2021" +description = "Benchmarking for Cuprate's database abstraction" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/database/benchmark" +keywords = ["cuprate", "database", "benchmark"] + +[features] +default = [] + +[dependencies] +criterion = { workspace = true } +cuprate-database = { path = "../", features = ["default"] } +cuprate-helper = { path = "../../helper", features = ["fs", "thread"] } +# cuprate-types = { path = "../../types", features = ["service"] } +function_name = { version = "0.3.0" } +tempfile = { version = "3.10.0" } + +[[bench]] +name = "main" +harness = false \ No newline at end of file diff --git a/benches/criterion/cuprate-database/README.md b/benches/criterion/cuprate-database/README.md new file mode 100644 index 000000000..6e8314f5a --- /dev/null +++ b/benches/criterion/cuprate-database/README.md @@ -0,0 +1,48 @@ +# `cuprate-database-benchmark` +This is a benchmarking suite that allows testing/benchmarking `cuprate-database` with [`criterion`](https://bheisler.github.io/criterion.rs/book/criterion_rs.html). + +For more information on `cargo bench` and `criterion`: +- https://doc.rust-lang.org/cargo/commands/cargo-bench.html +- https://bheisler.github.io/criterion.rs/book/criterion_rs.html + + +1. [Usage](#Usage) +1. [File Structure](#file-structure) + - [`src/`](#src) + - [`benches/`](#benches) + +# Usage +Ensure the system is as quiet as possible (no background tasks) before starting and during the benchmarks. + +To start all benchmarks, run: +```bash +cargo bench --package cuprate-database-benchmarks +``` + +# File Structure +A quick reference of the structure of the folders & files in `cuprate-database`. + +Note that `lib.rs/mod.rs` files are purely for re-exporting/visibility/lints, and contain no code. Each sub-directory has a corresponding `mod.rs`. + +## `src/` +The top-level `src/` files. + +The actual `cuprate-database-benchmark` library crate is just used as a helper for the benchmarks within `benches/`. + +| File | Purpose | +|---------------------|---------| +| `helper.rs` | Helper functions + +## `benches/` +The actual benchmarks. + +Each file represents some logical benchmark grouping. + +| File | Purpose | +|-----------------------|---------| +| `db.rs` | `trait Database{Ro,Rw,Iter}` benchmarks +| `db_multi_thread.rs` | Same as `db.rs` but multi-threaded +| `env.rs` | `trait {Env, EnvInner, TxR{o,w}, Tables[Mut]}` benchmarks +| `env_multi_thread.rs` | Same as `env.rs` but multi-threaded +| `service.rs` | `cuprate_database::service` benchmarks +| `storable.rs` | `trait Storable` benchmarks \ No newline at end of file diff --git a/benches/criterion/cuprate-database/benches/db.rs b/benches/criterion/cuprate-database/benches/db.rs new file mode 100644 index 000000000..686d60acf --- /dev/null +++ b/benches/criterion/cuprate-database/benches/db.rs @@ -0,0 +1,583 @@ +//! `trait Database{Ro,Rw,Iter}` benchmarks. + +use std::time::Instant; + +//---------------------------------------------------------------------------------------------------- Import +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +use function_name::named; + +use cuprate_database::{ + tables::Outputs, + types::{Output, PreRctOutputId}, + DatabaseIter, DatabaseRo, DatabaseRw, Env, EnvInner, TxRw, +}; + +use cuprate_database_benchmark::tmp_env; + +//---------------------------------------------------------------------------------------------------- Criterion +criterion_group! { + benches, + ro_get, + ro_len, + ro_first, + ro_last, + ro_is_empty, + ro_contains, + rw_get, + rw_len, + rw_first, + rw_last, + rw_is_empty, + rw_contains, + put, + delete, + pop_first, + pop_last, + get_range, + iter, + keys, + values, +} +criterion_main!(benches); + +//---------------------------------------------------------------------------------------------------- Constants +/// The (1st) key. +const KEY: PreRctOutputId = PreRctOutputId { + amount: 1, + amount_index: 123, +}; + +/// The expected value. +const VALUE: Output = Output { + key: [35; 32], + height: 45_761_798, + output_flags: 0, + tx_idx: 2_353_487, +}; + +//---------------------------------------------------------------------------------------------------- DatabaseRo +// Read-only table operations. +// This uses `TxRw + TablesMut` briefly to insert values, then +// uses `TxRo + Tables` for the actual operation. +// +// See further below for using `TxRw + TablesMut` on the same operations. + +/// [`DatabaseRo::get`] +#[named] +fn ro_get(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let _value: Output = table.get(black_box(&KEY)).unwrap(); + }); + }); +} + +/// [`DatabaseRo::len`] +#[named] +fn ro_len(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(table.len()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::first`] +#[named] +fn ro_first(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let (_, _): (PreRctOutputId, Output) = black_box(table.first()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::last`] +#[named] +fn ro_last(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let (_, _): (PreRctOutputId, Output) = black_box(table.last()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::is_empty`] +#[named] +fn ro_is_empty(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(table.is_empty()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::contains`] +#[named] +fn ro_contains(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + table.contains(black_box(&KEY)).unwrap(); + }); + }); +} + +//---------------------------------------------------------------------------------------------------- DatabaseRo (using TxRw) +// These are the same benchmarks as above, but it uses a +// `TxRw` and a `TablesMut` instead to ensure our read/write tables +// using read operations perform the same as normal read-only tables. + +/// [`DatabaseRo::get`] +#[named] +fn rw_get(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let _value: Output = table.get(black_box(&KEY)).unwrap(); + }); + }); +} + +/// [`DatabaseRo::len`] +#[named] +fn rw_len(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(table.len()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::first`] +#[named] +fn rw_first(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let (_, _): (PreRctOutputId, Output) = black_box(table.first()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::last`] +#[named] +fn rw_last(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let (_, _): (PreRctOutputId, Output) = black_box(table.last()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::is_empty`] +#[named] +fn rw_is_empty(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(table.is_empty()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::contains`] +#[named] +fn rw_contains(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + table.contains(black_box(&KEY)).unwrap(); + }); + }); +} + +//---------------------------------------------------------------------------------------------------- DatabaseRw +/// [`DatabaseRw::put`] +#[named] +fn put(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + + c.bench_function(function_name!(), |b| { + b.iter(|| { + table.put(black_box(&key), black_box(&VALUE)).unwrap(); + key.amount += 1; + }); + }); +} + +/// [`DatabaseRw::delete`] +#[named] +fn delete(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + + c.bench_function(function_name!(), |b| { + b.iter_custom(|iters| { + for _ in 0..iters { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + key = KEY; + + let start = Instant::now(); + for _ in 0..iters { + table.delete(&key).unwrap(); + key.amount += 1; + } + start.elapsed() + }); + }); +} + +/// [`DatabaseRw::pop_first`] +#[named] +fn pop_first(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + + c.bench_function(function_name!(), |b| { + b.iter_custom(|iters| { + for _ in 0..iters { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + key = KEY; + + let start = Instant::now(); + for _ in 0..iters { + table.pop_first().unwrap(); + key.amount += 1; + } + start.elapsed() + }); + }); +} + +/// [`DatabaseRw::pop_last`] +#[named] +fn pop_last(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + + c.bench_function(function_name!(), |b| { + b.iter_custom(|iters| { + for _ in 0..iters { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + key = KEY; + + let start = Instant::now(); + for _ in 0..iters { + table.pop_last().unwrap(); + key.amount += 1; + } + start.elapsed() + }); + }); +} + +// TODO: waiting on PR 102 +// /// [`DatabaseRw::take`] +// #[named] +// fn take(c: &mut Criterion) { +// let (env, _tempdir) = tmp_env(); +// let env_inner = env.env_inner(); +// let tx_rw = env_inner.tx_rw().unwrap(); +// let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + +// let mut key = KEY; +// for _ in 0..100 { +// table.put(&key, &VALUE).unwrap(); +// key.amount += 1; +// } + +// c.bench_function(function_name!(), |b| { +// b.iter(|| { +// table.put(&KEY, &VALUE).unwrap(); +// let value: Output = black_box(table.take(&black_box(KEY)).unwrap()); +// }); +// }); +// } + +//---------------------------------------------------------------------------------------------------- DatabaseIter +/// [`DatabaseRo::get_range`] +#[named] +fn get_range(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + for _ in 0..100 { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + drop(table); + TxRw::commit(tx_rw).unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let range = table.get_range(black_box(..)).unwrap(); + for result in range { + let _value: Output = black_box(result.unwrap()); + } + }); + }); +} + +/// [`DatabaseRo::iter`] +#[named] +fn iter(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + for _ in 0..100 { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + drop(table); + TxRw::commit(tx_rw).unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let iter = black_box(table.iter()).unwrap(); + for result in iter { + let _: (PreRctOutputId, Output) = black_box(result.unwrap()); + } + }); + }); +} + +/// [`DatabaseRo::keys`] +#[named] +fn keys(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + for _ in 0..100 { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + drop(table); + TxRw::commit(tx_rw).unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let keys = black_box(table.keys()).unwrap(); + for result in keys { + let _: PreRctOutputId = black_box(result.unwrap()); + } + }); + }); +} + +/// [`DatabaseRo::values`] +#[named] +fn values(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + for _ in 0..100 { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + drop(table); + TxRw::commit(tx_rw).unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let values = black_box(table.values()).unwrap(); + for result in values { + let _: Output = black_box(result.unwrap()); + } + }); + }); +} diff --git a/benches/criterion/cuprate-database/benches/db_multi_thread.rs b/benches/criterion/cuprate-database/benches/db_multi_thread.rs new file mode 100644 index 000000000..808992947 --- /dev/null +++ b/benches/criterion/cuprate-database/benches/db_multi_thread.rs @@ -0,0 +1,584 @@ +//! Same as `db.rs` but multi-threaded. +//! TODO: create multi-threaded benchmarks + +use std::time::Instant; + +//---------------------------------------------------------------------------------------------------- Import +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +use function_name::named; + +use cuprate_database::{ + tables::Outputs, + types::{Output, PreRctOutputId}, + DatabaseIter, DatabaseRo, DatabaseRw, Env, EnvInner, TxRw, +}; + +use cuprate_database_benchmark::tmp_env_all_threads; + +//---------------------------------------------------------------------------------------------------- Criterion +criterion_group! { + benches, + ro_get, + ro_len, + ro_first, + ro_last, + ro_is_empty, + ro_contains, + rw_get, + rw_len, + rw_first, + rw_last, + rw_is_empty, + rw_contains, + put, + delete, + pop_first, + pop_last, + get_range, + iter, + keys, + values, +} +criterion_main!(benches); + +//---------------------------------------------------------------------------------------------------- Constants +/// The (1st) key. +const KEY: PreRctOutputId = PreRctOutputId { + amount: 1, + amount_index: 123, +}; + +/// The expected value. +const VALUE: Output = Output { + key: [35; 32], + height: 45_761_798, + output_flags: 0, + tx_idx: 2_353_487, +}; + +//---------------------------------------------------------------------------------------------------- DatabaseRo +// Read-only table operations. +// This uses `TxRw + TablesMut` briefly to insert values, then +// uses `TxRo + Tables` for the actual operation. +// +// See further below for using `TxRw + TablesMut` on the same operations. + +/// [`DatabaseRo::get`] +#[named] +fn ro_get(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let _value: Output = table.get(black_box(&KEY)).unwrap(); + }); + }); +} + +/// [`DatabaseRo::len`] +#[named] +fn ro_len(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(table.len()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::first`] +#[named] +fn ro_first(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let (_, _): (PreRctOutputId, Output) = black_box(table.first()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::last`] +#[named] +fn ro_last(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let (_, _): (PreRctOutputId, Output) = black_box(table.last()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::is_empty`] +#[named] +fn ro_is_empty(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(table.is_empty()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::contains`] +#[named] +fn ro_contains(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + table.contains(black_box(&KEY)).unwrap(); + }); + }); +} + +//---------------------------------------------------------------------------------------------------- DatabaseRo (using TxRw) +// These are the same benchmarks as above, but it uses a +// `TxRw` and a `TablesMut` instead to ensure our read/write tables +// using read operations perform the same as normal read-only tables. + +/// [`DatabaseRo::get`] +#[named] +fn rw_get(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let _value: Output = table.get(black_box(&KEY)).unwrap(); + }); + }); +} + +/// [`DatabaseRo::len`] +#[named] +fn rw_len(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(table.len()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::first`] +#[named] +fn rw_first(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let (_, _): (PreRctOutputId, Output) = black_box(table.first()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::last`] +#[named] +fn rw_last(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let (_, _): (PreRctOutputId, Output) = black_box(table.last()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::is_empty`] +#[named] +fn rw_is_empty(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(table.is_empty()).unwrap(); + }); + }); +} + +/// [`DatabaseRo::contains`] +#[named] +fn rw_contains(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + let tx_rw = env_inner.tx_rw().unwrap(); + let table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + table.contains(black_box(&KEY)).unwrap(); + }); + }); +} + +//---------------------------------------------------------------------------------------------------- DatabaseRw +/// [`DatabaseRw::put`] +#[named] +fn put(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + + c.bench_function(function_name!(), |b| { + b.iter(|| { + table.put(black_box(&key), black_box(&VALUE)).unwrap(); + key.amount += 1; + }); + }); +} + +/// [`DatabaseRw::delete`] +#[named] +fn delete(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + + c.bench_function(function_name!(), |b| { + b.iter_custom(|iters| { + for _ in 0..iters { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + key = KEY; + + let start = Instant::now(); + for _ in 0..iters { + table.delete(&key).unwrap(); + key.amount += 1; + } + start.elapsed() + }); + }); +} + +/// [`DatabaseRw::pop_first`] +#[named] +fn pop_first(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + + c.bench_function(function_name!(), |b| { + b.iter_custom(|iters| { + for _ in 0..iters { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + key = KEY; + + let start = Instant::now(); + for _ in 0..iters { + table.pop_first().unwrap(); + key.amount += 1; + } + start.elapsed() + }); + }); +} + +/// [`DatabaseRw::pop_last`] +#[named] +fn pop_last(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + + c.bench_function(function_name!(), |b| { + b.iter_custom(|iters| { + for _ in 0..iters { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + key = KEY; + + let start = Instant::now(); + for _ in 0..iters { + table.pop_last().unwrap(); + key.amount += 1; + } + start.elapsed() + }); + }); +} + +// TODO: waiting on PR 102 +// /// [`DatabaseRw::take`] +// #[named] +// fn take(c: &mut Criterion) { +// let (env, _tempdir) = tmp_env_all_threads(); +// let env_inner = env.env_inner(); +// let tx_rw = env_inner.tx_rw().unwrap(); +// let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + +// let mut key = KEY; +// for _ in 0..100 { +// table.put(&key, &VALUE).unwrap(); +// key.amount += 1; +// } + +// c.bench_function(function_name!(), |b| { +// b.iter(|| { +// table.put(&KEY, &VALUE).unwrap(); +// let value: Output = black_box(table.take(&black_box(KEY)).unwrap()); +// }); +// }); +// } + +//---------------------------------------------------------------------------------------------------- DatabaseIter +/// [`DatabaseRo::get_range`] +#[named] +fn get_range(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + for _ in 0..100 { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + drop(table); + TxRw::commit(tx_rw).unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let range = table.get_range(black_box(..)).unwrap(); + for result in range { + let _value: Output = black_box(result.unwrap()); + } + }); + }); +} + +/// [`DatabaseRo::iter`] +#[named] +fn iter(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + for _ in 0..100 { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + drop(table); + TxRw::commit(tx_rw).unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let iter = black_box(table.iter()).unwrap(); + for result in iter { + let _: (PreRctOutputId, Output) = black_box(result.unwrap()); + } + }); + }); +} + +/// [`DatabaseRo::keys`] +#[named] +fn keys(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + for _ in 0..100 { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + drop(table); + TxRw::commit(tx_rw).unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let keys = black_box(table.keys()).unwrap(); + for result in keys { + let _: PreRctOutputId = black_box(result.unwrap()); + } + }); + }); +} + +/// [`DatabaseRo::values`] +#[named] +fn values(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + for _ in 0..100 { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + drop(table); + TxRw::commit(tx_rw).unwrap(); + + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let values = black_box(table.values()).unwrap(); + for result in values { + let _: Output = black_box(result.unwrap()); + } + }); + }); +} diff --git a/benches/criterion/cuprate-database/benches/env.rs b/benches/criterion/cuprate-database/benches/env.rs new file mode 100644 index 000000000..4c0f6d49a --- /dev/null +++ b/benches/criterion/cuprate-database/benches/env.rs @@ -0,0 +1,161 @@ +//! `trait {Env, EnvInner, TxR{o,w}, Tables[Mut]}` benchmarks. + +//---------------------------------------------------------------------------------------------------- Import +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +use function_name::named; + +use cuprate_database::{ + config::Config, + resize::{page_size, ResizeAlgorithm}, + tables::Outputs, + ConcreteEnv, Env, EnvInner, TxRo, TxRw, +}; + +use cuprate_database_benchmark::tmp_env; + +//---------------------------------------------------------------------------------------------------- Criterion +criterion_group! { + benches, + open, + env_inner, + tx_ro, + tx_rw, + open_tables, + open_tables_mut, + resize, + current_map_size, + disk_size_bytes, +} +criterion_main!(benches); + +//---------------------------------------------------------------------------------------------------- Env benchmarks +/// [`Env::open`]. +#[named] +fn open(c: &mut Criterion) { + let tempdir = tempfile::tempdir().unwrap(); + let config = Config::low_power(Some(tempdir.path().into())); + + c.bench_function(function_name!(), |b| { + b.iter_with_large_drop(|| { + ConcreteEnv::open(config.clone()).unwrap(); + }); + }); +} + +/// [`Env::env_inner`]. +#[named] +fn env_inner(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(env.env_inner()); + }); + }); +} + +/// Create and commit read-only transactions. +#[named] +fn tx_ro(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let tx_ro = black_box(env_inner.tx_ro()).unwrap(); + TxRo::commit(black_box(tx_ro)).unwrap(); + }); + }); +} + +/// Create and commit read/write transactions. +#[named] +fn tx_rw(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let tx_rw = black_box(env_inner.tx_rw()).unwrap(); + TxRw::commit(black_box(tx_rw)).unwrap(); + }); + }); +} + +/// Open all database tables in read-only mode. +#[named] +fn open_tables(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_ro = env_inner.tx_ro().unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(env_inner.open_db_ro::(&tx_ro)).unwrap(); + // env_inner.open_tables(&tx_ro).unwrap(); + // TODO: waiting on PR 102 + }); + }); +} + +/// Open all database tables in read/write mode. +#[named] +fn open_tables_mut(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(env_inner.open_db_rw::(&tx_rw)).unwrap(); + // env_inner.open_tables_mut(&mut tx_rw).unwrap(); + // TODO: waiting on PR 102 + }); + }); +} + +/// `Env` memory map resizes. +#[named] +fn resize(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + + // Resize by the OS page size. + let page_size = page_size(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + // This test is only valid for `Env`'s that need to resize manually. + if ConcreteEnv::MANUAL_RESIZE { + env.resize_map(black_box(Some(ResizeAlgorithm::FixedBytes(page_size)))); + } + }); + }); +} + +/// Access current memory map size of the database. +#[named] +fn current_map_size(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + // This test is only valid for `Env`'s that need to resize manually. + if ConcreteEnv::MANUAL_RESIZE { + black_box(env.current_map_size()); + } + }); + }); +} + +/// Access on-disk size of the database. +#[named] +fn disk_size_bytes(c: &mut Criterion) { + let (env, _tempdir) = tmp_env(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(env.disk_size_bytes()).unwrap(); + }); + }); +} diff --git a/benches/criterion/cuprate-database/benches/env_multi_thread.rs b/benches/criterion/cuprate-database/benches/env_multi_thread.rs new file mode 100644 index 000000000..ec796c89f --- /dev/null +++ b/benches/criterion/cuprate-database/benches/env_multi_thread.rs @@ -0,0 +1,162 @@ +//! Same as `env.rs` but multi-threaded. +//! TODO: create multi-threaded benchmarks + +//---------------------------------------------------------------------------------------------------- Import +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +use function_name::named; + +use cuprate_database::{ + config::Config, + resize::{page_size, ResizeAlgorithm}, + tables::Outputs, + ConcreteEnv, Env, EnvInner, TxRo, TxRw, +}; + +use cuprate_database_benchmark::tmp_env_all_threads; + +//---------------------------------------------------------------------------------------------------- Criterion +criterion_group! { + benches, + open, + env_inner, + tx_ro, + tx_rw, + open_tables, + open_tables_mut, + resize, + current_map_size, + disk_size_bytes, +} +criterion_main!(benches); + +//---------------------------------------------------------------------------------------------------- Env benchmarks +/// [`Env::open`]. +#[named] +fn open(c: &mut Criterion) { + let tempdir = tempfile::tempdir().unwrap(); + let config = Config::low_power(Some(tempdir.path().into())); + + c.bench_function(function_name!(), |b| { + b.iter_with_large_drop(|| { + ConcreteEnv::open(config.clone()).unwrap(); + }); + }); +} + +/// [`Env::env_inner`]. +#[named] +fn env_inner(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(env.env_inner()); + }); + }); +} + +/// Create and commit read-only transactions. +#[named] +fn tx_ro(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let tx_ro = black_box(env_inner.tx_ro()).unwrap(); + TxRo::commit(black_box(tx_ro)).unwrap(); + }); + }); +} + +/// Create and commit read/write transactions. +#[named] +fn tx_rw(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let tx_rw = black_box(env_inner.tx_rw()).unwrap(); + TxRw::commit(black_box(tx_rw)).unwrap(); + }); + }); +} + +/// Open all database tables in read-only mode. +#[named] +fn open_tables(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_ro = env_inner.tx_ro().unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(env_inner.open_db_ro::(&tx_ro)).unwrap(); + // env_inner.open_tables(&tx_ro).unwrap(); + // TODO: waiting on PR 102 + }); + }); +} + +/// Open all database tables in read/write mode. +#[named] +fn open_tables_mut(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(env_inner.open_db_rw::(&tx_rw)).unwrap(); + // env_inner.open_tables_mut(&mut tx_rw).unwrap(); + // TODO: waiting on PR 102 + }); + }); +} + +/// `Env` memory map resizes. +#[named] +fn resize(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + + // Resize by the OS page size. + let page_size = page_size(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + // This test is only valid for `Env`'s that need to resize manually. + if ConcreteEnv::MANUAL_RESIZE { + env.resize_map(black_box(Some(ResizeAlgorithm::FixedBytes(page_size)))); + } + }); + }); +} + +/// Access current memory map size of the database. +#[named] +fn current_map_size(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + // This test is only valid for `Env`'s that need to resize manually. + if ConcreteEnv::MANUAL_RESIZE { + black_box(env.current_map_size()); + } + }); + }); +} + +/// Access on-disk size of the database. +#[named] +fn disk_size_bytes(c: &mut Criterion) { + let (env, _tempdir) = tmp_env_all_threads(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(env.disk_size_bytes()).unwrap(); + }); + }); +} diff --git a/benches/criterion/cuprate-database/benches/main.rs b/benches/criterion/cuprate-database/benches/main.rs new file mode 100644 index 000000000..74865f2a2 --- /dev/null +++ b/benches/criterion/cuprate-database/benches/main.rs @@ -0,0 +1,91 @@ +//! TODO + +//---------------------------------------------------------------------------------------------------- Lints +// Forbid lints. +// Our code, and code generated (e.g macros) cannot overrule these. +#![forbid( + // `unsafe` is allowed but it _must_ be + // commented with `SAFETY: reason`. + clippy::undocumented_unsafe_blocks, + + // Never. + unused_unsafe, + redundant_semicolons, + unused_allocation, + coherence_leak_check, + while_true, + clippy::missing_docs_in_private_items, + + // Maybe can be put into `#[deny]`. + unconditional_recursion, + for_loops_over_fallibles, + unused_braces, + unused_doc_comments, + unused_labels, + keyword_idents, + non_ascii_idents, + variant_size_differences, + single_use_lifetimes, + + // Probably can be put into `#[deny]`. + future_incompatible, + let_underscore, + break_with_label_and_loop, + duplicate_macro_attributes, + exported_private_dependencies, + large_assignments, + overlapping_range_endpoints, + semicolon_in_expressions_from_macros, + noop_method_call, + unreachable_pub, +)] +// Deny lints. +// Some of these are `#[allow]`'ed on a per-case basis. +#![deny( + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, + clippy::cargo, + unused_mut, + missing_docs, + deprecated, + unused_comparisons, + nonstandard_style +)] +#![allow(unreachable_code, unused_variables, dead_code, unused_imports)] // TODO: remove +#![allow( + // FIXME: this lint affects crates outside of + // `database/` for some reason, allow for now. + clippy::cargo_common_metadata, + + // FIXME: adding `#[must_use]` onto everything + // might just be more annoying than useful... + // although it is sometimes nice. + clippy::must_use_candidate, + + // TODO: should be removed after all `todo!()`'s are gone. + clippy::diverging_sub_expression, + + clippy::module_name_repetitions, + clippy::module_inception, + clippy::redundant_pub_crate, + clippy::option_if_let_else, + clippy::significant_drop_tightening, +)] +// Allow some lints when running in debug mode. +#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] + +mod db; +mod env; +mod storable; + +criterion::criterion_main! { + db::benches, + env::benches, + storable::benches, +} diff --git a/benches/criterion/cuprate-database/benches/service.rs b/benches/criterion/cuprate-database/benches/service.rs new file mode 100644 index 000000000..9c4a74fff --- /dev/null +++ b/benches/criterion/cuprate-database/benches/service.rs @@ -0,0 +1,25 @@ +//! `cuprate_database::service` benchmarks. + +//---------------------------------------------------------------------------------------------------- Import +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +use function_name::named; + +use cuprate_database::{ + config::Config, + resize::{page_size, ResizeAlgorithm}, + tables::Outputs, + ConcreteEnv, Env, EnvInner, TxRo, TxRw, +}; + +use cuprate_database_benchmark::tmp_env_all_threads; + +//---------------------------------------------------------------------------------------------------- Criterion +criterion_group! { + benches, +} +criterion_main!(benches); + +//---------------------------------------------------------------------------------------------------- Benchmarks + +// TODO diff --git a/benches/criterion/cuprate-database/benches/storable.rs b/benches/criterion/cuprate-database/benches/storable.rs new file mode 100644 index 000000000..b427e2656 --- /dev/null +++ b/benches/criterion/cuprate-database/benches/storable.rs @@ -0,0 +1,81 @@ +//! `trait Storable` benchmarks. + +//---------------------------------------------------------------------------------------------------- Import +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +use function_name::named; + +use cuprate_database::{ + types::{Output, PreRctOutputId}, + Storable, +}; + +//---------------------------------------------------------------------------------------------------- Criterion +criterion_group! { + benches, + pre_rct_output_id_as_bytes, + pre_rct_output_id_from_bytes, + output_as_bytes, + output_from_bytes +} +criterion_main!(benches); + +//---------------------------------------------------------------------------------------------------- Constants +/// 16 bytes. +const PRE_RCT_OUTPUT_ID: PreRctOutputId = PreRctOutputId { + amount: 1, + amount_index: 123, +}; + +/// 48 bytes. +const OUTPUT: Output = Output { + key: [35; 32], + height: 45_761_798, + output_flags: 0, + tx_idx: 2_353_487, +}; + +//---------------------------------------------------------------------------------------------------- Storable benchmarks +/// [`PreRctOutputId`] cast as bytes. +#[named] +fn pre_rct_output_id_as_bytes(c: &mut Criterion) { + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(Storable::as_bytes(black_box(&PRE_RCT_OUTPUT_ID))); + }); + }); +} + +/// [`PreRctOutputId`] cast from bytes. +#[named] +fn pre_rct_output_id_from_bytes(c: &mut Criterion) { + let bytes = Storable::as_bytes(&PRE_RCT_OUTPUT_ID); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let _: PreRctOutputId = black_box(Storable::from_bytes(black_box(bytes))); + }); + }); +} + +/// [`Output`] cast as bytes. +#[named] +fn output_as_bytes(c: &mut Criterion) { + c.bench_function(function_name!(), |b| { + b.iter(|| { + black_box(Storable::as_bytes(black_box(&OUTPUT))); + }); + }); +} + +/// [`Output`] cast from bytes. +#[named] +fn output_from_bytes(c: &mut Criterion) { + let bytes = Storable::as_bytes(&OUTPUT); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + let _: Output = black_box(Storable::from_bytes(black_box(bytes))); + }); + }); +} diff --git a/benches/criterion/cuprate-database/src/helper.rs b/benches/criterion/cuprate-database/src/helper.rs new file mode 100644 index 000000000..ecf2fd199 --- /dev/null +++ b/benches/criterion/cuprate-database/src/helper.rs @@ -0,0 +1,30 @@ +//! TODO + +// TODO: set-up env based on config + +//---------------------------------------------------------------------------------------------------- Import +use cuprate_database::{config::Config, ConcreteEnv, Env}; + +//---------------------------------------------------------------------------------------------------- Tests +/// Create an `Env` in a temporarily directory. +/// The directory is automatically removed after the `TempDir` is dropped. +/// +/// TODO: changing this to `-> impl Env` causes lifetime errors... +#[allow(clippy::missing_panics_doc)] +pub fn tmp_env() -> (ConcreteEnv, tempfile::TempDir) { + let tempdir = tempfile::tempdir().unwrap(); + let config = Config::low_power(Some(tempdir.path().into())); + let env = ConcreteEnv::open(config).unwrap(); + + (env, tempdir) +} + +/// Same as [`tmp_env`] but uses all system threads. +#[allow(clippy::missing_panics_doc)] +pub fn tmp_env_all_threads() -> (ConcreteEnv, tempfile::TempDir) { + let tempdir = tempfile::tempdir().unwrap(); + let config = Config::fast(Some(tempdir.path().into())); + let env = ConcreteEnv::open(config).unwrap(); + + (env, tempdir) +} diff --git a/benches/criterion/cuprate-database/src/lib.rs b/benches/criterion/cuprate-database/src/lib.rs new file mode 100644 index 000000000..569aacbbc --- /dev/null +++ b/benches/criterion/cuprate-database/src/lib.rs @@ -0,0 +1,83 @@ +//! TODO + +//---------------------------------------------------------------------------------------------------- Lints +// Forbid lints. +// Our code, and code generated (e.g macros) cannot overrule these. +#![forbid( + // `unsafe` is allowed but it _must_ be + // commented with `SAFETY: reason`. + clippy::undocumented_unsafe_blocks, + + // Never. + unused_unsafe, + redundant_semicolons, + unused_allocation, + coherence_leak_check, + while_true, + clippy::missing_docs_in_private_items, + + // Maybe can be put into `#[deny]`. + unconditional_recursion, + for_loops_over_fallibles, + unused_braces, + unused_doc_comments, + unused_labels, + keyword_idents, + non_ascii_idents, + variant_size_differences, + single_use_lifetimes, + + // Probably can be put into `#[deny]`. + future_incompatible, + let_underscore, + break_with_label_and_loop, + duplicate_macro_attributes, + exported_private_dependencies, + large_assignments, + overlapping_range_endpoints, + semicolon_in_expressions_from_macros, + noop_method_call, + unreachable_pub, +)] +// Deny lints. +// Some of these are `#[allow]`'ed on a per-case basis. +#![deny( + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, + clippy::cargo, + unused_mut, + missing_docs, + deprecated, + unused_comparisons, + nonstandard_style +)] +#![allow(unreachable_code, unused_variables, dead_code, unused_imports)] // TODO: remove +#![allow( + // FIXME: this lint affects crates outside of + // `database/` for some reason, allow for now. + clippy::cargo_common_metadata, + + // FIXME: adding `#[must_use]` onto everything + // might just be more annoying than useful... + // although it is sometimes nice. + clippy::must_use_candidate, + + // TODO: should be removed after all `todo!()`'s are gone. + clippy::diverging_sub_expression, + + clippy::module_name_repetitions, + clippy::module_inception, + clippy::redundant_pub_crate, + clippy::option_if_let_else, +)] +// Allow some lints when running in debug mode. +#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] + +mod helper; +pub use helper::{tmp_env, tmp_env_all_threads}; From 1cada331f030f5840fc8b2e9437d09be8b87c2ec Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Mon, 21 Oct 2024 20:12:22 -0400 Subject: [PATCH 16/33] fix `cargo b` failing this `cfg()` existing makes a regular workspace `cargo b` fail --- benches/benchmark/bin/src/main.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/benches/benchmark/bin/src/main.rs b/benches/benchmark/bin/src/main.rs index 2e6272094..02c480a08 100644 --- a/benches/benchmark/bin/src/main.rs +++ b/benches/benchmark/bin/src/main.rs @@ -20,6 +20,13 @@ use cfg_if::cfg_if; /// 1. Copy + paste a `cfg_if` block /// 2. Change it to your benchmark's feature flag /// 3. Change it to your benchmark's type +#[allow( + clippy::allow_attributes, + unused_variables, + unused_mut, + unreachable_code, + reason = "clippy does not account for all cfg()s" +)] fn main() { log::init_logger(); @@ -27,7 +34,8 @@ fn main() { cfg_if! { if #[cfg(not(any(feature = "example")))] { - compile_error!("No feature specified. Use `--features $BENCHMARK_FEATURE` when building."); + println!("No feature specified. Use `--features $BENCHMARK_FEATURE` when building."); + return; } } From da3417f71f5a9f23cc36039ae6acc089cc6acc7f Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Mon, 21 Oct 2024 20:11:59 -0400 Subject: [PATCH 17/33] changes --- Cargo.lock | 12 + Cargo.toml | 1 + benches/benchmark/bin/src/main.rs | 10 +- benches/criterion/cuprate-database/Cargo.toml | 28 +- .../criterion/cuprate-database/benches/db.rs | 710 ++++++++++-------- .../benches/db_multi_thread.rs | 584 -------------- .../criterion/cuprate-database/benches/env.rs | 94 +-- .../benches/env_multi_thread.rs | 162 ---- .../cuprate-database/benches/main.rs | 80 +- .../cuprate-database/benches/service.rs | 25 - .../cuprate-database/benches/storable.rs | 36 +- .../cuprate-database/src/constants.rs | 17 + .../criterion/cuprate-database/src/helper.rs | 30 - benches/criterion/cuprate-database/src/lib.rs | 84 +-- .../criterion/cuprate-database/src/tmp_env.rs | 87 +++ 15 files changed, 608 insertions(+), 1352 deletions(-) delete mode 100644 benches/criterion/cuprate-database/benches/db_multi_thread.rs delete mode 100644 benches/criterion/cuprate-database/benches/env_multi_thread.rs delete mode 100644 benches/criterion/cuprate-database/benches/service.rs create mode 100644 benches/criterion/cuprate-database/src/constants.rs delete mode 100644 benches/criterion/cuprate-database/src/helper.rs create mode 100644 benches/criterion/cuprate-database/src/tmp_env.rs diff --git a/Cargo.lock b/Cargo.lock index 655d12748..20cb0ed17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,6 +739,18 @@ dependencies = [ name = "cuprate-constants" version = "0.1.0" +[[package]] +name = "cuprate-criterion-database" +version = "0.0.0" +dependencies = [ + "criterion", + "cuprate-blockchain", + "cuprate-database", + "cuprate-helper", + "function_name", + "tempfile", +] + [[package]] name = "cuprate-criterion-example" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index d756b08a1..f09794fcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "benches/benchmark/example", "benches/criterion/example", "benches/criterion/cuprate-json-rpc", + "benches/criterion/cuprate-database", # Consensus "consensus", "consensus/fast-sync", diff --git a/benches/benchmark/bin/src/main.rs b/benches/benchmark/bin/src/main.rs index 2e6272094..02c480a08 100644 --- a/benches/benchmark/bin/src/main.rs +++ b/benches/benchmark/bin/src/main.rs @@ -20,6 +20,13 @@ use cfg_if::cfg_if; /// 1. Copy + paste a `cfg_if` block /// 2. Change it to your benchmark's feature flag /// 3. Change it to your benchmark's type +#[allow( + clippy::allow_attributes, + unused_variables, + unused_mut, + unreachable_code, + reason = "clippy does not account for all cfg()s" +)] fn main() { log::init_logger(); @@ -27,7 +34,8 @@ fn main() { cfg_if! { if #[cfg(not(any(feature = "example")))] { - compile_error!("No feature specified. Use `--features $BENCHMARK_FEATURE` when building."); + println!("No feature specified. Use `--features $BENCHMARK_FEATURE` when building."); + return; } } diff --git a/benches/criterion/cuprate-database/Cargo.toml b/benches/criterion/cuprate-database/Cargo.toml index c37d265b9..562977188 100644 --- a/benches/criterion/cuprate-database/Cargo.toml +++ b/benches/criterion/cuprate-database/Cargo.toml @@ -1,24 +1,30 @@ [package] -name = "cuprate-database-benchmark" +name = "cuprate-criterion-database" version = "0.0.0" edition = "2021" -description = "Benchmarking for Cuprate's database abstraction" +description = "Criterion benchmarking for cuprate-database" license = "MIT" authors = ["hinto-janai"] -repository = "https://github.com/Cuprate/cuprate/tree/main/database/benchmark" +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-database" keywords = ["cuprate", "database", "benchmark"] [features] -default = [] +default = ["heed"] +heed = ["cuprate-database/heed", "cuprate-blockchain/heed"] +redb = ["cuprate-database/redb", "cuprate-blockchain/redb"] [dependencies] -criterion = { workspace = true } -cuprate-database = { path = "../", features = ["default"] } -cuprate-helper = { path = "../../helper", features = ["fs", "thread"] } -# cuprate-types = { path = "../../types", features = ["service"] } -function_name = { version = "0.3.0" } -tempfile = { version = "3.10.0" } +criterion = { workspace = true } +cuprate-database = { path = "../../../storage/database" } +cuprate-blockchain = { path = "../../../storage/blockchain" } +cuprate-helper = { path = "../../../helper", features = ["fs", "thread"] } + +function_name = { workspace = true } +tempfile = { workspace = true} [[bench]] name = "main" -harness = false \ No newline at end of file +harness = false + +[lints] +workspace = true \ No newline at end of file diff --git a/benches/criterion/cuprate-database/benches/db.rs b/benches/criterion/cuprate-database/benches/db.rs index 686d60acf..78a6b58d9 100644 --- a/benches/criterion/cuprate-database/benches/db.rs +++ b/benches/criterion/cuprate-database/benches/db.rs @@ -1,343 +1,559 @@ -//! `trait Database{Ro,Rw,Iter}` benchmarks. +//! Database operations. +//! +//! This module tests the functions from: +//! - [`cuprate_database::DatabaseRo`] +//! - [`cuprate_database::DatabaseRw`] +//! - [`cuprate_database::DatabaseIter`] +//! +//! There are 2 flavors of (read-only) benchmarks: +//! - Single threaded that uses [`TmpEnv::new`] +//! - Multi threaded that uses [`TmpEnv::new_all_threads`] +//! +//! They benchmark the same thing, just with different +//! amount of threads. This is done as 1 "inner" function +//! that contains the logic and 2 others to setup the [`TmpEnv`] +//! with different amounts of threads, e.g.: +//! - [`ro_get`] (inner benchmark logic) +//! - [`ro_get_single_thread`] (just calls `ro_get` with 1 thread) +//! - [`ro_get_multi_thread`] (just calls `ro_get` with all threads) +//! +//! Writes are single-threaded, so they only use [`TmpEnv::new`]. + +#![expect(clippy::significant_drop_tightening, clippy::needless_pass_by_value)] + +// TODO +use cuprate_helper as _; +use tempfile as _; use std::time::Instant; -//---------------------------------------------------------------------------------------------------- Import use criterion::{black_box, criterion_group, criterion_main, Criterion}; - use function_name::named; -use cuprate_database::{ +use cuprate_blockchain::{ tables::Outputs, types::{Output, PreRctOutputId}, - DatabaseIter, DatabaseRo, DatabaseRw, Env, EnvInner, TxRw, }; +use cuprate_database::{DatabaseIter, DatabaseRo, DatabaseRw, Env, EnvInner}; -use cuprate_database_benchmark::tmp_env; +use cuprate_criterion_database::{TmpEnv, KEY, VALUE}; -//---------------------------------------------------------------------------------------------------- Criterion criterion_group! { benches, - ro_get, - ro_len, - ro_first, - ro_last, - ro_is_empty, - ro_contains, - rw_get, - rw_len, - rw_first, - rw_last, - rw_is_empty, - rw_contains, + + // `DatabaseRo` + ro_get_single_thread, + ro_get_multi_thread, + ro_len_single_thread, + ro_len_multi_thread, + ro_first_single_thread, + ro_first_multi_thread, + ro_last_single_thread, + ro_last_multi_thread, + ro_is_empty_single_thread, + ro_is_empty_multi_thread, + ro_contains_single_thread, + ro_contains_multi_thread, + + // `DatabaseRo` with a `TxRw` + rw_get_single_thread, + rw_get_multi_thread, + rw_len_single_thread, + rw_len_multi_thread, + rw_first_single_thread, + rw_first_multi_thread, + rw_last_single_thread, + rw_last_multi_thread, + rw_is_empty_single_thread, + rw_is_empty_multi_thread, + rw_contains_single_thread, + rw_contains_multi_thread, + + // `DatabaseIter` + get_range_single_thread, + get_range_multi_thread, + iter_single_thread, + iter_multi_thread, + keys_single_thread, + keys_multi_thread, + values_single_thread, + values_multi_thread, + + // `DatabaseRw` put, delete, pop_first, pop_last, - get_range, - iter, - keys, - values, + take, } -criterion_main!(benches); - -//---------------------------------------------------------------------------------------------------- Constants -/// The (1st) key. -const KEY: PreRctOutputId = PreRctOutputId { - amount: 1, - amount_index: 123, -}; -/// The expected value. -const VALUE: Output = Output { - key: [35; 32], - height: 45_761_798, - output_flags: 0, - tx_idx: 2_353_487, -}; +criterion_main!(benches); -//---------------------------------------------------------------------------------------------------- DatabaseRo +//---------------------------------------------------------------------------------------------------- DatabaseRo::get // Read-only table operations. // This uses `TxRw + TablesMut` briefly to insert values, then // uses `TxRo + Tables` for the actual operation. // // See further below for using `TxRw + TablesMut` on the same operations. -/// [`DatabaseRo::get`] -#[named] -fn ro_get(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - +fn ro_get(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { let _value: Output = table.get(black_box(&KEY)).unwrap(); }); }); } -/// [`DatabaseRo::len`] +/// [`DatabaseRo::get`] (single-thread). #[named] -fn ro_len(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn ro_get_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + ro_get(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +/// [`DatabaseRo::get`] (multi-thread). +#[named] +fn ro_get_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + ro_get(env, function_name!(), c); +} +//---------------------------------------------------------------------------------------------------- DatabaseRo::len +fn ro_len(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { black_box(table.len()).unwrap(); }); }); } -/// [`DatabaseRo::first`] +/// [`DatabaseRo::len`] (single-thread). #[named] -fn ro_first(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn ro_len_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + ro_len(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +/// [`DatabaseRo::len`] (multi-thread). +#[named] +fn ro_len_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + ro_len(env, function_name!(), c); +} +//---------------------------------------------------------------------------------------------------- DatabaseRo::first +fn ro_first(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { let (_, _): (PreRctOutputId, Output) = black_box(table.first()).unwrap(); }); }); } -/// [`DatabaseRo::last`] +/// [`DatabaseRo::first`] (single-thread). #[named] -fn ro_last(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn ro_first_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + ro_first(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +/// [`DatabaseRo::first`] (multi-thread). +#[named] +fn ro_first_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + ro_first(env, function_name!(), c); +} +//---------------------------------------------------------------------------------------------------- DatabaseRo::last +/// [`DatabaseRo::last`] +fn ro_last(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { let (_, _): (PreRctOutputId, Output) = black_box(table.last()).unwrap(); }); }); } -/// [`DatabaseRo::is_empty`] +/// [`DatabaseRo::last`] (single-thread). #[named] -fn ro_is_empty(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn ro_last_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + ro_last(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +/// [`DatabaseRo::last`] (multi-thread). +#[named] +fn ro_last_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + ro_last(env, function_name!(), c); +} +//---------------------------------------------------------------------------------------------------- DatabaseRo::is_empty +/// [`DatabaseRo::is_empty`] +fn ro_is_empty(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { black_box(table.is_empty()).unwrap(); }); }); } -/// [`DatabaseRo::contains`] +/// [`DatabaseRo::is_empty`] (single-thread). #[named] -fn ro_contains(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn ro_is_empty_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + ro_is_empty(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +/// [`DatabaseRo::is_empty`] (multi-thread). +#[named] +fn ro_is_empty_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + ro_is_empty(env, function_name!(), c); +} +//---------------------------------------------------------------------------------------------------- DatabaseRo::contains +/// [`DatabaseRo::contains`] +fn ro_contains(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { table.contains(black_box(&KEY)).unwrap(); }); }); } -//---------------------------------------------------------------------------------------------------- DatabaseRo (using TxRw) -// These are the same benchmarks as above, but it uses a -// `TxRw` and a `TablesMut` instead to ensure our read/write tables -// using read operations perform the same as normal read-only tables. +/// [`DatabaseRo::contains`] (single-thread). +#[named] +fn ro_contains_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + ro_contains(env, function_name!(), c); +} -/// [`DatabaseRo::get`] +/// [`DatabaseRo::contains`] (multi-thread). #[named] -fn rw_get(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn ro_contains_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + ro_contains(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +//---------------------------------------------------------------------------------------------------- DatabaseRw::get +// These are the same benchmarks as above, but it uses a +// `TxRw` and a `TablesMut` instead to ensure our read/write tables +// using read operations perform the same as normal read-only tables. +fn rw_get(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { let _value: Output = table.get(black_box(&KEY)).unwrap(); }); }); } -/// [`DatabaseRo::len`] +/// [`DatabaseRw::get`] (single-thread). #[named] -fn rw_len(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn rw_get_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + rw_get(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +/// [`DatabaseRw::get`] (multi-thread). +#[named] +fn rw_get_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + rw_get(env, function_name!(), c); +} +//---------------------------------------------------------------------------------------------------- DatabaseRw::len +fn rw_len(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { black_box(table.len()).unwrap(); }); }); } -/// [`DatabaseRo::first`] +/// [`DatabaseRw::len`] (single-thread). #[named] -fn rw_first(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn rw_len_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + rw_len(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +/// [`DatabaseRw::len`] (multi-thread). +#[named] +fn rw_len_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + rw_len(env, function_name!(), c); +} +//---------------------------------------------------------------------------------------------------- DatabaseRw::first +fn rw_first(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { let (_, _): (PreRctOutputId, Output) = black_box(table.first()).unwrap(); }); }); } -/// [`DatabaseRo::last`] +/// [`DatabaseRw::first`] (single-thread). #[named] -fn rw_last(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn rw_first_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + rw_first(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +/// [`DatabaseRw::first`] (multi-thread). +#[named] +fn rw_first_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + rw_first(env, function_name!(), c); +} +//---------------------------------------------------------------------------------------------------- DatabaseRw::last +fn rw_last(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { let (_, _): (PreRctOutputId, Output) = black_box(table.last()).unwrap(); }); }); } -/// [`DatabaseRo::is_empty`] +/// [`DatabaseRw::last`] (single-thread). #[named] -fn rw_is_empty(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn rw_last_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + rw_last(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +/// [`DatabaseRw::last`] (multi-thread). +#[named] +fn rw_last_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + rw_last(env, function_name!(), c); +} +//---------------------------------------------------------------------------------------------------- DatabaseRw::is_empty +fn rw_is_empty(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { black_box(table.is_empty()).unwrap(); }); }); } -/// [`DatabaseRo::contains`] +/// [`DatabaseRw::is_empty`] (single-thread). #[named] -fn rw_contains(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); +fn rw_is_empty_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + rw_is_empty(env, function_name!(), c); +} - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); +/// [`DatabaseRw::is_empty`] (multi-thread). +#[named] +fn rw_is_empty_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + rw_is_empty(env, function_name!(), c); +} +//---------------------------------------------------------------------------------------------------- DatabaseRw::contains +fn rw_contains(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(function_name!(), |b| { + c.bench_function(name, |b| { b.iter(|| { table.contains(black_box(&KEY)).unwrap(); }); }); } -//---------------------------------------------------------------------------------------------------- DatabaseRw +/// [`DatabaseRw::contains`] (single-thread). +#[named] +fn rw_contains_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); + rw_contains(env, function_name!(), c); +} + +/// [`DatabaseRw::contains`] (multi-thread). +#[named] +fn rw_contains_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value(); + rw_contains(env, function_name!(), c); +} + +//---------------------------------------------------------------------------------------------------- DatabaseIter::get_range +fn get_range(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(name, |b| { + b.iter(|| { + let range = table.get_range(black_box(..)).unwrap(); + for result in range { + let _: Output = black_box(result.unwrap()); + } + }); + }); +} + +/// [`DatabaseIter::get_range`] (single-thread). +#[named] +fn get_range_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value_100(); + get_range(env, function_name!(), c); +} + +/// [`DatabaseIter::get_range`] (multi-thread). +#[named] +fn get_range_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value_100(); + get_range(env, function_name!(), c); +} + +//---------------------------------------------------------------------------------------------------- DatabaseIter::iter +fn iter(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(name, |b| { + b.iter(|| { + let iter = black_box(table.iter()).unwrap(); + for result in iter { + let _: (PreRctOutputId, Output) = black_box(result.unwrap()); + } + }); + }); +} + +/// [`DatabaseIter::iter`] (single-thread). +#[named] +fn iter_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value_100(); + iter(env, function_name!(), c); +} + +/// [`DatabaseIter::iter`] (multi-thread). +#[named] +fn iter_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value_100(); + iter(env, function_name!(), c); +} + +//---------------------------------------------------------------------------------------------------- DatabaseIter::keys +/// [`DatabaseRo::keys`] +fn keys(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(name, |b| { + b.iter(|| { + let keys = black_box(table.keys()).unwrap(); + for result in keys { + let _: PreRctOutputId = black_box(result.unwrap()); + } + }); + }); +} + +/// [`DatabaseIter::keys`] (single-thread). +#[named] +fn keys_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value_100(); + keys(env, function_name!(), c); +} + +/// [`DatabaseIter::iter`] (multi-thread). +#[named] +fn keys_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value_100(); + keys(env, function_name!(), c); +} + +//---------------------------------------------------------------------------------------------------- DatabaseIter::values +/// [`DatabaseRo::values`] +fn values(env: TmpEnv, name: &'static str, c: &mut Criterion) { + let env_inner = env.env.env_inner(); + let tx_ro = env_inner.tx_ro().unwrap(); + let table = env_inner.open_db_ro::(&tx_ro).unwrap(); + + c.bench_function(name, |b| { + b.iter(|| { + let values = black_box(table.values()).unwrap(); + for result in values { + let _: Output = black_box(result.unwrap()); + } + }); + }); +} + +/// [`DatabaseIter::values`] (single-thread). +#[named] +fn values_single_thread(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value_100(); + values(env, function_name!(), c); +} + +/// [`DatabaseIter::iter`] (multi-thread). +#[named] +fn values_multi_thread(c: &mut Criterion) { + let env = TmpEnv::new_all_threads().with_key_value_100(); + values(env, function_name!(), c); +} + +//---------------------------------------------------------------------------------------------------- DatabaseRw::put /// [`DatabaseRw::put`] #[named] fn put(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); + let env = TmpEnv::new(); + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); @@ -351,11 +567,12 @@ fn put(c: &mut Criterion) { }); } +//---------------------------------------------------------------------------------------------------- DatabaseRw::delete /// [`DatabaseRw::delete`] #[named] fn delete(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); + let env = TmpEnv::new(); + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); @@ -380,11 +597,12 @@ fn delete(c: &mut Criterion) { }); } +//---------------------------------------------------------------------------------------------------- DatabaseRw::pop_first /// [`DatabaseRw::pop_first`] #[named] fn pop_first(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); + let env = TmpEnv::new(); + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); @@ -409,11 +627,12 @@ fn pop_first(c: &mut Criterion) { }); } +//---------------------------------------------------------------------------------------------------- DatabaseRw::pop_last /// [`DatabaseRw::pop_last`] #[named] fn pop_last(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); + let env = TmpEnv::new(); + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); @@ -438,146 +657,19 @@ fn pop_last(c: &mut Criterion) { }); } -// TODO: waiting on PR 102 -// /// [`DatabaseRw::take`] -// #[named] -// fn take(c: &mut Criterion) { -// let (env, _tempdir) = tmp_env(); -// let env_inner = env.env_inner(); -// let tx_rw = env_inner.tx_rw().unwrap(); -// let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - -// let mut key = KEY; -// for _ in 0..100 { -// table.put(&key, &VALUE).unwrap(); -// key.amount += 1; -// } - -// c.bench_function(function_name!(), |b| { -// b.iter(|| { -// table.put(&KEY, &VALUE).unwrap(); -// let value: Output = black_box(table.take(&black_box(KEY)).unwrap()); -// }); -// }); -// } - -//---------------------------------------------------------------------------------------------------- DatabaseIter -/// [`DatabaseRo::get_range`] -#[named] -fn get_range(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - for _ in 0..100 { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - drop(table); - TxRw::commit(tx_rw).unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let range = table.get_range(black_box(..)).unwrap(); - for result in range { - let _value: Output = black_box(result.unwrap()); - } - }); - }); -} - -/// [`DatabaseRo::iter`] +//---------------------------------------------------------------------------------------------------- DatabaseRw::take +/// [`DatabaseRw::take`] #[named] -fn iter(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); +fn take(c: &mut Criterion) { + let env = TmpEnv::new_all_threads(); + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - let mut key = KEY; - for _ in 0..100 { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - drop(table); - TxRw::commit(tx_rw).unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(function_name!(), |b| { b.iter(|| { - let iter = black_box(table.iter()).unwrap(); - for result in iter { - let _: (PreRctOutputId, Output) = black_box(result.unwrap()); - } - }); - }); -} - -/// [`DatabaseRo::keys`] -#[named] -fn keys(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - for _ in 0..100 { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - drop(table); - TxRw::commit(tx_rw).unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let keys = black_box(table.keys()).unwrap(); - for result in keys { - let _: PreRctOutputId = black_box(result.unwrap()); - } - }); - }); -} - -/// [`DatabaseRo::values`] -#[named] -fn values(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - for _ in 0..100 { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - drop(table); - TxRw::commit(tx_rw).unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let values = black_box(table.values()).unwrap(); - for result in values { - let _: Output = black_box(result.unwrap()); - } + table.put(&KEY, &VALUE).unwrap(); + let _: Output = black_box(table.take(&black_box(KEY)).unwrap()); }); }); } diff --git a/benches/criterion/cuprate-database/benches/db_multi_thread.rs b/benches/criterion/cuprate-database/benches/db_multi_thread.rs deleted file mode 100644 index 808992947..000000000 --- a/benches/criterion/cuprate-database/benches/db_multi_thread.rs +++ /dev/null @@ -1,584 +0,0 @@ -//! Same as `db.rs` but multi-threaded. -//! TODO: create multi-threaded benchmarks - -use std::time::Instant; - -//---------------------------------------------------------------------------------------------------- Import -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -use function_name::named; - -use cuprate_database::{ - tables::Outputs, - types::{Output, PreRctOutputId}, - DatabaseIter, DatabaseRo, DatabaseRw, Env, EnvInner, TxRw, -}; - -use cuprate_database_benchmark::tmp_env_all_threads; - -//---------------------------------------------------------------------------------------------------- Criterion -criterion_group! { - benches, - ro_get, - ro_len, - ro_first, - ro_last, - ro_is_empty, - ro_contains, - rw_get, - rw_len, - rw_first, - rw_last, - rw_is_empty, - rw_contains, - put, - delete, - pop_first, - pop_last, - get_range, - iter, - keys, - values, -} -criterion_main!(benches); - -//---------------------------------------------------------------------------------------------------- Constants -/// The (1st) key. -const KEY: PreRctOutputId = PreRctOutputId { - amount: 1, - amount_index: 123, -}; - -/// The expected value. -const VALUE: Output = Output { - key: [35; 32], - height: 45_761_798, - output_flags: 0, - tx_idx: 2_353_487, -}; - -//---------------------------------------------------------------------------------------------------- DatabaseRo -// Read-only table operations. -// This uses `TxRw + TablesMut` briefly to insert values, then -// uses `TxRo + Tables` for the actual operation. -// -// See further below for using `TxRw + TablesMut` on the same operations. - -/// [`DatabaseRo::get`] -#[named] -fn ro_get(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let _value: Output = table.get(black_box(&KEY)).unwrap(); - }); - }); -} - -/// [`DatabaseRo::len`] -#[named] -fn ro_len(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - black_box(table.len()).unwrap(); - }); - }); -} - -/// [`DatabaseRo::first`] -#[named] -fn ro_first(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let (_, _): (PreRctOutputId, Output) = black_box(table.first()).unwrap(); - }); - }); -} - -/// [`DatabaseRo::last`] -#[named] -fn ro_last(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let (_, _): (PreRctOutputId, Output) = black_box(table.last()).unwrap(); - }); - }); -} - -/// [`DatabaseRo::is_empty`] -#[named] -fn ro_is_empty(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - black_box(table.is_empty()).unwrap(); - }); - }); -} - -/// [`DatabaseRo::contains`] -#[named] -fn ro_contains(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - table.contains(black_box(&KEY)).unwrap(); - }); - }); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRo (using TxRw) -// These are the same benchmarks as above, but it uses a -// `TxRw` and a `TablesMut` instead to ensure our read/write tables -// using read operations perform the same as normal read-only tables. - -/// [`DatabaseRo::get`] -#[named] -fn rw_get(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_rw = env_inner.tx_rw().unwrap(); - let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let _value: Output = table.get(black_box(&KEY)).unwrap(); - }); - }); -} - -/// [`DatabaseRo::len`] -#[named] -fn rw_len(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_rw = env_inner.tx_rw().unwrap(); - let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - black_box(table.len()).unwrap(); - }); - }); -} - -/// [`DatabaseRo::first`] -#[named] -fn rw_first(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_rw = env_inner.tx_rw().unwrap(); - let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let (_, _): (PreRctOutputId, Output) = black_box(table.first()).unwrap(); - }); - }); -} - -/// [`DatabaseRo::last`] -#[named] -fn rw_last(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_rw = env_inner.tx_rw().unwrap(); - let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let (_, _): (PreRctOutputId, Output) = black_box(table.last()).unwrap(); - }); - }); -} - -/// [`DatabaseRo::is_empty`] -#[named] -fn rw_is_empty(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_rw = env_inner.tx_rw().unwrap(); - let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - black_box(table.is_empty()).unwrap(); - }); - }); -} - -/// [`DatabaseRo::contains`] -#[named] -fn rw_contains(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - table.put(&KEY, &VALUE).unwrap(); - drop(table); - tx_rw.commit().unwrap(); - - let tx_rw = env_inner.tx_rw().unwrap(); - let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - table.contains(black_box(&KEY)).unwrap(); - }); - }); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRw -/// [`DatabaseRw::put`] -#[named] -fn put(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - - c.bench_function(function_name!(), |b| { - b.iter(|| { - table.put(black_box(&key), black_box(&VALUE)).unwrap(); - key.amount += 1; - }); - }); -} - -/// [`DatabaseRw::delete`] -#[named] -fn delete(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - - c.bench_function(function_name!(), |b| { - b.iter_custom(|iters| { - for _ in 0..iters { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - key = KEY; - - let start = Instant::now(); - for _ in 0..iters { - table.delete(&key).unwrap(); - key.amount += 1; - } - start.elapsed() - }); - }); -} - -/// [`DatabaseRw::pop_first`] -#[named] -fn pop_first(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - - c.bench_function(function_name!(), |b| { - b.iter_custom(|iters| { - for _ in 0..iters { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - key = KEY; - - let start = Instant::now(); - for _ in 0..iters { - table.pop_first().unwrap(); - key.amount += 1; - } - start.elapsed() - }); - }); -} - -/// [`DatabaseRw::pop_last`] -#[named] -fn pop_last(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - - c.bench_function(function_name!(), |b| { - b.iter_custom(|iters| { - for _ in 0..iters { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - key = KEY; - - let start = Instant::now(); - for _ in 0..iters { - table.pop_last().unwrap(); - key.amount += 1; - } - start.elapsed() - }); - }); -} - -// TODO: waiting on PR 102 -// /// [`DatabaseRw::take`] -// #[named] -// fn take(c: &mut Criterion) { -// let (env, _tempdir) = tmp_env_all_threads(); -// let env_inner = env.env_inner(); -// let tx_rw = env_inner.tx_rw().unwrap(); -// let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - -// let mut key = KEY; -// for _ in 0..100 { -// table.put(&key, &VALUE).unwrap(); -// key.amount += 1; -// } - -// c.bench_function(function_name!(), |b| { -// b.iter(|| { -// table.put(&KEY, &VALUE).unwrap(); -// let value: Output = black_box(table.take(&black_box(KEY)).unwrap()); -// }); -// }); -// } - -//---------------------------------------------------------------------------------------------------- DatabaseIter -/// [`DatabaseRo::get_range`] -#[named] -fn get_range(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - for _ in 0..100 { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - drop(table); - TxRw::commit(tx_rw).unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let range = table.get_range(black_box(..)).unwrap(); - for result in range { - let _value: Output = black_box(result.unwrap()); - } - }); - }); -} - -/// [`DatabaseRo::iter`] -#[named] -fn iter(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - for _ in 0..100 { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - drop(table); - TxRw::commit(tx_rw).unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let iter = black_box(table.iter()).unwrap(); - for result in iter { - let _: (PreRctOutputId, Output) = black_box(result.unwrap()); - } - }); - }); -} - -/// [`DatabaseRo::keys`] -#[named] -fn keys(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - for _ in 0..100 { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - drop(table); - TxRw::commit(tx_rw).unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let keys = black_box(table.keys()).unwrap(); - for result in keys { - let _: PreRctOutputId = black_box(result.unwrap()); - } - }); - }); -} - -/// [`DatabaseRo::values`] -#[named] -fn values(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); - - let mut key = KEY; - for _ in 0..100 { - table.put(&key, &VALUE).unwrap(); - key.amount += 1; - } - - drop(table); - TxRw::commit(tx_rw).unwrap(); - - let tx_ro = env_inner.tx_ro().unwrap(); - let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let values = black_box(table.values()).unwrap(); - for result in values { - let _: Output = black_box(result.unwrap()); - } - }); - }); -} diff --git a/benches/criterion/cuprate-database/benches/env.rs b/benches/criterion/cuprate-database/benches/env.rs index 4c0f6d49a..cc7ffae57 100644 --- a/benches/criterion/cuprate-database/benches/env.rs +++ b/benches/criterion/cuprate-database/benches/env.rs @@ -1,40 +1,45 @@ -//! `trait {Env, EnvInner, TxR{o,w}, Tables[Mut]}` benchmarks. +//! Same as `env.rs` but multi-threaded. +//! TODO: create multi-threaded benchmarks -//---------------------------------------------------------------------------------------------------- Import -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +#![expect(clippy::significant_drop_tightening)] + +// TODO +use cuprate_helper as _; +use tempfile as _; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; use function_name::named; +use cuprate_blockchain::tables::{OpenTables, Outputs}; use cuprate_database::{ - config::Config, - resize::{page_size, ResizeAlgorithm}, - tables::Outputs, + config::ConfigBuilder, + resize::{ResizeAlgorithm, PAGE_SIZE}, ConcreteEnv, Env, EnvInner, TxRo, TxRw, }; -use cuprate_database_benchmark::tmp_env; +use cuprate_criterion_database::TmpEnv; -//---------------------------------------------------------------------------------------------------- Criterion criterion_group! { benches, open, env_inner, tx_ro, tx_rw, - open_tables, - open_tables_mut, + open_db_ro, + open_db_rw, resize, current_map_size, disk_size_bytes, } criterion_main!(benches); -//---------------------------------------------------------------------------------------------------- Env benchmarks /// [`Env::open`]. #[named] fn open(c: &mut Criterion) { let tempdir = tempfile::tempdir().unwrap(); - let config = Config::low_power(Some(tempdir.path().into())); + let config = ConfigBuilder::new(tempdir.path().to_path_buf().into()) + .low_power() + .build(); c.bench_function(function_name!(), |b| { b.iter_with_large_drop(|| { @@ -46,20 +51,20 @@ fn open(c: &mut Criterion) { /// [`Env::env_inner`]. #[named] fn env_inner(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); + let env = TmpEnv::new(); c.bench_function(function_name!(), |b| { b.iter(|| { - black_box(env.env_inner()); + drop(black_box(env.env.env_inner())); }); }); } -/// Create and commit read-only transactions. +/// [`EnvInner::tx_ro`]. #[named] fn tx_ro(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); + let env = TmpEnv::new(); + let env_inner = env.env.env_inner(); c.bench_function(function_name!(), |b| { b.iter(|| { @@ -69,11 +74,11 @@ fn tx_ro(c: &mut Criterion) { }); } -/// Create and commit read/write transactions. +/// [`EnvInner::tx_rw`]. #[named] fn tx_rw(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); + let env = TmpEnv::new(); + let env_inner = env.env.env_inner(); c.bench_function(function_name!(), |b| { b.iter(|| { @@ -83,79 +88,78 @@ fn tx_rw(c: &mut Criterion) { }); } -/// Open all database tables in read-only mode. +/// [`EnvInner::open_db_ro`]. #[named] -fn open_tables(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); +fn open_db_ro(c: &mut Criterion) { + // `with_key_value()` creates the `Outputs` + // table so the `open_db_ro` below doesn't panic. + let env = TmpEnv::new().with_key_value(); + let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); c.bench_function(function_name!(), |b| { b.iter(|| { - black_box(env_inner.open_db_ro::(&tx_ro)).unwrap(); - // env_inner.open_tables(&tx_ro).unwrap(); - // TODO: waiting on PR 102 + env_inner.open_db_ro::(&tx_ro).unwrap(); }); }); } -/// Open all database tables in read/write mode. +/// [`EnvInner::open_db_rw`]. #[named] -fn open_tables_mut(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); - let env_inner = env.env_inner(); +fn open_db_rw(c: &mut Criterion) { + let env = TmpEnv::new(); + let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); c.bench_function(function_name!(), |b| { b.iter(|| { - black_box(env_inner.open_db_rw::(&tx_rw)).unwrap(); - // env_inner.open_tables_mut(&mut tx_rw).unwrap(); - // TODO: waiting on PR 102 + env_inner.open_db_rw::(&tx_rw).unwrap(); + env_inner.open_tables_mut(&tx_rw).unwrap(); }); }); } -/// `Env` memory map resizes. +/// [`Env::resize`]. #[named] fn resize(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); + let env = TmpEnv::new(); - // Resize by the OS page size. - let page_size = page_size(); + // Resize env.by the OS page size. + let resize = Some(ResizeAlgorithm::FixedBytes(*PAGE_SIZE)); c.bench_function(function_name!(), |b| { b.iter(|| { // This test is only valid for `Env`'s that need to resize manually. if ConcreteEnv::MANUAL_RESIZE { - env.resize_map(black_box(Some(ResizeAlgorithm::FixedBytes(page_size)))); + env.env.resize_map(resize); } }); }); } -/// Access current memory map size of the database. +/// [`Env::current_map_size`]. #[named] fn current_map_size(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); + let env = TmpEnv::new(); c.bench_function(function_name!(), |b| { b.iter(|| { // This test is only valid for `Env`'s that need to resize manually. if ConcreteEnv::MANUAL_RESIZE { - black_box(env.current_map_size()); + black_box(env.env.current_map_size()); } }); }); } -/// Access on-disk size of the database. +/// [`Env::disk_size_bytes`]. #[named] fn disk_size_bytes(c: &mut Criterion) { - let (env, _tempdir) = tmp_env(); + let env = TmpEnv::new(); c.bench_function(function_name!(), |b| { b.iter(|| { - black_box(env.disk_size_bytes()).unwrap(); + black_box(env.env.disk_size_bytes()).unwrap(); }); }); } diff --git a/benches/criterion/cuprate-database/benches/env_multi_thread.rs b/benches/criterion/cuprate-database/benches/env_multi_thread.rs deleted file mode 100644 index ec796c89f..000000000 --- a/benches/criterion/cuprate-database/benches/env_multi_thread.rs +++ /dev/null @@ -1,162 +0,0 @@ -//! Same as `env.rs` but multi-threaded. -//! TODO: create multi-threaded benchmarks - -//---------------------------------------------------------------------------------------------------- Import -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -use function_name::named; - -use cuprate_database::{ - config::Config, - resize::{page_size, ResizeAlgorithm}, - tables::Outputs, - ConcreteEnv, Env, EnvInner, TxRo, TxRw, -}; - -use cuprate_database_benchmark::tmp_env_all_threads; - -//---------------------------------------------------------------------------------------------------- Criterion -criterion_group! { - benches, - open, - env_inner, - tx_ro, - tx_rw, - open_tables, - open_tables_mut, - resize, - current_map_size, - disk_size_bytes, -} -criterion_main!(benches); - -//---------------------------------------------------------------------------------------------------- Env benchmarks -/// [`Env::open`]. -#[named] -fn open(c: &mut Criterion) { - let tempdir = tempfile::tempdir().unwrap(); - let config = Config::low_power(Some(tempdir.path().into())); - - c.bench_function(function_name!(), |b| { - b.iter_with_large_drop(|| { - ConcreteEnv::open(config.clone()).unwrap(); - }); - }); -} - -/// [`Env::env_inner`]. -#[named] -fn env_inner(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - black_box(env.env_inner()); - }); - }); -} - -/// Create and commit read-only transactions. -#[named] -fn tx_ro(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let tx_ro = black_box(env_inner.tx_ro()).unwrap(); - TxRo::commit(black_box(tx_ro)).unwrap(); - }); - }); -} - -/// Create and commit read/write transactions. -#[named] -fn tx_rw(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - let tx_rw = black_box(env_inner.tx_rw()).unwrap(); - TxRw::commit(black_box(tx_rw)).unwrap(); - }); - }); -} - -/// Open all database tables in read-only mode. -#[named] -fn open_tables(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_ro = env_inner.tx_ro().unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - black_box(env_inner.open_db_ro::(&tx_ro)).unwrap(); - // env_inner.open_tables(&tx_ro).unwrap(); - // TODO: waiting on PR 102 - }); - }); -} - -/// Open all database tables in read/write mode. -#[named] -fn open_tables_mut(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - let env_inner = env.env_inner(); - let tx_rw = env_inner.tx_rw().unwrap(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - black_box(env_inner.open_db_rw::(&tx_rw)).unwrap(); - // env_inner.open_tables_mut(&mut tx_rw).unwrap(); - // TODO: waiting on PR 102 - }); - }); -} - -/// `Env` memory map resizes. -#[named] -fn resize(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - - // Resize by the OS page size. - let page_size = page_size(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - // This test is only valid for `Env`'s that need to resize manually. - if ConcreteEnv::MANUAL_RESIZE { - env.resize_map(black_box(Some(ResizeAlgorithm::FixedBytes(page_size)))); - } - }); - }); -} - -/// Access current memory map size of the database. -#[named] -fn current_map_size(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - // This test is only valid for `Env`'s that need to resize manually. - if ConcreteEnv::MANUAL_RESIZE { - black_box(env.current_map_size()); - } - }); - }); -} - -/// Access on-disk size of the database. -#[named] -fn disk_size_bytes(c: &mut Criterion) { - let (env, _tempdir) = tmp_env_all_threads(); - - c.bench_function(function_name!(), |b| { - b.iter(|| { - black_box(env.disk_size_bytes()).unwrap(); - }); - }); -} diff --git a/benches/criterion/cuprate-database/benches/main.rs b/benches/criterion/cuprate-database/benches/main.rs index 74865f2a2..cd9f36048 100644 --- a/benches/criterion/cuprate-database/benches/main.rs +++ b/benches/criterion/cuprate-database/benches/main.rs @@ -1,84 +1,6 @@ //! TODO -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - clippy::missing_docs_in_private_items, - - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_doc_comments, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, - unreachable_pub, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style -)] -#![allow(unreachable_code, unused_variables, dead_code, unused_imports)] // TODO: remove -#![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - - // TODO: should be removed after all `todo!()`'s are gone. - clippy::diverging_sub_expression, - - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, - clippy::significant_drop_tightening, -)] -// Allow some lints when running in debug mode. -#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] +#![expect(unused_crate_dependencies)] mod db; mod env; diff --git a/benches/criterion/cuprate-database/benches/service.rs b/benches/criterion/cuprate-database/benches/service.rs deleted file mode 100644 index 9c4a74fff..000000000 --- a/benches/criterion/cuprate-database/benches/service.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! `cuprate_database::service` benchmarks. - -//---------------------------------------------------------------------------------------------------- Import -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -use function_name::named; - -use cuprate_database::{ - config::Config, - resize::{page_size, ResizeAlgorithm}, - tables::Outputs, - ConcreteEnv, Env, EnvInner, TxRo, TxRw, -}; - -use cuprate_database_benchmark::tmp_env_all_threads; - -//---------------------------------------------------------------------------------------------------- Criterion -criterion_group! { - benches, -} -criterion_main!(benches); - -//---------------------------------------------------------------------------------------------------- Benchmarks - -// TODO diff --git a/benches/criterion/cuprate-database/benches/storable.rs b/benches/criterion/cuprate-database/benches/storable.rs index b427e2656..9962b3e59 100644 --- a/benches/criterion/cuprate-database/benches/storable.rs +++ b/benches/criterion/cuprate-database/benches/storable.rs @@ -1,16 +1,14 @@ -//! `trait Storable` benchmarks. +//! [`Storable`] benchmarks. -//---------------------------------------------------------------------------------------------------- Import use criterion::{black_box, criterion_group, criterion_main, Criterion}; use function_name::named; -use cuprate_database::{ - types::{Output, PreRctOutputId}, - Storable, -}; +use cuprate_blockchain::types::{Output, PreRctOutputId}; +use cuprate_database::Storable; + +use cuprate_criterion_database::{KEY, VALUE}; -//---------------------------------------------------------------------------------------------------- Criterion criterion_group! { benches, pre_rct_output_id_as_bytes, @@ -20,28 +18,12 @@ criterion_group! { } criterion_main!(benches); -//---------------------------------------------------------------------------------------------------- Constants -/// 16 bytes. -const PRE_RCT_OUTPUT_ID: PreRctOutputId = PreRctOutputId { - amount: 1, - amount_index: 123, -}; - -/// 48 bytes. -const OUTPUT: Output = Output { - key: [35; 32], - height: 45_761_798, - output_flags: 0, - tx_idx: 2_353_487, -}; - -//---------------------------------------------------------------------------------------------------- Storable benchmarks /// [`PreRctOutputId`] cast as bytes. #[named] fn pre_rct_output_id_as_bytes(c: &mut Criterion) { c.bench_function(function_name!(), |b| { b.iter(|| { - black_box(Storable::as_bytes(black_box(&PRE_RCT_OUTPUT_ID))); + black_box(Storable::as_bytes(black_box(&KEY))); }); }); } @@ -49,7 +31,7 @@ fn pre_rct_output_id_as_bytes(c: &mut Criterion) { /// [`PreRctOutputId`] cast from bytes. #[named] fn pre_rct_output_id_from_bytes(c: &mut Criterion) { - let bytes = Storable::as_bytes(&PRE_RCT_OUTPUT_ID); + let bytes = Storable::as_bytes(&KEY); c.bench_function(function_name!(), |b| { b.iter(|| { @@ -63,7 +45,7 @@ fn pre_rct_output_id_from_bytes(c: &mut Criterion) { fn output_as_bytes(c: &mut Criterion) { c.bench_function(function_name!(), |b| { b.iter(|| { - black_box(Storable::as_bytes(black_box(&OUTPUT))); + black_box(Storable::as_bytes(black_box(&VALUE))); }); }); } @@ -71,7 +53,7 @@ fn output_as_bytes(c: &mut Criterion) { /// [`Output`] cast from bytes. #[named] fn output_from_bytes(c: &mut Criterion) { - let bytes = Storable::as_bytes(&OUTPUT); + let bytes = Storable::as_bytes(&VALUE); c.bench_function(function_name!(), |b| { b.iter(|| { diff --git a/benches/criterion/cuprate-database/src/constants.rs b/benches/criterion/cuprate-database/src/constants.rs new file mode 100644 index 000000000..bcb191930 --- /dev/null +++ b/benches/criterion/cuprate-database/src/constants.rs @@ -0,0 +1,17 @@ +//! General constants. + +use cuprate_blockchain::types::{Output, OutputFlags, PreRctOutputId}; + +/// The (1st) key. +pub const KEY: PreRctOutputId = PreRctOutputId { + amount: 1, + amount_index: 123, +}; + +/// The expected value. +pub const VALUE: Output = Output { + key: [35; 32], + height: 45_761_798, + output_flags: OutputFlags::empty(), + tx_idx: 2_353_487, +}; diff --git a/benches/criterion/cuprate-database/src/helper.rs b/benches/criterion/cuprate-database/src/helper.rs deleted file mode 100644 index ecf2fd199..000000000 --- a/benches/criterion/cuprate-database/src/helper.rs +++ /dev/null @@ -1,30 +0,0 @@ -//! TODO - -// TODO: set-up env based on config - -//---------------------------------------------------------------------------------------------------- Import -use cuprate_database::{config::Config, ConcreteEnv, Env}; - -//---------------------------------------------------------------------------------------------------- Tests -/// Create an `Env` in a temporarily directory. -/// The directory is automatically removed after the `TempDir` is dropped. -/// -/// TODO: changing this to `-> impl Env` causes lifetime errors... -#[allow(clippy::missing_panics_doc)] -pub fn tmp_env() -> (ConcreteEnv, tempfile::TempDir) { - let tempdir = tempfile::tempdir().unwrap(); - let config = Config::low_power(Some(tempdir.path().into())); - let env = ConcreteEnv::open(config).unwrap(); - - (env, tempdir) -} - -/// Same as [`tmp_env`] but uses all system threads. -#[allow(clippy::missing_panics_doc)] -pub fn tmp_env_all_threads() -> (ConcreteEnv, tempfile::TempDir) { - let tempdir = tempfile::tempdir().unwrap(); - let config = Config::fast(Some(tempdir.path().into())); - let env = ConcreteEnv::open(config).unwrap(); - - (env, tempdir) -} diff --git a/benches/criterion/cuprate-database/src/lib.rs b/benches/criterion/cuprate-database/src/lib.rs index 569aacbbc..8f75f4ef1 100644 --- a/benches/criterion/cuprate-database/src/lib.rs +++ b/benches/criterion/cuprate-database/src/lib.rs @@ -1,83 +1,9 @@ //! TODO -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, +#![allow(unused_crate_dependencies, reason = "used in benchmarks")] - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - clippy::missing_docs_in_private_items, +mod constants; +mod tmp_env; - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_doc_comments, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, - unreachable_pub, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style -)] -#![allow(unreachable_code, unused_variables, dead_code, unused_imports)] // TODO: remove -#![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - - // TODO: should be removed after all `todo!()`'s are gone. - clippy::diverging_sub_expression, - - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, -)] -// Allow some lints when running in debug mode. -#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] - -mod helper; -pub use helper::{tmp_env, tmp_env_all_threads}; +pub use constants::{KEY, VALUE}; +pub use tmp_env::TmpEnv; diff --git a/benches/criterion/cuprate-database/src/tmp_env.rs b/benches/criterion/cuprate-database/src/tmp_env.rs new file mode 100644 index 000000000..d5d263ed0 --- /dev/null +++ b/benches/criterion/cuprate-database/src/tmp_env.rs @@ -0,0 +1,87 @@ +//! An [`Env`] inside a [`TempDir`]. + +use tempfile::TempDir; + +use cuprate_blockchain::tables::Outputs; +use cuprate_database::{config::ConfigBuilder, ConcreteEnv, DatabaseRw, Env, EnvInner, TxRw}; + +use crate::constants::{KEY, VALUE}; + +/// A temporary in-memory [`Env`]. +/// +/// This is a [`ConcreteEnv`] that uses [`TempDir`] as the +/// backing file location - this is an in-memory file on Linux. +pub struct TmpEnv { + pub env: ConcreteEnv, + pub tempdir: TempDir, +} + +impl Default for TmpEnv { + fn default() -> Self { + Self::new() + } +} + +impl TmpEnv { + /// Create an `Env` in a temporary directory. + /// + /// The directory is automatically removed after the [`TempDir`] is dropped. + #[expect(clippy::missing_panics_doc)] + pub fn new() -> Self { + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().to_path_buf().into(); + let config = ConfigBuilder::new(path).low_power().build(); + let env = ConcreteEnv::open(config).unwrap(); + + Self { env, tempdir } + } + + /// Same as [`Self::new`] but uses all system threads for the [`Env`]. + #[expect(clippy::missing_panics_doc)] + pub fn new_all_threads() -> Self { + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().to_path_buf().into(); + let config = ConfigBuilder::new(path).fast().build(); + let env = ConcreteEnv::open(config).unwrap(); + + Self { env, tempdir } + } + + /// Inserts [`KEY`] and [`VALUE`] inside the [`Outputs`] table. + #[must_use] + pub fn with_key_value(self) -> Self { + let env_inner = self.env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + table.put(&KEY, &VALUE).unwrap(); + drop(table); + tx_rw.commit().unwrap(); + + drop(env_inner); + self + } + + /// Inserts [`VALUE`] inside the [`Outputs`] table 100 times. + /// + /// The key is an incrementing [`KEY`], i.e. the keys are + /// `KEY + {0..99}`, each one has [`VALUE`] as the value. + #[must_use] + pub fn with_key_value_100(self) -> Self { + let env_inner = self.env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); + + let mut key = KEY; + for _ in 0..100 { + table.put(&key, &VALUE).unwrap(); + key.amount += 1; + } + + drop(table); + tx_rw.commit().unwrap(); + + drop(env_inner); + self + } +} From 0aa5f77c5e30cdb7ffe5265af17c6085f5627cf4 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Tue, 22 Oct 2024 17:08:06 -0400 Subject: [PATCH 18/33] fix cargo --- Cargo.lock | 1 + benches/criterion/cuprate-database/Cargo.toml | 17 ++++++++++++++--- .../criterion/cuprate-database/benches/db.rs | 1 + .../criterion/cuprate-database/benches/env.rs | 1 + .../cuprate-database/benches/storable.rs | 2 ++ 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20cb0ed17..a371015cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -748,6 +748,7 @@ dependencies = [ "cuprate-database", "cuprate-helper", "function_name", + "rand", "tempfile", ] diff --git a/benches/criterion/cuprate-database/Cargo.toml b/benches/criterion/cuprate-database/Cargo.toml index 562977188..345448b85 100644 --- a/benches/criterion/cuprate-database/Cargo.toml +++ b/benches/criterion/cuprate-database/Cargo.toml @@ -14,13 +14,24 @@ heed = ["cuprate-database/heed", "cuprate-blockchain/heed"] redb = ["cuprate-database/redb", "cuprate-blockchain/redb"] [dependencies] +# FIXME: +# Some crates/features that are unused here but +# needed in other crates are pulled in, see: +# - +# +# Remove: +# - rand +# - cuprate-blockchain/asynch +# - cuprate-blockchain/tx + criterion = { workspace = true } cuprate-database = { path = "../../../storage/database" } -cuprate-blockchain = { path = "../../../storage/blockchain" } -cuprate-helper = { path = "../../../helper", features = ["fs", "thread"] } +cuprate-blockchain = { path = "../../../storage/blockchain", features = ["service"] } +cuprate-helper = { path = "../../../helper", features = ["asynch", "fs", "thread", "tx"] } function_name = { workspace = true } -tempfile = { workspace = true} +tempfile = { workspace = true } +rand = { workspace = true, features = ["std", "std_rng"] } [[bench]] name = "main" diff --git a/benches/criterion/cuprate-database/benches/db.rs b/benches/criterion/cuprate-database/benches/db.rs index 78a6b58d9..45934ff98 100644 --- a/benches/criterion/cuprate-database/benches/db.rs +++ b/benches/criterion/cuprate-database/benches/db.rs @@ -19,6 +19,7 @@ //! //! Writes are single-threaded, so they only use [`TmpEnv::new`]. +#![allow(unused_crate_dependencies, unused_attributes)] #![expect(clippy::significant_drop_tightening, clippy::needless_pass_by_value)] // TODO diff --git a/benches/criterion/cuprate-database/benches/env.rs b/benches/criterion/cuprate-database/benches/env.rs index cc7ffae57..75b8793c2 100644 --- a/benches/criterion/cuprate-database/benches/env.rs +++ b/benches/criterion/cuprate-database/benches/env.rs @@ -1,6 +1,7 @@ //! Same as `env.rs` but multi-threaded. //! TODO: create multi-threaded benchmarks +#![allow(unused_crate_dependencies, unused_attributes)] #![expect(clippy::significant_drop_tightening)] // TODO diff --git a/benches/criterion/cuprate-database/benches/storable.rs b/benches/criterion/cuprate-database/benches/storable.rs index 9962b3e59..60d3263ce 100644 --- a/benches/criterion/cuprate-database/benches/storable.rs +++ b/benches/criterion/cuprate-database/benches/storable.rs @@ -1,5 +1,7 @@ //! [`Storable`] benchmarks. +#![allow(unused_crate_dependencies, unused_attributes)] + use criterion::{black_box, criterion_group, criterion_main, Criterion}; use function_name::named; From 8ae208b9f08e95ff48244b132c58d63230a8ef3a Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Tue, 22 Oct 2024 20:36:14 -0400 Subject: [PATCH 19/33] fix benches --- .../criterion/cuprate-database/benches/db.rs | 392 ++++-------------- .../criterion/cuprate-database/benches/env.rs | 67 +-- .../cuprate-database/benches/main.rs | 2 - .../cuprate-database/benches/storable.rs | 5 +- benches/criterion/cuprate-database/src/lib.rs | 2 - .../criterion/cuprate-database/src/tmp_env.rs | 23 +- 6 files changed, 137 insertions(+), 354 deletions(-) diff --git a/benches/criterion/cuprate-database/benches/db.rs b/benches/criterion/cuprate-database/benches/db.rs index 45934ff98..e0d4945d1 100644 --- a/benches/criterion/cuprate-database/benches/db.rs +++ b/benches/criterion/cuprate-database/benches/db.rs @@ -4,27 +4,9 @@ //! - [`cuprate_database::DatabaseRo`] //! - [`cuprate_database::DatabaseRw`] //! - [`cuprate_database::DatabaseIter`] -//! -//! There are 2 flavors of (read-only) benchmarks: -//! - Single threaded that uses [`TmpEnv::new`] -//! - Multi threaded that uses [`TmpEnv::new_all_threads`] -//! -//! They benchmark the same thing, just with different -//! amount of threads. This is done as 1 "inner" function -//! that contains the logic and 2 others to setup the [`TmpEnv`] -//! with different amounts of threads, e.g.: -//! - [`ro_get`] (inner benchmark logic) -//! - [`ro_get_single_thread`] (just calls `ro_get` with 1 thread) -//! - [`ro_get_multi_thread`] (just calls `ro_get` with all threads) -//! -//! Writes are single-threaded, so they only use [`TmpEnv::new`]. #![allow(unused_crate_dependencies, unused_attributes)] -#![expect(clippy::significant_drop_tightening, clippy::needless_pass_by_value)] - -// TODO -use cuprate_helper as _; -use tempfile as _; +#![expect(clippy::significant_drop_tightening)] use std::time::Instant; @@ -40,45 +22,30 @@ use cuprate_database::{DatabaseIter, DatabaseRo, DatabaseRw, Env, EnvInner}; use cuprate_criterion_database::{TmpEnv, KEY, VALUE}; criterion_group! { - benches, - + name = benches; + config = Criterion::default(); + targets = // `DatabaseRo` - ro_get_single_thread, - ro_get_multi_thread, - ro_len_single_thread, - ro_len_multi_thread, - ro_first_single_thread, - ro_first_multi_thread, - ro_last_single_thread, - ro_last_multi_thread, - ro_is_empty_single_thread, - ro_is_empty_multi_thread, - ro_contains_single_thread, - ro_contains_multi_thread, + ro_get, + ro_len, + ro_first, + ro_last, + ro_is_empty, + ro_contains, // `DatabaseRo` with a `TxRw` - rw_get_single_thread, - rw_get_multi_thread, - rw_len_single_thread, - rw_len_multi_thread, - rw_first_single_thread, - rw_first_multi_thread, - rw_last_single_thread, - rw_last_multi_thread, - rw_is_empty_single_thread, - rw_is_empty_multi_thread, - rw_contains_single_thread, - rw_contains_multi_thread, + rw_get, + rw_len, + rw_first, + rw_last, + rw_is_empty, + rw_contains, // `DatabaseIter` - get_range_single_thread, - get_range_multi_thread, - iter_single_thread, - iter_multi_thread, - keys_single_thread, - keys_multi_thread, - values_single_thread, - values_multi_thread, + get_range, + iter, + keys, + values, // `DatabaseRw` put, @@ -87,353 +54,210 @@ criterion_group! { pop_last, take, } - criterion_main!(benches); -//---------------------------------------------------------------------------------------------------- DatabaseRo::get +//---------------------------------------------------------------------------------------------------- DatabaseRo // Read-only table operations. // This uses `TxRw + TablesMut` briefly to insert values, then // uses `TxRo + Tables` for the actual operation. // // See further below for using `TxRw + TablesMut` on the same operations. -fn ro_get(env: TmpEnv, name: &'static str, c: &mut Criterion) { +/// [`DatabaseRo::get`] +#[named] +fn ro_get(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { - let _value: Output = table.get(black_box(&KEY)).unwrap(); + let _: Output = table.get(black_box(&KEY)).unwrap(); }); }); } -/// [`DatabaseRo::get`] (single-thread). +/// [`DatabaseRo::len`] #[named] -fn ro_get_single_thread(c: &mut Criterion) { +fn ro_len(c: &mut Criterion) { let env = TmpEnv::new().with_key_value(); - ro_get(env, function_name!(), c); -} - -/// [`DatabaseRo::get`] (multi-thread). -#[named] -fn ro_get_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - ro_get(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRo::len -fn ro_len(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { black_box(table.len()).unwrap(); }); }); } -/// [`DatabaseRo::len`] (single-thread). +/// [`DatabaseRo::first`] #[named] -fn ro_len_single_thread(c: &mut Criterion) { +fn ro_first(c: &mut Criterion) { let env = TmpEnv::new().with_key_value(); - ro_len(env, function_name!(), c); -} - -/// [`DatabaseRo::len`] (multi-thread). -#[named] -fn ro_len_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - ro_len(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRo::first -fn ro_first(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { let (_, _): (PreRctOutputId, Output) = black_box(table.first()).unwrap(); }); }); } -/// [`DatabaseRo::first`] (single-thread). +/// [`DatabaseRo::last`] #[named] -fn ro_first_single_thread(c: &mut Criterion) { +fn ro_last(c: &mut Criterion) { let env = TmpEnv::new().with_key_value(); - ro_first(env, function_name!(), c); -} - -/// [`DatabaseRo::first`] (multi-thread). -#[named] -fn ro_first_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - ro_first(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRo::last -/// [`DatabaseRo::last`] -fn ro_last(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { let (_, _): (PreRctOutputId, Output) = black_box(table.last()).unwrap(); }); }); } -/// [`DatabaseRo::last`] (single-thread). +/// [`DatabaseRo::is_empty`] #[named] -fn ro_last_single_thread(c: &mut Criterion) { +fn ro_is_empty(c: &mut Criterion) { let env = TmpEnv::new().with_key_value(); - ro_last(env, function_name!(), c); -} - -/// [`DatabaseRo::last`] (multi-thread). -#[named] -fn ro_last_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - ro_last(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRo::is_empty -/// [`DatabaseRo::is_empty`] -fn ro_is_empty(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { black_box(table.is_empty()).unwrap(); }); }); } -/// [`DatabaseRo::is_empty`] (single-thread). +/// [`DatabaseRo::contains`] #[named] -fn ro_is_empty_single_thread(c: &mut Criterion) { +fn ro_contains(c: &mut Criterion) { let env = TmpEnv::new().with_key_value(); - ro_is_empty(env, function_name!(), c); -} - -/// [`DatabaseRo::is_empty`] (multi-thread). -#[named] -fn ro_is_empty_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - ro_is_empty(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRo::contains -/// [`DatabaseRo::contains`] -fn ro_contains(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { table.contains(black_box(&KEY)).unwrap(); }); }); } -/// [`DatabaseRo::contains`] (single-thread). -#[named] -fn ro_contains_single_thread(c: &mut Criterion) { - let env = TmpEnv::new().with_key_value(); - ro_contains(env, function_name!(), c); -} - -/// [`DatabaseRo::contains`] (multi-thread). -#[named] -fn ro_contains_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - ro_contains(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRw::get +//---------------------------------------------------------------------------------------------------- DatabaseRo (TxRw) // These are the same benchmarks as above, but it uses a // `TxRw` and a `TablesMut` instead to ensure our read/write tables // using read operations perform the same as normal read-only tables. -fn rw_get(env: TmpEnv, name: &'static str, c: &mut Criterion) { +/// [`DatabaseRw::get`] +#[named] +fn rw_get(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value(); let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { - let _value: Output = table.get(black_box(&KEY)).unwrap(); + let _: Output = table.get(black_box(&KEY)).unwrap(); }); }); } -/// [`DatabaseRw::get`] (single-thread). +/// [`DatabaseRw::len`] #[named] -fn rw_get_single_thread(c: &mut Criterion) { +fn rw_len(c: &mut Criterion) { let env = TmpEnv::new().with_key_value(); - rw_get(env, function_name!(), c); -} - -/// [`DatabaseRw::get`] (multi-thread). -#[named] -fn rw_get_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - rw_get(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRw::len -fn rw_len(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { black_box(table.len()).unwrap(); }); }); } -/// [`DatabaseRw::len`] (single-thread). +/// [`DatabaseRw::first`] #[named] -fn rw_len_single_thread(c: &mut Criterion) { +fn rw_first(c: &mut Criterion) { let env = TmpEnv::new().with_key_value(); - rw_len(env, function_name!(), c); -} - -/// [`DatabaseRw::len`] (multi-thread). -#[named] -fn rw_len_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - rw_len(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRw::first -fn rw_first(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { let (_, _): (PreRctOutputId, Output) = black_box(table.first()).unwrap(); }); }); } -/// [`DatabaseRw::first`] (single-thread). +/// [`DatabaseRw::last`] #[named] -fn rw_first_single_thread(c: &mut Criterion) { +fn rw_last(c: &mut Criterion) { let env = TmpEnv::new().with_key_value(); - rw_first(env, function_name!(), c); -} - -/// [`DatabaseRw::first`] (multi-thread). -#[named] -fn rw_first_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - rw_first(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRw::last -fn rw_last(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { let (_, _): (PreRctOutputId, Output) = black_box(table.last()).unwrap(); }); }); } -/// [`DatabaseRw::last`] (single-thread). +/// [`DatabaseRw::is_empty`] #[named] -fn rw_last_single_thread(c: &mut Criterion) { +fn rw_is_empty(c: &mut Criterion) { let env = TmpEnv::new().with_key_value(); - rw_last(env, function_name!(), c); -} - -/// [`DatabaseRw::last`] (multi-thread). -#[named] -fn rw_last_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - rw_last(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRw::is_empty -fn rw_is_empty(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { black_box(table.is_empty()).unwrap(); }); }); } -/// [`DatabaseRw::is_empty`] (single-thread). +/// [`DatabaseRw::contains`] #[named] -fn rw_is_empty_single_thread(c: &mut Criterion) { +fn rw_contains(c: &mut Criterion) { let env = TmpEnv::new().with_key_value(); - rw_is_empty(env, function_name!(), c); -} - -/// [`DatabaseRw::is_empty`] (multi-thread). -#[named] -fn rw_is_empty_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - rw_is_empty(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRw::contains -fn rw_contains(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let table = env_inner.open_db_rw::(&tx_rw).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { table.contains(black_box(&KEY)).unwrap(); }); }); } -/// [`DatabaseRw::contains`] (single-thread). +//---------------------------------------------------------------------------------------------------- DatabaseIter +/// [`DatabaseIter::get_range`] #[named] -fn rw_contains_single_thread(c: &mut Criterion) { - let env = TmpEnv::new().with_key_value(); - rw_contains(env, function_name!(), c); -} - -/// [`DatabaseRw::contains`] (multi-thread). -#[named] -fn rw_contains_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value(); - rw_contains(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseIter::get_range -fn get_range(env: TmpEnv, name: &'static str, c: &mut Criterion) { +fn get_range(c: &mut Criterion) { + let env = TmpEnv::new().with_key_value_100(); let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { let range = table.get_range(black_box(..)).unwrap(); for result in range { @@ -443,27 +267,15 @@ fn get_range(env: TmpEnv, name: &'static str, c: &mut Criterion) { }); } -/// [`DatabaseIter::get_range`] (single-thread). +/// [`DatabaseIter::iter`] #[named] -fn get_range_single_thread(c: &mut Criterion) { +fn iter(c: &mut Criterion) { let env = TmpEnv::new().with_key_value_100(); - get_range(env, function_name!(), c); -} - -/// [`DatabaseIter::get_range`] (multi-thread). -#[named] -fn get_range_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value_100(); - get_range(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseIter::iter -fn iter(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { let iter = black_box(table.iter()).unwrap(); for result in iter { @@ -473,28 +285,15 @@ fn iter(env: TmpEnv, name: &'static str, c: &mut Criterion) { }); } -/// [`DatabaseIter::iter`] (single-thread). +/// [`DatabaseIter::keys`] #[named] -fn iter_single_thread(c: &mut Criterion) { +fn keys(c: &mut Criterion) { let env = TmpEnv::new().with_key_value_100(); - iter(env, function_name!(), c); -} - -/// [`DatabaseIter::iter`] (multi-thread). -#[named] -fn iter_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value_100(); - iter(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseIter::keys -/// [`DatabaseRo::keys`] -fn keys(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { let keys = black_box(table.keys()).unwrap(); for result in keys { @@ -504,28 +303,15 @@ fn keys(env: TmpEnv, name: &'static str, c: &mut Criterion) { }); } -/// [`DatabaseIter::keys`] (single-thread). +/// [`DatabaseIter::values`] #[named] -fn keys_single_thread(c: &mut Criterion) { +fn values(c: &mut Criterion) { let env = TmpEnv::new().with_key_value_100(); - keys(env, function_name!(), c); -} - -/// [`DatabaseIter::iter`] (multi-thread). -#[named] -fn keys_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value_100(); - keys(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseIter::values -/// [`DatabaseRo::values`] -fn values(env: TmpEnv, name: &'static str, c: &mut Criterion) { let env_inner = env.env.env_inner(); let tx_ro = env_inner.tx_ro().unwrap(); let table = env_inner.open_db_ro::(&tx_ro).unwrap(); - c.bench_function(name, |b| { + c.bench_function(function_name!(), |b| { b.iter(|| { let values = black_box(table.values()).unwrap(); for result in values { @@ -535,21 +321,7 @@ fn values(env: TmpEnv, name: &'static str, c: &mut Criterion) { }); } -/// [`DatabaseIter::values`] (single-thread). -#[named] -fn values_single_thread(c: &mut Criterion) { - let env = TmpEnv::new().with_key_value_100(); - values(env, function_name!(), c); -} - -/// [`DatabaseIter::iter`] (multi-thread). -#[named] -fn values_multi_thread(c: &mut Criterion) { - let env = TmpEnv::new_all_threads().with_key_value_100(); - values(env, function_name!(), c); -} - -//---------------------------------------------------------------------------------------------------- DatabaseRw::put +//---------------------------------------------------------------------------------------------------- DatabaseRw /// [`DatabaseRw::put`] #[named] fn put(c: &mut Criterion) { @@ -568,7 +340,6 @@ fn put(c: &mut Criterion) { }); } -//---------------------------------------------------------------------------------------------------- DatabaseRw::delete /// [`DatabaseRw::delete`] #[named] fn delete(c: &mut Criterion) { @@ -598,7 +369,6 @@ fn delete(c: &mut Criterion) { }); } -//---------------------------------------------------------------------------------------------------- DatabaseRw::pop_first /// [`DatabaseRw::pop_first`] #[named] fn pop_first(c: &mut Criterion) { @@ -628,7 +398,6 @@ fn pop_first(c: &mut Criterion) { }); } -//---------------------------------------------------------------------------------------------------- DatabaseRw::pop_last /// [`DatabaseRw::pop_last`] #[named] fn pop_last(c: &mut Criterion) { @@ -658,11 +427,10 @@ fn pop_last(c: &mut Criterion) { }); } -//---------------------------------------------------------------------------------------------------- DatabaseRw::take /// [`DatabaseRw::take`] #[named] fn take(c: &mut Criterion) { - let env = TmpEnv::new_all_threads(); + let env = TmpEnv::new(); let env_inner = env.env.env_inner(); let tx_rw = env_inner.tx_rw().unwrap(); let mut table = env_inner.open_db_rw::(&tx_rw).unwrap(); diff --git a/benches/criterion/cuprate-database/benches/env.rs b/benches/criterion/cuprate-database/benches/env.rs index 75b8793c2..e7213d061 100644 --- a/benches/criterion/cuprate-database/benches/env.rs +++ b/benches/criterion/cuprate-database/benches/env.rs @@ -1,19 +1,13 @@ -//! Same as `env.rs` but multi-threaded. -//! TODO: create multi-threaded benchmarks +//! [`Env`] benchmarks. #![allow(unused_crate_dependencies, unused_attributes)] #![expect(clippy::significant_drop_tightening)] -// TODO -use cuprate_helper as _; -use tempfile as _; - use criterion::{black_box, criterion_group, criterion_main, Criterion}; use function_name::named; -use cuprate_blockchain::tables::{OpenTables, Outputs}; +use cuprate_blockchain::tables::Outputs; use cuprate_database::{ - config::ConfigBuilder, resize::{ResizeAlgorithm, PAGE_SIZE}, ConcreteEnv, Env, EnvInner, TxRo, TxRw, }; @@ -21,33 +15,43 @@ use cuprate_database::{ use cuprate_criterion_database::TmpEnv; criterion_group! { - benches, - open, + name = benches; + config = Criterion::default(); + targets = + // open, env_inner, tx_ro, tx_rw, open_db_ro, open_db_rw, + create_db, resize, current_map_size, disk_size_bytes, } criterion_main!(benches); -/// [`Env::open`]. -#[named] -fn open(c: &mut Criterion) { - let tempdir = tempfile::tempdir().unwrap(); - let config = ConfigBuilder::new(tempdir.path().to_path_buf().into()) - .low_power() - .build(); - - c.bench_function(function_name!(), |b| { - b.iter_with_large_drop(|| { - ConcreteEnv::open(config.clone()).unwrap(); - }); - }); -} +// FIXME: This function is hard to time due to: +// - heed errors +// - "too many open files" errors +// +// /// [`Env::open`]. +// #[named] +// fn open(c: &mut Criterion) { +// c.bench_function(function_name!(), |b| { +// b.iter_custom(|_| { +// let tempdir = tempfile::tempdir().unwrap(); +// let config = ConfigBuilder::new(tempdir.path().to_path_buf().into()).build(); +// +// let now = std::time::Instant::now(); +// ConcreteEnv::open(config).unwrap(); +// let elapsed = now.elapsed(); +// +// tempdir.close().unwrap(); +// elapsed +// }); +// }); +// } /// [`Env::env_inner`]. #[named] @@ -115,7 +119,20 @@ fn open_db_rw(c: &mut Criterion) { c.bench_function(function_name!(), |b| { b.iter(|| { env_inner.open_db_rw::(&tx_rw).unwrap(); - env_inner.open_tables_mut(&tx_rw).unwrap(); + }); + }); +} + +/// [`EnvInner::create_db`]. +#[named] +fn create_db(c: &mut Criterion) { + let env = TmpEnv::new(); + let env_inner = env.env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter(|| { + env_inner.create_db::(&tx_rw).unwrap(); }); }); } diff --git a/benches/criterion/cuprate-database/benches/main.rs b/benches/criterion/cuprate-database/benches/main.rs index cd9f36048..0dba9693c 100644 --- a/benches/criterion/cuprate-database/benches/main.rs +++ b/benches/criterion/cuprate-database/benches/main.rs @@ -1,5 +1,3 @@ -//! TODO - #![expect(unused_crate_dependencies)] mod db; diff --git a/benches/criterion/cuprate-database/benches/storable.rs b/benches/criterion/cuprate-database/benches/storable.rs index 60d3263ce..e2add7d33 100644 --- a/benches/criterion/cuprate-database/benches/storable.rs +++ b/benches/criterion/cuprate-database/benches/storable.rs @@ -3,7 +3,6 @@ #![allow(unused_crate_dependencies, unused_attributes)] use criterion::{black_box, criterion_group, criterion_main, Criterion}; - use function_name::named; use cuprate_blockchain::types::{Output, PreRctOutputId}; @@ -12,7 +11,9 @@ use cuprate_database::Storable; use cuprate_criterion_database::{KEY, VALUE}; criterion_group! { - benches, + name = benches; + config = Criterion::default(); + targets = pre_rct_output_id_as_bytes, pre_rct_output_id_from_bytes, output_as_bytes, diff --git a/benches/criterion/cuprate-database/src/lib.rs b/benches/criterion/cuprate-database/src/lib.rs index 8f75f4ef1..2c527dcd4 100644 --- a/benches/criterion/cuprate-database/src/lib.rs +++ b/benches/criterion/cuprate-database/src/lib.rs @@ -1,5 +1,3 @@ -//! TODO - #![allow(unused_crate_dependencies, reason = "used in benchmarks")] mod constants; diff --git a/benches/criterion/cuprate-database/src/tmp_env.rs b/benches/criterion/cuprate-database/src/tmp_env.rs index d5d263ed0..1a05e66f6 100644 --- a/benches/criterion/cuprate-database/src/tmp_env.rs +++ b/benches/criterion/cuprate-database/src/tmp_env.rs @@ -3,7 +3,9 @@ use tempfile::TempDir; use cuprate_blockchain::tables::Outputs; -use cuprate_database::{config::ConfigBuilder, ConcreteEnv, DatabaseRw, Env, EnvInner, TxRw}; +use cuprate_database::{ + config::ConfigBuilder, resize::PAGE_SIZE, ConcreteEnv, DatabaseRw, Env, EnvInner, TxRw, +}; use crate::constants::{KEY, VALUE}; @@ -33,16 +35,15 @@ impl TmpEnv { let config = ConfigBuilder::new(path).low_power().build(); let env = ConcreteEnv::open(config).unwrap(); - Self { env, tempdir } - } - - /// Same as [`Self::new`] but uses all system threads for the [`Env`]. - #[expect(clippy::missing_panics_doc)] - pub fn new_all_threads() -> Self { - let tempdir = tempfile::tempdir().unwrap(); - let path = tempdir.path().to_path_buf().into(); - let config = ConfigBuilder::new(path).fast().build(); - let env = ConcreteEnv::open(config).unwrap(); + // Resize to a very large map to prevent resize errors. + if ConcreteEnv::MANUAL_RESIZE { + // SAFETY: no write transactions exist yet. + unsafe { + env.env_inner() + .resize(PAGE_SIZE.get() * 1024 * 1024 * 1024) + .unwrap(); + } + } Self { env, tempdir } } From d89840e53fe18a8908880646d2b53acd985581e6 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 24 Oct 2024 19:31:37 -0400 Subject: [PATCH 20/33] add `benches/criterion/cuprate-cryptonight` --- Cargo.lock | 9 ++ Cargo.toml | 1 + .../criterion/cuprate-cryptonight/Cargo.toml | 22 +++++ .../cuprate-cryptonight/benches/hash.rs | 92 +++++++++++++++++++ .../cuprate-cryptonight/benches/main.rs | 8 ++ .../criterion/cuprate-cryptonight/src/lib.rs | 2 + 6 files changed, 134 insertions(+) create mode 100644 benches/criterion/cuprate-cryptonight/Cargo.toml create mode 100644 benches/criterion/cuprate-cryptonight/benches/hash.rs create mode 100644 benches/criterion/cuprate-cryptonight/benches/main.rs create mode 100644 benches/criterion/cuprate-cryptonight/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 655d12748..a78ba71b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,6 +739,15 @@ dependencies = [ name = "cuprate-constants" version = "0.1.0" +[[package]] +name = "cuprate-criterion-cryptonight" +version = "0.0.0" +dependencies = [ + "criterion", + "cuprate-cryptonight", + "function_name", +] + [[package]] name = "cuprate-criterion-example" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index d756b08a1..0f476353b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "benches/benchmark/example", "benches/criterion/example", "benches/criterion/cuprate-json-rpc", + "benches/criterion/cuprate-cryptonight", # Consensus "consensus", "consensus/fast-sync", diff --git a/benches/criterion/cuprate-cryptonight/Cargo.toml b/benches/criterion/cuprate-cryptonight/Cargo.toml new file mode 100644 index 000000000..dfa8b682f --- /dev/null +++ b/benches/criterion/cuprate-cryptonight/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "cuprate-criterion-cryptonight" +version = "0.0.0" +edition = "2021" +description = "Criterion benchmarking for cuprate-cryptonight" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-cryptonight" +keywords = ["cuprate", "cryptonight", "criterion", "benchmark"] + +[dependencies] +criterion = { workspace = true } +function_name = { workspace = true } + +cuprate-cryptonight = { path = "../../../cryptonight" } + +[[bench]] +name = "main" +harness = false + +[lints] +workspace = true \ No newline at end of file diff --git a/benches/criterion/cuprate-cryptonight/benches/hash.rs b/benches/criterion/cuprate-cryptonight/benches/hash.rs new file mode 100644 index 000000000..552c3fc66 --- /dev/null +++ b/benches/criterion/cuprate-cryptonight/benches/hash.rs @@ -0,0 +1,92 @@ +//! Benchmarks for [`Response`]. +#![allow(unused_attributes, unused_crate_dependencies, dropping_copy_types)] + +use std::time::Duration; + +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use function_name::named; + +use cuprate_cryptonight::{ + cryptonight_hash_r, cryptonight_hash_v0, cryptonight_hash_v1, cryptonight_hash_v2, +}; + +criterion_group! { + name = benches; + // Criterion suggests that higher measurement time is required for these hash functions. + config = Criterion::default().measurement_time(Duration::from_secs(8)); + targets = + r_8, r_64, r_512, r_4096, r_65536, + v0_8, v0_64, v0_512, v0_4096, v0_65536, + v1_8, v1_64, v1_512, v1_4096, v1_65536, + v2_8, v2_64, v2_512, v2_4096, v2_65536, +} + +criterion_main!(benches); + +/// Generate the benchmark functions for the cryptonight hash functions. +macro_rules! impl_hash_benchmark { + ($( + // The actual hash function. + $hash_fn:ident { + // Inside these braces: + // - The name of the benchmark function + // - The input(s) to the hash function for that benchmark function + $( + $fn_name:ident => ($($input:expr),* $(,)?) + ),* $(,)? + } + )*) => { + $( + $( + #[named] + fn $fn_name(c: &mut Criterion) { + c.bench_function(function_name!(), |b| { + b.iter(|| { + drop( + black_box( + $hash_fn( + $(black_box($input)),* + ) + ) + ); + }); + }); + } + )* + )* + }; +} + +impl_hash_benchmark! { + cryptonight_hash_r { + r_8 => (&[3; 8], 500_000), + r_64 => (&[3; 64], 500_000), + r_512 => (&[3; 512], 500_000), + r_4096 => (&[3; 4096], 500_000), + r_65536 => (&[3; 65536], 500_000), + } + + cryptonight_hash_v0 { + v0_8 => (&[3; 8]), + v0_64 => (&[3; 64]), + v0_512 => (&[3; 512]), + v0_4096 => (&[3; 4096]), + v0_65536 => (&[3; 65536]), + } + + cryptonight_hash_v1 { + v1_8 => (&[3; 8]), + v1_64 => (&[3; 64]), + v1_512 => (&[3; 512]), + v1_4096 => (&[3; 4096]), + v1_65536 => (&[3; 65536]), + } + + cryptonight_hash_v2 { + v2_8 => (&[3; 8]), + v2_64 => (&[3; 64]), + v2_512 => (&[3; 512]), + v2_4096 => (&[3; 4096]), + v2_65536 => (&[3; 65536]), + } +} diff --git a/benches/criterion/cuprate-cryptonight/benches/main.rs b/benches/criterion/cuprate-cryptonight/benches/main.rs new file mode 100644 index 000000000..79ce94001 --- /dev/null +++ b/benches/criterion/cuprate-cryptonight/benches/main.rs @@ -0,0 +1,8 @@ +//! Benchmarks for `cuprate-cryptonight`. +#![allow(unused_crate_dependencies)] + +mod hash; + +criterion::criterion_main! { + hash::benches +} diff --git a/benches/criterion/cuprate-cryptonight/src/lib.rs b/benches/criterion/cuprate-cryptonight/src/lib.rs new file mode 100644 index 000000000..d8b24aedf --- /dev/null +++ b/benches/criterion/cuprate-cryptonight/src/lib.rs @@ -0,0 +1,2 @@ +//! Benchmark lib for `cuprate-cryptonight`. +#![allow(unused_crate_dependencies, reason = "used in benchmarks")] From 615cef90ee79baa5a2ec18ff5c45ff0969ceb28f Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 24 Oct 2024 19:50:20 -0400 Subject: [PATCH 21/33] storage: remove todo `println!()` --- storage/database/src/backend/heed/env.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/storage/database/src/backend/heed/env.rs b/storage/database/src/backend/heed/env.rs index 568379e5d..d934a1b43 100644 --- a/storage/database/src/backend/heed/env.rs +++ b/storage/database/src/backend/heed/env.rs @@ -78,11 +78,12 @@ impl Drop for ConcreteEnv { // TODO: use tracing. // - let result = self.env.read().unwrap().clear_stale_readers(); - match result { - Ok(n) => println!("LMDB stale readers cleared: {n}"), - Err(e) => println!("LMDB stale reader clear error: {e:?}"), - } + drop(self.env.read().unwrap().clear_stale_readers()); + // let result = self.env.read().unwrap().clear_stale_readers(); + // match result { + // Ok(n) => println!("LMDB stale readers cleared: {n}"), + // Err(e) => println!("LMDB stale reader clear error: {e:?}"), + // } } } From a5ba54422f1a6dff89b79ed419c82f1d9b8c860d Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Fri, 25 Oct 2024 19:45:34 -0400 Subject: [PATCH 22/33] add `benches/criterion/cuprate-rpc-types` --- Cargo.lock | 13 ++++ Cargo.toml | 1 + .../criterion/cuprate-rpc-types/Cargo.toml | 26 ++++++++ .../cuprate-rpc-types/benches/epee.rs | 65 +++++++++++++++++++ .../cuprate-rpc-types/benches/main.rs | 10 +++ .../cuprate-rpc-types/benches/serde.rs | 63 ++++++++++++++++++ .../criterion/cuprate-rpc-types/src/lib.rs | 1 + 7 files changed, 179 insertions(+) create mode 100644 benches/criterion/cuprate-rpc-types/Cargo.toml create mode 100644 benches/criterion/cuprate-rpc-types/benches/epee.rs create mode 100644 benches/criterion/cuprate-rpc-types/benches/main.rs create mode 100644 benches/criterion/cuprate-rpc-types/benches/serde.rs create mode 100644 benches/criterion/cuprate-rpc-types/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 655d12748..0faf8f8bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -758,6 +758,19 @@ dependencies = [ "serde_json", ] +[[package]] +name = "cuprate-criterion-rpc-types" +version = "0.0.0" +dependencies = [ + "criterion", + "cuprate-epee-encoding", + "cuprate-rpc-types", + "cuprate-test-utils", + "function_name", + "paste", + "serde_json", +] + [[package]] name = "cuprate-cryptonight" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d756b08a1..b93d9c10c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "benches/benchmark/example", "benches/criterion/example", "benches/criterion/cuprate-json-rpc", + "benches/criterion/cuprate-rpc-types", # Consensus "consensus", "consensus/fast-sync", diff --git a/benches/criterion/cuprate-rpc-types/Cargo.toml b/benches/criterion/cuprate-rpc-types/Cargo.toml new file mode 100644 index 000000000..996ebcdba --- /dev/null +++ b/benches/criterion/cuprate-rpc-types/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "cuprate-criterion-rpc-types" +version = "0.0.0" +edition = "2021" +description = "Criterion benchmarking for cuprate-rpc-types" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-rpc-types" +keywords = ["cuprate", "rpc", "types", "criterion", "benchmark"] + +[dependencies] +cuprate-epee-encoding = { path = "../../../net/epee-encoding" } +cuprate-rpc-types = { path = "../../../rpc/types", features = ["serde", "epee"] } +cuprate-test-utils = { path = "../../../test-utils" } + +criterion = { workspace = true } +function_name = { workspace = true } +serde_json = { workspace = true, features = ["default"] } +paste = { workspace = true } + +[[bench]] +name = "main" +harness = false + +[lints] +workspace = true \ No newline at end of file diff --git a/benches/criterion/cuprate-rpc-types/benches/epee.rs b/benches/criterion/cuprate-rpc-types/benches/epee.rs new file mode 100644 index 000000000..0ac63130d --- /dev/null +++ b/benches/criterion/cuprate-rpc-types/benches/epee.rs @@ -0,0 +1,65 @@ +//! This module contains benchmarks for any +//! non-trivial/manual `epee` implementation. +//! +//! Types with the standard `epee` derive implementation are not included. + +#![allow(unused_attributes, unused_crate_dependencies)] + +use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; +use function_name::named; + +use cuprate_epee_encoding::{from_bytes, to_bytes}; +use cuprate_rpc_types::bin::{GetBlocksRequest, GetBlocksResponse}; + +criterion_group! { + name = benches; + config = Criterion::default(); + targets = + epee_to_bytes_get_blocks_request, + epee_from_bytes_get_blocks_request, + epee_to_bytes_get_blocks_response, + epee_from_bytes_get_blocks_response, +} +criterion_main!(benches); + +/// TODO +macro_rules! impl_epee_benchmark { + ( + $( + $t:ty + ),* $(,)? + ) => { paste::paste! { + $( + #[named] + fn [](c: &mut Criterion) { + let bytes = to_bytes($t::default()).unwrap(); + + c.bench_function(function_name!(), |b| { + b.iter_batched( + || bytes.clone(), + |mut bytes| drop(from_bytes::<$t, _>(black_box(&mut bytes)).unwrap()), + BatchSize::SmallInput, + ); + }); + } + + #[named] + fn [](c: &mut Criterion) { + let t = $t::default(); + + c.bench_function(function_name!(), |b| { + b.iter_batched( + || t.clone(), + |t| drop(to_bytes(black_box(t)).unwrap()), + BatchSize::SmallInput, + ); + }); + } + )* + }}; +} + +impl_epee_benchmark! { + GetBlocksRequest, + GetBlocksResponse +} diff --git a/benches/criterion/cuprate-rpc-types/benches/main.rs b/benches/criterion/cuprate-rpc-types/benches/main.rs new file mode 100644 index 000000000..6267a9195 --- /dev/null +++ b/benches/criterion/cuprate-rpc-types/benches/main.rs @@ -0,0 +1,10 @@ +//! Benchmarks for `cuprate-json-rpc`. +#![allow(unused_crate_dependencies)] + +mod serde; +mod epee; + +criterion::criterion_main! { + epee::benches, + serde::benches, +} diff --git a/benches/criterion/cuprate-rpc-types/benches/serde.rs b/benches/criterion/cuprate-rpc-types/benches/serde.rs new file mode 100644 index 000000000..08c73637b --- /dev/null +++ b/benches/criterion/cuprate-rpc-types/benches/serde.rs @@ -0,0 +1,63 @@ +//! This module contains benchmarks for any +//! non-trivial/manual `serde` implementation. +//! +//! Types with the standard `serde` derive implementation are not included. + +#![allow(unused_attributes, unused_crate_dependencies)] + +use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; +use function_name::named; +use serde_json::{from_str, to_string}; + +use cuprate_rpc_types::misc::TxEntry; + +criterion_group! { + name = benches; + config = Criterion::default(); + targets = + serde_from_str_tx_entry, + serde_to_string_tx_entry, +} +criterion_main!(benches); + +/// TODO +macro_rules! impl_serde_benchmark { + ( + $( + // The type to test => + // A `const: &str` from `cuprate_test_utils` for that type or just an inline expression + $t:ty => $t_example:expr + ),* $(,)? + ) => { paste::paste! { + $( + #[named] + fn [](c: &mut Criterion) { + c.bench_function(function_name!(), |b| { + b.iter(|| { + drop(from_str::<$t>( + black_box($t_example) + ).unwrap()); + }); + }); + } + + #[named] + fn [](c: &mut Criterion) { + let t = $t::default(); + + c.bench_function(function_name!(), |b| { + b.iter_batched( + || t.clone(), + |t| drop(to_string(black_box(&t)).unwrap()), + BatchSize::SmallInput, + ); + }); + } + )* + }}; +} + +impl_serde_benchmark! { + TxEntry => r#"{"as_hex":"","as_json":"","double_spend_seen":false,"prunable_as_hex":"","prunable_hash":"","pruned_as_hex":"","received_timestamp":0,"relayed":false,"tx_hash":"","in_pool":false}"#, + // Distribution => "TODO: enable after type is finalized" +} diff --git a/benches/criterion/cuprate-rpc-types/src/lib.rs b/benches/criterion/cuprate-rpc-types/src/lib.rs new file mode 100644 index 000000000..5a529588c --- /dev/null +++ b/benches/criterion/cuprate-rpc-types/src/lib.rs @@ -0,0 +1 @@ +#![allow(unused_crate_dependencies, reason = "used in benchmarks")] From d2b30c1cbdc326524d67f323834a71916938a5f5 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Fri, 25 Oct 2024 20:03:30 -0400 Subject: [PATCH 23/33] add benches --- .../cuprate-rpc-types/benches/epee.rs | 41 ++++++++----- .../cuprate-rpc-types/benches/main.rs | 2 +- .../cuprate-rpc-types/benches/serde.rs | 60 ++++++++++++++----- 3 files changed, 72 insertions(+), 31 deletions(-) diff --git a/benches/criterion/cuprate-rpc-types/benches/epee.rs b/benches/criterion/cuprate-rpc-types/benches/epee.rs index 0ac63130d..daec6e36a 100644 --- a/benches/criterion/cuprate-rpc-types/benches/epee.rs +++ b/benches/criterion/cuprate-rpc-types/benches/epee.rs @@ -1,5 +1,10 @@ //! This module contains benchmarks for any -//! non-trivial/manual `epee` implementation. +//! +//! - non-trivial +//! - manual +//! - common +//! +//! type with a `epee` implementation. //! //! Types with the standard `epee` derive implementation are not included. @@ -11,29 +16,21 @@ use function_name::named; use cuprate_epee_encoding::{from_bytes, to_bytes}; use cuprate_rpc_types::bin::{GetBlocksRequest, GetBlocksResponse}; -criterion_group! { - name = benches; - config = Criterion::default(); - targets = - epee_to_bytes_get_blocks_request, - epee_from_bytes_get_blocks_request, - epee_to_bytes_get_blocks_response, - epee_from_bytes_get_blocks_response, -} -criterion_main!(benches); - -/// TODO -macro_rules! impl_epee_benchmark { +/// Create [`to_bytes`] and [`from_bytes`] benchmarks for `epee` types. +macro_rules! generate_epee_benchmarks { ( $( $t:ty ),* $(,)? ) => { paste::paste! { + // Generate the benchmarking functions. $( #[named] fn [](c: &mut Criterion) { let bytes = to_bytes($t::default()).unwrap(); + // `iter_batched()` is used so the `Default::default()` + // is not part of the timings. c.bench_function(function_name!(), |b| { b.iter_batched( || bytes.clone(), @@ -56,10 +53,22 @@ macro_rules! impl_epee_benchmark { }); } )* + + // Enable all the benchmark functions created in this macro. + criterion_group! { + name = benches; + config = Criterion::default(); + targets = + $( + [], + [], + )* + } + criterion_main!(benches); }}; } -impl_epee_benchmark! { +generate_epee_benchmarks! { GetBlocksRequest, - GetBlocksResponse + // GetBlocksResponse // TODO: fix epee impl } diff --git a/benches/criterion/cuprate-rpc-types/benches/main.rs b/benches/criterion/cuprate-rpc-types/benches/main.rs index 6267a9195..dedb44a13 100644 --- a/benches/criterion/cuprate-rpc-types/benches/main.rs +++ b/benches/criterion/cuprate-rpc-types/benches/main.rs @@ -1,8 +1,8 @@ //! Benchmarks for `cuprate-json-rpc`. #![allow(unused_crate_dependencies)] -mod serde; mod epee; +mod serde; criterion::criterion_main! { epee::benches, diff --git a/benches/criterion/cuprate-rpc-types/benches/serde.rs b/benches/criterion/cuprate-rpc-types/benches/serde.rs index 08c73637b..c94ee0a90 100644 --- a/benches/criterion/cuprate-rpc-types/benches/serde.rs +++ b/benches/criterion/cuprate-rpc-types/benches/serde.rs @@ -1,5 +1,10 @@ //! This module contains benchmarks for any -//! non-trivial/manual `serde` implementation. +//! +//! - non-trivial +//! - manual +//! - common +//! +//! type with a `serde` implementation. //! //! Types with the standard `serde` derive implementation are not included. @@ -9,19 +14,21 @@ use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion use function_name::named; use serde_json::{from_str, to_string}; -use cuprate_rpc_types::misc::TxEntry; - -criterion_group! { - name = benches; - config = Criterion::default(); - targets = - serde_from_str_tx_entry, - serde_to_string_tx_entry, -} -criterion_main!(benches); +use cuprate_rpc_types::{ + json::{ + CalcPowRequest, GetBlockHeadersRangeResponse, GetBlockResponse, GetBlockTemplateResponse, + GetConnectionsResponse, GetInfoResponse, GetLastBlockHeaderResponse, SyncInfoResponse, + }, + misc::TxEntry, +}; +use cuprate_test_utils::rpc::data::json::{ + CALC_POW_REQUEST, GET_BLOCK_HEADERS_RANGE_RESPONSE, GET_BLOCK_RESPONSE, + GET_BLOCK_TEMPLATE_RESPONSE, GET_CONNECTIONS_RESPONSE, GET_INFO_RESPONSE, + GET_LAST_BLOCK_HEADER_RESPONSE, SYNC_INFO_RESPONSE, +}; -/// TODO -macro_rules! impl_serde_benchmark { +/// Generate [`from_str`] and [`to_string`] benchmarks for `serde` types. +macro_rules! generate_serde_benchmarks { ( $( // The type to test => @@ -29,6 +36,7 @@ macro_rules! impl_serde_benchmark { $t:ty => $t_example:expr ),* $(,)? ) => { paste::paste! { + // Generate the benchmarking functions. $( #[named] fn [](c: &mut Criterion) { @@ -53,11 +61,35 @@ macro_rules! impl_serde_benchmark { ); }); } + )* + + // Enable all the benchmark functions created in this macro. + criterion_group! { + name = benches; + config = Criterion::default(); + targets = + $( + [], + [], + )* + } + criterion_main!(benches); }}; } -impl_serde_benchmark! { +generate_serde_benchmarks! { + // Custom serde types. TxEntry => r#"{"as_hex":"","as_json":"","double_spend_seen":false,"prunable_as_hex":"","prunable_hash":"","pruned_as_hex":"","received_timestamp":0,"relayed":false,"tx_hash":"","in_pool":false}"#, // Distribution => "TODO: enable after type is finalized" + + // Common types or heavy types (heap types, many fields, etc). + GetLastBlockHeaderResponse => GET_LAST_BLOCK_HEADER_RESPONSE, + CalcPowRequest => CALC_POW_REQUEST, + SyncInfoResponse => SYNC_INFO_RESPONSE, + GetInfoResponse => GET_INFO_RESPONSE, + GetBlockResponse => GET_BLOCK_RESPONSE, + GetConnectionsResponse => GET_CONNECTIONS_RESPONSE, + GetBlockTemplateResponse => GET_BLOCK_TEMPLATE_RESPONSE, + GetBlockHeadersRangeResponse => GET_BLOCK_HEADERS_RANGE_RESPONSE } From 725b532de97cb3a660bc89b3b91356e5cc51fa4a Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Fri, 25 Oct 2024 20:23:28 -0400 Subject: [PATCH 24/33] fix json --- .../cuprate-rpc-types/benches/serde.rs | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/benches/criterion/cuprate-rpc-types/benches/serde.rs b/benches/criterion/cuprate-rpc-types/benches/serde.rs index c94ee0a90..8c3c3cf78 100644 --- a/benches/criterion/cuprate-rpc-types/benches/serde.rs +++ b/benches/criterion/cuprate-rpc-types/benches/serde.rs @@ -21,18 +21,12 @@ use cuprate_rpc_types::{ }, misc::TxEntry, }; -use cuprate_test_utils::rpc::data::json::{ - CALC_POW_REQUEST, GET_BLOCK_HEADERS_RANGE_RESPONSE, GET_BLOCK_RESPONSE, - GET_BLOCK_TEMPLATE_RESPONSE, GET_CONNECTIONS_RESPONSE, GET_INFO_RESPONSE, - GET_LAST_BLOCK_HEADER_RESPONSE, SYNC_INFO_RESPONSE, -}; /// Generate [`from_str`] and [`to_string`] benchmarks for `serde` types. macro_rules! generate_serde_benchmarks { ( $( - // The type to test => - // A `const: &str` from `cuprate_test_utils` for that type or just an inline expression + // The type to test => JSON of that type $t:ty => $t_example:expr ),* $(,)? ) => { paste::paste! { @@ -78,18 +72,20 @@ macro_rules! generate_serde_benchmarks { }}; } +// FIXME: these could be taken from `cuprate-test-utils::rpc::data::json` but +// those are wrapped in JSON-RPC, so we have to copy+paste the inner portion here. generate_serde_benchmarks! { // Custom serde types. TxEntry => r#"{"as_hex":"","as_json":"","double_spend_seen":false,"prunable_as_hex":"","prunable_hash":"","pruned_as_hex":"","received_timestamp":0,"relayed":false,"tx_hash":"","in_pool":false}"#, // Distribution => "TODO: enable after type is finalized" // Common types or heavy types (heap types, many fields, etc). - GetLastBlockHeaderResponse => GET_LAST_BLOCK_HEADER_RESPONSE, - CalcPowRequest => CALC_POW_REQUEST, - SyncInfoResponse => SYNC_INFO_RESPONSE, - GetInfoResponse => GET_INFO_RESPONSE, - GetBlockResponse => GET_BLOCK_RESPONSE, - GetConnectionsResponse => GET_CONNECTIONS_RESPONSE, - GetBlockTemplateResponse => GET_BLOCK_TEMPLATE_RESPONSE, - GetBlockHeadersRangeResponse => GET_BLOCK_HEADERS_RANGE_RESPONSE + GetLastBlockHeaderResponse => r#"{"block_header":{"block_size":200419,"block_weight":200419,"cumulative_difficulty":366125734645190820,"cumulative_difficulty_top64":0,"depth":0,"difficulty":282052561854,"difficulty_top64":0,"hash":"57238217820195ac4c08637a144a885491da167899cf1d20e8e7ce0ae0a3434e","height":3195020,"long_term_weight":200419,"major_version":16,"miner_tx_hash":"7a42667237d4f79891bb407c49c712a9299fb87fce799833a7b633a3a9377dbd","minor_version":16,"nonce":1885649739,"num_txes":37,"orphan_status":false,"pow_hash":"","prev_hash":"22c72248ae9c5a2863c94735d710a3525c499f70707d1c2f395169bc5c8a0da3","reward":615702960000,"timestamp":1721245548,"wide_cumulative_difficulty":"0x514bd6a74a7d0a4","wide_difficulty":"0x41aba48bbe"},"credits":0,"status":"OK","top_hash":"","untrusted":false}"#, + CalcPowRequest => r#"{"major_version":14,"height":2286447,"block_blob":"0e0ed286da8006ecdc1aab3033cf1716c52f13f9d8ae0051615a2453643de94643b550d543becd0000000002abc78b0101ffefc68b0101fcfcf0d4b422025014bb4a1eade6622fd781cb1063381cad396efa69719b41aa28b4fce8c7ad4b5f019ce1dc670456b24a5e03c2d9058a2df10fec779e2579753b1847b74ee644f16b023c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051399a1bc46a846474f5b33db24eae173a26393b976054ee14f9feefe99925233802867097564c9db7a36af5bb5ed33ab46e63092bd8d32cef121608c3258edd55562812e21cc7e3ac73045745a72f7d74581d9a0849d6f30e8b2923171253e864f4e9ddea3acb5bc755f1c4a878130a70c26297540bc0b7a57affb6b35c1f03d8dbd54ece8457531f8cba15bb74516779c01193e212050423020e45aa2c15dcb","seed_hash":"d432f499205150873b2572b5f033c9c6e4b7c6f3394bd2dd93822cd7085e7307"}"#, + SyncInfoResponse => r#"{"credits":0,"height":3195157,"next_needed_pruning_seed":0,"overview":"[]","peers":[{"info":{"address":"142.93.128.65:44986","address_type":1,"avg_download":1,"avg_upload":1,"connection_id":"a5803c4c2dac49e7b201dccdef54c862","current_download":2,"current_upload":1,"height":3195157,"host":"142.93.128.65","incoming":true,"ip":"142.93.128.65","live_time":18,"local_ip":false,"localhost":false,"peer_id":"6830e9764d3e5687","port":"44986","pruning_seed":0,"recv_count":20340,"recv_idle_time":0,"rpc_credits_per_hash":0,"rpc_port":18089,"send_count":32235,"send_idle_time":6,"state":"normal","support_flags":1}},{"info":{"address":"4iykytmumafy5kjahdqc7uzgcs34s2vwsadfjpk4znvsa5vmcxeup2qd.onion:18083","address_type":4,"avg_download":0,"avg_upload":0,"connection_id":"277f7c821bc546878c8bd29977e780f5","current_download":0,"current_upload":0,"height":3195157,"host":"4iykytmumafy5kjahdqc7uzgcs34s2vwsadfjpk4znvsa5vmcxeup2qd.onion","incoming":false,"ip":"","live_time":2246,"local_ip":false,"localhost":false,"peer_id":"0000000000000001","port":"","pruning_seed":389,"recv_count":65164,"recv_idle_time":15,"rpc_credits_per_hash":0,"rpc_port":0,"send_count":99120,"send_idle_time":15,"state":"normal","support_flags":0}}],"status":"OK","target_height":0,"top_hash":"","untrusted":false}"#, + GetInfoResponse => r#"{"adjusted_time":1721245289,"alt_blocks_count":16,"block_size_limit":600000,"block_size_median":300000,"block_weight_limit":600000,"block_weight_median":300000,"bootstrap_daemon_address":"","busy_syncing":false,"credits":0,"cumulative_difficulty":366127702242611947,"cumulative_difficulty_top64":0,"database_size":235169075200,"difficulty":280716748706,"difficulty_top64":0,"free_space":30521749504,"grey_peerlist_size":4996,"height":3195028,"height_without_bootstrap":3195028,"incoming_connections_count":62,"mainnet":true,"nettype":"mainnet","offline":false,"outgoing_connections_count":1143,"restricted":false,"rpc_connections_count":1,"stagenet":false,"start_time":1720462427,"status":"OK","synchronized":true,"target":120,"target_height":0,"testnet":false,"top_block_hash":"bdf06d18ed1931a8ee62654e9b6478cc459bc7072628b8e36f4524d339552946","top_hash":"","tx_count":43205750,"tx_pool_size":12,"untrusted":false,"update_available":false,"version":"0.18.3.3-release","was_bootstrap_ever_used":false,"white_peerlist_size":1000,"wide_cumulative_difficulty":"0x514bf349299d2eb","wide_difficulty":"0x415c05a7a2"}"#, + GetBlockResponse => r#"{"blob":"1010c58bab9b06b27bdecfc6cd0a46172d136c08831cf67660377ba992332363228b1b722781e7807e07f502cef8a70101ff92f8a7010180e0a596bb1103d7cbf826b665d7a532c316982dc8dbc24f285cbc18bbcc27c7164cd9b3277a85d034019f629d8b36bd16a2bfce3ea80c31dc4d8762c67165aec21845494e32b7582fe00211000000297a787a000000000000000000000000","block_header":{"block_size":106,"block_weight":106,"cumulative_difficulty":236046001376524168,"cumulative_difficulty_top64":0,"depth":443517,"difficulty":313732272488,"difficulty_top64":0,"hash":"43bd1f2b6556dcafa413d8372974af59e4e8f37dbf74dc6b2a9b7212d0577428","height":2751506,"long_term_weight":176470,"major_version":16,"miner_tx_hash":"e49b854c5f339d7410a77f2a137281d8042a0ffc7ef9ab24cd670b67139b24cd","minor_version":16,"nonce":4110909056,"num_txes":0,"orphan_status":false,"pow_hash":"","prev_hash":"b27bdecfc6cd0a46172d136c08831cf67660377ba992332363228b1b722781e7","reward":600000000000,"timestamp":1667941829,"wide_cumulative_difficulty":"0x3469a966eb2f788","wide_difficulty":"0x490be69168"},"credits":0,"json":"{\n \"major_version\": 16, \n \"minor_version\": 16, \n \"timestamp\": 1667941829, \n \"prev_id\": \"b27bdecfc6cd0a46172d136c08831cf67660377ba992332363228b1b722781e7\", \n \"nonce\": 4110909056, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 2751566, \n \"vin\": [ {\n \"gen\": {\n \"height\": 2751506\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 600000000000, \n \"target\": {\n \"tagged_key\": {\n \"key\": \"d7cbf826b665d7a532c316982dc8dbc24f285cbc18bbcc27c7164cd9b3277a85\", \n \"view_tag\": \"d0\"\n }\n }\n }\n ], \n \"extra\": [ 1, 159, 98, 157, 139, 54, 189, 22, 162, 191, 206, 62, 168, 12, 49, 220, 77, 135, 98, 198, 113, 101, 174, 194, 24, 69, 73, 78, 50, 183, 88, 47, 224, 2, 17, 0, 0, 0, 41, 122, 120, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}","miner_tx_hash":"e49b854c5f339d7410a77f2a137281d8042a0ffc7ef9ab24cd670b67139b24cd","status":"OK","top_hash":"","untrusted":false}"#, + GetConnectionsResponse => r#"{"connections":[{"address":"3evk3kezfjg44ma6tvesy7rbxwwpgpympj45xar5fo4qajrsmkoaqdqd.onion:18083","address_type":4,"avg_download":0,"avg_upload":0,"connection_id":"22ef856d0f1d44cc95e84fecfd065fe2","current_download":0,"current_upload":0,"height":3195026,"host":"3evk3kezfjg44ma6tvesy7rbxwwpgpympj45xar5fo4qajrsmkoaqdqd.onion","incoming":false,"ip":"","live_time":76651,"local_ip":false,"localhost":false,"peer_id":"0000000000000001","port":"","pruning_seed":0,"recv_count":240328,"recv_idle_time":34,"rpc_credits_per_hash":0,"rpc_port":0,"send_count":3406572,"send_idle_time":30,"state":"normal","support_flags":0},{"address":"4iykytmumafy5kjahdqc7uzgcs34s2vwsadfjpk4znvsa5vmcxeup2qd.onion:18083","address_type":4,"avg_download":0,"avg_upload":0,"connection_id":"c7734e15936f485a86d2b0534f87e499","current_download":0,"current_upload":0,"height":3195024,"host":"4iykytmumafy5kjahdqc7uzgcs34s2vwsadfjpk4znvsa5vmcxeup2qd.onion","incoming":false,"ip":"","live_time":76755,"local_ip":false,"localhost":false,"peer_id":"0000000000000001","port":"","pruning_seed":389,"recv_count":237657,"recv_idle_time":120,"rpc_credits_per_hash":0,"rpc_port":0,"send_count":3370566,"send_idle_time":120,"state":"normal","support_flags":0}],"status":"OK","untrusted":false}"#, + GetBlockTemplateResponse => r#"{"blockhashing_blob":"1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a00000000e0c20372be23d356347091025c5b5e8f2abf83ab618378565cce2b703491523401","blocktemplate_blob":"1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":283305047039,"difficulty_top64":0,"expected_reward":600000000000,"height":3195018,"next_seed_hash":"","prev_hash":"9d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a","reserved_offset":131,"seed_hash":"e2aa0b7b55042cd48b02e395d78fa66a29815ccc1584e38db2d1f0e8485cd44f","seed_height":3194880,"status":"OK","untrusted":false,"wide_difficulty":"0x41f64bf3ff"}"#, + GetBlockHeadersRangeResponse => r#"{"credits":0,"headers":[{"block_size":301413,"block_weight":301413,"cumulative_difficulty":13185267971483472,"cumulative_difficulty_top64":0,"depth":1649024,"difficulty":134636057921,"difficulty_top64":0,"hash":"86d1d20a40cefcf3dd410ff6967e0491613b77bf73ea8f1bf2e335cf9cf7d57a","height":1545999,"long_term_weight":301413,"major_version":6,"miner_tx_hash":"9909c6f8a5267f043c3b2b079fb4eacc49ef9c1dee1c028eeb1a259b95e6e1d9","minor_version":6,"nonce":3246403956,"num_txes":20,"orphan_status":false,"pow_hash":"","prev_hash":"0ef6e948f77b8f8806621003f5de24b1bcbea150bc0e376835aea099674a5db5","reward":5025593029981,"timestamp":1523002893,"wide_cumulative_difficulty":"0x2ed7ee6db56750","wide_difficulty":"0x1f58ef3541"},{"block_size":13322,"block_weight":13322,"cumulative_difficulty":13185402687569710,"cumulative_difficulty_top64":0,"depth":1649023,"difficulty":134716086238,"difficulty_top64":0,"hash":"b408bf4cfcd7de13e7e370c84b8314c85b24f0ba4093ca1d6eeb30b35e34e91a","height":1546000,"long_term_weight":13322,"major_version":7,"miner_tx_hash":"7f749c7c64acb35ef427c7454c45e6688781fbead9bbf222cb12ad1a96a4e8f6","minor_version":7,"nonce":3737164176,"num_txes":1,"orphan_status":false,"pow_hash":"","prev_hash":"86d1d20a40cefcf3dd410ff6967e0491613b77bf73ea8f1bf2e335cf9cf7d57a","reward":4851952181070,"timestamp":1523002931,"wide_cumulative_difficulty":"0x2ed80dcb69bf2e","wide_difficulty":"0x1f5db457de"}],"status":"OK","top_hash":"","untrusted":false}"# } From 636e48529b19dbfad2a38b2b9f9e2e640e2eac2c Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Fri, 25 Oct 2024 20:26:34 -0400 Subject: [PATCH 25/33] add to `typos.toml` --- typos.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/typos.toml b/typos.toml index fbd66d099..c4537fa5f 100644 --- a/typos.toml +++ b/typos.toml @@ -16,7 +16,8 @@ extend-ignore-identifiers-re = [ # False positive files. extend-exclude = [ "/misc/gpg_keys/", - "cryptonight/", + "/cryptonight/", "/test-utils/src/rpc/data/json.rs", - "rpc/types/src/json.rs", + "/rpc/types/src/json.rs", + "/benches/criterion/cuprate-rpc-types/benches/serde.rs", ] From 705cd7e2db233d000233197ee2d8dc4eeaf62221 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Fri, 25 Oct 2024 20:40:39 -0400 Subject: [PATCH 26/33] clippy --- benches/criterion/cuprate-rpc-types/benches/epee.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/criterion/cuprate-rpc-types/benches/epee.rs b/benches/criterion/cuprate-rpc-types/benches/epee.rs index daec6e36a..7244eb8bf 100644 --- a/benches/criterion/cuprate-rpc-types/benches/epee.rs +++ b/benches/criterion/cuprate-rpc-types/benches/epee.rs @@ -14,7 +14,7 @@ use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion use function_name::named; use cuprate_epee_encoding::{from_bytes, to_bytes}; -use cuprate_rpc_types::bin::{GetBlocksRequest, GetBlocksResponse}; +use cuprate_rpc_types::bin::GetBlocksRequest; /// Create [`to_bytes`] and [`from_bytes`] benchmarks for `epee` types. macro_rules! generate_epee_benchmarks { From 059d0e610507f2c6ec9b1faa528026a2f3599661 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 31 Oct 2024 17:06:28 -0400 Subject: [PATCH 27/33] add `benches/criterion/cuprate-blockchain` --- Cargo.lock | 15 +++++ Cargo.toml | 1 + .../criterion/cuprate-blockchain/Cargo.toml | 28 ++++++++ .../cuprate-blockchain/benches/block.rs | 67 +++++++++++++++++++ .../cuprate-blockchain/benches/main.rs | 7 ++ .../cuprate-blockchain/src/blocks.rs | 36 ++++++++++ .../criterion/cuprate-blockchain/src/lib.rs | 7 ++ .../cuprate-blockchain/src/tmp_env.rs | 53 +++++++++++++++ 8 files changed, 214 insertions(+) create mode 100644 benches/criterion/cuprate-blockchain/Cargo.toml create mode 100644 benches/criterion/cuprate-blockchain/benches/block.rs create mode 100644 benches/criterion/cuprate-blockchain/benches/main.rs create mode 100644 benches/criterion/cuprate-blockchain/src/blocks.rs create mode 100644 benches/criterion/cuprate-blockchain/src/lib.rs create mode 100644 benches/criterion/cuprate-blockchain/src/tmp_env.rs diff --git a/Cargo.lock b/Cargo.lock index 2555b9b64..41d5bfb62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -758,6 +758,21 @@ dependencies = [ name = "cuprate-constants" version = "0.1.0" +[[package]] +name = "cuprate-criterion-blockchain" +version = "0.0.0" +dependencies = [ + "criterion", + "cuprate-blockchain", + "cuprate-helper", + "cuprate-test-utils", + "cuprate-types", + "function_name", + "rand", + "serde_json", + "tempfile", +] + [[package]] name = "cuprate-criterion-example" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 6f2cb3fb0..ef41942f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "benches/benchmark/example", "benches/criterion/example", "benches/criterion/cuprate-json-rpc", + "benches/criterion/cuprate-blockchain", # Consensus "consensus", "consensus/context", diff --git a/benches/criterion/cuprate-blockchain/Cargo.toml b/benches/criterion/cuprate-blockchain/Cargo.toml new file mode 100644 index 000000000..de3dab111 --- /dev/null +++ b/benches/criterion/cuprate-blockchain/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "cuprate-criterion-blockchain" +version = "0.0.0" +edition = "2021" +description = "Criterion benchmarking for cuprate-blockchain" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-blockchain" +keywords = ["cuprate", "blockchain", "criterion", "benchmark"] + +[dependencies] +cuprate-blockchain = { path = "../../../storage/blockchain" } +cuprate-test-utils = { path = "../../../test-utils" } +cuprate-types = { path = "../../../types", default-features = false } +cuprate-helper = { path = "../../../helper", features = ["cast"] } + +criterion = { workspace = true } +function_name = { workspace = true } +serde_json = { workspace = true, features = ["default"] } +tempfile = { workspace = true } +rand = { workspace = true } + +[[bench]] +name = "main" +harness = false + +[lints] +workspace = true diff --git a/benches/criterion/cuprate-blockchain/benches/block.rs b/benches/criterion/cuprate-blockchain/benches/block.rs new file mode 100644 index 000000000..2a3c55a04 --- /dev/null +++ b/benches/criterion/cuprate-blockchain/benches/block.rs @@ -0,0 +1,67 @@ +//! Benchmarks for [`block`] functions. + +#![allow(unused_attributes, unused_crate_dependencies)] + +use std::time::Instant; + +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use function_name::named; + +use cuprate_blockchain::{ + cuprate_database::{Env, EnvInner}, + ops::block, + tables::OpenTables, +}; +use cuprate_test_utils::data::{BLOCK_V16_TX0, BLOCK_V1_TX2, BLOCK_V9_TX3}; +use cuprate_types::VerifiedBlockInformation; + +use cuprate_criterion_blockchain::generate_fake_blocks; + +criterion_group! { + name = benches; + config = Criterion::default(); + targets = + add_block_v1_tx2, + add_block_v9_tx3, + add_block_v16_tx0, +} +criterion_main!(benches); + +/// Inner function for benchmarking [`block::add_block`]. +#[expect(clippy::significant_drop_tightening)] +fn add_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedBlockInformation) { + let env = cuprate_criterion_blockchain::TmpEnv::new(); + + c.bench_function(function_name, |b| { + // We use `iter_custom` because we need to generate an + // appropriate amount of blocks and only time the `add_block`. + b.iter_custom(|count| { + let blocks = black_box(generate_fake_blocks(block, count)); + + let env_inner = env.env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut tables = env_inner.open_tables_mut(&tx_rw).unwrap(); + + let start = Instant::now(); + for block in &blocks { + black_box(block::add_block(block, &mut tables)).unwrap(); + } + start.elapsed() + }); + }); +} + +#[named] +fn add_block_v1_tx2(c: &mut Criterion) { + add_block_inner(c, function_name!(), &BLOCK_V1_TX2); +} + +#[named] +fn add_block_v9_tx3(c: &mut Criterion) { + add_block_inner(c, function_name!(), &BLOCK_V9_TX3); +} + +#[named] +fn add_block_v16_tx0(c: &mut Criterion) { + add_block_inner(c, function_name!(), &BLOCK_V16_TX0); +} diff --git a/benches/criterion/cuprate-blockchain/benches/main.rs b/benches/criterion/cuprate-blockchain/benches/main.rs new file mode 100644 index 000000000..daa06c1da --- /dev/null +++ b/benches/criterion/cuprate-blockchain/benches/main.rs @@ -0,0 +1,7 @@ +#![allow(unused_crate_dependencies)] + +mod block; + +criterion::criterion_main! { + block::benches, +} diff --git a/benches/criterion/cuprate-blockchain/src/blocks.rs b/benches/criterion/cuprate-blockchain/src/blocks.rs new file mode 100644 index 000000000..a5629af40 --- /dev/null +++ b/benches/criterion/cuprate-blockchain/src/blocks.rs @@ -0,0 +1,36 @@ +use rand::Rng; + +use cuprate_helper::cast::u64_to_usize; +use cuprate_types::VerifiedBlockInformation; + +/// Generate fake [`VerifiedBlockInformation`]s. +/// +/// This function generates fake blocks, +/// cloning the `base` block `count` amount of times, +/// starting at height `0` and sequentially incrementing, +/// i.e. block height `{0,1,2,...}`. +/// +/// Each block has a random [`VerifiedBlockInformation::block_hash`]. +/// +/// # Hack +/// This is used for these benchmarks because +/// [`cuprate_blockchain::ops::block::add_block`] +/// asserts that blocks with non-sequential heights cannot be added. +/// To get around this, we manually edit the block heights. +/// +/// The hash must also be faked as +/// [`cuprate_blockchain::ops::blockchain::chain_height`] +/// which is used for an [`assert`] relies on the `hash -> height` table. +pub fn generate_fake_blocks( + base: &VerifiedBlockInformation, + count: u64, +) -> Vec { + (0..count) + .map(|height| { + let mut block = base.clone(); + block.height = u64_to_usize(height); + block.block_hash = rand::thread_rng().r#gen(); + block + }) + .collect() +} diff --git a/benches/criterion/cuprate-blockchain/src/lib.rs b/benches/criterion/cuprate-blockchain/src/lib.rs new file mode 100644 index 000000000..59f417e9a --- /dev/null +++ b/benches/criterion/cuprate-blockchain/src/lib.rs @@ -0,0 +1,7 @@ +#![allow(unused_crate_dependencies, reason = "used in benchmarks")] + +mod tmp_env; +mod blocks; + +pub use blocks::generate_fake_blocks; +pub use tmp_env::TmpEnv; diff --git a/benches/criterion/cuprate-blockchain/src/tmp_env.rs b/benches/criterion/cuprate-blockchain/src/tmp_env.rs new file mode 100644 index 000000000..147705db3 --- /dev/null +++ b/benches/criterion/cuprate-blockchain/src/tmp_env.rs @@ -0,0 +1,53 @@ +//! An [`Env`] inside a [`TempDir`]. + +use tempfile::TempDir; + +use cuprate_blockchain::{ + config::ReaderThreads, + cuprate_database::{config::ConfigBuilder, resize::PAGE_SIZE, ConcreteEnv, Env}, +}; + +/// A temporary in-memory [`Env`]. +/// +/// This is a [`ConcreteEnv`] that uses [`TempDir`] as the +/// backing file location - this is an in-memory file on Linux. +pub struct TmpEnv { + pub env: ConcreteEnv, + pub tempdir: TempDir, +} + +impl Default for TmpEnv { + fn default() -> Self { + Self::new() + } +} + +impl TmpEnv { + /// Create an `Env` in a temporary directory. + /// + /// The directory is automatically removed after the [`TempDir`] is dropped. + #[expect(clippy::missing_panics_doc)] + pub fn new() -> Self { + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().to_path_buf().into(); + let db_config = ConfigBuilder::new(path).low_power().build(); + let reader_threads = ReaderThreads::One; + let config = cuprate_blockchain::config::Config { + db_config, + reader_threads, + }; + let env = cuprate_blockchain::open(config).unwrap(); + + // Resize to a very large map to prevent resize errors. + if ConcreteEnv::MANUAL_RESIZE { + // SAFETY: no write transactions exist yet. + unsafe { + env.env_inner() + .resize(PAGE_SIZE.get() * 1024 * 1024 * 1024) + .unwrap(); + } + } + + Self { env, tempdir } + } +} From e5db01a2e3515a3022e6812651cc50941476c7d5 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 31 Oct 2024 17:09:41 -0400 Subject: [PATCH 28/33] fmt --- benches/criterion/cuprate-blockchain/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/criterion/cuprate-blockchain/src/lib.rs b/benches/criterion/cuprate-blockchain/src/lib.rs index 59f417e9a..8687f45ed 100644 --- a/benches/criterion/cuprate-blockchain/src/lib.rs +++ b/benches/criterion/cuprate-blockchain/src/lib.rs @@ -1,7 +1,7 @@ #![allow(unused_crate_dependencies, reason = "used in benchmarks")] -mod tmp_env; mod blocks; +mod tmp_env; pub use blocks::generate_fake_blocks; pub use tmp_env::TmpEnv; From 1cf86ed5c03d9d9ff0dd2203ff9817846a570f73 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 31 Oct 2024 19:44:09 -0400 Subject: [PATCH 29/33] alt_block --- .../cuprate-blockchain/benches/block.rs | 67 +++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/benches/criterion/cuprate-blockchain/benches/block.rs b/benches/criterion/cuprate-blockchain/benches/block.rs index 2a3c55a04..a03bf18e5 100644 --- a/benches/criterion/cuprate-blockchain/benches/block.rs +++ b/benches/criterion/cuprate-blockchain/benches/block.rs @@ -1,19 +1,20 @@ -//! Benchmarks for [`block`] functions. +//! Benchmarks for [`block`] and [`alt_block`] functions. #![allow(unused_attributes, unused_crate_dependencies)] -use std::time::Instant; +use std::{num::NonZeroU64, time::Instant}; use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use cuprate_helper::cast::usize_to_u64; use function_name::named; use cuprate_blockchain::{ cuprate_database::{Env, EnvInner}, - ops::block, + ops::{alt_block, block}, tables::OpenTables, }; use cuprate_test_utils::data::{BLOCK_V16_TX0, BLOCK_V1_TX2, BLOCK_V9_TX3}; -use cuprate_types::VerifiedBlockInformation; +use cuprate_types::{AltBlockInformation, ChainId, VerifiedBlockInformation}; use cuprate_criterion_blockchain::generate_fake_blocks; @@ -24,6 +25,9 @@ criterion_group! { add_block_v1_tx2, add_block_v9_tx3, add_block_v16_tx0, + add_alt_block_v1_tx2, + add_alt_block_v9_tx3, + add_alt_block_v16_tx0, } criterion_main!(benches); @@ -65,3 +69,58 @@ fn add_block_v9_tx3(c: &mut Criterion) { fn add_block_v16_tx0(c: &mut Criterion) { add_block_inner(c, function_name!(), &BLOCK_V16_TX0); } + +/// Inner function for benchmarking [`alt_block::add_alt_block`]. +#[expect(clippy::significant_drop_tightening)] +fn add_alt_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedBlockInformation) { + let env = cuprate_criterion_blockchain::TmpEnv::new(); + + c.bench_function(function_name, |b| { + // We use `iter_custom` because we need to generate an + // appropriate amount of blocks and only time the `add_block`. + b.iter_custom(|count| { + // Map the block to a fake alt block. + let blocks = generate_fake_blocks(block, count) + .into_iter() + .enumerate() + .map(|(i, b)| AltBlockInformation { + block: b.block, + block_blob: b.block_blob, + txs: b.txs, + block_hash: b.block_hash, + pow_hash: b.pow_hash, + height: b.height, + weight: b.weight, + long_term_weight: b.long_term_weight, + cumulative_difficulty: b.cumulative_difficulty, + chain_id: ChainId(NonZeroU64::new(usize_to_u64(i) + 1).unwrap()), + }) + .collect::>(); + + let env_inner = env.env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut tables = env_inner.open_tables_mut(&tx_rw).unwrap(); + + let start = Instant::now(); + for block in &blocks { + black_box(alt_block::add_alt_block(block, &mut tables)).unwrap(); + } + start.elapsed() + }); + }); +} + +#[named] +fn add_alt_block_v1_tx2(c: &mut Criterion) { + add_alt_block_inner(c, function_name!(), &BLOCK_V1_TX2); +} + +#[named] +fn add_alt_block_v9_tx3(c: &mut Criterion) { + add_alt_block_inner(c, function_name!(), &BLOCK_V9_TX3); +} + +#[named] +fn add_alt_block_v16_tx0(c: &mut Criterion) { + add_alt_block_inner(c, function_name!(), &BLOCK_V16_TX0); +} From f65ff1bb0ec9833d5215d139d5bb3da2a35b2849 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 31 Oct 2024 19:53:36 -0400 Subject: [PATCH 30/33] fix alt_block height --- .../cuprate-blockchain/benches/block.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/benches/criterion/cuprate-blockchain/benches/block.rs b/benches/criterion/cuprate-blockchain/benches/block.rs index a03bf18e5..4a2bd885e 100644 --- a/benches/criterion/cuprate-blockchain/benches/block.rs +++ b/benches/criterion/cuprate-blockchain/benches/block.rs @@ -48,6 +48,7 @@ fn add_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedBlock let start = Instant::now(); for block in &blocks { + let block = black_box(block); black_box(block::add_block(block, &mut tables)).unwrap(); } start.elapsed() @@ -75,6 +76,18 @@ fn add_block_v16_tx0(c: &mut Criterion) { fn add_alt_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedBlockInformation) { let env = cuprate_criterion_blockchain::TmpEnv::new(); + // We must have at least 1 block or else `add_alt_block` will panic. + { + let env_inner = env.env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut tables = env_inner.open_tables_mut(&tx_rw).unwrap(); + + let mut block = BLOCK_V1_TX2.clone(); + block.height = 0; + + block::add_block(&block, &mut tables).unwrap(); + } + c.bench_function(function_name, |b| { // We use `iter_custom` because we need to generate an // appropriate amount of blocks and only time the `add_block`. @@ -89,7 +102,7 @@ fn add_alt_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedB txs: b.txs, block_hash: b.block_hash, pow_hash: b.pow_hash, - height: b.height, + height: b.height + 1, weight: b.weight, long_term_weight: b.long_term_weight, cumulative_difficulty: b.cumulative_difficulty, @@ -103,6 +116,7 @@ fn add_alt_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedB let start = Instant::now(); for block in &blocks { + let block = black_box(block); black_box(alt_block::add_alt_block(block, &mut tables)).unwrap(); } start.elapsed() From 7e005012edbf012b43e205967bedcec6448c9d87 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 28 Nov 2024 09:41:31 -0500 Subject: [PATCH 31/33] cargo.toml fixes --- Cargo.lock | 12 ++++++------ benches/criterion/cuprate-blockchain/Cargo.toml | 8 ++++---- benches/criterion/cuprate-cryptonight/Cargo.toml | 4 ++-- benches/criterion/cuprate-database/Cargo.toml | 8 ++++---- benches/criterion/cuprate-helper/Cargo.toml | 6 +++--- benches/criterion/cuprate-rpc-types/Cargo.toml | 6 +++--- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c90fdbfb..482c458be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -799,15 +799,17 @@ name = "cuprate-constants" version = "0.1.0" [[package]] -name = "cuprate-criterion-database" +name = "cuprate-criterion-blockchain" version = "0.0.0" dependencies = [ "criterion", "cuprate-blockchain", - "cuprate-database", "cuprate-helper", + "cuprate-test-utils", + "cuprate-types", "function_name", "rand", + "serde_json", "tempfile", ] @@ -821,17 +823,15 @@ dependencies = [ ] [[package]] -name = "cuprate-criterion-blockchain" +name = "cuprate-criterion-database" version = "0.0.0" dependencies = [ "criterion", "cuprate-blockchain", + "cuprate-database", "cuprate-helper", - "cuprate-test-utils", - "cuprate-types", "function_name", "rand", - "serde_json", "tempfile", ] diff --git a/benches/criterion/cuprate-blockchain/Cargo.toml b/benches/criterion/cuprate-blockchain/Cargo.toml index de3dab111..585bf7e35 100644 --- a/benches/criterion/cuprate-blockchain/Cargo.toml +++ b/benches/criterion/cuprate-blockchain/Cargo.toml @@ -9,10 +9,10 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cu keywords = ["cuprate", "blockchain", "criterion", "benchmark"] [dependencies] -cuprate-blockchain = { path = "../../../storage/blockchain" } -cuprate-test-utils = { path = "../../../test-utils" } -cuprate-types = { path = "../../../types", default-features = false } -cuprate-helper = { path = "../../../helper", features = ["cast"] } +cuprate-blockchain = { workspace = true } +cuprate-test-utils = { workspace = true } +cuprate-types = { workspace = true, default-features = false } +cuprate-helper = { workspace = true, features = ["cast"] } criterion = { workspace = true } function_name = { workspace = true } diff --git a/benches/criterion/cuprate-cryptonight/Cargo.toml b/benches/criterion/cuprate-cryptonight/Cargo.toml index dfa8b682f..21e4d9251 100644 --- a/benches/criterion/cuprate-cryptonight/Cargo.toml +++ b/benches/criterion/cuprate-cryptonight/Cargo.toml @@ -9,11 +9,11 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cu keywords = ["cuprate", "cryptonight", "criterion", "benchmark"] [dependencies] +cuprate-cryptonight = { workspace = true } + criterion = { workspace = true } function_name = { workspace = true } -cuprate-cryptonight = { path = "../../../cryptonight" } - [[bench]] name = "main" harness = false diff --git a/benches/criterion/cuprate-database/Cargo.toml b/benches/criterion/cuprate-database/Cargo.toml index 345448b85..c8248537c 100644 --- a/benches/criterion/cuprate-database/Cargo.toml +++ b/benches/criterion/cuprate-database/Cargo.toml @@ -24,11 +24,11 @@ redb = ["cuprate-database/redb", "cuprate-blockchain/redb"] # - cuprate-blockchain/asynch # - cuprate-blockchain/tx -criterion = { workspace = true } -cuprate-database = { path = "../../../storage/database" } -cuprate-blockchain = { path = "../../../storage/blockchain", features = ["service"] } -cuprate-helper = { path = "../../../helper", features = ["asynch", "fs", "thread", "tx"] } +cuprate-database = { workspace = true } +cuprate-blockchain = { workspace = true } +cuprate-helper = { workspace = true, features = ["asynch", "fs", "thread", "tx"] } +criterion = { workspace = true } function_name = { workspace = true } tempfile = { workspace = true } rand = { workspace = true, features = ["std", "std_rng"] } diff --git a/benches/criterion/cuprate-helper/Cargo.toml b/benches/criterion/cuprate-helper/Cargo.toml index c9d0c8d30..93a669834 100644 --- a/benches/criterion/cuprate-helper/Cargo.toml +++ b/benches/criterion/cuprate-helper/Cargo.toml @@ -9,9 +9,9 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/ex keywords = ["cuprate", "helper", "criterion", "benchmark"] [dependencies] -cuprate-constants = { path = "../../../constants", features = ["block"] } -cuprate-helper = { path = "../../../helper", features = ["cast", "map", "num", "tx"] } -cuprate-test-utils = { path = "../../../test-utils" } +cuprate-constants = { workspace = true, features = ["block"] } +cuprate-helper = { workspace = true, features = ["cast", "map", "num", "tx"] } +cuprate-test-utils = { workspace = true } criterion = { workspace = true } function_name = { workspace = true } diff --git a/benches/criterion/cuprate-rpc-types/Cargo.toml b/benches/criterion/cuprate-rpc-types/Cargo.toml index 996ebcdba..21119605c 100644 --- a/benches/criterion/cuprate-rpc-types/Cargo.toml +++ b/benches/criterion/cuprate-rpc-types/Cargo.toml @@ -9,9 +9,9 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cu keywords = ["cuprate", "rpc", "types", "criterion", "benchmark"] [dependencies] -cuprate-epee-encoding = { path = "../../../net/epee-encoding" } -cuprate-rpc-types = { path = "../../../rpc/types", features = ["serde", "epee"] } -cuprate-test-utils = { path = "../../../test-utils" } +cuprate-epee-encoding = { workspace = true } +cuprate-rpc-types = { workspace = true, features = ["serde", "epee"] } +cuprate-test-utils = { workspace = true } criterion = { workspace = true } function_name = { workspace = true } From f15f08be451e1adbacdaae80f5b2152639ce6902 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 28 Nov 2024 09:56:51 -0500 Subject: [PATCH 32/33] 1.83 clippy fix --- pruning/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pruning/src/lib.rs b/pruning/src/lib.rs index cd31598aa..e49aedb1f 100644 --- a/pruning/src/lib.rs +++ b/pruning/src/lib.rs @@ -327,7 +327,7 @@ impl DecompressedPruningSeed { /// /// This function will also error if `block_height` > `blockchain_height` /// - pub fn get_next_unpruned_block( + pub const fn get_next_unpruned_block( &self, block_height: usize, blockchain_height: usize, From d0ca28beca41afe3531b0ba3084e80dc2aa15071 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 28 Nov 2024 15:46:34 -0500 Subject: [PATCH 33/33] clippy fix --- benches/criterion/cuprate-cryptonight/benches/hash.rs | 2 +- benches/criterion/cuprate-rpc-types/benches/serde.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/benches/criterion/cuprate-cryptonight/benches/hash.rs b/benches/criterion/cuprate-cryptonight/benches/hash.rs index 552c3fc66..13dd90697 100644 --- a/benches/criterion/cuprate-cryptonight/benches/hash.rs +++ b/benches/criterion/cuprate-cryptonight/benches/hash.rs @@ -32,7 +32,7 @@ macro_rules! impl_hash_benchmark { // - The name of the benchmark function // - The input(s) to the hash function for that benchmark function $( - $fn_name:ident => ($($input:expr),* $(,)?) + $fn_name:ident => ($($input:expr_2021),* $(,)?) ),* $(,)? } )*) => { diff --git a/benches/criterion/cuprate-rpc-types/benches/serde.rs b/benches/criterion/cuprate-rpc-types/benches/serde.rs index 8c3c3cf78..2f6124862 100644 --- a/benches/criterion/cuprate-rpc-types/benches/serde.rs +++ b/benches/criterion/cuprate-rpc-types/benches/serde.rs @@ -27,7 +27,7 @@ macro_rules! generate_serde_benchmarks { ( $( // The type to test => JSON of that type - $t:ty => $t_example:expr + $t:ty => $t_example:expr_2021 ),* $(,)? ) => { paste::paste! { // Generate the benchmarking functions.