Skip to main content

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};