From c7cfa18ce96e1d8858a2afd7268248f258a79704 Mon Sep 17 00:00:00 2001 From: peterino2 Date: Fri, 31 Oct 2025 00:36:14 -0700 Subject: [PATCH] added scopes --- lib/p2/src/structures/pscopes.zig | 131 ++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 lib/p2/src/structures/pscopes.zig diff --git a/lib/p2/src/structures/pscopes.zig b/lib/p2/src/structures/pscopes.zig new file mode 100644 index 0000000..a1ea80f --- /dev/null +++ b/lib/p2/src/structures/pscopes.zig @@ -0,0 +1,131 @@ +// pscopes +// +// a tiny scope based profiling library +// +// as well as contains info for arbitrary timestamping and timestamp resolution + +const timestampTicks = if (builtin.target.cpu.arch == .x86_64) aarch64_ticks else rdtsc; + +pub inline fn rdtsc_fenced() u64 { + var hi: u32 = 0; + var low: u32 = 0; + + asm ( + \\rdtsc + \\mfence + : [low] "={eax}" (low), + [hi] "={edx}" (hi), + ); + return (@as(u64, hi) << 32) | @as(u64, low); +} + +// rdtsc actually gets read +pub inline fn rdtsc() u64 { + var hi: u32 = 0; + var low: u32 = 0; + + asm ( + \\rdtsc + : [low] "={eax}" (low), + [hi] "={edx}" (hi), + ); + return (@as(u64, hi) << 32) | @as(u64, low); +} + +fn aarch64_ticks() u64 { + return asm volatile ("mrs %[ret], CNTVCT_EL0" + : [ret] "=r" (-> u64), + ); +} + +fn aarch64_freq() u64 { + return asm volatile ("mrs %[ret], CNTFRQ_EL0" + : [ret] "=r" (-> u64), + ); +} + +pub const TimingScope = struct { + // value in tsc ticks + startTsc: u64, + endTsc: ?u64 = null, + + context: *ScopesContext, + + pub inline fn init(c: *ScopesContext) @This() { + return .{ + .startTsc = rdtsc(), + .context = c, + }; + } + + pub fn duration(self: *@This()) ?f64 { + _ = self; + } + + pub inline fn end(self: *@This()) void { + self.endTsc = rdtsc(); + } +}; + +pub const ScopesContext = struct { + allocator: std.mem.Allocator, + ticksPerMs: ?f64 = null, + timer: std.time.Timer, + + pub fn timingScope(self: *@This()) TimingScope { + _ = self; + } + + // calibrates the ticks per second divider. + // spins for 1 ms then compares against the monotonic timer + // should be called once every second + pub fn calibrate(self: *@This()) void { + if (builtin.target.cpu.arch == .x86_64) { + self.timer.reset(); + const startStamp = rdtsc(); + while (self.timer.read() < std.time.ns_per_ms) {} + const endStamp = rdtsc(); + + self.ticksPerMs = @floatFromInt((endStamp - startStamp)); + } else if (builtin.target.cpu.arch == .aarch64) { + self.ticksPerMs = @as(f64, @floatFromInt(aarch64_freq())) / 1000; + } else { + // not ticking + // unreachable; + } + } + + pub fn create(allocator: std.mem.Allocator) !*@This() { + const self = try allocator.create(@This()); + self.* = .{ + .allocator = allocator, + .timer = try std.time.Timer.start(), + }; + return self; + } + + pub fn destroy(self: *@This()) void { + self.allocator.destroy(self); + } +}; + +test "calibrate scoes" { + const allocator = std.testing.allocator; + + const scopes = try ScopesContext.create(allocator); + defer scopes.destroy(); + scopes.calibrate(); + + std.debug.print("calibration ticksPerMs = {d}\n", .{scopes.ticksPerMs.?}); + scopes.calibrate(); + std.debug.print("calibration ticksPerMs = {d}\n", .{scopes.ticksPerMs.?}); + scopes.calibrate(); + std.debug.print("calibration ticksPerMs = {d}\n", .{scopes.ticksPerMs.?}); + scopes.calibrate(); + std.debug.print("calibration ticksPerMs = {d}\n", .{scopes.ticksPerMs.?}); + scopes.calibrate(); + std.debug.print("calibration ticksPerMs = {d}\n", .{scopes.ticksPerMs.?}); +} + +const std = @import("std"); +const builtin = @import("builtin");