此初版是為 Lua 5.0 編寫的。雖然對於後續版本仍有很大的關聯性,但有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買此書,您也協助了 支援 Lua 專案


23.3 – 剖析

儘管名稱為除錯,除錯函式庫對於除錯以外的工作也很有用。此類常見工作為剖析。對於有計時的剖析,最好使用 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 次,writeio.write)被呼叫了 10,000 次,以此類推。

你可以對這個剖析器進行多項改善,例如對輸出進行排序、印出更好的函式名稱,以及改善輸出格式。儘管如此,這個基本的剖析器已經相當實用了,而且可以用作更進階工具的基礎。