std.time — Tempo e Medição de Performance
O módulo std.time fornece funcionalidades para trabalhar com tempo no Zig, incluindo medição de alta resolução, timestamps, conversões de unidades e delays. É fundamental para benchmarking, profiling, timeouts e qualquer operação que precise de referência temporal precisa.
Visão Geral
const std = @import("std");
const time = std.time;
O std.time usa nanossegundos como unidade base e fornece constantes de conversão para trabalhar com diferentes escalas temporais.
Constantes de Conversão
pub const ns_per_us: u64 = 1_000; // nanossegundos por microssegundo
pub const ns_per_ms: u64 = 1_000_000; // nanossegundos por milissegundo
pub const ns_per_s: u64 = 1_000_000_000; // nanossegundos por segundo
pub const ns_per_min: u64 = 60 * ns_per_s; // nanossegundos por minuto
pub const us_per_ms: u64 = 1_000; // microssegundos por milissegundo
pub const us_per_s: u64 = 1_000_000; // microssegundos por segundo
pub const ms_per_s: u64 = 1_000; // milissegundos por segundo
pub const s_per_min: u64 = 60;
pub const s_per_hour: u64 = 3_600;
pub const s_per_day: u64 = 86_400;
Funções Principais
// Timestamp em nanossegundos (relógio monotônico)
pub fn nanoTimestamp() i128
// Timestamp em milissegundos desde epoch
pub fn milliTimestamp() i64
// Timestamp em segundos desde epoch
pub fn timestamp() i64
// Suspende a execução por N nanossegundos
pub fn sleep(ns: u64) void
// Timer de alta resolução
pub const Timer = struct {
pub fn start() Timer
pub fn read(self: Timer) u64 // nanossegundos decorridos
pub fn reset(self: *Timer) void
pub fn lap(self: *Timer) u64 // lê e reseta
};
// Instant para medição monotônica
pub const Instant = struct {
pub fn now() ?Instant
pub fn since(self: Instant, earlier: Instant) u64
};
Exemplo 1: Timestamps e Conversões
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Diferentes resoluções de timestamp
const ts_s = std.time.timestamp();
const ts_ms = std.time.milliTimestamp();
const ts_ns = std.time.nanoTimestamp();
try stdout.writeAll("=== Timestamps ===\n");
try stdout.print("Segundos desde epoch: {d}\n", .{ts_s});
try stdout.print("Milissegundos desde epoch: {d}\n", .{ts_ms});
try stdout.print("Nanossegundos desde epoch: {d}\n", .{ts_ns});
// Conversão para componentes legíveis
const epoch_seconds: u64 = @intCast(ts_s);
const dias = epoch_seconds / std.time.s_per_day;
const horas_restantes = (epoch_seconds % std.time.s_per_day) / std.time.s_per_hour;
const minutos_restantes = (epoch_seconds % std.time.s_per_hour) / std.time.s_per_min;
const segundos_restantes = epoch_seconds % std.time.s_per_min;
try stdout.print("\nDias desde epoch: {d}\n", .{dias});
try stdout.print("Hora UTC: {d:0>2}:{d:0>2}:{d:0>2}\n", .{
horas_restantes,
minutos_restantes,
segundos_restantes,
});
// Epoch para data simples (cálculo aproximado)
const anos_aprox = dias / 365;
try stdout.print("Anos desde 1970: ~{d}\n", .{anos_aprox});
}
Exemplo 2: Timer de Alta Resolução
const std = @import("std");
fn fibonacci(n: u64) u64 {
if (n <= 1) return n;
var a: u64 = 0;
var b: u64 = 1;
for (2..n + 1) |_| {
const temp = a + b;
a = b;
b = temp;
}
return b;
}
fn bubbleSort(arr: []i32) void {
if (arr.len <= 1) return;
for (0..arr.len - 1) |i| {
for (0..arr.len - 1 - i) |j| {
if (arr[j] > arr[j + 1]) {
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.writeAll("=== Benchmarks com Timer ===\n\n");
// Benchmark: Fibonacci
var timer = try std.time.Timer.start();
const fib_resultado = fibonacci(40);
const fib_tempo = timer.read();
try stdout.print("Fibonacci(40) = {d}\n", .{fib_resultado});
try stdout.print("Tempo: {d} ns ({d:.3} ms)\n\n", .{
fib_tempo,
@as(f64, @floatFromInt(fib_tempo)) / @as(f64, @floatFromInt(std.time.ns_per_ms)),
});
// Benchmark: Bubble Sort
var dados: [1000]i32 = undefined;
var prng = std.Random.DefaultPrng.init(42);
const random = prng.random();
for (&dados) |*d| {
d.* = random.intRangeAtMost(i32, 0, 10000);
}
timer.reset();
bubbleSort(&dados);
const sort_tempo = timer.read();
try stdout.print("Bubble Sort (1000 elementos)\n", .{});
try stdout.print("Tempo: {d} ns ({d:.3} ms)\n", .{
sort_tempo,
@as(f64, @floatFromInt(sort_tempo)) / @as(f64, @floatFromInt(std.time.ns_per_ms)),
});
try stdout.print("Primeiro: {d}, Último: {d}\n", .{ dados[0], dados[999] });
}
Exemplo 3: Sleep e Intervalos
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.writeAll("=== Demonstração de Sleep ===\n\n");
// Sleep preciso com verificação
const intervalos = [_]struct { nome: []const u8, ns: u64 }{
.{ .nome = "100ms", .ns = 100 * std.time.ns_per_ms },
.{ .nome = "250ms", .ns = 250 * std.time.ns_per_ms },
.{ .nome = "500ms", .ns = 500 * std.time.ns_per_ms },
};
for (intervalos) |intervalo| {
var timer = try std.time.Timer.start();
std.time.sleep(intervalo.ns);
const real = timer.read();
const esperado_ms = @as(f64, @floatFromInt(intervalo.ns)) / @as(f64, @floatFromInt(std.time.ns_per_ms));
const real_ms = @as(f64, @floatFromInt(real)) / @as(f64, @floatFromInt(std.time.ns_per_ms));
const desvio = real_ms - esperado_ms;
try stdout.print("Sleep {s}: esperado={d:.1}ms, real={d:.3}ms, desvio={d:.3}ms\n", .{
intervalo.nome,
esperado_ms,
real_ms,
desvio,
});
}
// Contagem regressiva
try stdout.writeAll("\nContagem regressiva: ");
var count: u8 = 5;
while (count > 0) : (count -= 1) {
try stdout.print("{d}... ", .{count});
std.time.sleep(1 * std.time.ns_per_s);
}
try stdout.writeAll("Pronto!\n");
}
Exemplo 4: Medição com Instant
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Instant fornece tempo monotônico (não afetado por ajustes de relógio)
const inicio = std.time.Instant.now() orelse {
try stdout.writeAll("Timer monotônico não disponível nesta plataforma\n");
return;
};
// Simula trabalho
var soma: u64 = 0;
for (0..1_000_000) |i| {
soma +%= i;
}
const fim = std.time.Instant.now() orelse return;
const decorrido = fim.since(inicio);
try stdout.print("Soma de 0..999999 = {d}\n", .{soma});
try stdout.print("Tempo decorrido: {d} ns\n", .{decorrido});
try stdout.print(" {d:.3} us\n", .{
@as(f64, @floatFromInt(decorrido)) / @as(f64, @floatFromInt(std.time.ns_per_us)),
});
// Operações por segundo
const ops_por_segundo = @as(f64, 1_000_000.0) /
(@as(f64, @floatFromInt(decorrido)) / @as(f64, @floatFromInt(std.time.ns_per_s)));
try stdout.print("Operações por segundo: {d:.0}\n", .{ops_por_segundo});
}
Padrão: Função de Benchmark Reutilizável
fn benchmark(comptime func: anytype, args: anytype, repeticoes: u32) struct { media_ns: u64, min_ns: u64, max_ns: u64 } {
var total: u64 = 0;
var min: u64 = std.math.maxInt(u64);
var max: u64 = 0;
for (0..repeticoes) |_| {
var timer = std.time.Timer.start() catch return .{ .media_ns = 0, .min_ns = 0, .max_ns = 0 };
_ = @call(.auto, func, args);
const elapsed = timer.read();
total += elapsed;
if (elapsed < min) min = elapsed;
if (elapsed > max) max = elapsed;
}
return .{
.media_ns = total / repeticoes,
.min_ns = min,
.max_ns = max,
};
}
Módulos Relacionados
- std.os — Interface com o sistema operacional
- std.process — Gerenciamento de processos
- std.thread — Threads e concorrência
- std.debug — Depuração
- std.math — Funções matemáticas