调优(Profile)
反射的另外一个常见的用法是用以调优,即程序使用资源的分析。对于事件相关的调优,最好使用 C 接口,因为每次调用钩子函数开销太大从而可能导致测试结果无效。
性能调优工具的主要数据结构是两个表,其中一个表将函数和它们的调用计数关联起来,另一个表关联函数和函数名。
local Counters = {}
local Names = {}
我们可以选择在性能分析完成后再获取函数的名称,但是如果能在一个函数 F 处于活动状态时获取其名称会得到更好的结果。
现在,我们定义一个钩子函数,该钩子的任务是获取当前正在被调用的函数,并递增相应的计数器,再收集函数名。
local function hook ()
local f = debug.getinfo(2, "f").func
local count = Counters[f]
if count == nil then
Counters[f] = 1
Names[f] = debug.getinfo(2, "Sn")
else
Counters[f] = count + 1
end
end
运行带有钩子的程序,假设我们要分析的程序位于一个文件中,且用户通过参数把该文件名传递给性能分析器。
% lua profiler main-prog
这样,性能分析器就能从arg[1]中得到文件名,设置钩子并运行文件:
local f = assert(loadfile(arg[1]))
debug.sethook(hool, "c) --设置call时间的钩子
f()
debug.sethook()
我们通过函数 getname 来显式结果
function getname (func)
local n = Names[func]
if n.what == "C" then
return n.name
end
local lc = string.format("[%s]:%d", n.short_src, n.linedefined)
if n.what ~= "main" and n.namewhat ~= "" then
return string.format("%s (%s)", lc, n.name)
else
return lc
end
end
由于 Lua 语言中的函数名并不是特别确定,所以我们给每个函数再加上位置信息,以 file:line这样的形式给出。如果一个函数没有名称,那么就只使用它的位置。如果函数是 C 函数,那么就只使用它的名称。
for func, count in pairs(Counters) do
print(getname(func), count)
end