mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-29 01:14:40 +00:00
151 lines
3.3 KiB
Odin
151 lines
3.3 KiB
Odin
package time
|
|
|
|
import "base:runtime"
|
|
import "base:intrinsics"
|
|
|
|
Tick :: struct {
|
|
_nsec: i64, // relative amount
|
|
}
|
|
tick_now :: proc "contextless" () -> Tick {
|
|
return _tick_now()
|
|
}
|
|
|
|
tick_diff :: proc "contextless" (start, end: Tick) -> Duration {
|
|
d := end._nsec - start._nsec
|
|
return Duration(d)
|
|
}
|
|
|
|
tick_lap_time :: proc "contextless" (prev: ^Tick) -> Duration {
|
|
d: Duration
|
|
t := tick_now()
|
|
if prev._nsec != 0 {
|
|
d = tick_diff(prev^, t)
|
|
}
|
|
prev^ = t
|
|
return d
|
|
}
|
|
|
|
tick_since :: proc "contextless" (start: Tick) -> Duration {
|
|
return tick_diff(start, tick_now())
|
|
}
|
|
|
|
|
|
@(deferred_in_out=_tick_duration_end)
|
|
SCOPED_TICK_DURATION :: proc "contextless" (d: ^Duration) -> Tick {
|
|
return tick_now()
|
|
}
|
|
|
|
|
|
_tick_duration_end :: proc "contextless" (d: ^Duration, t: Tick) {
|
|
d^ = tick_since(t)
|
|
}
|
|
|
|
when ODIN_ARCH == .amd64 {
|
|
@(private)
|
|
x86_has_invariant_tsc :: proc "contextless" () -> bool {
|
|
eax, _, _, _ := intrinsics.x86_cpuid(0x80_000_000, 0)
|
|
|
|
// Is this processor *really* ancient?
|
|
if eax < 0x80_000_007 {
|
|
return false
|
|
}
|
|
|
|
// check if the invariant TSC bit is set
|
|
_, _, _, edx := intrinsics.x86_cpuid(0x80_000_007, 0)
|
|
return (edx & (1 << 8)) != 0
|
|
}
|
|
}
|
|
|
|
when ODIN_OS != .Darwin && ODIN_OS != .Linux && ODIN_OS != .FreeBSD {
|
|
_get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
has_invariant_tsc :: proc "contextless" () -> bool {
|
|
when ODIN_ARCH == .amd64 {
|
|
return x86_has_invariant_tsc()
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
tsc_frequency :: proc "contextless" (fallback_sleep := 2 * Second) -> (u64, bool) {
|
|
if !has_invariant_tsc() {
|
|
return 0, false
|
|
}
|
|
|
|
hz, ok := _get_tsc_frequency()
|
|
if !ok {
|
|
// fallback to approximate TSC
|
|
tsc_begin := intrinsics.read_cycle_counter()
|
|
tick_begin := tick_now()
|
|
|
|
sleep(fallback_sleep)
|
|
|
|
tsc_end := intrinsics.read_cycle_counter()
|
|
tick_end := tick_now()
|
|
|
|
time_diff := u128(duration_nanoseconds(tick_diff(tick_begin, tick_end)))
|
|
hz = u64((u128(tsc_end - tsc_begin) * 1_000_000_000) / time_diff)
|
|
}
|
|
|
|
return hz, true
|
|
}
|
|
|
|
/*
|
|
Benchmark helpers
|
|
*/
|
|
|
|
Benchmark_Error :: enum {
|
|
Okay = 0,
|
|
Allocation_Error,
|
|
}
|
|
|
|
Benchmark_Options :: struct {
|
|
setup: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
|
|
bench: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
|
|
teardown: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
|
|
|
|
rounds: int,
|
|
bytes: int,
|
|
input: []u8,
|
|
|
|
count: int,
|
|
processed: int,
|
|
output: []u8, // Unused for hash benchmarks
|
|
hash: u128,
|
|
|
|
/*
|
|
Performance
|
|
*/
|
|
duration: Duration,
|
|
rounds_per_second: f64,
|
|
megabytes_per_second: f64,
|
|
}
|
|
|
|
benchmark :: proc(options: ^Benchmark_Options, allocator := context.allocator) -> (err: Benchmark_Error) {
|
|
assert(options != nil)
|
|
assert(options.bench != nil)
|
|
|
|
if options.setup != nil {
|
|
options->setup(allocator) or_return
|
|
}
|
|
|
|
diff: Duration
|
|
{
|
|
SCOPED_TICK_DURATION(&diff)
|
|
options->bench(allocator) or_return
|
|
}
|
|
options.duration = diff
|
|
|
|
times_per_second := f64(Second) / f64(diff)
|
|
options.rounds_per_second = times_per_second * f64(options.count)
|
|
options.megabytes_per_second = f64(options.processed) / f64(1024 * 1024) * times_per_second
|
|
|
|
if options.teardown != nil {
|
|
options->teardown(allocator) or_return
|
|
}
|
|
return
|
|
}
|