Moonshark/runner/lua/time.lua

129 lines
3.5 KiB
Lua

-- time.lua
local ffi = require('ffi')
local is_windows = (ffi.os == "Windows")
-- Define C structures and functions based on platform
if is_windows then
ffi.cdef[[
typedef struct {
int64_t QuadPart;
} LARGE_INTEGER;
int QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount);
int QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency);
]]
else
ffi.cdef[[
typedef long time_t;
typedef struct timeval {
long tv_sec;
long tv_usec;
} timeval;
int gettimeofday(struct timeval* tv, void* tz);
time_t time(time_t* t);
]]
end
local time = {}
local has_initialized = false
local start_time, timer_freq
-- Initialize timing system based on platform
local function init()
if has_initialized then return end
if ffi.os == "Windows" then
local frequency = ffi.new("LARGE_INTEGER")
ffi.C.QueryPerformanceFrequency(frequency)
timer_freq = tonumber(frequency.QuadPart)
local counter = ffi.new("LARGE_INTEGER")
ffi.C.QueryPerformanceCounter(counter)
start_time = tonumber(counter.QuadPart)
else
-- Nothing special needed for Unix platform init
start_time = ffi.C.time(nil)
end
has_initialized = true
end
-- PHP-compatible microtime implementation
function time.microtime(get_as_float)
init()
if ffi.os == "Windows" then
local counter = ffi.new("LARGE_INTEGER")
ffi.C.QueryPerformanceCounter(counter)
local now = tonumber(counter.QuadPart)
local seconds = math.floor((now - start_time) / timer_freq)
local microseconds = ((now - start_time) % timer_freq) * 1000000 / timer_freq
if get_as_float then
return seconds + microseconds / 1000000
else
return string.format("0.%06d %d", microseconds, seconds)
end
else
local tv = ffi.new("struct timeval")
ffi.C.gettimeofday(tv, nil)
if get_as_float then
return tonumber(tv.tv_sec) + tonumber(tv.tv_usec) / 1000000
else
return string.format("0.%06d %d", tv.tv_usec, tv.tv_sec)
end
end
end
-- High-precision monotonic timer (returns seconds with microsecond precision)
function time.monotonic()
init()
if ffi.os == "Windows" then
local counter = ffi.new("LARGE_INTEGER")
ffi.C.QueryPerformanceCounter(counter)
local now = tonumber(counter.QuadPart)
return (now - start_time) / timer_freq
else
local tv = ffi.new("struct timeval")
ffi.C.gettimeofday(tv, nil)
return tonumber(tv.tv_sec) - start_time + tonumber(tv.tv_usec) / 1000000
end
end
-- Benchmark function that measures execution time
function time.benchmark(func, iterations, warmup)
iterations = iterations or 1000
warmup = warmup or 10
-- Warmup
for i=1, warmup do func() end
local start = time.microtime(true)
for i=1, iterations do
func()
end
local finish = time.microtime(true)
local elapsed = (finish - start) * 1000000 -- Convert to microseconds
return elapsed / iterations
end
-- Simple sleep function using coroutine yielding
function time.sleep(seconds)
if type(seconds) ~= "number" or seconds <= 0 then
return
end
local start = time.monotonic()
while time.monotonic() - start < seconds do
-- Use coroutine.yield to avoid consuming CPU
coroutine.yield()
end
end
_G.microtime = time.microtime
return time