Skip to content

Commit bfebe04

Browse files
authored
chore: add c++ vs rust benchmarks and just helpers (#68)
1 parent e03d030 commit bfebe04

3 files changed

Lines changed: 164 additions & 6 deletions

File tree

benches/fastpfor_benchmark.rs

Lines changed: 116 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,8 @@ fn benchmark_block_sizes(c: &mut Criterion) {
271271
for block_size in block_sizes {
272272
group.throughput(Throughput::Elements(size as u64));
273273
group.bench_function(format!("compress_{block_size}"), |b| {
274-
b.iter(|| {
275-
let mut codec = FastPFOR::new(DEFAULT_PAGE_SIZE, block_size);
276-
black_box(compress_data(&mut codec, black_box(&data)))
277-
});
274+
let mut codec = FastPFOR::new(DEFAULT_PAGE_SIZE, block_size);
275+
b.iter(|| black_box(compress_data(&mut codec, black_box(&data))));
278276
});
279277
}
280278

@@ -286,8 +284,8 @@ fn benchmark_block_sizes(c: &mut Criterion) {
286284

287285
group.throughput(Throughput::Elements(size as u64));
288286
group.bench_function(format!("decompress_{block_size}"), |b| {
287+
let mut codec = FastPFOR::new(DEFAULT_PAGE_SIZE, block_size);
289288
b.iter(|| {
290-
let mut codec = FastPFOR::new(DEFAULT_PAGE_SIZE, block_size);
291289
black_box(decompress_data(&mut codec, black_box(&compressed), size));
292290
});
293291
});
@@ -335,12 +333,124 @@ fn benchmark_compression_ratio(c: &mut Criterion) {
335333
group.finish();
336334
}
337335

336+
/// Compare encoding and decoding speed of the C++ `FastPFor128` codec against
337+
/// the pure-Rust `FastPFOR` codec with `BLOCK_SIZE_128`.
338+
///
339+
/// Both implementations process identical input data so the numbers are directly
340+
/// comparable. The benchmark is only compiled when the `cpp` feature is enabled.
341+
///
342+
/// Each sub-benchmark is labelled `cpp/<pattern>/<size>` or
343+
/// `rust/<pattern>/<size>` so Criterion can show them side-by-side.
344+
#[cfg(feature = "cpp")]
345+
fn benchmark_cpp_vs_rust(c: &mut Criterion) {
346+
use fastpfor::cpp::{Codec32, FastPFor128Codec};
347+
348+
fn cpp_encode(codec: &FastPFor128Codec, data: &[u32]) -> Vec<u32> {
349+
let mut out = vec![0u32; data.len() * 2 + 1024];
350+
let encoded = codec.encode32(data, &mut out).unwrap();
351+
let new_len = encoded.len();
352+
out.truncate(new_len);
353+
out
354+
}
355+
356+
fn cpp_decode(codec: &FastPFor128Codec, compressed: &[u32], original_len: usize) -> usize {
357+
let mut out = vec![0u32; original_len];
358+
codec.decode32(compressed, &mut out).unwrap().len()
359+
}
360+
361+
let patterns: &[(&str, DataGeneratorFn)] = &[
362+
(
363+
"uniform_small_value_distribution",
364+
generate_uniform_data_small_value_distribution,
365+
),
366+
(
367+
"uniform_large_value_distribution",
368+
generate_uniform_data_large_value_distribution,
369+
),
370+
("clustered", generate_clustered_data),
371+
("sequential", generate_sequential_data),
372+
("sparse", generate_sparse_data),
373+
];
374+
375+
// Encoding
376+
377+
let mut group = c.benchmark_group("cpp_vs_rust/encode");
378+
for &size in SIZES {
379+
for (name, generator) in patterns {
380+
let data = generator(size);
381+
group.throughput(Throughput::Elements(size as u64));
382+
383+
group.bench_with_input(
384+
BenchmarkId::new(format!("cpp/{name}"), size),
385+
&data,
386+
|b, data| {
387+
let codec = FastPFor128Codec::new();
388+
b.iter(|| black_box(cpp_encode(&codec, black_box(data))));
389+
},
390+
);
391+
392+
group.bench_with_input(
393+
BenchmarkId::new(format!("rust/{name}"), size),
394+
&data,
395+
|b, data| {
396+
let mut codec = FastPFOR::new(DEFAULT_PAGE_SIZE, BLOCK_SIZE_128);
397+
b.iter(|| black_box(compress_data(&mut codec, black_box(data))));
398+
},
399+
);
400+
}
401+
}
402+
group.finish();
403+
404+
// Decoding
405+
406+
let mut group = c.benchmark_group("cpp_vs_rust/decode");
407+
for &size in SIZES {
408+
for (name, generator) in patterns {
409+
let data = generator(size);
410+
411+
// Pre-compress once outside the timed loop for each implementation.
412+
let cpp_codec = FastPFor128Codec::new();
413+
let cpp_compressed = cpp_encode(&cpp_codec, &data);
414+
let rust_compressed = prepare_compressed_data(&data, BLOCK_SIZE_128);
415+
416+
group.throughput(Throughput::Elements(size as u64));
417+
418+
group.bench_with_input(
419+
BenchmarkId::new(format!("cpp/{name}"), size),
420+
&cpp_compressed,
421+
|b, compressed| {
422+
let codec = FastPFor128Codec::new();
423+
b.iter(|| black_box(cpp_decode(&codec, black_box(compressed), size)));
424+
},
425+
);
426+
427+
group.bench_with_input(
428+
BenchmarkId::new(format!("rust/{name}"), size),
429+
&rust_compressed,
430+
|b, compressed| {
431+
let mut codec = FastPFOR::new(DEFAULT_PAGE_SIZE, BLOCK_SIZE_128);
432+
b.iter(|| black_box(decompress_data(&mut codec, black_box(compressed), size)));
433+
},
434+
);
435+
}
436+
}
437+
group.finish();
438+
}
439+
338440
criterion_group!(
339441
benches,
340442
benchmark_compression,
341443
benchmark_decompression,
342444
benchmark_roundtrip,
343445
benchmark_block_sizes,
344-
benchmark_compression_ratio
446+
benchmark_compression_ratio,
345447
);
448+
449+
#[cfg(feature = "cpp")]
450+
criterion_group!(cpp_benches, benchmark_cpp_vs_rust);
451+
452+
#[cfg(not(feature = "cpp"))]
346453
criterion_main!(benches);
454+
455+
#[cfg(feature = "cpp")]
456+
criterion_main!(benches, cpp_benches);

benches/justfile

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env just --justfile
2+
3+
# Run all benchmarks (Rust only)
4+
all:
5+
cargo bench --bench fastpfor_benchmark --no-default-features --features rust
6+
7+
# Run all benchmarks with C++ comparison enabled using portable SIMD
8+
all-with-cpp-portable: (_all-with-cpp 'portable')
9+
10+
# Run all benchmarks with C++ comparison enabled using native SIMD
11+
all-with-cpp-native: (_all-with-cpp 'native')
12+
13+
_all-with-cpp mode:
14+
cargo bench --bench fastpfor_benchmark --features cpp_{{mode}},rust
15+
16+
# Benchmark compression speed across data patterns
17+
compression:
18+
cargo bench --bench fastpfor_benchmark --features rust -- compression
19+
20+
# Benchmark decompression speed across data patterns
21+
decompression:
22+
cargo bench --bench fastpfor_benchmark --features rust -- decompression
23+
24+
# Benchmark full compress+decompress roundtrip
25+
roundtrip:
26+
cargo bench --bench fastpfor_benchmark --features rust -- roundtrip
27+
28+
# Benchmark BLOCK_SIZE_128 vs BLOCK_SIZE_256
29+
block-sizes:
30+
cargo bench --bench fastpfor_benchmark --features rust -- block_sizes
31+
32+
# Benchmark compression ratio across data patterns
33+
compression-ratio:
34+
cargo bench --bench fastpfor_benchmark --features rust -- compression_ratio
35+
36+
# Benchmark C++ FastPFor128 vs pure-Rust FastPFOR
37+
cpp-vs-rust mode='portable':
38+
cargo bench --bench fastpfor_benchmark --features rust,cpp_{{mode}} -- cpp_vs_rust
39+
40+
# Benchmark C++ vs Rust encoding speed only
41+
cpp-vs-rust-encode mode='portable':
42+
cargo bench --bench fastpfor_benchmark --features rust,cpp_{{mode}} -- "cpp_vs_rust/encode"
43+
44+
# Benchmark C++ vs Rust decoding speed only
45+
cpp-vs-rust-decode mode='portable':
46+
cargo bench --bench fastpfor_benchmark --features rust,cpp_{{mode}} -- "cpp_vs_rust/decode"

justfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export RUSTFLAGS := env('RUSTFLAGS', if ci_mode == '1' {'-D warnings'} else {''}
1414
export RUSTDOCFLAGS := env('RUSTDOCFLAGS', if ci_mode == '1' {'-D warnings'} else {''})
1515
export RUST_BACKTRACE := env('RUST_BACKTRACE', if ci_mode == '1' {'1'} else {'0'})
1616

17+
mod bench 'benches/justfile'
18+
1719
@_default:
1820
{{just}} --list
1921

0 commit comments

Comments
 (0)