"""
# Time Package
The Time Package provides classes and methods for timing operations,
dealing with dates and times, and scheduling tasks.
"""
use "lib:rt" if linux
use @clock_gettime[I32](clock: U32, ts: Pointer[(I64, I64)])
if lp64 and (linux or bsd)
use @clock_gettime[I32](clock: U32, ts: Pointer[(I32, I32)])
if ilp32 and (linux or bsd)
use @mach_absolute_time[U64]() if osx
type _Clock is (_ClockRealtime | _ClockMonotonic)
primitive _ClockRealtime
fun apply(): U32 =>
ifdef linux or bsd then
0
else
compile_error "no clock_gettime realtime clock"
end
primitive _ClockMonotonic
fun apply(): U32 =>
ifdef linux then
1
elseif bsd then
4
else
compile_error "no clock_gettime monotonic clock"
end
primitive Time
"""
A collection of ways to fetch the current time.
"""
fun now(): (I64 /*sec*/, I64 /*nsec*/) =>
"""
The wall-clock adjusted system time with nanoseconds.
Return: (seconds, nanoseconds)
"""
ifdef osx then
var ts: (I64, I64) = (0, 0)
@gettimeofday[I32](addressof ts, U64(0))
(ts._1, ts._2 * 1000)
elseif linux or bsd then
_clock_gettime(_ClockRealtime)
elseif windows then
var ft: (U32, U32) = (0, 0)
@GetSystemTimeAsFileTime[None](addressof ft)
var qft = ft._1.u64() or (ft._2.u64() << 32)
var epoch = qft.i64() - 116444736000000000
var sec = epoch / 10000000
var nsec = (epoch - (sec * 10000000)) * 100
(sec, nsec)
else
compile_error "unsupported platform"
end
fun seconds(): I64 =>
"""
The wall-clock adjusted system time.
"""
@time[I64](U64(0))
fun millis(): U64 =>
"""
Monotonic unadjusted milliseconds.
"""
ifdef osx then
@mach_absolute_time() / 1000000
elseif linux or bsd then
var ts = _clock_gettime(_ClockMonotonic)
((ts._1 * 1000) + (ts._2 / 1000000)).u64()
elseif windows then
(let qpc, let qpf) = _query_performance_counter()
(qpc * 1000) / qpf
else
compile_error "unsupported platform"
end
fun micros(): U64 =>
"""
Monotonic unadjusted microseconds.
"""
ifdef osx then
@mach_absolute_time() / 1000
elseif linux or bsd then
var ts = _clock_gettime(_ClockMonotonic)
((ts._1 * 1000000) + (ts._2 / 1000)).u64()
elseif windows then
(let qpc, let qpf) = _query_performance_counter()
(qpc * 1000000) / qpf
else
compile_error "unsupported platform"
end
fun nanos(): U64 =>
"""
Monotonic unadjusted nanoseconds.
"""
ifdef osx then
@mach_absolute_time()
elseif linux or bsd then
var ts = _clock_gettime(_ClockMonotonic)
((ts._1 * 1000000000) + ts._2).u64()
elseif windows then
(let qpc, let qpf) = _query_performance_counter()
(qpc * 1000000000) / qpf
else
compile_error "unsupported platform"
end
fun cycles(): U64 =>
"""
Processor cycle count. Don't use this for performance timing, as it does
not control for out-of-order execution.
"""
@"llvm.readcyclecounter"[U64]()
fun perf_begin(): U64 =>
"""
Get a cycle count for beginning a performance testing block. This will
will prevent instructions from before this call leaking into the block and
instructions after this call being executed earlier.
"""
ifdef x86 then
@"internal.x86.cpuid"[(I32, I32, I32, I32)](I32(0))
@"llvm.x86.rdtsc"[U64]()
else
compile_error "perf_begin only supported on x86"
end
fun perf_end(): U64 =>
"""
Get a cycle count for ending a performance testing block. This will
will prevent instructions from after this call leaking into the block and
instructions before this call being executed later.
"""
ifdef x86 then
var aux: I32 = 0
var ts = @"internal.x86.rdtscp"[U64](addressof aux)
@"internal.x86.cpuid"[(I32, I32, I32, I32)](I32(0))
ts
else
compile_error "perf_end only supported on x86"
end
fun _clock_gettime(clock: _Clock): (I64, I64) =>
"""
Return a clock time on linux and bsd.
"""
ifdef lp64 and (linux or bsd) then
var ts: (I64, I64) = (0, 0)
@clock_gettime(clock(), addressof ts)
ts
elseif ilp32 and (linux or bsd) then
var ts: (I32, I32) = (0, 0)
@clock_gettime(clock(), addressof ts)
(ts._1.i64(), ts._2.i64())
else
compile_error "no clock_gettime"
end
fun _query_performance_counter(): (U64 /* qpc */, U64 /* qpf */) =>
"""
Return QPC and QPF.
"""
ifdef windows then
var pf: (U32, U32) = (0, 0)
var pc: (U32, U32) = (0, 0)
@QueryPerformanceFrequency[U32](addressof pf)
@QueryPerformanceCounter[U32](addressof pc)
let qpf = pf._1.u64() or (pf._2.u64() << 32)
let qpc = pc._1.u64() or (pc._2.u64() << 32)
(qpc, qpf)
else
compile_error "no QueryPerformanceCounter"
end