| Crates.io | protest-criterion |
| lib.rs | protest-criterion |
| version | 1.1.0 |
| created_at | 2025-11-01 19:12:26.029043+00 |
| updated_at | 2025-11-02 17:28:15.214239+00 |
| description | Property-based benchmarking integration between Protest and Criterion |
| homepage | |
| repository | https://github.com/shrynx/protest |
| max_upload_size | |
| id | 1912321 |
| size | 50,213 |
Property-Based Benchmarking with Criterion
Integration between Protest property-based testing and Criterion benchmarking framework.
Property-based benchmarking allows you to:
Add to your Cargo.toml:
[dev-dependencies]
protest = "0.1"
protest-criterion = "0.1"
criterion = "0.5"
Create benches/my_benchmark.rs:
use criterion::{criterion_group, criterion_main, Criterion};
use protest_criterion::PropertyBencher;
use protest::primitives::IntGenerator;
fn bench_abs(c: &mut Criterion) {
c.bench_function_over_inputs(
"i32::abs",
|b, input: &i32| b.iter(|| input.abs()),
IntGenerator::new(-1000, 1000),
100, // number of samples
);
}
criterion_group!(benches, bench_abs);
criterion_main!(benches);
Run with:
cargo bench
Use bench_function_over_inputs to benchmark a function with diverse inputs:
use criterion::{criterion_group, criterion_main, Criterion};
use protest_criterion::PropertyBencher;
use protest::primitives::IntGenerator;
fn bench_multiplication(c: &mut Criterion) {
c.bench_function_over_inputs(
"i32 multiplication",
|b, input: &(i32, i32)| {
let (a, b_val) = *input;
b.iter(|| a * b_val)
},
protest::primitives::TupleGenerator::new((
IntGenerator::new(-1000, 1000),
IntGenerator::new(-1000, 1000),
)),
50,
);
}
criterion_group!(benches, bench_multiplication);
criterion_main!(benches);
Use bench_property to benchmark property checks:
use criterion::{criterion_group, criterion_main, Criterion};
use protest_criterion::PropertyBencher;
use protest::primitives::VecGenerator;
use protest::Generator;
fn bench_reverse_property(c: &mut Criterion) {
c.bench_property(
"vec reverse is involutive",
VecGenerator::new(
protest::primitives::IntGenerator::new(0, 100),
0,
1000,
),
|v: &Vec<i32>| {
let mut reversed = v.clone();
reversed.reverse();
reversed.reverse();
assert_eq!(v, &reversed);
},
100,
);
}
criterion_group!(benches, bench_reverse_property);
criterion_main!(benches);
Compare performance across different input sizes:
use criterion::{criterion_group, criterion_main, Criterion, BenchmarkId};
use protest_criterion::PropertyBenchmarkGroup;
use protest::primitives::{IntGenerator, VecGenerator};
use protest::Generator;
fn bench_sort_by_size(c: &mut Criterion) {
let mut group = c.benchmark_group("sort_by_size");
for size in [10, 100, 1000, 10000] {
let generator = VecGenerator::new(
IntGenerator::new(0, 1000),
size,
size,
);
group.bench_generated(
&size.to_string(),
generator,
|v: &Vec<i32>| {
let mut sorted = v.clone();
sorted.sort();
},
);
}
group.finish();
}
criterion_group!(benches, bench_sort_by_size);
criterion_main!(benches);
Benchmark sorting with various input distributions:
use criterion::{criterion_group, criterion_main, Criterion};
use protest_criterion::PropertyBencher;
use protest::primitives::{IntGenerator, VecGenerator};
use protest::Generator;
fn bench_sorting(c: &mut Criterion) {
// Random data
c.bench_property(
"sort/random",
VecGenerator::new(IntGenerator::new(0, 10000), 1000, 1000),
|v: &Vec<i32>| {
let mut sorted = v.clone();
sorted.sort();
},
50,
);
// Nearly sorted data
c.bench_property(
"sort/nearly_sorted",
VecGenerator::new(IntGenerator::new(0, 100), 1000, 1000),
|v: &Vec<i32>| {
let mut sorted = v.clone();
sorted.sort();
},
50,
);
}
criterion_group!(benches, bench_sorting);
criterion_main!(benches);
use criterion::{criterion_group, criterion_main, Criterion};
use protest_criterion::PropertyBencher;
use protest::primitives::StringGenerator;
fn bench_string_ops(c: &mut Criterion) {
c.bench_function_over_inputs(
"String::to_uppercase",
|b, input: &String| b.iter(|| input.to_uppercase()),
StringGenerator::new(10, 100),
50,
);
c.bench_function_over_inputs(
"String::contains",
|b, input: &String| b.iter(|| input.contains("test")),
StringGenerator::new(10, 1000),
50,
);
}
criterion_group!(benches, bench_string_ops);
criterion_main!(benches);
use criterion::{criterion_group, criterion_main, Criterion};
use protest_criterion::PropertyBencher;
use protest::primitives::{IntGenerator, HashMapGenerator, StringGenerator};
use std::collections::HashMap;
fn bench_hashmap(c: &mut Criterion) {
c.bench_property(
"HashMap insert and lookup",
HashMapGenerator::new(
StringGenerator::new(5, 20),
IntGenerator::new(0, 1000),
10,
100,
),
|map: &HashMap<String, i32>| {
let mut m = map.clone();
m.insert("test_key".to_string(), 42);
let _ = m.get("test_key");
},
50,
);
}
criterion_group!(benches, bench_hashmap);
criterion_main!(benches);
PropertyBencher TraitExtension trait for Criterion providing property-based benchmarking methods.
bench_function_over_inputsfn bench_function_over_inputs<I, G, F>(
&mut self,
name: &str,
bench_fn: F,
generator: G,
sample_count: usize,
) where
I: Clone + 'static,
G: Generator<I>,
F: FnMut(&mut criterion::Bencher, &I);
Benchmarks a function with inputs generated by a property-based generator.
Parameters:
name - Name of the benchmarkbench_fn - Function to benchmark (receives bencher and input reference)generator - Generator for creating test inputssample_count - Number of different inputs to generate and benchmarkbench_propertyfn bench_property<I, G, P>(
&mut self,
name: &str,
generator: G,
property: P,
sample_count: usize,
) where
I: Clone + 'static,
G: Generator<I>,
P: Fn(&I) + 'static;
Benchmarks a property test function.
Parameters:
name - Name of the benchmarkgenerator - Generator for creating test inputsproperty - Property function to benchmark (can include assertions)sample_count - Number of different inputs to generate and benchmarkPropertyBenchmarkGroup TraitExtension trait for BenchmarkGroup for more ergonomic grouped benchmarks.
bench_generatedfn bench_generated<I, G, F>(
&mut self,
id: &str,
generator: G,
f: F,
) where
I: Clone + 'static,
G: Generator<I>,
F: FnMut(&I) + 'static;
Benchmarks with generated inputs within a benchmark group.
// For fast operations, use more samples
c.bench_function_over_inputs(
"fast_op",
|b, input: &i32| b.iter(|| input + 1),
IntGenerator::new(0, 100),
200, // More samples for stable statistics
);
// For slow operations, fewer samples
c.bench_function_over_inputs(
"slow_op",
|b, input: &Vec<i32>| b.iter(|| expensive_operation(input)),
VecGenerator::new(IntGenerator::new(0, 1000), 1000, 1000),
20, // Fewer samples to keep benchmark time reasonable
);
// Good: Reflects real-world data
VecGenerator::new(IntGenerator::new(0, 1_000_000), 100, 10000)
// Bad: Unrealistic range
VecGenerator::new(IntGenerator::new(0, 10), 10, 10)
let mut group = c.benchmark_group("scenarios");
// Best case
group.bench_generated("best_case", sorted_generator, |v| {
let mut result = v.clone();
result.sort();
});
// Worst case
group.bench_generated("worst_case", reverse_sorted_generator, |v| {
let mut result = v.clone();
result.sort();
});
// Average case
group.bench_generated("average_case", random_generator, |v| {
let mut result = v.clone();
result.sort();
});
group.finish();
use rand::{SeedableRng, rngs::StdRng};
// For reproducible benchmarks
let mut rng = StdRng::seed_from_u64(42);
protest-criterion works seamlessly with all Protest generators:
IntGenerator, StringGenerator, BoolGenerator, etc.VecGenerator, HashMapGenerator, HashSetGeneratorTupleGenerator, OptionGeneratorGenerator<T>See protest documentation for available generators.
See the benches/ directory for complete examples:
example_benchmarks.rs - Basic usagecriterion::BatchSize appropriatelyfn bench_traditional(c: &mut Criterion) {
c.bench_function("sort", |b| {
b.iter(|| {
let mut v = vec![5, 2, 8, 1, 9];
v.sort();
});
});
}
Limitations:
fn bench_property_based(c: &mut Criterion) {
c.bench_property(
"sort",
VecGenerator::new(IntGenerator::new(0, 1000), 0, 1000),
|v: &Vec<i32>| {
let mut sorted = v.clone();
sorted.sort();
},
100, // Tests across 100 different inputs
);
}
Benefits:
Contributions are welcome! Please see the main Protest repository for contribution guidelines.
This project is licensed under the MIT License - see the LICENSE file for details.
Built on top of: