Skip to documentation
SLOP

tiny.profiling.memory

Reference tiny.profiling memory

Defined in tiny.profiling.

API (15)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callstiny.profilingmemory
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsanalyze.comparecompareMemoryRunstest; no linksrc.profiling.memorytest: profiling memory applies worklo...test; no linksrc.profiling.memorytest: profiling memory compares suppo...private; no linksrc.profiling.memoryallocationEffectIntervalprivate; no linksrc.profiling.memorycomparisonStatusprivate; no linksrc.profiling.memoryfindprivate; no linksrc.profiling.memorymetricBudgetKindprivate; no linksrc.profiling.memorythresholdPercentmemorycompare
Static calls · unresolved targets: 2 · external targets: 0.
Called byCallsprivate; no linksrc.profiling.budgetbelongsToprivate; no linksrc.profiling.memorymetricBudgetKindcatalogfindprivate; no linksrc.profiling.memoryisAllocationKeymemoryisAllocationMetric
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate; no linksrc.profiling.memoryloadForOomtest; no linksrc.profiling.memorytest: profiling memory caveats partia...test; no linksrc.profiling.memorytest: profiling memory marks a missin...test; no linksrc.profiling.memorytest: profiling memory metric keys in...test; no linksrc.profiling.memorytest: profiling memory metrics accept...+3 moreprivate; no linksrc.profiling.memoryloadRepetitionsprivate; no linksrc.profiling.memoryloadStructuredmemoryload
Static calls · unresolved targets: 1 · external targets: 0.

Source: src/profiling/memory.zig

zig
const std = @import("std");const capture = @import("capture");const pretty = @import("pretty");const sys = @import("sys");const allocation = @import("root.zig").allocation;const allocation_trace = @import("capture/root.zig").memtrace;const catalog = @import("root.zig").catalog;const host = @import("root.zig").host;const json = @import("root.zig").json;const schema = @import("root.zig").schema;const pretty_json = pretty.json;const max_structured_bytes = 128 * 1024 * 1024;const max_repetition_bytes = 1024 * 1024;pub const effect_method =    "deterministic_percentile_bootstrap_unpaired_mean_percent_change";pub const effect_bootstrap_iterations = capture.compare.effect.bootstrap_iterations;pub const effect_confidence_per_mille = capture.compare.effect.confidence_per_mille;pub const Unit = enum {    bytes,    count,    pub fn name(self: Unit) []const u8 {        return @tagName(self);    }};pub const Distribution = enum {    point_estimate,    raw_executions,    incomplete_executions,    pub fn name(self: Distribution) []const u8 {        return @tagName(self);    }};pub const BudgetKind = enum {    allocation,    memory,    pub fn name(self: BudgetKind) []const u8 {        return @tagName(self);    }};pub const LoadOptions = struct {    structured_path: ?[]const u8,    allocation_repetitions_path: ?[]const u8 = null,    expected_executions: usize = 1,    workload: []const u8 = "",};pub const Metric = struct {    workload: []const u8,    key: []const u8,    label: []const u8,    source_kind: []const u8,    source_path: []const u8,    source_line: usize,    unit: Unit,    value: f64,    sample_count: u64 = 1,    samples: []const f64 = &.{},    distribution: Distribution = .point_estimate,};pub const Comparison = struct {    workload: []const u8,    key: []const u8,    label: []const u8,    budget_kind: BudgetKind,    unit: Unit,    baseline_value: f64,    candidate_value: f64,    percent_change: f64,    threshold_percent: f64,    baseline_samples: u64 = 1,    candidate_samples: u64 = 1,    baseline_distribution: Distribution = .point_estimate,    candidate_distribution: Distribution = .point_estimate,    effect_low_percent: ?f64 = null,    effect_high_percent: ?f64 = null,    status: []const u8,};const Source = struct {    kind: []const u8,    path: []const u8,    line: usize,};const AllocationTotals = struct {    workload: []const u8 = "",    source_kind: []const u8 = "allocations",    source_path: []const u8 = "",    saw: bool = false,    totals: allocation.Totals = .{},};const RepetitionField = struct {    summary_name: []const u8,    metric_name: []const u8,    unit: Unit,};const repetition_fields = [_]RepetitionField{    .{ .summary_name = "allocations", .metric_name = "allocation_events", .unit = .count },    .{ .summary_name = "frees", .metric_name = "free_events", .unit = .count },    .{ .summary_name = "resizes", .metric_name = "resize_events", .unit = .count },    .{ .summary_name = "remaps", .metric_name = "remap_events", .unit = .count },    .{ .summary_name = "allocated_bytes", .metric_name = "allocated_bytes", .unit = .bytes },    .{ .summary_name = "live_allocations", .metric_name = "live_allocations", .unit = .count },    .{ .summary_name = "live_bytes", .metric_name = "live_bytes", .unit = .bytes },    .{        .summary_name = "high_water_live_bytes",        .metric_name = "high_water_live_bytes",        .unit = .bytes,    },    .{        .summary_name = "high_water_retained_bytes",        .metric_name = "high_water_retained_bytes",        .unit = .bytes,    },};const RepetitionEvidence = struct {    observed: usize = 0,    complete: bool = true,    counts: [repetition_fields.len]usize = @as([repetition_fields.len]usize, @splat(0)),    samples: [repetition_fields.len][host.process.max_executions]f64 = undefined,};pub fn load(allocator: std.mem.Allocator, options: LoadOptions) ![]const Metric {    var result: std.ArrayList(Metric) = .empty;    if (options.structured_path) |actual_path| {        try loadStructured(allocator, &result, actual_path);    }    if (options.expected_executions > 1) {        if (options.allocation_repetitions_path) |path| {            try loadRepetitions(allocator, &result, options, path);        }    }    return try result.toOwnedSlice(allocator);}fn loadStructured(    allocator: std.mem.Allocator,    result: *std.ArrayList(Metric),    actual_path: []const u8,) !void {    const text = sys.fs.readFileAlloc(allocator, actual_path, max_structured_bytes) catch |err| switch (err) {        error.FileNotFound => return,        else => |actual| return actual,    };    defer allocator.free(text);    var allocations: AllocationTotals = .{};    var lines = std.mem.splitScalar(u8, text, '\n');    while (lines.next()) |line| {        const trimmed = std.mem.trim(u8, line, " \t\r");        if (trimmed.len == 0) continue;        var parsed = std.json.parseFromSlice(std.json.Value, allocator, trimmed, .{}) catch continue;        defer parsed.deinit();        const object = json.object(parsed.value) catch continue;        const row = json.object(object.get("row") orelse continue) catch continue;        const source = parseSource(allocator, object) catch continue;        const workload = parseWorkload(allocator, object) catch continue;        const classification = schema.classify(row, source.kind);        if (classification.family == .allocation) {            try recordAllocation(allocator, &allocations, workload, source, row);        } else if (schema.isMemoryMetric(row, source.kind)) {            try appendRowMetrics(allocator, result, workload, source, row);        }    }    if (allocations.saw) try appendAllocationMetrics(allocator, result, allocations);}fn loadRepetitions(    allocator: std.mem.Allocator,    result: *std.ArrayList(Metric),    options: LoadOptions,    path: []const u8,) !void {    if (options.expected_executions > host.process.max_executions) {        return error.InvalidProfilingJson;    }    const evidence = try readRepetitions(allocator, path, options.expected_executions);    for (repetition_fields, 0..) |field, field_index| {        const count = evidence.counts[field_index];        if (count == 0) {            markAllocationMetricIncomplete(result.items, options.workload, field.metric_name);            continue;        }        const key = try std.fmt.allocPrint(            allocator,            "allocations|{s}",            .{field.metric_name},        );        removeMetric(result, options.workload, key);        const samples = try allocator.dupe(            f64,            evidence.samples[field_index][0..count],        );        try result.append(allocator, .{            .workload = options.workload,            .key = key,            .label = key,            .source_kind = "allocation_repetitions",            .source_path = path,            .source_line = 0,            .unit = field.unit,            .value = mean(samples),            .sample_count = @intCast(samples.len),            .samples = samples,            .distribution = if (evidence.complete and                evidence.observed == options.expected_executions and                samples.len == options.expected_executions)                .raw_executions            else                .incomplete_executions,        });    }}fn readRepetitions(    allocator: std.mem.Allocator,    path: []const u8,    expected_executions: usize,) !RepetitionEvidence {    var evidence = RepetitionEvidence{};    const text = sys.fs.readFileAlloc(allocator, path, max_repetition_bytes) catch |err| switch (err) {        error.FileNotFound => {            evidence.complete = false;            return evidence;        },        else => |actual| return actual,    };    defer allocator.free(text);    var lines = std.mem.splitScalar(u8, text, '\n');    while (lines.next()) |line| {        const trimmed = std.mem.trim(u8, line, " \t\r");        if (trimmed.len == 0) continue;        if (evidence.observed >= host.process.max_executions) {            return error.InvalidProfilingJson;        }        evidence.observed += 1;        parseRepetitionLine(            allocator,            &evidence,            trimmed,            evidence.observed,        ) catch |err| switch (err) {            error.OutOfMemory => return err,            else => evidence.complete = false,        };    }    if (evidence.observed != expected_executions) evidence.complete = false;    return evidence;}fn parseRepetitionLine(    allocator: std.mem.Allocator,    evidence: *RepetitionEvidence,    line: []const u8,    expected_index: usize,) !void {    var parsed = try std.json.parseFromSlice(std.json.Value, allocator, line, .{});    defer parsed.deinit();    const object = try json.object(parsed.value);    const row_schema = json.string(object.get("schema")) orelse        return error.InvalidProfilingJson;    if (!std.mem.eql(u8, row_schema, allocation_trace.repetition_schema)) {        return error.InvalidProfilingJson;    }    const execution_index = json.asU64(object.get("execution_index")) orelse        return error.InvalidProfilingJson;    if (execution_index != expected_index) return error.InvalidProfilingJson;    const state = json.string(object.get("state")) orelse        return error.InvalidProfilingJson;    if (!std.mem.eql(u8, state, "summary_written")) {        return error.IncompleteProfilingEvidence;    }    const summary = try json.object(object.get("summary") orelse        return error.InvalidProfilingJson);    const integrity = try json.object(summary.get("capture_integrity") orelse        return error.InvalidProfilingJson);    const status = json.string(integrity.get("status")) orelse        return error.InvalidProfilingJson;    if (!std.mem.eql(u8, status, "complete")) {        evidence.complete = false;    }    for (repetition_fields, 0..) |field, field_index| {        const value = json.asF64(summary.get(field.summary_name)) orelse {            evidence.complete = false;            continue;        };        const count = evidence.counts[field_index];        evidence.samples[field_index][count] = value;        evidence.counts[field_index] += 1;    }}fn removeMetric(    result: *std.ArrayList(Metric),    workload: []const u8,    key: []const u8,) void {    for (result.items, 0..) |row, index| {        if (std.mem.eql(u8, row.workload, workload) and            std.mem.eql(u8, row.key, key))        {            _ = result.orderedRemove(index);            return;        }    }}fn markAllocationMetricIncomplete(    metrics: []Metric,    workload: []const u8,    metric_name: []const u8,) void {    for (metrics) |*row| {        if (!std.mem.eql(u8, row.workload, workload)) continue;        if (!std.mem.startsWith(u8, row.key, "allocations|")) continue;        if (!std.mem.eql(u8, row.key["allocations|".len..], metric_name)) continue;        row.distribution = .incomplete_executions;        row.sample_count = 0;        return;    }}fn mean(samples: []const f64) f64 {    std.debug.assert(samples.len > 0);    var total: f64 = 0;    for (samples) |sample| total += sample;    return total / @as(f64, @floatFromInt(samples.len));}pub fn compare(    allocator: std.mem.Allocator,    base: []const Metric,    candidate: []const Metric,    default_threshold_percent: f64,) ![]const Comparison {    var rows: std.ArrayList(Comparison) = .empty;    for (candidate) |candidate_metric| {        const base_metric = find(base, candidate_metric.workload, candidate_metric.key) orelse continue;        if (base_metric.value <= 0) continue;        const percent = ((candidate_metric.value - base_metric.value) / base_metric.value) * 100;        const budget_kind = metricBudgetKind(candidate_metric);        const threshold = thresholdPercent(candidate_metric, default_threshold_percent);        if (percent < threshold) continue;        const effect_interval = try allocationEffectInterval(            allocator,            base_metric,            candidate_metric,            budget_kind,        );        try rows.append(allocator, .{            .workload = candidate_metric.workload,            .key = candidate_metric.key,            .label = candidate_metric.label,            .budget_kind = budget_kind,            .unit = candidate_metric.unit,            .baseline_value = base_metric.value,            .candidate_value = candidate_metric.value,            .percent_change = percent,            .threshold_percent = threshold,            .baseline_samples = base_metric.sample_count,            .candidate_samples = candidate_metric.sample_count,            .baseline_distribution = base_metric.distribution,            .candidate_distribution = candidate_metric.distribution,            .effect_low_percent = if (effect_interval) |interval| interval.low else null,            .effect_high_percent = if (effect_interval) |interval| interval.high else null,            .status = comparisonStatus(                base_metric,                candidate_metric,                threshold,                effect_interval,                budget_kind,            ),        });    }    return try rows.toOwnedSlice(allocator);}fn allocationEffectInterval(    allocator: std.mem.Allocator,    base: Metric,    candidate: Metric,    budget_kind: BudgetKind,) !?capture.compare.effect.Interval {    if (budget_kind != .allocation) return null;    if (base.distribution != .raw_executions or        candidate.distribution != .raw_executions)    {        return null;    }    if (base.samples.len < 2 or candidate.samples.len < 2) return null;    var effect_storage = try capture.compare.EffectStorage.init(allocator, .{        .max_samples_per_distribution = @max(            base.samples.len,            candidate.samples.len,        ),    });    defer effect_storage.deinit(allocator);    effect_storage.activate();    return try capture.compare.effect.bootstrapMeanPercentChangeInterval(        &effect_storage,        base.samples,        candidate.samples,        capture.compare.effect.percentChangeSeed(candidate.workload),    );}fn comparisonStatus(    base: Metric,    candidate: Metric,    threshold_percent: f64,    effect_interval: ?capture.compare.effect.Interval,    budget_kind: BudgetKind,) []const u8 {    if (budget_kind != .allocation) return "memory_regression";    if (base.distribution == .incomplete_executions or        candidate.distribution == .incomplete_executions)    {        return "allocation_repetition_incomplete";    }    if (effect_interval) |interval| {        if (interval.low >= threshold_percent) {            return "allocation_sample_regression";        }        return "allocation_sample_regression_uncertain";    }    if (base.distribution == .raw_executions or        candidate.distribution == .raw_executions)    {        return "allocation_sample_regression_candidate";    }    return "allocation_regression_candidate";}fn appendRowMetrics(    allocator: std.mem.Allocator,    result: *std.ArrayList(Metric),    workload: []const u8,    source: Source,    row: std.json.ObjectMap,) !void {    if (eventIs(row, "bench_end")) {        if (normalizedBenchCounter(row, "alloc_bytes")) |value| try appendMetric(allocator, result, workload, source, row, "alloc_bytes_per_eval", .bytes, value);        if (normalizedBenchCounter(row, "alloc_count")) |value| try appendMetric(allocator, result, workload, source, row, "alloc_count_per_eval", .count, value);        return;    }    const name = fieldString(row, "nameOrId");    const metric_name = json.string(row.get("metric")) orelse name;    if (std.mem.eql(u8, source.kind, "allocations") and allocation.isMetricName(metric_name)) {        const value = firstNumber(row, &.{ "value", "median", "mean", "allocated_bytes", "alloc_bytes", "alloc_count" }) orelse return;        try appendSyntheticMetric(allocator, result, workload, source, metric_name, parseUnit(row, metric_name), value);        return;    }    const unit = parseUnit(row, metric_name);    const value = firstNumber(row, &.{ "value", "median", "mean", "allocated_bytes", "retained_bytes", "high_water_live_bytes", "alloc_bytes", "alloc_count" }) orelse return;    try appendMetric(allocator, result, workload, source, row, metric_name, unit, value);}fn appendMetric(    allocator: std.mem.Allocator,    result: *std.ArrayList(Metric),    workload: []const u8,    source: Source,    row: std.json.ObjectMap,    metric_name: []const u8,    unit: Unit,    value: f64,) !void {    const key = try memoryKey(allocator, row, metric_name);    try result.append(allocator, .{        .workload = workload,        .key = key,        .label = try memoryLabel(allocator, row, key),        .source_kind = source.kind,        .source_path = source.path,        .source_line = source.line,        .unit = unit,        .value = value,    });}fn recordAllocation(    allocator: std.mem.Allocator,    totals: *AllocationTotals,    workload: []const u8,    source: Source,    row: std.json.ObjectMap,) !void {    if (!totals.saw) {        totals.saw = true;        totals.workload = try allocator.dupe(u8, workload);        totals.source_kind = try allocator.dupe(u8, source.kind);        totals.source_path = try allocator.dupe(u8, source.path);    }    totals.totals.record(row);}fn appendAllocationMetrics(allocator: std.mem.Allocator, result: *std.ArrayList(Metric), totals: AllocationTotals) !void {    const source = Source{ .kind = totals.source_kind, .path = totals.source_path, .line = 0 };    for (totals.totals.metrics()) |metric_value| try appendSyntheticMetric(allocator, result, totals.workload, source, metric_value.name, parseMetricUnit(metric_value), @floatFromInt(metric_value.value));}fn appendSyntheticMetric(    allocator: std.mem.Allocator,    result: *std.ArrayList(Metric),    workload: []const u8,    source: Source,    metric_name: []const u8,    unit: Unit,    value: f64,) !void {    const key = try std.fmt.allocPrint(allocator, "allocations|{s}", .{metric_name});    try result.append(allocator, .{        .workload = workload,        .key = key,        .label = key,        .source_kind = source.kind,        .source_path = source.path,        .source_line = source.line,        .unit = unit,        .value = value,    });}fn thresholdPercent(metric: Metric, default_threshold_percent: f64) f64 {    if (catalog.find(metric.workload)) |workload| {        return switch (metricBudgetKind(metric)) {            .allocation => workload.allocationBudgetPercent() orelse default_threshold_percent,            .memory => workload.rssThresholdPercent(default_threshold_percent),        };    }    return default_threshold_percent;}fn find(metrics: []const Metric, workload: []const u8, key: []const u8) ?Metric {    for (metrics) |metric| {        if (std.mem.eql(u8, metric.workload, workload) and std.mem.eql(u8, metric.key, key)) return metric;    }    return null;}fn parseSource(allocator: std.mem.Allocator, object: std.json.ObjectMap) !Source {    const source = try json.object(object.get("source") orelse return error.InvalidProfilingJson);    return .{        .kind = try allocator.dupe(u8, json.string(source.get("kind")) orelse ""),        .path = try allocator.dupe(u8, json.string(source.get("path")) orelse ""),        .line = @intCast(json.asU64(source.get("line")) orelse 0),    };}fn parseWorkload(allocator: std.mem.Allocator, object: std.json.ObjectMap) ![]const u8 {    const workload = try json.object(object.get("workload") orelse return error.InvalidProfilingJson);    return try allocator.dupe(u8, json.string(workload.get("name")) orelse return error.InvalidProfilingJson);}fn parseUnit(row: std.json.ObjectMap, metric_name: []const u8) Unit {    if (json.string(row.get("unit"))) |unit| {        if (std.mem.eql(u8, unit, "bytes")) return .bytes;        if (std.mem.eql(u8, unit, "count")) return .count;    }    return if (std.mem.indexOf(u8, metric_name, "bytes") != null) .bytes else .count;}fn parseMetricUnit(metric_value: allocation.Metric) Unit {    if (std.mem.eql(u8, metric_value.unit, "bytes")) return .bytes;    return .count;}fn firstNumber(row: std.json.ObjectMap, fields: []const []const u8) ?f64 {    for (fields) |field| {        if (json.asF64(row.get(field))) |value| return value;    }    return null;}fn normalizedBenchCounter(row: std.json.ObjectMap, field: []const u8) ?f64 {    const value = json.asF64(row.get(field)) orelse return null;    const samples = json.asF64(row.get("samples")) orelse 1;    const evals = json.asF64(row.get("evals")) orelse 1;    const divisor = samples * evals;    if (divisor <= 0) return value;    return value / divisor;}fn eventIs(row: std.json.ObjectMap, expected: []const u8) bool {    return if (json.string(row.get("event"))) |event| std.mem.eql(u8, event, expected) else false;}fn memoryKey(allocator: std.mem.Allocator, row: std.json.ObjectMap, metric_name: []const u8) ![]const u8 {    return try std.fmt.allocPrint(allocator, "{s}|{s}|{s}|{s}|{s}|{s}|{s}|{s}|{s}|{s}", .{        fieldString(row, "benchmark"),        fieldString(row, "suite"),        fieldString(row, "mode"),        fieldString(row, "pipeline"),        fieldString(row, "kind"),        fieldString(row, "summary"),        fieldString(row, "workload"),        fieldString(row, "phase"),        fieldString(row, "nameOrId"),        metric_name,    });}fn memoryLabel(allocator: std.mem.Allocator, row: std.json.ObjectMap, fallback: []const u8) ![]const u8 {    const benchmark = fieldString(row, "benchmark");    const row_workload = fieldString(row, "workload");    const summary = fieldString(row, "summary");    const phase = fieldString(row, "phase");    const name = fieldString(row, "nameOrId");    if (benchmark.len == 0 and row_workload.len == 0 and summary.len == 0 and phase.len == 0 and name.len == 0) return fallback;    return try std.fmt.allocPrint(allocator, "{s} {s} {s} {s} {s}", .{ benchmark, row_workload, summary, phase, name });}fn fieldString(row: std.json.ObjectMap, field: []const u8) []const u8 {    if (std.mem.eql(u8, field, "nameOrId")) {        return json.string(row.get("name")) orelse json.string(row.get("id")) orelse "";    }    return json.string(row.get(field)) orelse "";}fn isAllocationKey(key: []const u8) bool {    return std.mem.startsWith(u8, key, "allocations|");}pub fn isAllocationMetric(metric: Metric) bool {    if (isAllocationKey(metric.key)) return true;    if (catalog.find(metric.workload)) |workload| {        return workload.supportsAllocationCounters();    }    return false;}fn metricBudgetKind(metric: Metric) BudgetKind {    return if (isAllocationMetric(metric)) .allocation else .memory;}fn repetitionTestLine(    allocator: std.mem.Allocator,    execution_index: usize,    allocated_bytes: u64,    high_water_live_bytes: u64,    integrity_status: []const u8,) ![]const u8 {    return try pretty_json.renderMinifiedLineAlloc(        allocator,        .{            .schema = allocation_trace.repetition_schema,            .execution_index = execution_index,            .exit_code = @as(u8, 0),            .state = "summary_written",            .summary = .{                .allocations = @as(u8, 4),                .frees = @as(u8, 4),                .resizes = @as(u8, 0),                .remaps = @as(u8, 0),                .allocated_bytes = allocated_bytes,                .live_allocations = @as(u8, 0),                .live_bytes = @as(u8, 0),                .high_water_live_bytes = high_water_live_bytes,                .high_water_retained_bytes = allocated_bytes,                .capture_integrity = .{ .status = integrity_status },            },        },    );}fn loadForOom(allocator: std.mem.Allocator, options: LoadOptions) !void {    var arena_state = std.heap.ArenaAllocator.init(allocator);    defer arena_state.deinit();    _ = try load(arena_state.allocator(), options);}test "profiling memory metrics parse bench allocation rows" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    try sys.fs.writeFile(".zig-cache/profile-memory-test.jsonl",        \\{"schema":"tiny.profiling.structured/v1","workload":{"name":"gpalloc.allocator","package":"lib/gpalloc","step":"gpalloc-bench"},"source":{"kind":"bench_jsonl","path":"bench.jsonl","line":1},"row":{"schema":"tiny.profiling.metric/v1","event":"bench_end","family":"timing","metric":"duration_ns","unit":"ns","suite":"gpalloc","name":"alloc","sample_ns":[10,20],"samples":3,"evals":2,"alloc_count":6,"alloc_bytes":96}}        \\    );    const metrics = try load(allocator, .{        .structured_path = ".zig-cache/profile-memory-test.jsonl",    });    try std.testing.expectEqual(@as(usize, 2), metrics.len);    try std.testing.expectEqualStrings("gpalloc.allocator", metrics[0].workload);    try std.testing.expectEqual(Unit.bytes, metrics[0].unit);    try std.testing.expectEqual(@as(f64, 16), metrics[0].value);    sys.fs.deleteFile(".zig-cache/profile-memory-test.jsonl") catch {};}test "profiling memory retains complete allocation repetition distributions" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const path = ".zig-cache/profile-memory-repetitions-test.jsonl";    defer sys.fs.deleteFile(path) catch {};    const first = try repetitionTestLine(allocator, 1, 10, 5, "complete");    const second = try repetitionTestLine(allocator, 2, 20, 10, "complete");    const third = try repetitionTestLine(allocator, 3, 30, 15, "complete");    try sys.fs.writeFile(path, first);    try sys.fs.appendFile(path, &.{ second, third });    const options = LoadOptions{        .structured_path = null,        .allocation_repetitions_path = path,        .expected_executions = 3,        .workload = "allocator",    };    const metrics = try load(allocator, options);    try std.testing.expectEqual(@as(usize, repetition_fields.len), metrics.len);    const allocated = find(metrics, "allocator", "allocations|allocated_bytes").?;    try std.testing.expectEqual(Distribution.raw_executions, allocated.distribution);    try std.testing.expectEqual(@as(u64, 3), allocated.sample_count);    try std.testing.expectEqual(@as(f64, 20), allocated.value);    try std.testing.expectEqualSlices(f64, &.{ 10, 20, 30 }, allocated.samples);    const high_water = find(        metrics,        "allocator",        "allocations|high_water_live_bytes",    ).?;    try std.testing.expectEqual(@as(f64, 10), high_water.value);    try std.testing.checkAllAllocationFailures(        std.testing.allocator,        loadForOom,        .{options},    );}test "profiling memory caveats partial and missing allocation repetitions" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const path = ".zig-cache/profile-memory-repetition-caveat-test.jsonl";    defer sys.fs.deleteFile(path) catch {};    const first = try repetitionTestLine(allocator, 1, 10, 5, "complete");    const partial = try repetitionTestLine(        allocator,        2,        30,        15,        "missing_stop_event",    );    try sys.fs.writeFile(path, first);    try sys.fs.appendFile(path, &.{partial});    const metrics = try load(allocator, .{        .structured_path = null,        .allocation_repetitions_path = path,        .expected_executions = 3,        .workload = "allocator",    });    const allocated = find(metrics, "allocator", "allocations|allocated_bytes").?;    try std.testing.expectEqual(        Distribution.incomplete_executions,        allocated.distribution,    );    try std.testing.expectEqual(@as(u64, 2), allocated.sample_count);    try std.testing.expectEqual(@as(f64, 20), allocated.value);}test "profiling memory marks a missing repetition artifact incomplete" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const structured_path = ".zig-cache/profile-memory-missing-repetitions-test.jsonl";    defer sys.fs.deleteFile(structured_path) catch {};    try sys.fs.writeFile(        structured_path,        "{\"schema\":\"tiny.profiling.structured/v1\"," ++            "\"workload\":{\"name\":\"allocator\"}," ++            "\"source\":{\"kind\":\"allocations\"," ++            "\"path\":\"allocations.jsonl\",\"line\":0}," ++            "\"row\":{\"schema\":\"tiny.profiling.metric/v1\"," ++            "\"family\":\"memory\",\"metric\":\"allocated_bytes\"," ++            "\"name\":\"allocated_bytes\",\"unit\":\"bytes\",\"value\":10}}\n",    );    const metrics = try load(allocator, .{        .structured_path = structured_path,        .allocation_repetitions_path = ".zig-cache/profile-memory-repetitions-does-not-exist.jsonl",        .expected_executions = 3,        .workload = "allocator",    });    const allocated = find(metrics, "allocator", "allocations|allocated_bytes").?;    try std.testing.expectEqual(        Distribution.incomplete_executions,        allocated.distribution,    );    try std.testing.expectEqual(@as(u64, 0), allocated.sample_count);}test "profiling memory compares supported and caveated allocation samples" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const baseline_samples = [_]f64{ 10, 10, 10, 10 };    const supported_samples = [_]f64{ 20, 20, 20, 20 };    const uncertain_samples = [_]f64{ 5, 5, 35, 35 };    const baseline = repetitionMetric(10, &baseline_samples, .raw_executions);    const supported = repetitionMetric(20, &supported_samples, .raw_executions);    const supported_rows = try compare(allocator, &.{baseline}, &.{supported}, 10);    try std.testing.expectEqualStrings(        "allocation_sample_regression",        supported_rows[0].status,    );    try std.testing.expect(supported_rows[0].effect_low_percent.? >= 10);    const uncertain = repetitionMetric(20, &uncertain_samples, .raw_executions);    const uncertain_rows = try compare(allocator, &.{baseline}, &.{uncertain}, 10);    try std.testing.expectEqualStrings(        "allocation_sample_regression_uncertain",        uncertain_rows[0].status,    );    const incomplete = repetitionMetric(20, &supported_samples, .incomplete_executions);    const incomplete_rows = try compare(allocator, &.{baseline}, &.{incomplete}, 10);    try std.testing.expectEqualStrings(        "allocation_repetition_incomplete",        incomplete_rows[0].status,    );}test "profiling memory applies workload budgets to compact allocation counters" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const baseline = Metric{        .workload = "gpalloc.allocator",        .key = "gpalloc|small|alloc_bytes_per_eval",        .label = "gpalloc small",        .source_kind = "bench_jsonl",        .source_path = "bench.jsonl",        .source_line = 1,        .unit = .bytes,        .value = 100,    };    var candidate = baseline;    candidate.value = 129;    try std.testing.expectEqual(        @as(usize, 0),        (try compare(allocator, &.{baseline}, &.{candidate}, 10)).len,    );    candidate.value = 130;    const rows = try compare(allocator, &.{baseline}, &.{candidate}, 10);    try std.testing.expectEqual(@as(usize, 1), rows.len);    try std.testing.expectEqual(BudgetKind.allocation, rows[0].budget_kind);    try std.testing.expectEqual(@as(f64, 30), rows[0].threshold_percent);    try std.testing.expectEqualStrings(        "allocation_regression_candidate",        rows[0].status,    );}fn repetitionMetric(    value: f64,    samples: []const f64,    distribution: Distribution,) Metric {    return .{        .workload = "allocator",        .key = "allocations|allocated_bytes",        .label = "allocated bytes",        .source_kind = "allocation_repetitions",        .source_path = "repetitions.jsonl",        .source_line = 0,        .unit = .bytes,        .value = value,        .sample_count = @intCast(samples.len),        .samples = samples,        .distribution = distribution,    };}test "profiling memory metrics aggregate allocation events" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    try sys.fs.writeFile(".zig-cache/profile-memory-allocations-test.jsonl",        \\{"schema":"tiny.profiling.structured/v1","workload":{"name":"gpalloc.allocator","package":"lib/gpalloc","step":"gpalloc-bench"},"source":{"kind":"allocations","path":"allocations.jsonl","line":1},"row":{"kind":"alloc","len":64}}        \\{"schema":"tiny.profiling.structured/v1","workload":{"name":"gpalloc.allocator","package":"lib/gpalloc","step":"gpalloc-bench"},"source":{"kind":"allocations","path":"allocations.jsonl","line":2},"row":{"kind":"resize","old_len":64,"new_len":96}}        \\{"schema":"tiny.profiling.structured/v1","workload":{"name":"gpalloc.allocator","package":"lib/gpalloc","step":"gpalloc-bench"},"source":{"kind":"allocations","path":"allocations.jsonl","line":3},"row":{"kind":"free","len":96}}        \\    );    const metrics = try load(allocator, .{        .structured_path = ".zig-cache/profile-memory-allocations-test.jsonl",    });    try std.testing.expectEqual(@as(usize, 5), metrics.len);    const allocated = find(metrics, "gpalloc.allocator", "allocations|allocated_bytes").?;    try std.testing.expectEqual(Unit.bytes, allocated.unit);    try std.testing.expectEqual(@as(f64, 96), allocated.value);    sys.fs.deleteFile(".zig-cache/profile-memory-allocations-test.jsonl") catch {};}test "profiling memory metrics accept summarized allocation rows" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    try sys.fs.writeFile(".zig-cache/profile-memory-allocation-summary-test.jsonl",        \\{"schema":"tiny.profiling.structured/v1","workload":{"name":"gpalloc.allocator","package":"lib/gpalloc","step":"gpalloc-bench"},"source":{"kind":"allocations","path":"allocations.jsonl","line":0},"row":{"schema":"tiny.profiling.metric/v1","family":"memory","metric":"allocated_bytes","name":"allocated_bytes","unit":"bytes","value":96}}        \\{"schema":"tiny.profiling.structured/v1","workload":{"name":"gpalloc.allocator","package":"lib/gpalloc","step":"gpalloc-bench"},"source":{"kind":"allocations","path":"allocations.jsonl","line":0},"row":{"schema":"tiny.profiling.metric/v1","family":"memory","metric":"allocation_events","name":"allocation_events","unit":"count","value":2}}        \\    );    const metrics = try load(allocator, .{        .structured_path = ".zig-cache/profile-memory-allocation-summary-test.jsonl",    });    try std.testing.expectEqual(@as(usize, 2), metrics.len);    const allocated = find(metrics, "gpalloc.allocator", "allocations|allocated_bytes").?;    try std.testing.expectEqual(Unit.bytes, allocated.unit);    try std.testing.expectEqual(@as(f64, 96), allocated.value);    const events = find(metrics, "gpalloc.allocator", "allocations|allocation_events").?;    try std.testing.expectEqual(Unit.count, events.unit);    try std.testing.expectEqual(@as(f64, 2), events.value);    sys.fs.deleteFile(".zig-cache/profile-memory-allocation-summary-test.jsonl") catch {};}test "profiling memory metric keys include benchmark identity" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    try sys.fs.writeFile(".zig-cache/profile-memory-key-test.jsonl",        \\{"schema":"tiny.profiling.structured/v1","workload":{"name":"smg.graph","package":"tools/smg","step":"smg-bench"},"source":{"kind":"bench_jsonl","path":"bench.jsonl","line":1},"row":{"schema":"tiny.profiling.metric/v1","event":"bench_end","family":"timing","metric":"duration_ns","unit":"ns","suite":"bench","name":"a","sample_ns":[10,20],"samples":1,"evals":1,"alloc_count":1,"alloc_bytes":10}}        \\{"schema":"tiny.profiling.structured/v1","workload":{"name":"smg.graph","package":"tools/smg","step":"smg-bench"},"source":{"kind":"bench_jsonl","path":"bench.jsonl","line":2},"row":{"schema":"tiny.profiling.metric/v1","event":"bench_end","family":"timing","metric":"duration_ns","unit":"ns","suite":"bench","name":"b","sample_ns":[10,20],"samples":1,"evals":1,"alloc_count":2,"alloc_bytes":20}}        \\    );    const metrics = try load(allocator, .{        .structured_path = ".zig-cache/profile-memory-key-test.jsonl",    });    try std.testing.expectEqual(@as(usize, 4), metrics.len);    try std.testing.expect(!std.mem.eql(u8, metrics[0].key, metrics[2].key));    sys.fs.deleteFile(".zig-cache/profile-memory-key-test.jsonl") catch {};}

Source: src/profiling/root.zig:32

zig
pub const memory = @import("memory.zig");

Complete caller list for memory.load

8 direct callers.

Audit

Definitions16
Public names16
Members38
Version26.7.0
Revisiondaab053ee433