The profile module

We will implement the profiler as a new module. Create a new file, profiler.lua, and declare the profiler table. This table will contain four other tables: one table for the names of every function, one for the number of times a function is called, one for the total time spent on the function, and one for timing the functions.

The key to each of these tables is going to be a function object:

local profiler = {}

profiler.names = { }
profiler.counts = { }
profiler.times = { }
profiler.timers = { }

The profiler module will have two important public functions: start and stop. These functions will start and stop the profiler, respectively. The profiler module will have an internal function, private_hook. The start and stop functions just set and clear the private_hook function as a debug hook. The debug hook will fire for both call and return events:

profiler.start = function()
debug.sethook(profiler.private_hook, "c r")
end

profiler.stop = function()
debug.sethook()
end

Once the profiler is done profiling, there needs to be a way to collect and review the data it collected. This is where the dump function of the profiler comes in. This function will look trough all the records in the profiler and print out their values:

profiler.dump = function()
for k, v in pairs(profiler.names) do
local out = "function " .. v
out = out .. " was called "
out = out .. profiler.counts[k]
out = out .. " times and took "
out = out .. profiler.times[k]
out = out .. " seconds to execute"
print(out)
end
end

Finally, let's take a look at how the private_hook function works. This function does all of the actual profiling work. The explanation of this function is going to be broken up into a few sections of code here. First, the function needs to get the debug info of its caller, that is, two levels up the stack. If the caller was not Lua, or if the calling function is profiler.stop, the private_hook function needs to stop execution:

profiler.private_hook = function(event)
local info = debug.getinfo(2, "fSn")
if info.what ~= "Lua" then
return
end

local f = info.func
if f == profiler.stop then
return
end

Next, we handle the event when a function call is seen for the first time. We know if this is the first time a function is called if it is not in the profiler.names table. Additionally, we need to check and make sure the current event is a call event. If both of these conditions are true, default values need to be added to the profiler.names, profiler.counts, profiler.times, and profiler.timers tables:

  if profiler.names[f] == nil and event == "call" then
profiler.names[f] = info.name
profiler.counts[f] = 1
profiler.times[f] = 0.0
profiler.timers[f] = os.clock()

On the other hand, if the  profiler.names[f] table already contains the function, we know some profiling work has to be done. The profiler needs to do different work based on the event being processed:

  • If the event is a call event, the number of times the function has been called needs to be increased, and the timer for the function needs to be set.
  • If the event is a return event, the time between the call event and now needs to be calculated, and added to the total running time of the function:
elseif profiler.names[f] ~= nil then
if event == "call" then
profiler.counts[f] = profiler.counts[f] + 1
profiler.timers[f] = os.clock()
elseif event == "return" then
local t = profiler.times[f];
local d = os.difftime(
profiler.timers[f],
os.clock()
)
profiler.times[f] = t + d
end
end
end -- End private_hook function

Finally, finish up the module by returning the profiler table:

return profiler
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.75.217