此初版是為 Lua 5.0 編寫的。雖然對於後續版本仍有很大的關聯性,但有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買此書,您也協助了 支援 Lua 專案。
![]() |
程式設計於 Lua | ![]() |
第三部分。標準函式庫 第 23 章。除錯函式庫 |
儘管名稱為除錯,除錯函式庫對於除錯以外的工作也很有用。此類常見工作為剖析。對於有計時的剖析,最好使用 C 介面:每個掛鉤的 Lua 呼叫的開銷太高,通常會使任何測量無效。不過,對於計數剖析,Lua 程式碼做得很好。在本節中,我們將開發一個基礎剖析器,它會列出程式中每個函式在執行期間被呼叫的次數。
我們程式的主要資料結構是一個將函式與其呼叫計數器關聯的表格,以及一個將函式與其名稱關聯的表格。這些表格的索引是函式本身。
local Counters = {} local Names = {}我們可以在剖析後擷取名稱資料,但請記住,如果我們在函式處於活動狀態時取得其名稱,我們會獲得更好的結果,因為 Lua 可以查看呼叫函式的程式碼以找出其名稱。
現在我們定義掛鉤函式。它的工作是取得正在呼叫的函式並遞增對應的計數器;它也會收集函式名稱
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下一步是使用此掛鉤執行程式。我們假設程式的 main 程式區塊在一個檔案中,而且使用者會將此檔案名稱作為引數傳遞給剖析器
prompt> lua profiler main-prog使用此架構,我們取得
arg[1]
中的檔案名稱,啟用掛鉤,然後執行檔案
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 中的函式名稱非常不確定,我們會將函式的每個位置新增到函式中,表示為一對 檔案:行。如果函式沒有名稱,我們只會使用其位置。如果函式是 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
如果我們將剖析器套用在我們在 第 10.2 節 中開發的 markov
範例上,我們會得到類似這樣的結果
[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這表示第 4 行的匿名函式(也就是在
allwords
內定義的迭代器函式)被呼叫了 884,723 次,write
(io.write
)被呼叫了 10,000 次,以此類推。
你可以對這個剖析器進行多項改善,例如對輸出進行排序、印出更好的函式名稱,以及改善輸出格式。儘管如此,這個基本的剖析器已經相當實用了,而且可以用作更進階工具的基礎。
版權所有 © 2003–2004 Roberto Ierusalimschy。保留所有權利。 | ![]() |