Profiler In Lua

lua-users home
wiki

This is a (very) simple profiler module written entirely in Lua. It uses the os.time() function for second-precise measurements.

(It was written in about 12 minutes, so bear with me here. --Pedro)

Usage: call Profiler:activate() to start measuring, Profiler:deactivate() to stop, and then Profiler:print_results() to see the results. Tweak the print function to your heart's desire to sort the way you want, etc.

Note that through overloading os.time() with a non-ANSI, millisecond-precision function one can enhance the thing. But for my application seconds was good enough to spot the lines that were making time go quadratical instead of linear.

Also note that for now it can only measure line execution counts and times. One could extend it to call execution if necessary. Some preparations have been made, but not nearly enough.

--| Profiler module.
--b "Pedro Miller Rabinovitch" <miller@inf.puc-rio.br>
--$Id: prof.lua,v 1.4 2003/10/17 00:17:21 miller Exp $
--TODO  add function call profiling. Some of the skeleton is already in
---     place, but call profiling behaves different, so a couple of new
---     functionalities must be added.
--TODO  add methods for proper result retrieval
--TODO  use optional clock() function for millisecond precision, if it's
---     available

local E, I = {}, {}
--& Profiler module.
Profiler = E

--. Keeps track of the hit counts of each item
E.counts = {
  line = {}
}
--. These should be inside the _line_ table above.
E.last_line = nil
E.last_time = os.time()
E.started, E.ended = nil, nil

--% Activates the profiling system.
--@ [kind] (string) Optional hook kind. For now, only 'line' works,
--- so just avoid it. >: )
function E:activate( kind )
  kind = kind or 'line'

  local function hook_counter( hk_name, param,... )
    local t = self.counts[hk_name][param]
    if t == nil then
      t = { count=0, time = 0 }
      self.counts[hk_name][param] = t
    end
    self.counts[hk_name][param].count =
     self.counts[hk_name][param].count + 1

    if self.last_line then
      local delta = os.time() - self.last_time
      if delta > 0 then
        self.counts[hk_name][self.last_line].time =
         self.counts[hk_name][self.last_line].time + delta
        self.last_time = os.time()
      end
    end

    self.last_line = param
  end

  self.started = os.time()
  debug.sethook( hook_counter, kind )
end

--% Deactivates the profiling system.
--@ [kind] (string) Optional hook kind. For now, only 'line' works,
--- so just avoid it.
function E:deactivate( kind )
  kind = kind or 'line'
  self.ended = os.time()
  debug.sethook( nil, kind )
end

--% Prints the results.
--@ [kind] (string) Optional hook... Aah, you got it by now.
--TODO add print output formatting and sorting
function E:print_results( kind )
  kind = kind or 'line'
  print( kind, 'count', 'approx. time (s)' )
  print( '----', '-----', '----------------' )
  for i,v in pairs( self.counts[kind] ) do
    print( i, v.count, v.time )
  end
  print( self.ended - self.started,
    ' second(s) total (approximately).' )
end

RecentChanges · preferences
edit · history
Last edited January 6, 2007 4:50 am GMT (diff)