Skip to content

Repository files navigation

p99.Zig

Language License GitHub release Last Commit CI

Very low-cost measuring of performance percentiles, for Zig

Table of Contents

Introduction

p99 is a lightweight, low-overhead library designed for generating real-time performance percentiles in high-frequency or latency-sensitive environments.

p99.Zig is the Zig implementation.

How It Works

p99.Zig uses a fixed-size, zero-allocation logarithmic histogram with exactly 64 buckets.

Each bucket represents a power-of-two range of nanoseconds:

  • Bucket 0 represents [0, 1] nanoseconds;
  • Bucket 1 represents [2, 3] nanoseconds;
  • Bucket 2 represents [4, 7] nanoseconds;
  • ...
  • Bucket 63 represents [2^63, 2^64 - 1] nanoseconds.

Finding the bucket index is extremely fast and branchless, implemented using the CPU's count leading zeros instruction (@clz).

When querying percentiles (e.g., P50, P99), the library performs linear interpolation within the target bucket to approximate the duration with high accuracy.

Installation

The recommended way to add p99.Zig to your project is using the zig fetch command. Run the following in your project root:

zig fetch --save https://github.com/synesissoftware/p99.Zig/archive/refs/tags/v0.0.1.tar.gz

This will automatically download the package, compute its hash, and add it to your build.zig.zon dependencies:

.{
    .name = .my_project,
    .version = "0.1.0",
    .dependencies = .{
        .p99 = .{
            .url = "https://github.com/synesissoftware/p99.Zig/archive/refs/tags/v0.0.1.tar.gz",
            .hash = "1220...", // Automatically calculated by zig fetch
        },
    },
}

Then, expose the dependency in your build.zig:

const p99_dep = b.dependency("p99", .{
    .target = target,
    .optimize = optimize,
});
exe.root_module.addImport("p99", p99_dep.module("p99"));

Build Options

p99.Zig supports the following compile-time build options:

  • binary-scaling (bool, default: false): Replaces integer division in the integer-based percentile methods (valueAtP90, valueAtP95, valueAtP99, etc.) with $2^{32}$ fixed-point binary scaling. Each percentile multiplier (e.g., 0.90 for p90) is pre-encoded as a u32 constant and the target rank is computed via a single multiplication and a 32-bit right-shift, avoiding the cost of integer division entirely. This yields a significant speedup for percentile queries with a negligible loss of accuracy (the scaled multiplier differs from the true value by less than $10^{-9}$). The generic valueAtPercentile(f64) method is unaffected by this feature.

To enable this option in your project, pass it when fetching the dependency in your build.zig:

const p99_dep = b.dependency("p99", .{
    .target = target,
    .optimize = optimize,
    .@"binary-scaling" = true,
});

Minimal Example

Here is a minimal example demonstrating how to use p99.Zig:

const std = @import("std");
const p99 = @import("p99");

pub fn main(init: std.process.Init) !void {
    var buffer: [4096]u8 = undefined;
    var stdout_impl = std.Io.File.stdout().writer(init.io, &buffer);
    const stdout = &stdout_impl.interface;

    var h = p99.Histogram{};

    // Push events (durations in nanoseconds)
    _ = h.pushEventTimeNs(100);
    _ = h.pushEventTimeNs(250);
    _ = h.pushEventTimeNs(500);
    _ = h.pushEventTimeNs(1000);
    _ = h.pushEventTimeNs(5000);

    // Query percentiles
    const p50 = h.valueAtP50().?;
    const p99_val = h.valueAtP99().?;

    try stdout.print("P50: {d} ns\n", .{p50});
    try stdout.print("P99: {d} ns\n", .{p99_val});

    try stdout.flush();
}

Benchmarks

A benchmark suite is included in benches/benchmark_histogram.zig to measure performance.

To run the benchmarks in ReleaseFast mode (without binary scaling):

zig build bench -Doptimize=ReleaseFast

To run the benchmarks with the binary-scaling optimization enabled:

zig build bench -Dbinary-scaling=true -Doptimize=ReleaseFast

Project Information

Where to get help

GitHub Page

Contribution guidelines

Defect reports, feature requests, and pull requests are welcome on https://github.com/synesissoftware/p99.Zig.

Dependencies

p99.Zig has no (non-development) dependencies beyond the Zig standard library.

Dev Dependencies

p99.Zig has no development dependencies beyond the Zig standard library.

Related Projects

Other implementations of the p99 specification include:

License

p99.Zig is released under the 3-clause BSD license. See LICENSE for details.

About

Very low-cost measuring of performance percentiles, for Zig

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages