2022-07-06 04:23:29 +00:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-07-11 10:01:16 +00:00
|
|
|
pub fn begin(self: *const Self) void {
|
2022-07-06 04:23:29 +00:00
|
|
|
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;
|
|
|
|
|
2022-07-11 10:01:16 +00:00
|
|
|
const next = allocator.create(Node) catch unreachable;
|
2022-07-17 08:28:01 +00:00
|
|
|
next.* = .{};
|
|
|
|
|
2022-07-06 04:23:29 +00:00
|
|
|
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");
|
2022-07-11 10:01:16 +00:00
|
|
|
fn slow_func() void {
|
|
|
|
pm_slow_func.begin();
|
2022-07-06 04:23:29 +00:00
|
|
|
defer pm_slow_func.end();
|
|
|
|
|
|
|
|
std.time.sleep(100);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
test "profile test" {
|
2022-07-11 10:01:16 +00:00
|
|
|
Testing.slow_func();
|
2022-07-06 04:23:29 +00:00
|
|
|
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);
|
|
|
|
}
|