23.3 Profiles

尽管debug库名字上看来是一个调式库,除了用于调式以外,还可以用于完成其他任务。这种常见的任务就是profiling。对于一个实时的profile来说(For a profile with timing),最好使用C接口来完成:对于每一个hook过多的Lua调用代价太大并且通常会导致测量的结果不准确。然而,对于计数的profiles而言,Lua代码可以很好的胜任。下面这部分我们将实现一个简单的profiler:列出在程序运行过程中,每一个函数被调用的次数。

我们程序的主要数据结构是两张表,一张关联函数和他们调用次数的表,一张关联函数和函数名的表。这两个表的索引下标是函数本身。

local Counters = {}

local Names = {}

profiling之后,我们可以访问函数名数据,但是记住:在函数在活动状态的情况下,可以得到比较好的结果,因为那时候Lua会察看正在运行的函数的代码来查找指定的函数名。

现在我们定义hook函数,他的任务就是获取正在执行的函数并将对应的计数器加1;同时这个hook函数也收集函数名信息:

local function hook ()

    local f = debug.getinfo(2, "f").func

    if Counters[f] == nil then  -- first time `f' is called?

       Counters[f] = 1

       Names[f] = debug.getinfo(2, "Sn")

    else   -- only increment the counter

       Counters[f] = Counters[f] + 1

    end

end

下一步就是使用这个hook运行程序,我们假设程序的主chunk在一个文件内,并且用户将这个文件名作为profiler的参数:

prompt> lua profiler main-prog

这种情况下,我们的文件名保存在arg[1],打开hook并运行文件:

local f = assert(loadfile(arg[1]))

debug.sethook(hook, "c")    -- turn on the hook

f()    -- run the main program

debug.sethook()   -- turn off the hook

最后一步是显示结果,下一个函数为一个函数产生名称,因为在Lua中的函数名不确定,所以我们对每一个函数加上他的位置信息,型如file:line 。如果一个函数没有名字,那么我们只用它的位置表示。如果一个函数是C函数,我们只是用它的名字表示(他没有位置信息)。

function getname (func)

    local n = Names[func]

    if n.what == "C" then

       return n.name

    end

    local loc = string.format("[%s]:%s",

           n.short_src, n.linedefined)

    if n.namewhat ~= "" then

       return string.format("%s (%s)", loc, n.name)

    else

       return string.format("%s", loc)

    end

end

最后,我们打印每一个函数和他的计数器:

for func, count in pairs(Counters) do

    print(getname(func), count)

end

如果我们将我们的profiler应用到Section 10.2的马尔科夫链的例子上,我们得到如下结果:

[markov.lua]:4 884723

write  10000

[markov.lua]:0 (f)       1

read   31103

sub    884722

[markov.lua]:1 (allwords)       1

[markov.lua]:20 (prefix)        894723

find   915824

[markov.lua]:26 (insert)        884723

random  10000

sethook 1

insert  884723

那意味着第四行的匿名函数(在allwords内定义的迭代函数)被调用884,723次,write(io.write)被调用10,000次。

你可以对这个profiler进行一些改进,比如对输出排序、打印出比较好的函数名、改善输出格式。不过,这个基本的profiler已经很有用,并且可以作为很多高级工具的基础。

 

 

 



相关链接:
lua程序设计目录 - 中国lua开发者 - lua论坛