Benchmark Utils - torch.utils.benchmark¶
- class torch.utils.benchmark.Timer(stmt='pass', setup='pass', global_setup='', timer=<built-in function perf_counter>, globals=None, label=None, sub_label=None, description=None, env=None, num_threads=1, language=Language.PYTHON)[source][source]¶
用於測量 PyTorch 語句執行時間的輔助類別。
有關如何使用此類別的完整教學,請參閱:https://pytorch.dev.org.tw/tutorials/recipes/recipes/benchmark.html
PyTorch Timer 基於 timeit.Timer(實際上在內部使用 timeit.Timer),但有一些關鍵差異:
- 感知執行階段 (Runtime aware)
Timer 將執行預熱 (warmup)(很重要,因為 PyTorch 的某些元素是延遲初始化的),設定執行緒池大小,以便進行公平比較,並在必要時同步非同步 CUDA 函式。
- 專注於重複 (replicates)
在測量程式碼時,尤其是複雜的內核/模型時,run-to-run 變化是一個重要的干擾因素。預期所有測量都應包含重複,以量化雜訊並允許計算中位數,這比平均值更穩健。為此,此類別通過概念性地合併 timeit.Timer.repeat 和 timeit.Timer.autorange 來偏離 timeit API。(確切的演算法在方法 docstring 中討論。)timeit 方法被複製用於不希望使用自適應策略的情況。
- 可選的中繼資料 (metadata)
在定義 Timer 時,您可以選擇指定 label、sub_label、description 和 env。(稍後定義)這些欄位包含在結果物件的表示中,並由 Compare 類別用於分組和顯示結果以進行比較。
- 指令計數
除了實際時間 (wall time) 之外,Timer 還可以在 Callgrind 下執行語句並報告執行的指令。
直接類比於 timeit.Timer 建構子引數
stmt、setup、timer、globals
PyTorch Timer 特定的建構子引數
label、sub_label、description、env、num_threads
- 參數
stmt (str) – 要在迴圈中執行和計時的程式碼片段。
setup (str) – 可選的設定程式碼。用於定義 stmt 中使用的變數
global_setup (str) – (僅限 C++) 程式碼,放置在檔案的頂層,用於諸如 #include 語句之類的操作。
timer (Callable[[], float]) – 傳回目前時間的可呼叫物件。如果 PyTorch 在沒有 CUDA 的情況下建置,或者沒有 GPU 存在,則預設為 timeit.default_timer;否則,它將在測量時間之前同步 CUDA。
globals (Optional[Dict[str, Any]]) – 一個 dict,定義了執行 stmt 時的全域變數。這是提供 stmt 需要的變數的另一種方法。
label (Optional[str]) – 總結 stmt 的字串。例如,如果 stmt 是 “torch.nn.functional.relu(torch.add(x, 1, out=out))”,則可以將 label 設定為 “ReLU(x + 1)” 以提高可讀性。
提供補充資訊,以消除具有相同 stmt 或 label 的測量之間的歧義。例如,在我們上面的範例中,sub_label 可能是 “float” 或 “int”,這樣可以很容易地區分:“ReLU(x + 1): (float)”
在使用 Compare 顯示測量或進行總結時:“ReLU(x + 1): (int)”。
用於區分具有相同 label 和 sub_label 的測量的字串。description 的主要用途是向 Compare 發出資料列的訊號。例如,您可以根據輸入大小來設定它,以建立以下形式的表格
| n=1 | n=4 | ... ------------- ... ReLU(x + 1): (float) | ... | ... | ... ReLU(x + 1): (int) | ... | ... | ...
使用 Compare。它也包含在顯示測量時。
env (Optional[str]) – 此標籤表示,否則相同的任務在不同的環境中執行,因此並不等效,例如,在 A/B 測試對內核的變更時。Compare 在合併重複執行時,會將具有不同 env 規格的測量視為不同。
num_threads (int) – 執行 stmt 時 PyTorch 執行緒池的大小。單執行緒效能很重要,既是關鍵推論工作負載,又是內在演算法效率的良好指標,因此預設設定為 1。這與預設的 PyTorch 執行緒池大小(試圖利用所有核心)形成對比。
- adaptive_autorange(threshold=0.1, *, min_run_time=0.01, max_run_time=10.0, callback=None)[原始碼][原始碼]¶
與 blocked_autorange 類似,但也檢查測量中的變異性,並重複執行直到 iqr/中位數 小於 threshold 或達到 max_run_time。
在高層次上,adaptive_autorange 執行以下偽代碼
`setup` times = [] while times.sum < max_run_time start = timer() for _ in range(block_size): `stmt` times.append(timer() - start) enough_data = len(times)>3 and times.sum > min_run_time small_iqr=times.iqr/times.mean<threshold if enough_data and small_iqr: break
- 參數
- 傳回
包含測量的運行時間和重複計數的 Measurement 物件,可用於計算統計數據。(平均值、中位數等)
- 傳回類型
- blocked_autorange(callback=None, min_run_time=0.2)[原始碼][原始碼]¶
測量多個複本,同時將計時器開銷保持在最低限度。
在高層次上,blocked_autorange 執行以下偽代碼
`setup` total_time = 0 while total_time < min_run_time start = timer() for _ in range(block_size): `stmt` total_time += (timer() - start)
請注意內部迴圈中的變數 block_size。區塊大小的選擇對於測量品質很重要,並且必須平衡兩個相互競爭的目標
較小的區塊大小會產生更多的複本,通常會有更好的統計數據。
較大的區塊大小可以更好地分攤 timer 呼叫的成本,並產生較少偏差的測量結果。這很重要,因為 CUDA 同步時間並非微不足道(數量級為個位數到低雙位數微秒),否則會使測量產生偏差。
blocked_autorange 透過運行預熱期來設定 block_size,增加區塊大小直到計時器開銷小於總計算量的 0.1%。然後將此值用於主測量迴圈。
- 傳回
包含測量的運行時間和重複計數的 Measurement 物件,可用於計算統計數據。(平均值、中位數等)
- 傳回類型
- collect_callgrind(number: int, *, repeats: None, collect_baseline: bool, retain_out_file: bool) CallgrindStats [原始碼][原始碼]¶
- collect_callgrind(number: int, *, repeats: int, collect_baseline: bool, retain_out_file: bool) Tuple[CallgrindStats, ...]
使用 Callgrind 收集指令計數。
與牆上時間不同,指令計數是確定性的(除非程式本身存在非確定性,以及來自 Python 直譯器的小量抖動。) 這使得它們非常適合詳細的效能分析。此方法在單獨的進程中運行 stmt,以便 Valgrind 可以檢測程式。由於檢測,效能會嚴重降低,但是,由於通常只需少量迭代即可獲得良好的測量結果,因此可以改善這種情況。
為了使用此方法 valgrind,必須安裝 callgrind_control 和 callgrind_annotate。
由於呼叫者(此程序)和 stmt 執行之間存在程序邊界,因此 globals 不能包含任意的記憶體內資料結構(與計時方法不同)。相反地,globals 被限制為內建模組、nn.Modules 和 TorchScripted 函式/模組,以減少序列化和後續反序列化帶來的意外。 GlobalsBridge 類別提供了關於此主題的更多詳細資訊。請特別注意 nn.Modules:它們依賴於 pickle,您可能需要在 setup 中新增一個 import 語句,才能正確傳輸它們。
預設情況下,將會收集並快取一個空語句的分析資料,以指示驅動 stmt 的 Python 迴圈中有多少指令。
- 傳回
一個 CallgrindStats 物件,提供指令計數以及一些用於分析和操作結果的基本功能。
- timeit(number=1000000)[原始碼][原始碼]¶
反映 timeit.Timer.timeit() 的語義。
執行主要語句 (stmt) number 次。 https://docs.python.org/3/library/timeit.html#timeit.Timer.timeit
- 傳回類型
- class torch.utils.benchmark.Measurement(number_per_run, raw_times, task_spec, metadata=None)[原始碼][原始碼]¶
Timer 量測的結果。
此類別儲存給定語句的一個或多個量測。它是可序列化的,並為下游消費者提供了幾個方便的方法(包括詳細的 __repr__)。
- class torch.utils.benchmark.CallgrindStats(task_spec, number_per_run, built_with_debug_symbols, baseline_inclusive_stats, baseline_exclusive_stats, stmt_inclusive_stats, stmt_exclusive_stats, stmt_callgrind_out)[原始碼][原始碼]¶
由 Timer 收集的 Callgrind 結果的頂層容器。
操作通常使用 FunctionCounts 類別完成,該類別透過呼叫 CallgrindStats.stats(…) 獲得。 也提供了一些方便的方法; 最重要的是 CallgrindStats.as_standardized()。
- as_standardized()[原始碼][原始碼]¶
從函式字串中剝離程式庫名稱和一些前綴。
當比較兩組不同的指令計數時,一個絆腳石可能是路徑前綴。 Callgrind 在報告函式時包含完整檔案路徑(應該如此)。 但是,這會在差異分析設定檔時導致問題。 如果像 Python 或 PyTorch 這樣的關鍵元件是在兩個設定檔中的不同位置建置的,這可能會導致類似的情況
23234231 /tmp/first_build_dir/thing.c:foo(...) 9823794 /tmp/first_build_dir/thing.c:bar(...) ... 53453 .../aten/src/Aten/...:function_that_actually_changed(...) ... -9823794 /tmp/second_build_dir/thing.c:bar(...) -23234231 /tmp/second_build_dir/thing.c:foo(...)
透過正規化字串並在差異分析時更好地取消等效呼叫站點,剝離前綴可以改善此問題。
- 傳回類型
- delta(other, inclusive=False)[原始碼][原始碼]¶
比較兩組計數。
收集指令計數的一個常見原因是確定特定變更對執行某個工作單元所需的指令數量的影響。 如果變更增加了指令數量,那麼下一個合理的提問是「為什麼」。 這通常涉及查看程式碼的哪個部分指令計數增加了。 這個函式會自動化這個過程,以便可以輕鬆地在包含和排除的基礎上比較計數。
- 傳回類型
- class torch.utils.benchmark.FunctionCounts(_data, inclusive, truncate_rows=True, _linewidth=None)[source][source]¶
用於操作 Callgrind 結果的容器。
- 它支援:
加法和減法,用於組合或比較結果。
類似元組的索引。
一個 denoise 函式,用於去除已知具有不確定性和相當多雜訊的 CPython 呼叫。
兩個高階方法(filter 和 transform)用於自訂操作。
- denoise()[source][source]¶
移除已知的雜訊指令。
CPython 直譯器中的幾個指令相當嘈雜。 這些指令涉及 Unicode 到字典的查詢,Python 使用它們來映射變數名稱。 FunctionCounts 通常是一個與內容無關的容器,但是對於獲得可靠的結果來說,這非常重要,因此需要一個例外。
- 傳回類型
- class torch.utils.benchmark.Compare(results)[source][source]¶
輔助類別,用於以格式化的表格顯示多個測量的結果。
表格格式基於
torch.utils.benchmark.Timer
中提供的資訊欄位(description、label、sub_label、num_threads 等)。可以使用
print()
直接列印表格,或將其轉換為 str。有關如何使用此類別的完整教學,請參閱:https://pytorch.dev.org.tw/tutorials/recipes/recipes/benchmark.html
- 參數
results (List[Measurement]) – 要顯示的 Measurment 清單。