diff --git a/Cargo.lock b/Cargo.lock index 0799728..fa6ebcc 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 8891b83..5d77862 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 cbf8611..c5c6767 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. +### 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 +``` - -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. +## 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 0000000..eaa2647 --- /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 0000000..84585ee --- /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 0000000..93e209a --- /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 0000000..00561ee --- /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 0000000..30a852c --- /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 0000000..278ddd6 --- /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 0000000..beb360f --- /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 0000000..965117e --- /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 0000000..3f46ff6 --- /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 0000000..1f8edee --- /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 0000000..2987b72 --- /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()); +}