效能分析檔¶
LNT 支援儲存和顯示效能分析檔。這些分析檔的目的是揭露測試樣本之間的程式碼產生差異,並允許輕鬆識別程式碼的熱區段。
LNT 中的分析檔原則¶
LNT 中的分析檔以自訂格式表示。使用者介面僅根據對此自訂格式的查詢進行操作。轉接程式是用於從其他格式轉換為 LNT 分析檔格式。分析檔資料會作為一般 JSON 報表的一部分上傳到 LNT 伺服器。
產生分析檔資料¶
分析檔產生可以直接透過 Python API 呼叫(lnt profile
是其包裝函式)或使用 lnt runtests
工具來驅動。
透過 lnt runtests test-suite
產生分析檔資料¶
備註
目前僅在 Linux 系統 上支援透過 LNT 收集效能分析資料,因為目前唯一撰寫的轉接器使用 Linux 的 perf
基礎架構。當更多轉接器被寫入時,LNT 可以增加對它們的支援。
如果您的測試系統已經在使用 lnt runtests
來建置和執行測試,產生效能分析資料的最簡單方法是只需添加一個參數
--use-perf=all
--use-perf
選項指定要用 Linux Perf 做什麼。選項有
none
:不要將perf
用於任何用途time
:使用perf
來測量編譯和執行時間。這可能比time
準確得多。profile
:僅將perf
用於效能分析。all
:將perf
用於效能分析和計時。
產生的效能分析資料與每個測試可執行檔一起存放,命名為 $TEST.perf_data
。這些效能分析資料會在測試執行結束時進行處理並轉換為 LNT 的效能分析資料格式,並插入到產生的 report.json
中。
在沒有 lnt runtests test-suite
的情況下產生效能分析資料¶
LNT 的一個支援用例是使用 LNT 伺服器進行效能追蹤,但使用不同於 lnt runtests
的測試驅動程式來實際建置、執行測試和收集測試統計資料。
效能分析資料位於提交給 LNT 的 JSON 報告中。本節將描述如何將效能分析資料添加到現有的 JSON 報告中;有關 JSON 報告的一般結構的詳細資訊,請參閱 匯入資料。
第一步是以適合透過 JSON 傳送的 LNT 格式產生效能分析資料本身。若要匯入效能分析資料,請使用 lnt profile upgrade
命令
lnt profile upgrade my_profile.perf_data /tmp/my_profile.lntprof
這裡假設 my_profile.perf_data
為 Linux Perf 格式,但可以是任何已註冊轉接器的格式(目前只有 Linux Perf,但預計隨著時間推移會添加更多)。
/tmp/my_profile.lntprof
現在是以節省空間的二進制形式存在的 LNT 效能分析資料。為了準備透過 JSON 傳送,我們必須對其進行 Base64 編碼
base64 -i /tmp/my_profile.lntprof > /tmp/my_profile.txt
現在我們只需要將其添加到報告中即可。效能分析資料看起來類似於雜湊,因為它們是帶有字串資料的樣本
{
"format_version": "2",
"machine": {
...
},
"run": {
...
},
"tests": [
{
"name": "nts.suite1/program1",
"execution_time": [ 0.1056, 0.1055 ],
"profile": "eJxNj8EOgjAMhu99Cm9wULMOEHgBE888QdkASWCQFWJ8e1v04JIt+9f//7qmfkVoEj8yMXdzO70v/RJn2hJYrRQiveSWATdJvwe3jUtgecgh9Wsh9T6gyJvKUjm0kegK0mmt9UCjJUSgB5q8KsobUJOQ96dozr8tAbRApPbssOeCcm83ddoLC7ijMcA/RGUUwXt7iviPEDLJN92yh62LR7I8aBUMysgLnaKNFNzzMo8y7uGplQ4sa/j6rfn60WYaGdRhtT9fP5+JUW4="
}
]
}
支援的格式¶
Linux Perf¶
Perf 效能分析資料直接從二進制 perf.data
檔案讀取,無需使用 perf
包裝工具或任何 Linux/GPL 標頭。這使得它可以在非 Linux 平台上執行,儘管這實際上只對除錯有用,因為預計可以讀取效能分析的二進制檔案/程式庫。
perf 匯入程式碼使用了一個稱為 cPerf 的 C++ 副檔名,它是為 LNT 專案編寫的。它不像 perf annotate
或 perf report
那樣功能豐富,但可以產生幾乎相同的機器可讀格式數據,而且速度快了約 6 倍。它之所以用 C++ 編寫,是因為很難編寫出在處理二進制數據時效率高的易讀 Python 程式碼。事件流匯總後,會建立一個 Python 字典物件,並將處理返回給 Python。速度在這個階段非常重要,因為設定檔匯入可能在較舊或效能較低的硬體上執行,而 LLVM 的測試套件包含數百個必須匯入的測試!
備註
在最新版本的 Perf 中,存在一個新的子命令:perf data
。這會以 CTF 格式 輸出事件追蹤,然後可以使用 babeltrace 及其 Python 綁定進行查詢。只要效能相似,這就可以移除 LNT 中大量的自訂程式碼。
新增對新設定檔格式的支援¶
若要建立新的設定檔配接器,必須在 lnt.testing.profile
套件中建立一個新的 Python 類別,該類別繼承自 ProfileImpl
類別。
- class lnt.testing.profile.profile.ProfileImpl¶
- static checkFile(fname)¶
如果「fname」是此設定檔實作的序列化版本,則傳回 True。
- static deserialize(fobj)¶
從「fobj」讀取設定檔,傳回新的設定檔物件。這可以是延遲的。
- getCodeForFunction(fname)¶
傳回一個「產生器」,它將在每次呼叫時傳回一個三元組。
(counters, address, text)
其中,計數器是一個字典:(例如)
{'cycles': 50.0}
,文字格式與 getDisassemblyFormat() 傳回的格式相同,而地址是一個整數。計數器值必須是百分比(函數總計的百分比),而不是絕對數字。
- getDisassemblyFormat()¶
傳回 getCodeForFunction() 傳回的組譯字串的格式。可能的值為:
raw
- 無可用解釋;純字串。
marked-up-disassembly
- LLVM 標記組譯格式。
- getFunctions()¶
傳回一個字典,包含函數名稱以及該函數的相關資訊。
資訊字典包含:
counters
- 函數的計數器值。length
- 呼叫 getCodeForFunction 以獲取所有指令的次數。
字典應該「不」包含反組譯/函數內容。 計數器值必須是百分比,而不是絕對數字。
例如:
{'main': {'counters': {'cycles': 50.0, 'branch-misses': 0}, 'length': 200}, 'dotest': {'counters': {'cycles': 50.0, 'branch-misses': 0}, 'length': 4} }
- getTopLevelCounters()¶
傳回一個包含整個設定檔計數器的字典。 這些將是絕對數字:例如
{'cycles': 5000.0}
。
- getVersion()¶
傳回設定檔版本。
- serialize(fname=None)¶
將設定檔序列化到給定的檔名(基本名稱)。 如果 fname 為 None,則以 bytes 實例傳回。
- static upgrade(old)¶
在「舊」中取得先前的設定檔實作,並為此版本傳回新的 ProfileImpl。 必須支援的唯一舊版本是緊鄰的前一個版本(例如,版本 3 只需要處理從版本 2 的升級)。
您的子類別可以按照指定的方式實作所有函數,也可以執行 perf.py
所做的事情,也就是只實作 checkFile()
和 deserialize()
靜態函數。 在此模型中,在 deserialize()
內部,您會將設定檔資料解析為簡單的字典結構,並從中建立 ProfileV1Impl
物件。 這是一個非常簡單的設定檔實作,僅適用於字典表示法
- class lnt.testing.profile.profilev1impl.ProfileV1(data)¶
ProfileV1 檔案不以任何方式進行巧妙的處理。 它們是簡單的 Python 物件,其設定檔資料以最明顯的方式佈局,以便進行產生/消耗,然後進行醃製和壓縮。
預計它們會透過簡單地儲存到
self.data
成員來建立。self.data
成員具有以下格式{ counters: {'cycles': 12345.0, 'branch-misses': 200.0}, # absolute values. disassembly-format: 'raw', functions: { name: { counters: {'cycles': 45.0, ...}, # Note counters are now percentages. data: [ [463464, {'cycles': 23.0, ...}, ' add r0, r0, r1'}], ... ] } } }
- static checkFile(fn)¶
如果「fname」是此設定檔實作的序列化版本,則傳回 True。
- static deserialize(fobj)¶
從「fobj」讀取設定檔,傳回新的設定檔物件。這可以是延遲的。
- getCodeForFunction(fname)¶
傳回一個「產生器」,它將在每次呼叫時傳回一個三元組。
(counters, address, text)
其中,計數器是一個字典:(例如)
{'cycles': 50.0}
,文字格式與 getDisassemblyFormat() 傳回的格式相同,而地址是一個整數。計數器值必須是百分比(函數總計的百分比),而不是絕對數字。
- getDisassemblyFormat()¶
傳回 getCodeForFunction() 傳回的組譯字串的格式。可能的值為:
raw
- 無可用解釋;純字串。
marked-up-disassembly
- LLVM 標記組譯格式。
- getFunctions()¶
傳回一個字典,包含函數名稱以及該函數的相關資訊。
資訊字典包含:
counters
- 函數的計數器值。length
- 呼叫 getCodeForFunction 以獲取所有指令的次數。
字典應該「不」包含反組譯/函數內容。 計數器值必須是百分比,而不是絕對數字。
例如:
{'main': {'counters': {'cycles': 50.0, 'branch-misses': 0}, 'length': 200}, 'dotest': {'counters': {'cycles': 50.0, 'branch-misses': 0}, 'length': 4} }
- getTopLevelCounters()¶
傳回一個包含整個設定檔計數器的字典。 這些將是絕對數字:例如
{'cycles': 5000.0}
。
- getVersion()¶
傳回設定檔版本。
- serialize(fname=None)¶
將設定檔序列化到給定的檔名(基本名稱)。 如果 fname 為 None,則以 bytes 實例傳回。
- static upgrade(old)¶
在「舊」中取得先前的設定檔實作,並為此版本傳回新的 ProfileImpl。 必須支援的唯一舊版本是緊鄰的前一個版本(例如,版本 3 只需要處理從版本 2 的升級)。
檢視效能分析¶
將效能分析提交至 LNT 後,可以透過手動輸入網址或透過「執行」頁面取得。
在執行結果頁面上,如果有效能分析資料可用,則將滑鼠懸停在表格列上時,應該會出現「檢視效能分析」連結。
備註
眾所周知,這種懸停效果不適合觸控螢幕,而且可能不太直觀。此頁面應該很快就會修改,以便讓效能分析資料連結更加明顯。
或者,也可以透過手動建構網址來檢視效能分析
db_default/v4/nts/profile/<test-id>/<run1-id>/<run2-id>
其中
test-id
是要顯示的測試的資料庫 TestIDrun1-id
是要顯示在左側的執行的資料庫 RunIDrun2-id
是要顯示在右側的執行的資料庫 RunID
顯然,這個網址有點難以建構,因此建議使用上面提到的執行頁面上的連結。