forked from vv/efemra
1
0
Fork 0
efemra/src/common/profiler.zig

133 lines
3.3 KiB
Zig

const std = @import("std");
const assert = std.debug.assert;
const testing = std.testing;
const time = @import("time.zig");
const Hasher64 = @import("fnv1a.zig").Hasher64;
// TODO memory
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
// eventually the linear allocator will run out
const node_limit = 1024;
// eventually the call stack will run out in the Gui
const depth_limit = 20;
threadlocal var ctx = Context{};
const Context = struct {
prevroot: ?*const Node = null,
root: Node = Node{},
current: ?*Node = null,
frame: u64 = 0,
count: u64 = 0,
depth: i32 = 0,
};
const Node = struct {
mark: ?*const ProfileMark = null,
parent: ?*Node = null,
fchild: ?*Node = null,
lchild: ?*Node = null,
sibling: ?*const Node = null,
begin: time.Instant = undefined,
end: time.Instant = undefined,
hash: u64 = 0,
depth: i32 = 0,
};
pub const ProfileMark = struct {
const Self = @This();
name: []const u8,
calls: u32 = 0,
sum: u64 = 0,
pub fn init(name: []const u8) Self {
return Self{
.name = name,
};
}
pub fn begin(self: *const Self) void {
const frame = time.getFrameCount();
if (frame != ctx.frame) {
ctx.current = &ctx.root;
ctx.frame = frame;
ctx.count = 0;
ctx.prevroot = &ctx.root;
ctx.root = .{ .hash = Hasher64.Bias };
ctx.depth = 0;
}
ctx.count += 1;
ctx.depth += 1;
if (ctx.count < node_limit and ctx.depth < depth_limit) {
const parent = ctx.current orelse &ctx.root;
const next = allocator.create(Node) catch unreachable;
next.* = .{};
next.mark = self;
next.parent = parent;
next.depth = ctx.depth;
var prev_hash = parent.hash;
const sibling = parent.lchild;
if (sibling) |s| {
s.sibling = next;
prev_hash = s.hash;
} else {
parent.fchild = next;
}
parent.lchild = next;
next.hash = Hasher64.string(self.name, prev_hash) | 1;
ctx.current = next;
next.begin = time.now();
}
}
pub fn end(self: *const Self) void {
const now = time.now();
if (ctx.count < node_limit and ctx.depth < depth_limit) {
const cur = ctx.current orelse unreachable;
assert(cur.mark == self);
assert(cur.depth == ctx.depth);
assert(cur.parent orelse null != null);
assert(cur.hash != 0);
cur.end = now;
ctx.current = cur.parent;
}
ctx.depth -= 1;
}
// TODO gui shit
};
const Testing = struct {
const pm_slow_func = ProfileMark.init("Test.slow_func");
fn slow_func() void {
pm_slow_func.begin();
defer pm_slow_func.end();
std.time.sleep(100);
}
};
test "profile test" {
Testing.slow_func();
const fchild = ctx.root.fchild orelse unreachable;
try testing.expectEqual(std.math.Order.lt, fchild.begin.order(fchild.end));
try testing.expect(fchild.end.since(fchild.begin) >= 100);
try testing.expectEqual(Hasher64.string("test.slow_func", 0) | 1, fchild.hash);
}