---
title: "Técnicas de Benchmarking em Zig: Medindo Performance Corretamente"
url: "https://ziglang.com.br/tutoriais/zig-benchmarking/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-benchmarking.MD"
description: "Aprenda a medir performance corretamente em Zig. Timer, benchmarking manual, armadilhas de medicao e comparacao de modos de otimizacao. Tutorial completo."
date: "2026-02-21"
author: ""
---

# Técnicas de Benchmarking em Zig: Medindo Performance Corretamente

Aprenda a medir performance corretamente em Zig. Timer, benchmarking manual, armadilhas de medicao e comparacao de modos de otimizacao. Tutorial completo.


"Se voce nao mede, voce nao sabe." Essa maxima e especialmente verdadeira em otimizacao de performance. Antes de otimizar qualquer coisa, voce precisa saber medir corretamente. Neste artigo, exploramos tecnicas de benchmarking em Zig que produzem resultados confiaveis e reproduziveis.

> Primeiro artigo da serie [Otimizacao de Performance em Zig](/tutoriais/zig-performance/).

## Por Que Benchmarking e Dificil

Medir performance de forma precisa e surpreendentemente complicado:

- **Cache do processador** muda resultados entre execucoes
- **Branch prediction** favorece caminhos executados repetidamente
- **Frequency scaling** da CPU altera a velocidade durante a execucao
- **Otimizacoes do compilador** podem eliminar codigo "morto"
- **Outros processos** competem por recursos

## Timer de Alta Resolucao

### std.time.Timer

O Zig oferece um timer de alta resolucao para benchmarking:

```zig
const std = @import("std");

pub fn benchmark(comptime func: fn () void) struct { min: u64, max: u64, media: u64 } {
    const N = 1000;
    var tempos: [N]u64 = undefined;

    for (&tempos) |*t| {
        var timer = std.time.Timer.start() catch unreachable;
        func();
        t.* = timer.read(); // Nanosegundos
    }

    // Ordenar para calcular percentis
    std.mem.sort(u64, &tempos, {}, std.sort.asc(u64));

    var soma: u64 = 0;
    for (tempos) |t| soma += t;

    return .{
        .min = tempos[0],
        .max = tempos[N - 1],
        .media = soma / N,
    };
}

test "benchmark soma de array" {
    const dados = blk: {
        var arr: [10_000]u32 = undefined;
        for (&arr, 0..) |*v, i| v.* = @intCast(i);
        break :blk arr;
    };

    const resultado = benchmark(struct {
        fn run() void {
            var soma: u64 = 0;
            for (&dados) |v| soma += v;
            std.mem.doNotOptimizeAway(soma);
        }
    }.run);

    std.debug.print(
        \\Benchmark soma_array:
        \\  Min:   {d} ns
        \\  Media: {d} ns
        \\  Max:   {d} ns
        \\
    , .{ resultado.min, resultado.media, resultado.max });
}
```

### Evitando que o Compilador Elimine seu Codigo

O maior erro em benchmarking e o compilador otimizar o codigo que voce quer medir:

```zig
const std = @import("std");

fn somaIngenua(dados: []const u32) u64 {
    var soma: u64 = 0;
    for (dados) |v| soma += v;
    return soma;
}

test "benchmark ERRADO — compilador pode eliminar" {
    const dados = [_]u32{ 1, 2, 3, 4, 5 } ** 2000;

    var timer = try std.time.Timer.start();

    // PROBLEMA: resultado nao e usado, compilador pode eliminar tudo
    _ = somaIngenua(&dados);

    const elapsed = timer.read();
    std.debug.print("Tempo: {d} ns\n", .{elapsed});
}

test "benchmark CORRETO — previne eliminacao" {
    const dados = [_]u32{ 1, 2, 3, 4, 5 } ** 2000;

    var timer = try std.time.Timer.start();

    const resultado = somaIngenua(&dados);

    // doNotOptimizeAway previne que o compilador elimine o calculo
    std.mem.doNotOptimizeAway(resultado);

    const elapsed = timer.read();
    std.debug.print("Tempo: {d} ns, resultado: {d}\n", .{ elapsed, resultado });
}
```

## Framework de Benchmarking Robusto

### Benchmark com Warmup e Estatisticas

```zig
const std = @import("std");

const BenchmarkConfig = struct {
    warmup_iterations: u32 = 100,
    iterations: u32 = 10_000,
    nome: []const u8 = "benchmark",
};

const BenchmarkResult = struct {
    nome: []const u8,
    min_ns: u64,
    max_ns: u64,
    media_ns: u64,
    mediana_ns: u64,
    p95_ns: u64,
    p99_ns: u64,
    throughput_ops_s: f64,

    pub fn imprimir(self: BenchmarkResult) void {
        std.debug.print(
            \\=== {s} ===
            \\  Min:       {d:>10} ns
            \\  Mediana:   {d:>10} ns
            \\  Media:     {d:>10} ns
            \\  P95:       {d:>10} ns
            \\  P99:       {d:>10} ns
            \\  Max:       {d:>10} ns
            \\  Throughput: {d:.2} ops/s
            \\
        , .{
            self.nome,
            self.min_ns,
            self.mediana_ns,
            self.media_ns,
            self.p95_ns,
            self.p99_ns,
            self.max_ns,
            self.throughput_ops_s,
        });
    }
};

fn runBenchmark(config: BenchmarkConfig, comptime func: fn () void) BenchmarkResult {
    // Warmup: aquecer caches e branch predictor
    for (0..config.warmup_iterations) |_| {
        func();
    }

    // Medicao real
    const allocator = std.heap.page_allocator;
    const tempos = allocator.alloc(u64, config.iterations) catch unreachable;
    defer allocator.free(tempos);

    for (tempos) |*t| {
        var timer = std.time.Timer.start() catch unreachable;
        func();
        t.* = timer.read();
    }

    // Ordenar para percentis
    std.mem.sort(u64, tempos, {}, std.sort.asc(u64));

    var soma: u128 = 0;
    for (tempos) |t| soma += t;

    const media = @as(u64, @intCast(soma / config.iterations));

    return .{
        .nome = config.nome,
        .min_ns = tempos[0],
        .max_ns = tempos[config.iterations - 1],
        .media_ns = media,
        .mediana_ns = tempos[config.iterations / 2],
        .p95_ns = tempos[@as(usize, @intFromFloat(@as(f64, @floatFromInt(config.iterations)) * 0.95))],
        .p99_ns = tempos[@as(usize, @intFromFloat(@as(f64, @floatFromInt(config.iterations)) * 0.99))],
        .throughput_ops_s = if (media > 0) 1_000_000_000.0 / @as(f64, @floatFromInt(media)) else 0,
    };
}
```

## Comparando Implementacoes

### A/B Testing de Algoritmos

```zig
const std = @import("std");

/// Busca linear — O(n)
fn buscaLinear(dados: []const u32, alvo: u32) ?usize {
    for (dados, 0..) |v, i| {
        if (v == alvo) return i;
    }
    return null;
}

/// Busca binaria — O(log n)
fn buscaBinaria(dados: []const u32, alvo: u32) ?usize {
    var low: usize = 0;
    var high: usize = dados.len;

    while (low < high) {
        const mid = low + (high - low) / 2;
        if (dados[mid] == alvo) return mid;
        if (dados[mid] < alvo) {
            low = mid + 1;
        } else {
            high = mid;
        }
    }
    return null;
}

test "comparar busca linear vs binaria" {
    // Dados ordenados
    const N = 100_000;
    var dados: [N]u32 = undefined;
    for (&dados, 0..) |*v, i| v.* = @intCast(i * 2);

    const alvo: u32 = N; // Elemento no meio

    const resultado_linear = runBenchmark(.{
        .nome = "Busca Linear",
        .iterations = 1000,
    }, struct {
        fn run() void {
            const r = buscaLinear(&dados, alvo);
            std.mem.doNotOptimizeAway(r);
        }
    }.run);

    const resultado_binaria = runBenchmark(.{
        .nome = "Busca Binaria",
        .iterations = 1000,
    }, struct {
        fn run() void {
            const r = buscaBinaria(&dados, alvo);
            std.mem.doNotOptimizeAway(r);
        }
    }.run);

    resultado_linear.imprimir();
    resultado_binaria.imprimir();

    // Busca binaria deve ser significativamente mais rapida
    std.debug.print("Speedup: {d:.1}x\n", .{
        @as(f64, @floatFromInt(resultado_linear.media_ns)) /
            @as(f64, @floatFromInt(resultado_binaria.media_ns)),
    });
}
```

## Modos de Otimizacao do Zig

### Comparando Debug, ReleaseSafe, ReleaseFast e ReleaseSmall

```zig
const std = @import("std");

/// Funcao computacionalmente intensiva
fn fibonacci(n: u64) u64 {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

test "benchmark fibonacci nos diferentes modos" {
    // Este teste deve ser executado com cada modo:
    // zig build test -Doptimize=Debug
    // zig build test -Doptimize=ReleaseSafe
    // zig build test -Doptimize=ReleaseFast
    // zig build test -Doptimize=ReleaseSmall

    const modo = if (@import("builtin").mode == .Debug)
        "Debug"
    else if (@import("builtin").mode == .ReleaseSafe)
        "ReleaseSafe"
    else if (@import("builtin").mode == .ReleaseFast)
        "ReleaseFast"
    else
        "ReleaseSmall";

    var timer = try std.time.Timer.start();
    const resultado = fibonacci(35);
    const elapsed = timer.read();

    std.mem.doNotOptimizeAway(resultado);

    std.debug.print("[{s}] fibonacci(35) = {d} em {d} ms\n", .{
        modo,
        resultado,
        elapsed / std.time.ns_per_ms,
    });
}
```

Resultados tipicos (Apple M2):

| Modo | fibonacci(35) | Tempo |
|------|--------------|-------|
| Debug | 9227465 | ~850 ms |
| ReleaseSafe | 9227465 | ~45 ms |
| ReleaseFast | 9227465 | ~35 ms |
| ReleaseSmall | 9227465 | ~48 ms |

## Armadilhas Comuns

### 1. Nao Aquecer Caches

```zig
// ERRADO: primeira iteracao sera lenta (cold cache)
var timer = try std.time.Timer.start();
for (0..100) |_| func();
const total = timer.read();

// CORRETO: warmup antes de medir
for (0..100) |_| func(); // Warmup
var timer = try std.time.Timer.start();
for (0..100) |_| func(); // Medicao
const total = timer.read();
```

### 2. Medir Apenas a Media

```zig
// ERRADO: media esconde outliers
const media = total_ns / iteracoes;

// CORRETO: reportar percentis
// Min, P50 (mediana), P95, P99, Max
// P99 e o que realmente importa em producao
```

### 3. Benchmarking com Dados Irrealistas

```zig
// ERRADO: dados sequenciais sempre cabem no cache
const dados = [_]u32{1, 2, 3, 4, 5} ** 1000;

// CORRETO: dados aleatorios simulam cenario real
var rng = std.Random.DefaultPrng.init(42);
var dados: [5000]u32 = undefined;
for (&dados) |*v| v.* = rng.random().int(u32);
```

## Conclusao

Benchmarking correto e o fundamento de qualquer esforço de otimizacao. Sem medicoes confiaveis, voce pode acabar otimizando codigo que nao importa ou piorando performance sem perceber. Use as tecnicas deste artigo para estabelecer uma baseline solida antes de aplicar as otimizacoes que veremos nos proximos artigos.

## Proximo Artigo

No [Artigo 2: Codigo Cache-Friendly](/tutoriais/zig-performance/artigo-2-cache-friendly/), exploramos como o layout de dados em memoria afeta dramaticamente a performance.

## Conteudo Relacionado

- [Codigo Cache-Friendly em Zig](/tutoriais/zig-performance/artigo-2-cache-friendly/) — Proximo artigo
- [Profiling e Benchmarks em Zig](/tutoriais/zig-profiling-benchmarks/) — Tutorial basico
- [Otimizacao Real: Estudo de Caso](/tutoriais/zig-performance/artigo-5-real-world-optimization/) — Caso pratico
- [Zig em Fintech e Trading](/cases/case-zig-fintech/) — Performance em producao
