Skip to content

Instantly share code, notes, and snippets.

@csfrancis
Created July 6, 2014 19:57
Show Gist options
  • Save csfrancis/9068d7168f6614bbdb41 to your computer and use it in GitHub Desktop.
Save csfrancis/9068d7168f6614bbdb41 to your computer and use it in GitHub Desktop.

Revisions

  1. csfrancis created this gist Jul 6, 2014.
    71 changes: 71 additions & 0 deletions tsc_timer.lua
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,71 @@
    -- A timer that uses the rdtsc instruction to read the CPU timestamp counter.
    --
    -- Requires DynAsm with Lua mode: https://github.com/luapower/dynasm
    --
    -- Use it like this:
    --
    -- local tsc_timer = require('tsc_timer')
    -- local t = tsc_timer.start()
    -- ... do some things ...
    -- t:stop()
    -- print(t:value())
    --
    -- value() returns the estimated number of milliseconds and the number of ticks elapsed
    --
    local dynasm = require('dynasm')
    local ffi = require('ffi')

    local rdtsc = dynasm.loadstring [[
    local ffi = require('ffi')
    local dasm = require('dasm')
    |.arch x64
    |.actionlist actions
    local Dst = dasm.new(actions)
    | rdtsc
    | shl rdx, 32
    | or rax, rdx
    | ret
    local code = Dst:build()
    return function()
    local _ = code
    return ffi.cast('uint64_t __cdecl (*)()', code)()
    end
    ]]()

    ffi.cdef [[
    int usleep(uint32_t usec);
    ]]

    local _M = {}
    local mt = { __index = _M }

    function _M.initialize()
    if _M.ticks_per_sec ~= nil then return end
    local start_time = rdtsc()
    ffi.C.usleep(200000)
    local end_time = rdtsc() - start_time
    _M.ticks_per_sec = end_time * 5
    end

    function _M.start()
    if _M.ticks_per_second == nil then _M.initialize() end
    return setmetatable({ start_time = rdtsc() }, mt)
    end

    function _M.stop(self)
    local end_time = rdtsc()
    if self.start_time == nil then error("timer has not been started") end
    if self.stop_time ~= nil then error("timer has already been stopped") end
    _M.end_time = end_time
    return _M.value(self)
    end

    function _M.value(self)
    return tonumber(self.end_time - self.start_time) / tonumber(self.ticks_per_sec) * 1000, tonumber(self.end_time - self.start_time)
    end

    return _M