129 lines
3.5 KiB
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
|