[FR] Add native Zig bindings via top-level `bindings/zig`
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 42/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- cmake, cpp, github-actions, zig
- Domain
- build-system, testing, tooling
Research direction
Start by comparing the existing Python and Rust binding layouts, then read the top-level CMakeLists.txt and the proposed bindings/zig files. Verify the C adapter, Zig API, build integration, and listed CI commands against the scoped APIs. Done means the 14 unit tests, seven examples, zig build test, and ctest -R zig_bindings_tests pass.
Written by the indexing model from the issue text.
Description
Problem Statement
As Zig gains traction in systems programming, an increasing number of polyglot codebases (C++ and Zig) require uniform performance tracking. Currently, organizations using google/benchmark across their C++ infrastructure lack a native way to harness the same execution engine, CLI flags (--benchmark_filter), and structured output formats (JSON/CSV) for their Zig services.
While Zig has excellent C interop, creating bindings against a pure C++ library requires a thin adapter layer. We want to explore adding official, native Zig bindings to google/benchmark without disrupting the existing C++ developer experience, build architecture, or performance characteristics.
Proposed Architecture
The Zig bindings are hosted co-located within the main repository under bindings/zig/, following the same pattern as existing Python and Rust bindings.
Repository Layout
google/benchmark/
├── CMakeLists.txt
├── src/
├── include/
└── bindings/
├── python/
├── rust/
└── zig/
├── CMakeLists.txt
├── build.zig
├── build.zig.zon
└── src/
├── zig_api.h # C adapter header
├── zig_api.cc # C adapter implementation
├── benchmark.zig # Idiomatic Zig public API
└── benchmark_test.zig
Build Integration
- For Zig Users:
build.ziginvokes CMake to build libbenchmark + the C adapter together as a combined static archive, avoiding C++ ABI mismatches (libstdc++ vs libc++). - For C++ Users: An optional flag
-DBENCHMARK_ENABLE_ZIG_BINDINGS=ONlooks forzigand runs the Zig test suite during CI.
The Interop Layer
Since Zig has zero-cost C interop (no FFI bridge crate needed), we use a thin extern "C" adapter layer (zig_api.h/cc) that wraps C++ methods. Zig calls these via @cImport:
const c = @cImport(@cInclude("zig_api.h"));
// Benchmark registration with comptime trampoline
pub fn registerBenchmark(name: [*:0]const u8, comptime func: fn (*State) void) Benchmark {
const S = struct {
fn trampoline(state_ptr: ?*anyopaque) callconv(.c) void {
if (state_ptr) |ptr| {
var state = State{ .ptr = ptr };
func(&state);
}
}
};
return Benchmark{ .ptr = c.benchmark_zig_register_benchmark(name, &S.trampoline) };
}
The comptime trampoline generates a unique static function per benchmark at compile time — zero heap allocation, zero dynamic dispatch.
Public Zig API
fn my_benchmark(state: *benchmark.State) void {
while (state.keepRunning()) {
// your code to benchmark
}
}
pub fn main() void {
const args = std.process.argsAlloc(std.heap.page_allocator) catch return;
defer std.process.argsFree(std.heap.page_allocator, args);
benchmark.initialize(args);
_ = benchmark.registerBenchmark("BM_MyBenchmark", my_benchmark)
.range(8, 1 << 20)
.threads(4)
.unit(.microsecond);
_ = benchmark.run();
}
Key Design Decisions
- Opaque pointers (
void*) —StateandBenchmarkare passed as opaquevoid*through the C boundary. Zig wraps them in typed structs. This avoids fragile layout coupling to C++ internals. - Comptime trampolines — Each
registerBenchmarkcall generates a unique C-compatible callback at compile time, eliminating heap allocation and runtime dispatch. - Combined static archive — The C adapter is compiled with the same g++ as libbenchmark via CMake, ensuring a single consistent C++ ABI (no libstdc++/libc++ conflicts).
- String convention — Zig uses
[*:0]const u8(sentinel-terminated) at the boundary, enforcing null-termination at compile time.
Scope
Covered:
Initialize,RunSpecifiedBenchmarks,RegisterBenchmark,ClearRegisteredBenchmarks,AddCustomContextState:KeepRunning,KeepRunningBatch,PauseTiming,ResumeTiming,SkipWithError,SetBytesProcessed,SetItemsProcessed,SetLabel,SetComplexityN,range,iterations,threads,threadIndexBenchmarkbuilder:Arg,Range,DenseRange,Args,Unit,Threads,ThreadRange,MinTime,Iterations,Repetitions,UseRealTime,UseManualTime,Complexity- Enums:
TimeUnit,BigO
Not covered (yet): ComputeStatistics, Fixture, ScopedPauseTiming, custom reporters.
Testing
- 14 unit tests covering all bound APIs
- 7 usage examples (basic, throughput, parameterized, threaded, pause/resume, skip, etc.)
- CI integration via GitHub Actions (
zig build test) and CMake (ctest -R zig_bindings_tests)
Open Questions
- Thread safety: Is the current approach (opaque
void*+ comptime trampolines) sufficient, or do we need to expose additionalState/Benchmarkinternals for advanced use cases? - Fixture support: Would Zig users benefit from a
Fixture-like pattern (equivalent to C++BENCHMARK_F)?
AI Usage
Code was generated with AI assistance and reviewed by the contributor (as per AGENTS.md).
- Dominant language
- C++
- Stars
- 10.4k
- Forks
- 1.8k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 8
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from google/benchmark
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
Difficulty 4/5 3-5 days Newbie friendliness 64/100
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
-
Difficulty 3/5 1-2 days Newbie friendliness 58/100
All issues in google/benchmark
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 90/100
AXERA-TECH/ax-llm#77 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 90/100
games-on-whales/wolf#509 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
bug-unconfirmed
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
NVIDIA/cuda-samples#453 ·