llvm-exegesis - LLVM 機器指令基準測試

語法

llvm-exegesis [選項]

說明

llvm-exegesis 是一個基準測試工具,它使用 LLVM 中可用的資訊來測量主機機器指令特性,如延遲、吞吐量或埠分解。

給定一個 LLVM 操作碼名稱和一個基準測試模式,llvm-exegesis 會產生一個程式碼片段,使其執行盡可能地序列化(或並行化),以便我們可以測量指令的延遲(或吞吐量/uop 分解的倒數)。程式碼片段會被 JIT 編譯,除非要求不要,否則會在主機子目標上執行。使用硬體效能計數器測量所花費的時間(或資源使用量)。結果以 YAML 格式列印到標準輸出。

此工具的主要目標是自動驗證(或無效化)LLVM 的 TableDef 排程模型。為此,我們還提供了結果分析。

llvm-exegesis 也可以對使用者提供的任意程式碼片段進行基準測試。

支援的平台

llvm-exegesis 目前僅在 Linux 上支援 X86(僅限 64 位元)、ARM(僅限 AArch64)、MIPS 和 PowerPC(僅限 PowerPC64LE)進行基準測試。並非所有基準測試功能都保證在每個平台上都能正常運作。llvm-exegesis 還有一個獨立的分析模式,在 LLVM 支援的每個平台上都支援。

片段註解

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 文件(預設情況下會寫入標準輸出,但您可以使用 –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:基準測試自定義程式碼片段

要測量自定義程式碼的延遲/uops,您可以指定 snippets-file 選項(- 從標準輸入讀取)。

$ echo "vzeroupper" | llvm-exegesis --mode=uops --snippets-file=-

實際的程式碼片段通常依賴於暫存器或記憶體。llvm-exegesis 會檢查暫存器的活躍性(即任何暫存器使用都有一個對應的定義或是一個“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-DEFLLVM-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 格式儲存的基準測試指令(延遲或 uop),您可以使用以下命令分析結果

  $ 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 將包含如下訊息

../_images/llvm-exegesis-analysis.png

請注意,只有在以除錯模式編譯 llvm-exegesis 時才會解析排程類別名稱,否則只會顯示類別 ID。不過,這不會使任何分析結果失效。

選項

--help

列印命令列選項的摘要。

--opcode-index=<LLVM 操作碼 索引>

透過索引指定要測量的操作碼。指定 -1 將會測量每個現有的操作碼。有關詳細資訊,請參閱示例 1。必須設定 opcode-indexopcode-namesnippets-file 其中之一。

--opcode-name=<操作碼 名稱 1>,<操作碼 名稱 2>,...

透過名稱指定要測量的操作碼。可以將多個操作碼指定為以逗號分隔的清單。有關詳細資訊,請參閱示例 1。必須設定 opcode-indexopcode-namesnippets-file 其中之一。

--snippets-file=<檔名>

指定要測量的自訂程式碼片段。有關詳細資訊,請參閱示例 2。必須設定 opcode-indexopcode-namesnippets-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/snippet size 次)。loop 將選擇性地複製程式碼片段,直到迴圈主體至少包含 loop-body-size 個指令,然後將結果包裝在一個將執行 min-instructions 個指令的迴圈中(因此,再次重複程式碼片段 min-instructions/snippet size 次)。loop 模式,尤其是迴圈展開,往往能更好地隱藏 CPU 前端對快取已解碼指令的架構的影響,但會消耗一個寄存器來計算迭代次數。如果對許多操作碼執行分析,則最好改用 min 模式,該模式將執行其他每種模式,並產生最小的測量結果。中間半重複模式將根據特定模式複製或在迴圈中執行程式碼片段。中間半重複模式將執行兩個基準測試,其中一個是第一個基準測試長度的兩倍,然後減去它們之間的差異以獲得沒有開銷的值。

--min-instructions=<指令 數目>

指定要執行的目標指令數。請注意程式碼片段的實際重複次數將為 min-instructions/snippet size。值越高,測量結果越準確,但基準測試時間會越長。

--loop-body-size=<Preferred loop body size>

僅對 -repetition-mode=[loop|min] 有效。不會直接循環執行程式碼片段,而是先複製程式碼片段,使循環主體至少包含這麼多指令。這可能會導致循環主體被快取在 CPU 操作快取/循環快取中,從而可能實現比 CPU 解碼器更高的吞吐量。

--max-configs-per-opcode=<value>

指定可為每個操作碼產生的最大配置數。預設值為 1,這意味著我們假設單一測量就足以描述操作碼的特性。但並非所有指令都適用此假設:例如,LEA 指令在 X86 上的效能特性取決於已分配暫存器和立即數的值。將 -max-configs-per-opcode 的值設定為大於 1,可讓 llvm-exegesis 探索更多配置,以找出是否有任何暫存器或立即數分配會導致不同的效能特性。

--benchmarks-file=</path/to/file>

用於讀取(analysis 模式)或寫入(latency/uops/inverse_throughput 模式)基準測試結果的檔案。“-” 使用標準輸入/標準輸出。

--analysis-clusters-output-file=</path/to/file>

如果提供,則將分析叢集以 CSV 格式寫入此檔案。“-” 列印到標準輸出。預設情況下,不會執行此分析。

--analysis-inconsistencies-output-file=</path/to/file>

如果非空,則將分析期間發現的不一致寫入此檔案。- 列印到標準輸出。預設情況下,不會執行此分析。

--analysis-filter=[all|reg-only|mem-only]

預設情況下,會分析所有基準測試結果,但有時僅查看不涉及記憶體的結果可能會很有用,反之亦然。此選項允許保留所有基準測試,或篩選掉(忽略)所有涉及記憶體的基準測試(涉及可能讀取或寫入記憶體的指令),或者相反,僅保留此類基準測試。

--analysis-clustering=[dbscan,naive]

指定要使用的叢集演算法。預設情況下,將使用 DBSCAN。單純叢集演算法更適合用於對 -analysis-inconsistencies-output-file= 輸出進行進一步處理,它將為每個操作碼建立一個叢集,並檢查叢集是否穩定(所有點都是相鄰的)。

--analysis-numpoints=<dbscan numPoints parameter>

指定用於 DBSCAN 分群的 numPoints 參數(分析 模式,僅限 DBSCAN)。

--analysis-clustering-epsilon=<dbscan epsilon 參數>

指定用於基準點分群的 epsilon 參數(分析 模式)。

--analysis-inconsistency-epsilon=<epsilon>

指定用於偵測分群何時與 LLVM 排程配置文件值不同的 epsilon 參數(分析 模式)。

--analysis-display-unstable-clusters

如果一個操作碼有多個基準測試,如果測量的效能特徵不同,則這些基準測試可能最終不會被分群到同一個分群中。預設情況下,所有此類操作碼都會被過濾掉。此旗標將改為僅顯示此類不穩定的操作碼。

--ignore-invalid-sched-class=false

如果設定,則忽略沒有排程類別的指令(類別索引 = 0)。

--mtriple=<triple 名稱>

目標三元組。請參閱 -version 以取得可用的目標。

--mcpu=<cpu 名稱>

如果設定,則使用此 CPU 的計數器測量 CPU 特性。這在建立新的排程模型時很有用(主機 CPU 對 LLVM 來說是未知的)。(-mcpu=help 以取得詳細資訊)

--analysis-override-benchmark-triple-and-cpu

預設情況下,llvm-exegesis 會分析基準測試的 triple/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 子系統與用於測量目標值的效能計數器一起進行測量。此旗標可以指定多次以測量多個事件。驗證計數器的最大數量取決於平台。

退出狀態

llvm-exegesis 成功時返回 0。否則,會將錯誤訊息列印到標準錯誤輸出,並且工具會返回非 0 值。