fluxbench_core/lib.rs
1#![warn(missing_docs)]
2//! FluxBench Core - Worker Runtime
3//!
4//! This crate provides the execution environment for benchmarks:
5//! - `Bencher` struct for iteration-based benchmarking
6//! - Timing: `std::time::Instant` per timed region, with the empty-timer overhead subtracted.
7//! Cycle counts come from RDTSCP (x86_64) or CNTVCT_EL0 (aarch64), and are 0 elsewhere
8//! - Global allocator interceptor for memory tracking
9//! - CPU affinity pinning for stable cycle-counter readings
10//!
11//! The `async` feature (default) adds `iter_async` and `drive_async_body` on tokio.
12//! Without it, the crate has no tokio dependency.
13
14mod allocator;
15mod bencher;
16mod group_config;
17mod measure;
18mod shutdown;
19mod worker;
20
21pub use allocator::{TrackingAllocator, current_allocation, reset_allocation_counter};
22#[cfg(feature = "async")]
23pub use bencher::drive_async_body;
24pub use bencher::{
25 BatchSize, Bencher, BenchmarkResult, DEFAULT_SAMPLE_COUNT, IterationMode, LARGE_INPUT_CHUNK,
26 MIN_SAMPLE_COUNT, MeasureConfig, MeasureError, SETUP_CHUNK, SampleSink, run_benchmark_loop,
27};
28pub use fluxbench_ipc::Throughput;
29pub use group_config::GroupConfig;
30/// Whether this platform provides hardware cycle counters (x86_64 RDTSCP or AArch64 CNTVCT_EL0).
31/// When `false`, cycle counts are reported as 0 and only wall-clock nanoseconds are available.
32pub use measure::HAS_CYCLE_COUNTER;
33pub use measure::Instant;
34pub use measure::Timer;
35pub use measure::{cycles_overhead, timer_overhead_ns};
36pub use worker::WorkerMain;
37
38/// Benchmark definition registered via `#[flux::bench]`
39#[derive(Debug, Clone)]
40pub struct BenchmarkDef {
41 /// Unique identifier
42 pub id: &'static str,
43 /// Human-readable name
44 pub name: &'static str,
45 /// Group this benchmark belongs to
46 pub group: &'static str,
47 /// Severity level for CI reporting
48 pub severity: Severity,
49 /// Per-benchmark regression threshold percentage (0.0 = use global threshold)
50 pub threshold: f64,
51 /// Time budget for the mean per-iteration time, in nanoseconds.
52 /// A mean above it fails the benchmark.
53 pub budget_ns: Option<u64>,
54 /// Tags for filtering
55 pub tags: &'static [&'static str],
56 /// Function pointer to the wrapper
57 pub runner_fn: fn(&mut Bencher),
58 /// Source file path
59 pub file: &'static str,
60 /// Source line number
61 pub line: u32,
62 /// Module path
63 pub module_path: &'static str,
64 /// Per-benchmark warmup override (nanoseconds)
65 pub warmup_ns: Option<u64>,
66 /// Per-benchmark measurement override (nanoseconds)
67 pub measurement_ns: Option<u64>,
68 /// Per-benchmark fixed mode: N samples of one iteration each, no warmup
69 pub samples: Option<u64>,
70 /// Per-benchmark target sample count. It overrides only the count.
71 pub sample_count: Option<u64>,
72 /// Per-benchmark fixed iterations per sample. `Some` selects fixed mode.
73 pub iters_per_sample: Option<u64>,
74 /// Per-benchmark minimum total measurement iterations
75 pub min_iterations: Option<u64>,
76 /// Per-benchmark maximum total measurement iterations
77 pub max_iterations: Option<u64>,
78 /// Per-benchmark work per call for throughput rates.
79 /// `Bencher::throughput` in the body overrides it.
80 pub throughput: Option<Throughput>,
81 /// Benchmark IDs that must run before this one
82 pub depends_on: &'static [&'static str],
83}
84
85/// Severity levels for CI integration
86#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
87pub enum Severity {
88 /// Critical benchmark - regression fails the build
89 Critical,
90 /// Warning level - logged but doesn't fail
91 Warning,
92 /// Informational only
93 Info,
94}
95
96/// Group definition registered via `#[flux::group]`.
97///
98/// `id` matches the `group` of `#[bench]`.
99/// The HTML report shows `description` and `tags` in the group's section.
100#[derive(Debug, Clone)]
101pub struct GroupDef {
102 /// Group identifier, as used by `#[bench(group = "...")]`
103 pub id: &'static str,
104 /// Description shown in the HTML report
105 pub description: &'static str,
106 /// Tags every benchmark in this group and its child groups carries for `--tag` and `--skip-tag`
107 pub tags: &'static [&'static str],
108 /// Parent group. `--group <parent>` also selects this group.
109 pub parent: Option<&'static str>,
110 /// Run config for every benchmark in this group and its child groups
111 pub config: GroupConfig,
112}
113
114/// Chart kind for `#[flux::chart]`
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum ChartType {
117 /// One bar per benchmark
118 Bar,
119 /// One point per benchmark, joined in the listed order
120 Line,
121}
122
123/// Chart definition registered via `#[flux::chart]`.
124///
125/// The HTML report draws it as an inline SVG.
126#[derive(Debug, Clone)]
127pub struct ChartDef {
128 /// Chart title
129 pub title: &'static str,
130 /// Chart kind
131 pub chart_type: ChartType,
132 /// Report metric to plot, e.g. `mean` or `p99`
133 pub metric: &'static str,
134 /// Benchmark IDs to plot, in order
135 pub benchmarks: &'static [&'static str],
136 /// Optional horizontal target line, in the metric's unit
137 pub target_line: Option<f64>,
138 /// Optional 1-based grid cell (row, col) in the `#[flux::report]` layout
139 pub position: Option<(u32, u32)>,
140}
141
142/// Report definition registered via `#[flux::report]`.
143///
144/// The HTML report takes its title and description from it.
145/// Charts sit in a `layout` grid of (rows, cols).
146#[derive(Debug, Clone)]
147pub struct ReportDef {
148 /// Report title
149 pub title: &'static str,
150 /// Report description shown under the title
151 pub description: &'static str,
152 /// Chart grid layout (rows, cols)
153 pub layout: (u32, u32),
154}
155
156/// Comparison group - groups multiple benchmarks for side-by-side comparison
157#[derive(Debug, Clone)]
158pub struct CompareDef {
159 /// Comparison identifier
160 pub id: &'static str,
161 /// Human-readable title
162 pub title: &'static str,
163 /// Benchmark IDs to compare
164 pub benchmarks: &'static [&'static str],
165 /// Optional baseline benchmark (first one if not specified)
166 pub baseline: Option<&'static str>,
167 /// Metric to compare (default: mean)
168 pub metric: &'static str,
169 /// Group for chart generation (comparisons with same group form a chart)
170 pub group: Option<&'static str>,
171 /// X-axis value for this comparison point (e.g., "1", "10", "100")
172 pub x: Option<&'static str>,
173 /// Series labels (display names) - must match benchmarks array length
174 /// If not specified, benchmark IDs are used as labels
175 pub series: Option<&'static [&'static str]>,
176}
177
178// Collect all registered benchmarks
179inventory::collect!(BenchmarkDef);
180inventory::collect!(GroupDef);
181inventory::collect!(ReportDef);
182inventory::collect!(ChartDef);
183inventory::collect!(CompareDef);
184
185/// Anchor to prevent LTO from stripping inventory entries
186#[used]
187#[doc(hidden)]
188pub static REGISTRY_ANCHOR: fn() = || {
189 for _ in inventory::iter::<BenchmarkDef> {}
190 for _ in inventory::iter::<GroupDef> {}
191 for _ in inventory::iter::<ReportDef> {}
192 for _ in inventory::iter::<ChartDef> {}
193 for _ in inventory::iter::<CompareDef> {}
194};