From a763182b9d4ec25f0136ef0600423b95623ff47a Mon Sep 17 00:00:00 2001 From: Aleksey Shakhmatov Date: Thu, 21 May 2026 08:13:18 +0300 Subject: [PATCH] feat: public API and build helper for consumers - root.zig: re-exports Benchmark, Suite, Options, Format, Result, BenchFn, parse_duration_ns; also pulls every submodule into the test block so `zig build test` covers them. - build_helper.zig: add_bench_step(b, opts) factory that creates the benchmark executable (ReleaseFast by default), wires zbench and any extra modules, attaches an addRunArtifact forwarding `--` args, and registers a `zig build ` step. --- src/build_helper.zig | 60 ++++++++++++++++++++++++++++++++++++++++++++ src/root.zig | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/build_helper.zig create mode 100644 src/root.zig diff --git a/src/build_helper.zig b/src/build_helper.zig new file mode 100644 index 0000000..e65de03 --- /dev/null +++ b/src/build_helper.zig @@ -0,0 +1,60 @@ +//! Build-system helper. Importable from a user's `build.zig` as +//! `@import("zbench_build")` (alongside `b.dependency("zbench", ...)`). +//! +//! Provides `add_bench_step`, a small factory that wires up a `zig build +//! bench` step around a user-written benchmark executable. + +const std = @import("std"); + +pub const BenchStepOptions = struct { + /// Build step name (e.g. "bench"). The `zig build ` invocation + /// triggers the run. + step_name: []const u8 = "bench", + /// Executable name. Defaults to `step_name`. + exe_name: ?[]const u8 = null, + /// Source file containing the user's `pub fn main(init)`. + root: std.Build.LazyPath, + target: std.Build.ResolvedTarget, + /// Optimize mode for the benchmark exe. ReleaseFast by default — there is + /// rarely a reason to benchmark debug builds. + optimize: std.builtin.OptimizeMode = .ReleaseFast, + /// The `zbench` module from `b.dependency("zbench", ...).module("zbench")`. + zbench: *std.Build.Module, + /// Extra modules to import into the benchmark exe (e.g. the code under + /// test). + extra_imports: []const ExtraImport = &.{}, + /// Step description shown by `zig build --help`. + description: []const u8 = "Run benchmarks", +}; + +pub const ExtraImport = struct { + name: []const u8, + module: *std.Build.Module, +}; + +/// Wire up a `zig build ` step that compiles the benchmark exe and +/// runs it, forwarding any CLI args after `--` to the benchmark. +/// +/// Returns the created `*Step.Compile` so the caller can attach further +/// configuration (link options, install rules, etc.) if needed. +pub fn add_bench_step(b: *std.Build, opts: BenchStepOptions) *std.Build.Step.Compile { + const exe = b.addExecutable(.{ + .name = opts.exe_name orelse opts.step_name, + .root_module = b.createModule(.{ + .root_source_file = opts.root, + .target = opts.target, + .optimize = opts.optimize, + }), + }); + exe.root_module.addImport("zbench", opts.zbench); + for (opts.extra_imports) |imp| { + exe.root_module.addImport(imp.name, imp.module); + } + + const run = b.addRunArtifact(exe); + if (b.args) |args| run.addArgs(args); + + const step = b.step(opts.step_name, opts.description); + step.dependOn(&run.step); + return exe; +} diff --git a/src/root.zig b/src/root.zig new file mode 100644 index 0000000..f9d5345 --- /dev/null +++ b/src/root.zig @@ -0,0 +1,44 @@ +//! zbench — Go-style benchmarking for Zig 0.16+. +//! +//! ```zig +//! const std = @import("std"); +//! const zbench = @import("zbench"); +//! +//! pub fn main(init: std.process.Init) !void { +//! var suite = zbench.Suite.init(init.gpa, init.io); +//! defer suite.deinit(); +//! +//! try suite.add("append", bench_append); +//! try suite.run_cli(init); +//! } +//! +//! fn bench_append(b: *zbench.Benchmark) !void { +//! var list: std.ArrayListUnmanaged(u8) = .empty; +//! defer list.deinit(b.allocator); +//! b.reset_timer(); +//! var i: u64 = 0; +//! while (i < b.n) : (i += 1) { +//! try list.append(b.allocator, @intCast(i & 0xff)); +//! } +//! b.keep(list.items); +//! } +//! ``` + +const bench = @import("benchmark.zig"); +const suite_mod = @import("suite.zig"); +const runner = @import("runner.zig"); + +pub const Benchmark = bench.Benchmark; +pub const BenchFn = bench.BenchFn; +pub const Suite = suite_mod.Suite; +pub const Options = suite_mod.Options; +pub const Format = suite_mod.Format; +pub const Result = runner.Result; +pub const parse_duration_ns = suite_mod.parse_duration_ns; + +test { + _ = @import("alloc.zig"); + _ = @import("stats.zig"); + _ = @import("runner.zig"); + _ = @import("suite.zig"); +}