What changed, and why it matters
This commit is a routine refactoring of benchmark tests. It moves performance-measuring code out of the main bitcoin crate into a separate 'benches' crate and switches the benchmarking framework from Rust's built-in nightly-only 'test' harness to the popular 'Criterion' library. No application logic, security checks, or public APIs are changed.
No security action needed. This is a test/development infrastructure change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change removes #[cfg(bench)] bench modules from bitcoin/src/blockdata/{block,transaction,witness}.rs and bitcoin/src/lib.rs’s feature(test) gate, and creates a new workspace-excluded benches/Cargo.toml with equivalent Criterion benchmarks. It also removes the cfg(bench) check from unexpected_cfgs lint configuration. The benchmark code itself remains functionally equivalent, only the harness and crate layout differ.
Changed components
benches/bitcoin/block.rsbenches/bitcoin/transaction.rsbenches/bitcoin/witness.rsbenches/Cargo.tomlbitcoin/src/blockdata/block.rsbitcoin/src/blockdata/transaction.rsbitcoin/src/blockdata/witness.rsbitcoin/src/lib.rsCargo.tomlbitcoin/Cargo.tomlInspect captured patch +173 / −153
diff --git a/Cargo.toml b/Cargo.toml
index 15b963ed..eabacff1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,6 @@
[workspace]
members = ["addresses", "base58", "bitcoin", "chacha20_poly1305", "consensus_encoding", "fuzz", "hashes", "internals", "io", "p2p", "primitives", "units"]
+exclude = ["benches"]
resolver = "2"
# Keep this patch for hashes because secp256k1 depends on bitcoin-hashes via crates.io
diff --git a/benches/Cargo.toml b/benches/Cargo.toml
new file mode 100644
index 00000000..f3c609a4
--- /dev/null
+++ b/benches/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "bitcoin-benches"
+version = "0.1.0"
+license = "CC0-1.0"
+description = "Criterion benchmarks for rust-bitcoin"
+edition = "2021"
+
+[dependencies]
+bitcoin = { path = "../bitcoin", default-features = false, features = ["std"] }
+criterion = "0.7"
+hex_lit = "0.1.1"
+
+[[bench]]
+name = "block"
+path = "bitcoin/block.rs"
+harness = false
+
+[[bench]]
+name = "transaction"
+path = "bitcoin/transaction.rs"
+harness = false
+
+[[bench]]
+name = "witness"
+path = "bitcoin/witness.rs"
+harness = false
diff --git a/benches/bitcoin/block.rs b/benches/bitcoin/block.rs
new file mode 100644
index 00000000..559f945c
--- /dev/null
+++ b/benches/bitcoin/block.rs
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: CC0-1.0
+
+use std::hint::black_box;
+
+use bitcoin::blockdata::block::Block;
+use bitcoin::consensus::{deserialize, Decodable, Encodable};
+use bitcoin::io::sink;
+use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
+
+fn bench_block(c: &mut Criterion) {
+ let raw_block = include_bytes!("../../bitcoin/tests/data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw");
+ assert_eq!(raw_block.len(), 1_381_836);
+ let block: Block = deserialize(&raw_block[..]).unwrap();
+
+ let mut g = c.benchmark_group("block");
+ g.throughput(Throughput::Bytes(raw_block.len() as u64));
+
+ g.bench_function(BenchmarkId::new("stream_reader", "big"), |b| {
+ let big_block = black_box(raw_block.as_ref());
+ b.iter(|| {
+ let mut reader = big_block;
+ let blk = Block::consensus_decode(&mut reader).unwrap();
+ black_box(blk);
+ });
+ });
+
+ g.bench_function(BenchmarkId::new("serialize", "big"), |b| {
+ let mut data = Vec::with_capacity(raw_block.len());
+ b.iter(|| {
+ let result = block.consensus_encode(&mut data);
+ black_box(&result);
+ data.clear();
+ });
+ });
+
+ g.bench_function(BenchmarkId::new("serialize_logic", "big"), |b| {
+ b.iter(|| {
+ let size = block.consensus_encode(&mut sink());
+ let _ = black_box(size);
+ });
+ });
+
+ g.bench_function(BenchmarkId::new("deserialize", "big"), |b| {
+ b.iter(|| {
+ let blk: Block = deserialize(&raw_block[..]).unwrap();
+ black_box(blk);
+ });
+ });
+
+ g.finish();
+}
+
+criterion_group!(benches, bench_block);
+criterion_main!(benches);
diff --git a/benches/bitcoin/transaction.rs b/benches/bitcoin/transaction.rs
new file mode 100644
index 00000000..ab8101fa
--- /dev/null
+++ b/benches/bitcoin/transaction.rs
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: CC0-1.0
+
+use std::hint::black_box;
+
+use bitcoin::blockdata::transaction::Transaction;
+use bitcoin::blockdata::transaction::TransactionExt as _; // for total_size()
+use bitcoin::consensus::{encode, Encodable};
+use bitcoin::io::sink;
+use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
+
+const SOME_TX: &str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";
+
+fn bench_tx(c: &mut Criterion) {
+ let mut g = c.benchmark_group("transaction");
+
+ g.bench_function(BenchmarkId::new("size", "some"), |b| {
+ let mut tx: Transaction = encode::deserialize_hex(SOME_TX).unwrap();
+ b.iter(|| {
+ black_box(black_box(&mut tx).total_size());
+ });
+ });
+
+ g.bench_function(BenchmarkId::new("serialize", "some"), |b| {
+ let tx: Transaction = encode::deserialize_hex(SOME_TX).unwrap();
+ let mut data = Vec::with_capacity(SOME_TX.len() / 2);
+ b.iter(|| {
+ let result = tx.consensus_encode(&mut data);
+ black_box(&result);
+ data.clear();
+ });
+ });
+
+ g.bench_function(BenchmarkId::new("serialize_logic", "some"), |b| {
+ let tx: Transaction = encode::deserialize_hex(SOME_TX).unwrap();
+ b.iter(|| {
+ let size = tx.consensus_encode(&mut sink());
+ let _ = black_box(size);
+ });
+ });
+
+ g.bench_function(BenchmarkId::new("deserialize", "raw_bytes"), |b| {
+ let raw_tx = hex_lit::hex!(SOME_TX);
+ b.iter(|| {
+ let tx: Transaction = encode::deserialize(&raw_tx).unwrap();
+ black_box(tx);
+ });
+ });
+
+ g.bench_function(BenchmarkId::new("deserialize_hex", "string"), |b| {
+ b.iter(|| {
+ let tx: Transaction = encode::deserialize_hex(SOME_TX).unwrap();
+ black_box(tx);
+ });
+ });
+
+ g.finish();
+}
+
+criterion_group!(benches, bench_tx);
+criterion_main!(benches);
diff --git a/benches/bitcoin/witness.rs b/benches/bitcoin/witness.rs
new file mode 100644
index 00000000..22adf16f
--- /dev/null
+++ b/benches/bitcoin/witness.rs
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: CC0-1.0
+
+use std::hint::black_box;
+
+use bitcoin::blockdata::witness::Witness;
+use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
+
+fn bench_witness(c: &mut Criterion) {
+ let mut g = c.benchmark_group("witness");
+
+ g.bench_function(BenchmarkId::new("to_vec", "big"), |b| {
+ let raw_witness = [[1u8]; 5];
+ let witness = Witness::from_slice(&raw_witness);
+ b.iter(|| {
+ black_box(witness.to_vec());
+ });
+ });
+
+ g.bench_function(BenchmarkId::new("to_vec", "small"), |b| {
+ let raw_witness = vec![vec![1u8]; 3];
+ let witness = Witness::from_slice(&raw_witness);
+ b.iter(|| {
+ black_box(witness.to_vec());
+ });
+ });
+
+ g.finish();
+}
+
+criterion_group!(benches, bench_witness);
+criterion_main!(benches);
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 6a51f436..99b56511 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -100,4 +100,4 @@ name = "serde"
required-features = ["std", "serde"]
[lints.rust]
-unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(fuzzing)', 'cfg(kani)'] }
+unexpected_cfgs = { level = "deny", check-cfg = ['cfg(fuzzing)', 'cfg(kani)'] }
diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs
index f84cc826..5725d05b 100644
--- a/bitcoin/src/blockdata/block.rs
+++ b/bitcoin/src/blockdata/block.rs
@@ -926,62 +926,3 @@ mod tests {
assert_eq!(coinbase.compute_txid().to_string(), cb_txid);
}
}
-
-#[cfg(bench)]
-mod benches {
- use io::sink;
- use test::{black_box, Bencher};
-
- use super::Block;
- use crate::consensus::{deserialize, Decodable, Encodable};
-
- #[bench]
- pub fn bench_stream_reader(bh: &mut Bencher) {
- let big_block = include_bytes!("../../tests/data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw");
- assert_eq!(big_block.len(), 1_381_836);
- let big_block = black_box(big_block);
-
- bh.iter(|| {
- let mut reader = &big_block[..];
- let block = Block::consensus_decode(&mut reader).unwrap();
- black_box(&block);
- });
- }
-
- #[bench]
- pub fn bench_block_serialize(bh: &mut Bencher) {
- let raw_block = include_bytes!("../../tests/data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw");
-
- let block: Block = deserialize(&raw_block[..]).unwrap();
-
- let mut data = Vec::with_capacity(raw_block.len());
-
- bh.iter(|| {
- let result = block.consensus_encode(&mut data);
- black_box(&result);
- data.clear();
- });
- }
-
- #[bench]
- pub fn bench_block_serialize_logic(bh: &mut Bencher) {
- let raw_block = include_bytes!("../../tests/data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw");
-
- let block: Block = deserialize(&raw_block[..]).unwrap();
-
- bh.iter(|| {
- let size = block.consensus_encode(&mut sink());
- black_box(&size);
- });
- }
-
- #[bench]
- pub fn bench_block_deserialize(bh: &mut Bencher) {
- let raw_block = include_bytes!("../../tests/data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw");
-
- bh.iter(|| {
- let block: Block = deserialize(&raw_block[..]).unwrap();
- black_box(&block);
- });
- }
-}
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index 55da0114..71e310a3 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -2139,64 +2139,3 @@ mod tests {
assert_eq!(coinbase_owned.wtxid(), Wtxid::COINBASE);
}
}
-
-#[cfg(bench)]
-mod benches {
- use io::sink;
- use test::{black_box, Bencher};
-
- use super::*;
- use crate::consensus::{encode, Encodable};
-
- const SOME_TX: &str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";
-
- #[bench]
- pub fn bench_transaction_size(bh: &mut Bencher) {
- let mut tx: Transaction = encode::deserialize_hex(SOME_TX).unwrap();
-
- bh.iter(|| {
- black_box(black_box(&mut tx).total_size());
- });
- }
-
- #[bench]
- pub fn bench_transaction_serialize(bh: &mut Bencher) {
- let tx: Transaction = encode::deserialize_hex(SOME_TX).unwrap();
- let mut data = Vec::with_capacity(SOME_TX.len());
-
- bh.iter(|| {
- let result = tx.consensus_encode(&mut data);
- black_box(&result);
- data.clear();
- });
- }
-
- #[bench]
- pub fn bench_transaction_serialize_logic(bh: &mut Bencher) {
- let tx: Transaction = encode::deserialize_hex(SOME_TX).unwrap();
-
- bh.iter(|| {
- let size = tx.consensus_encode(&mut sink());
- black_box(&size);
- });
- }
-
- #[bench]
- pub fn bench_transaction_deserialize(bh: &mut Bencher) {
- // hex_lit does not work in bench code for some reason. Perhaps criterion fixes this.
- let raw_tx = <Vec<u8> as hex::FromHex>::from_hex(SOME_TX).unwrap();
-
- bh.iter(|| {
- let tx: Transaction = encode::deserialize(&raw_tx).unwrap();
- black_box(&tx);
- });
- }
-
- #[bench]
- pub fn bench_transaction_deserialize_hex(bh: &mut Bencher) {
- bh.iter(|| {
- let tx: Transaction = encode::deserialize_hex(SOME_TX).unwrap();
- black_box(&tx);
- });
- }
-}
diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs
index 2034aa55..6c8f6ea7 100644
--- a/bitcoin/src/blockdata/witness.rs
+++ b/bitcoin/src/blockdata/witness.rs
@@ -514,30 +514,3 @@ mod test {
assert!(deserialize::<Witness>(&bytes).is_err()); // OversizedVectorAllocation
}
}
-
-#[cfg(bench)]
-mod benches {
- use test::{black_box, Bencher};
-
- use super::{Witness, WitnessExt};
-
- #[bench]
- pub fn bench_big_witness_to_vec(bh: &mut Bencher) {
- let raw_witness = [[1u8]; 5];
- let witness = Witness::from_slice(&raw_witness);
-
- bh.iter(|| {
- black_box(witness.to_vec());
- });
- }
-
- #[bench]
- pub fn bench_witness_to_vec(bh: &mut Bencher) {
- let raw_witness = vec![vec![1u8]; 3];
- let witness = Witness::from_slice(&raw_witness);
-
- bh.iter(|| {
- black_box(witness.to_vec());
- });
- }
-}
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index fbf80355..93c6529c 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -27,14 +27,12 @@
// Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_notable_trait))]
-#![cfg_attr(bench, feature(test))]
// Coding conventions.
#![warn(missing_docs)]
#![warn(deprecated_in_future)]
#![doc(test(attr(warn(unused))))]
// Instead of littering the codebase for non-fuzzing and bench code just globally allow.
#![cfg_attr(fuzzing, allow(dead_code, unused_imports))]
-#![cfg_attr(bench, allow(dead_code, unused_imports))]
// Exclude lints we don't think are valuable.
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
#![allow(clippy::manual_range_contains)] // More readable than clippy's format.
@@ -56,9 +54,6 @@ internals::const_assert!(
"platforms that have usize less than 32 bits are not supported"
);
-#[cfg(bench)]
-extern crate test;
-
#[macro_use]
extern crate alloc;
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.