1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//! Hot-loop decode binary for targeted flamegraph profiling on the
//! direct-path (`UserSliceBackend`) decode. Mirrors the
//! `compare_ffi.rs` `pure_rust` arm — pre-sizes the target with
//! `WILDCOPY_OVERLENGTH` slack so the per-frame eligibility gate
//! sends the decode through `run_direct_decode`.
//!
//! Usage:
//! profile_decode_direct <compressed_blob> <decompressed_size> [iters]
//!
//! The custom binary avoids criterion's setup overhead (build_raw_dict,
//! reservation churn, page-fault noise) that drowned the decode hot
//! path in `cargo flamegraph --bench compare_ffi` runs.
use std::env;
use std::fs;
use structured_zstd::WILDCOPY_OVERLENGTH;
use structured_zstd::decoding::FrameDecoder;
fn main() {
let args: Vec<String> = env::args().collect();
let path = args
.get(1)
.expect("usage: profile_decode_direct <blob> <expected_size> [iters]");
let expected: usize = args
.get(2)
.expect("expected_size required")
.parse()
.expect("expected_size parse");
let iters: usize = args
.get(3)
.map(|s| s.parse().unwrap())
.unwrap_or(50_000)
.max(1);
let compressed = fs::read(path).expect("read");
// checked_add to reject adversarial `expected_size` inputs that
// would wrap `usize` when added to `WILDCOPY_OVERLENGTH`.
let target_len = expected
.checked_add(WILDCOPY_OVERLENGTH)
.expect("expected_size + WILDCOPY_OVERLENGTH overflows usize");
let mut target = vec![0u8; target_len];
// Pre-touch pages so kernel zero-init isn't in the flamegraph.
for slot in target.iter_mut().step_by(4096) {
*slot = 0;
}
let mut decoder = FrameDecoder::new();
// First iteration validates that the user's `expected_size` matches
// the actual decoded length — otherwise the timed loop below would
// silently profile a different workload than the user intended.
let first = decoder
.decode_all(compressed.as_slice(), &mut target)
.expect("decode_all");
assert_eq!(
first, expected,
"decoded size mismatch: expected {expected}, got {first} \
(compressed blob does not decode to the size argument passed)",
);
let t0 = std::time::Instant::now();
let mut written_total = first as u64;
std::hint::black_box(&target[..first]);
for _ in 1..iters {
let n = decoder
.decode_all(compressed.as_slice(), &mut target)
.expect("decode_all");
written_total = written_total.wrapping_add(n as u64);
std::hint::black_box(&target[..n]);
}
let elapsed = t0.elapsed();
// Compute per-iter time as f64 nanoseconds: `Duration / u32` would
// truncate `iters` on 64-bit platforms (and produce a divide-by-0
// panic when `iters % 2^32 == 0`).
let per_iter_ns = elapsed.as_nanos() as f64 / iters as f64;
eprintln!(
"iters={iters} elapsed={elapsed:.3?} per_iter={per_iter_ns:.1}ns total_written={written_total}"
);
}