llvm-exegesis - LLVM 機器指令效能評測工具¶
概要¶
llvm-exegesis [選項]
描述¶
llvm-exegesis 是一個效能評測工具,它使用 LLVM 中可用的資訊來測量主機器的指令特性,例如延遲、吞吐量或埠分解。
給定一個 LLVM opcode 名稱和一個效能評測模式,llvm-exegesis 會產生一個程式碼片段,使其執行盡可能串列(resp. 盡可能並行),以便我們可以測量指令的延遲(resp. 反向吞吐量/uop 分解)。程式碼片段會被即時編譯,除非要求不執行,否則會在主機子目標上執行。時間(resp. 資源使用量)會使用硬體效能計數器測量。結果會以 YAML 格式列印到標準輸出。
此工具的主要目標是自動(不)驗證 LLVM 的 TableDef 排程模型。為此,我們也提供結果分析。
llvm-exegesis 也可以評測任意使用者提供的程式碼片段。
支援平台¶
llvm-exegesis 目前僅支援在 Linux 上針對 X86 (僅限 64 位元)、ARM (僅限 AArch64,程式碼片段生成稀疏)、MIPS 和 PowerPC (僅限 PowerPC64LE) 進行效能評測。並非所有效能評測功能都保證在每個平台上都能運作。llvm-exegesis 還有一個獨立的分析模式,在 LLVM 支援的每個平台上都支援。
若要在 llvm-exegesis 中啟用效能評測,LLVM 必須配置並使用啟用的 LLVM_ENABLE_LIBPFM 進行建置,因為 llvm-exegesis 依賴 libpfm4 來存取效能計數器。如果目標 CPU 不受 libpfm 支援,效能評測可能會失敗。這可以透過設定 LIBPFM_VERBOSE 和 LIBPFM_DEBUG 環境變數來啟用 libpfm 的 verbose 或 debug 模式來驗證。如果 libpfm 安裝在非標準目錄中,則可以透過設定 LIBRARY_PATH、C_INCLUDE_PATH 和 CPLUS_INCLUDE_PATH 環境變數來配置 LLVM 以找到必要的程式庫和標頭檔。此外,應設定 LD_LIBRARY_PATH,以便 llvm-exegesis 可以在執行期間找到 libpfm 程式庫。
程式碼片段註解¶
llvm-exegesis 支援評測任意組合語言片段。然而,評測這些片段通常需要一些設定,以便它們可以正確執行。llvm-exegesis 有五個註解和一些額外的工具來協助設定,以便可以正確評測程式碼片段。
LLVM-EXEGESIS-DEFREG <暫存器名稱> - 將此註解新增到要評測的文字組合語言片段,將暫存器標記為需要定義。除非傳入第二個參數,十六進位值,否則將自動提供一個值。這是使用 LLVM-EXEGESIS-DEFREG <暫存器名稱> <十六進位值> 格式完成的。<十六進位值> 是用來填滿暫存器的位元模式。如果它是一個小於暫存器的值,它會被符號擴充以符合暫存器的大小。
LLVM-EXEGESIS-LIVEIN <暫存器名稱> - 此註解允許指定在效能評測開始時應保留其值的暫存器。在某些情況下,可以透過暫存器從效能評測設定傳遞值。可以在具有 LLVM-EXEGESIS-LIVEIN 的效能評測腳本中使用的暫存器和分配給它們的值如下
暫存記憶體暫存器 - 此值放入的特定暫存器取決於平台(例如,在 X86 Linux 上它是 RDI 暫存器)。將此暫存器設定為 live in 可確保將指向記憶體區塊 (1MB) 的指標放置在此暫存器中,該指標可供程式碼片段使用。
LLVM-EXEGESIS-MEM-DEF <值名稱> <大小> <值> - 此註解允許指定記憶體定義,稍後可以使用 LLVM-EXEGESIS-MEM-MAP 註解將其對應到程式碼片段的執行程序中。每個值都使用 <值名稱> 參數命名,以便稍後可以在對應註解中參考它。大小以十進位位元組數指定,值以十六進位給出。如果值的大小小於指定的大小,則該值將重複直到填滿整個記憶體區段。使用此註解需要使用子程序執行模式。
LLVM-EXEGESIS-MEM-MAP <值名稱> <位址> - 此註解允許將先前定義的記憶體定義對應到程序的執行環境中。值名稱指的是先前定義的記憶體定義,位址是一個十進位數字,指定記憶體定義應從哪個位址開始。請注意,單個記憶體定義可以對應多次。使用此註解需要子程序執行模式。
LLVM-EXEGESIS-SNIPPET-ADDRESS <位址> - 此註解允許設定要執行的程式碼片段開始處將對應到的位址。位址以十六進位給出。請注意,程式碼片段也包含設定程式碼,因此正好位於指定位址的指令將不是程式碼片段中的第一個指令。使用此註解需要子程序執行模式。這在程式碼片段存取的記憶體取決於程式碼片段的位置時很有用,例如 RIP 相對定址。
LLVM-EXEGESIS-LOOP-REGISTER <暫存器名稱> - 此註解指定迴圈暫存器,用於在使用迴圈重複模式時追蹤當前迭代。llvm-exegesis 需要以高效能的方式(即,沒有記憶體存取)在迴圈重複模式中追蹤當前迴圈迭代,並使用暫存器來執行此操作。此暫存器具有架構特定的預設值(例如,X86 上的 R8),但這可能會與某些程式碼片段衝突。此註解允許變更暫存器以防止迴圈索引暫存器和程式碼片段之間的干擾。
範例 1:評測指令¶
假設您有一台 X86-64 機器。若要測量單個指令的延遲,請執行
$ llvm-exegesis --mode=latency --opcode-name=ADD64rr
測量指令的 uop 分解或反向吞吐量的工作方式類似
$ llvm-exegesis --mode=uops --opcode-name=ADD64rr
$ llvm-exegesis --mode=inverse_throughput --opcode-name=ADD64rr
輸出是一個 YAML 文件(預設是寫入 stdout,但您可以使用 –benchmarks-file 將輸出重新導向到檔案)
---
key:
opcode_name: ADD64rr
mode: latency
config: ''
cpu_name: haswell
llvm_triple: x86_64-unknown-linux-gnu
num_repetitions: 10000
measurements:
- { key: latency, value: 1.0058, debug_string: '' }
error: ''
info: 'explicit self cycles, selecting one aliasing configuration.
Snippet:
ADD64rr R8, R8, R10
'
...
若要測量主機架構的所有指令的延遲,請執行
$ llvm-exegesis --mode=latency --opcode-index=-1
範例 2:評測自訂程式碼片段¶
若要測量自訂程式碼片段的延遲/uop,您可以指定 snippets-file 選項 (- 從標準輸入讀取)。
$ echo "vzeroupper" | llvm-exegesis --mode=uops --snippets-file=-
真實世界的程式碼片段通常依賴於暫存器或記憶體。llvm-exegesis 會檢查暫存器的即時性(即,任何暫存器使用都有對應的 def 或是一個 “live in”)。如果您的程式碼依賴於某些暫存器的值,您需要使用程式碼片段註解來確保正確執行設定。
例如,以下程式碼片段依賴於 XMM1 的值(將由工具設定)和在 RDI 中傳入的記憶體緩衝區(live in)。
# LLVM-EXEGESIS-LIVEIN RDI
# LLVM-EXEGESIS-DEFREG XMM1 42
vmulps (%rdi), %xmm1, %xmm2
vhaddps %xmm2, %xmm2, %xmm3
addq $0x10, %rdi
範例 3:使用記憶體註解進行評測¶
某些程式碼片段需要特定位置的記憶體設定才能執行而不會崩潰。可以使用 LLVM-EXEGESIS-MEM-DEF 和 LLVM-EXEGESIS-MEM-MAP 註解來完成記憶體設定。若要執行以下程式碼片段
movq $8192, %rax
movq (%rax), %rdi
我們需要至少八個位元組的記憶體,從 0x2000 開始配置。我們可以使用新增到程式碼片段的以下註解來建立必要的執行環境
# LLVM-EXEGESIS-MEM-DEF test1 4096 7fffffff
# LLVM-EXEGESIS-MEM-MAP test1 8192
movq $8192, %rax
movq (%rax), %rdi
範例 4:分析¶
假設您在檔案 /tmp/benchmarks.yaml 中有一個 YAML 格式的已評測指令集(延遲或 uops),您可以使用以下命令分析結果
$ llvm-exegesis --mode=analysis \
--benchmarks-file=/tmp/benchmarks.yaml \
--analysis-clusters-output-file=/tmp/clusters.csv \
--analysis-inconsistencies-output-file=/tmp/inconsistencies.html
這會將具有相同效能特性的指令分組到叢集中。叢集將以以下格式寫出到 /tmp/clusters.csv
cluster_id,opcode_name,config,sched_class
...
2,ADD32ri8_DB,,WriteALU,1.00
2,ADD32ri_DB,,WriteALU,1.01
2,ADD32rr,,WriteALU,1.01
2,ADD32rr_DB,,WriteALU,1.00
2,ADD32rr_REV,,WriteALU,1.00
2,ADD64i32,,WriteALU,1.01
2,ADD64ri32,,WriteALU,1.01
2,MOVSX64rr32,,BSWAP32r_BSWAP64r_MOVSX64rr32,1.00
2,VPADDQYrr,,VPADDBYrr_VPADDDYrr_VPADDQYrr_VPADDWYrr_VPSUBBYrr_VPSUBDYrr_VPSUBQYrr_VPSUBWYrr,1.02
2,VPSUBQYrr,,VPADDBYrr_VPADDDYrr_VPADDQYrr_VPADDWYrr_VPSUBBYrr_VPSUBDYrr_VPSUBQYrr_VPSUBWYrr,1.01
2,ADD64ri8,,WriteALU,1.00
2,SETBr,,WriteSETCC,1.01
...
llvm-exegesis 也會分析叢集,以指出排程資訊中的不一致之處。輸出是一個 html 檔案。例如,/tmp/inconsistencies.html 將包含如下訊息

請注意,只有在 llvm-exegesis 在 debug 模式下編譯時,才會解析排程類別名稱,否則只會顯示類別 id。但是,這不會使任何分析結果失效。
選項¶
- --help¶
列印命令列選項的摘要。
- --opcode-index=<LLVM opcode 索引>¶
依索引指定要測量的 opcode。指定 -1 將導致測量每個現有的 opcode。請參閱範例 1 以取得詳細資訊。必須設定 opcode-index、opcode-name 或 snippets-file 之一。
- --opcode-name=<opcode 名稱 1>,<opcode 名稱 2>,...¶
依名稱指定要測量的 opcode。可以將多個 opcode 指定為逗號分隔的列表。請參閱範例 1 以取得詳細資訊。必須設定 opcode-index、opcode-name 或 snippets-file 之一。
- --snippets-file=<檔案名稱>¶
指定要測量的自訂程式碼片段。請參閱範例 2 以取得詳細資訊。必須設定 opcode-index、opcode-name 或 snippets-file 之一。
- --mode=[latency|uops|inverse_throughput|analysis]¶
指定執行模式。請注意,某些模式有額外的需求和選項。
latency 模式可以使用 RDTSC 或 LBR。latency[LBR] 僅在 X86 上可用(至少 Skylake)。若要在 latency 模式下執行,必須為 x86-lbr-sample-period 指定正值,且 –repetition-mode=loop。
在 analysis 模式下,您還需要指定至少一個 -analysis-clusters-output-file= 和 -analysis-inconsistencies-output-file=。
- --benchmark-phase=[prepare-snippet|prepare-and-assemble-snippet|assemble-measured-code|measure]¶
預設情況下,當指定 -mode= 時,將會執行和測量產生的程式碼片段,這需要我們在產生程式碼片段的硬體上執行,並且該硬體支援效能測量。但是,有可能在測量之前停止在某些階段。選項為:*
prepare-snippet
:僅產生最小的指令序列。*prepare-and-assemble-snippet
:與prepare-snippet
相同,但也傾印序列的摘錄(十六進位編碼)。*assemble-measured-code
:與prepare-and-assemble-snippet
相同。但也建立完整序列,可以使用--dump-object-to-disk
傾印到檔案。*measure
:與assemble-measured-code
相同,但也執行測量。
- --x86-lbr-sample-period=<nBranches/sample>¶
指定 LBR 取樣週期 - 在我們進行取樣之前有多少個分支。當為此選項指定正值且模式為 latency 時,我們將使用 LBR 進行測量。在選擇 “正確” 的取樣週期時,較小的值是首選,但如果取樣太頻繁,可能會發生節流。應使用質數以避免持續跳過某些區塊。
- --x86-disable-upper-sse-registers¶
使用較高的 xmm 暫存器 (xmm8-xmm15) 會強制執行更長的指令編碼,這可能會對前端擷取和解碼階段造成更大的壓力,可能會降低指令分派到後端的速度,尤其是在較舊的硬體上。將基準結果與啟用此模式的結果進行比較,可以幫助確定前端的效果,並且可以用於改進延遲和吞吐量估計。
- --repetition-mode=[duplicate|loop|min|middle-half-duplicate|middle-half-loop]¶
指定重複模式。duplicate 將建立一個大的、直線型的基本區塊,其中包含 min-instructions 個指令(重複程式碼片段 min-instructions/程式碼片段大小 次)。loop 將選擇性地重複程式碼片段,直到迴圈主體至少包含 loop-body-size 個指令,然後將結果包裝在迴圈中,該迴圈將執行 min-instructions 個指令(因此,再次,重複程式碼片段 min-instructions/程式碼片段大小 次)。loop 模式,尤其是使用迴圈展開,往往可以更好地隱藏 CPU 前端對快取解碼指令的架構的影響,但會消耗一個暫存器來計數迭代。如果對許多 opcode 執行分析,則最好改用 min 模式,它將執行每個其他模式,並產生最小的測量結果。中間一半重複模式將根據特定模式重複或在迴圈中執行程式碼片段。中間一半重複模式將執行兩個效能評測,第二個效能評測的長度是第一個效能評測的兩倍,然後減去它們之間的差異以獲得沒有額外負荷的值。
- --min-instructions=<指令數量>¶
指定目標執行的指令數量。請注意,程式碼片段的實際重複計數將為 min-instructions/程式碼片段大小。較高的值會產生更準確的測量結果,但會延長效能評測時間。
- --loop-body-size=<偏好的迴圈主體大小>¶
僅對 -repetition-mode=[loop|min] 有效。與直接在程式碼片段上迴圈不同,首先重複它,以便迴圈主體至少包含這麼多指令。這可能會導致迴圈主體快取在 CPU Op 快取/迴圈快取中,這允許比 CPU 解碼器更高的吞吐量。
- --max-configs-per-opcode=<值>¶
指定每個 opcode 可以產生的最大配置數。預設情況下,這是 1,表示我們假設單個測量足以描述 opcode 的特性。這可能不適用於所有指令:例如,X86 上 LEA 指令的效能特性取決於分配的暫存器和立即值的值。將 -max-configs-per-opcode 的值設定為大於 1,允許 llvm-exegesis 探索更多配置,以發現某些暫存器或立即值分配是否會導致不同的效能特性。
- --benchmarks-file=</path/to/file>¶
讀取 (analysis 模式) 或寫入 (latency/uops/inverse_throughput 模式) 效能評測結果的檔案。“-” 使用 stdin/stdout。
- --analysis-clusters-output-file=</path/to/file>¶
如果提供,將分析叢集作為 CSV 寫入到此檔案。“-” 列印到 stdout。預設情況下,不執行此分析。
- --analysis-inconsistencies-output-file=</path/to/file>¶
如果非空,將分析期間發現的不一致之處寫入到此檔案。- 列印到 stdout。預設情況下,不執行此分析。
- --analysis-filter=[all|reg-only|mem-only]¶
預設情況下,會分析所有效能評測結果,但有時僅查看不涉及記憶體的結果,或反之亦然,可能會很有用。此選項允許保留所有效能評測,或篩選掉(忽略)所有涉及記憶體的效能評測(涉及可能讀取或寫入記憶體的指令),或相反地,僅保留此類效能評測。
- --analysis-clustering=[dbscan,naive]¶
指定要使用的叢集演算法。預設情況下將使用 DBSCAN。Naive 叢集演算法更適合對 -analysis-inconsistencies-output-file= 輸出進行進一步處理,它將為每個 opcode 建立一個叢集,並檢查叢集是否穩定(所有點都是鄰居)。
- --analysis-numpoints=<dbscan numPoints 參數>¶
指定用於 DBSCAN 叢集的 numPoints 參數(analysis 模式,僅限 DBSCAN)。
- --analysis-clustering-epsilon=<dbscan epsilon 參數>¶
指定用於效能評測點叢集的 epsilon 參數(analysis 模式)。
- --analysis-inconsistency-epsilon=<epsilon>¶
指定用於偵測叢集何時與 LLVM 排程設定檔值不同的 epsilon 參數(analysis 模式)。
- --analysis-display-unstable-clusters¶
如果一個 opcode 有多個效能評測,如果測量的效能特性不同,則這些效能評測可能最終不會叢集到同一個叢集中。預設情況下,所有此類 opcode 都會被篩選掉。此標誌將改為僅顯示此類不穩定的 opcode。
- --ignore-invalid-sched-class=false¶
如果設定,則忽略沒有排程類別的指令(類別索引 = 0)。
- --mtriple=<triple 名稱>¶
目標三元組。請參閱 -version 以取得可用的目標。
- --mcpu=<cpu 名稱>¶
如果設定,使用此 CPU 的計數器測量 cpu 特性。這在建立新的排程模型時很有用(LLVM 未知主機 CPU)。(-mcpu=help 取得詳細資訊)
- --analysis-override-benchmark-triple-and-cpu¶
預設情況下,llvm-exegesis 將分析為其測量的三元組/CPU 的效能評測,但如果您想為某些其他組合(透過 -mtriple/-mcpu 指定)分析它們,您可以傳遞此標誌。
- --dump-object-to-disk=true¶
如果設定,llvm-exegesis 將產生的程式碼傾印到臨時檔案,以啟用程式碼檢查。預設情況下停用。
- --use-dummy-perf-counters¶
如果設定,llvm-exegesis 將不會讀取任何真實的效能計數器,而是傳回虛擬值。這可以用於確保程式碼片段在硬體效能計數器不可用時不會崩潰,以及用於偵錯 llvm-exegesis 本身。
- --execution-mode=[inprocess,subprocess]¶
此選項指定要使用的執行模式。inprocess 執行模式是預設模式。subprocess 執行模式允許額外功能,例如記憶體註解,但目前僅限於 Linux 上的 X86-64。
- --benchmark-repeat-count=<repeat-count>¶
此選項允許指定在執行延遲測量時重複測量的次數。預設情況下,llvm-exegesis 將重複延遲測量足夠的次數,以平衡執行時間和雜訊降低。
- --validation-counter=[instructions-retired,l1d-cache-load-misses,¶
- l1d-cache-store-misses,l1i-cache-load-misses,data-tlb-load-misses,¶
- data-tld-store-misses,instruction-tlb-load-misses]¶
此選項啟用驗證計數器的使用,驗證計數器測量額外的微架構事件,例如快取未命中,以驗證程式碼片段執行條件。這些事件是使用 perf 子系統與效能計數器一起測量的,效能計數器用於測量感興趣的值。可以多次指定此標誌以測量多個事件。驗證計數器的最大數量取決於平台。
- --benchmark-process-cpu=<cpu id>¶
此選項指定應用於執行效能評測子程序的 CPU 編號。啟動子程序時,llvm-exegesis 將設定子程序的親和性,使其僅包含指定的 CPU。此選項僅在子程序執行模式下有效。
結束狀態¶
llvm-exegesis 成功時傳回 0。否則,錯誤訊息會列印到標準錯誤,且工具會傳回非 0 值。