Skip to content

Instantly share code, notes, and snippets.

@kprotty
Created July 9, 2019 17:01
Show Gist options
  • Save kprotty/2158c10b6496d134ba68e5fd412f9bfa to your computer and use it in GitHub Desktop.
Save kprotty/2158c10b6496d134ba68e5fd412f9bfa to your computer and use it in GitHub Desktop.
const std = @import("std");
const builtin = @import("builtin");
pub fn main() !void {
if (std.os.argv.len < 2)
return error.NeedsIterationArgument;
const FIB = 35;
const RESOLUTION = Timer.Resolution.Millisecond;
const ITERATIONS = try std.fmt.parseInt(usize, std.mem.toSlice(u8, std.os.argv[1]), 10);
Timer.Time("fib-zig", ITERATIONS, RESOLUTION, struct { fn run() usize {
return fib_zig(FIB);
}});
Timer.Time("fib-asm", ITERATIONS, RESOLUTION, struct { fn run() usize {
return fib_asm(FIB);
}});
}
// LLVM version
pub export fn fib_zig(n: usize) usize {
if (n <= 1) return 1;
return fib_zig(n - 1) + fib_zig(n - 2);
}
// Custom SystemV version
extern fn fib_asm(n: usize) usize;
comptime { asm (
\\ .intel_syntax noprefix
\\ .global fib_asm
\\ .type fib_asm, @function
\\
\\ fib_asm:
\\ mov rax, 1
\\ cmp rdi, rax
\\ jle done
\\ dec rdi
\\ push rdi
\\ call fib_asm
\\ pop rdi
\\ push rax
\\ dec rdi
\\ call fib_asm
\\ pop rcx
\\ add rax, rcx
\\ done:
\\ ret
);}
pub const Timer = struct {
pub const Resolution = enum {
Second,
Millisecond,
Microsecond,
Nanosecond,
pub fn toUnit(self: Resolution) []const u8 {
return switch (self) {
.Second => "s",
.Millisecond => "ms",
.Microsecond => "μs",
.Nanosecond => "ns",
};
}
pub fn toSecondMultiple(self: Resolution) u64 {
return switch (self) {
.Second => u64(1),
.Millisecond => u64(1000),
.Microsecond => u64(1000 * 1000),
.Nanosecond => u64(1000 * 1000 * 1000),
};
}
};
pub fn Time(
comptime name: []const u8,
iterations: usize,
resolution: Resolution,
comptime action: type,
) void {
var it = u64(0);
var average: u64 = 0;
while (it < iterations) : (it += 1) {
const start = Timer.Get(resolution);
const result = action.run();
const end = Timer.Get(resolution);
average = ((average * it) + (end - start)) / (it + 1);
_ = Timer.BlackBox(result);
}
const unit = resolution.toUnit();
std.debug.warn("{} takes {}{} on average(runs: {})\n", name, average, unit, iterations);
}
pub usingnamespace switch (builtin.os) {
.windows => struct {
var freq: u64 = 0;
extern "kernel32" stdcallcc fn QueryPerformanceCounter(x: *u64) usize;
extern "kernel32" stdcallcc fn QueryPerformanceFrequency(x: *u64) usize;
pub fn BlackBox(result: u64) usize {
return QueryPerformanceCounter(@intToPtr(*u64, @intCast(usize, result)));
}
pub fn Get(resolution: Resolution) u64 {
var time: u64 = undefined;
if (freq == 0)
_ = QueryPerformanceFrequency(&freq);
_ = QueryPerformanceCounter(&time);
return time / (freq / resolution.toSecondMultiple());
}
},
.linux => struct {
const linux = std.os.linux;
pub fn BlackBox(result: u64) usize {
return linux.syscall2(linux.SYS_clock_gettime, linux.CLOCK_REALTIME, @intCast(usize, result));
}
pub fn Get(resolution: Resolution) u64 {
var tp: linux.timespec = undefined;
const ns = Resolution.Nanosecond.toSecondMultiple();
_ = linux.syscall2(linux.SYS_clock_gettime, linux.CLOCK_REALTIME, @ptrToInt(&tp));
const time = @intCast(u64, tp.tv_sec) * ns + @intCast(u64, tp.tv_nsec);
return time / (ns / resolution.toSecondMultiple());
}
},
else => @compileError("OS not supported"),
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment