評價此頁

Benchmark Utils - torch.utils.benchmark#

建立日期:2020 年 11 月 02 日 | 最後更新日期:2025 年 06 月 12 日

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]#

用於測量 PyTorch 語句執行時間的輔助類。

有關使用此類功能的完整教程,請參閱:https://pytorch.com.tw/tutorials/recipes/recipes/benchmark.html

PyTorch Timer 基於 timeit.Timer(實際上內部也使用 timeit.Timer),但有幾個關鍵區別

  1. 執行時感知

    Timer 將執行預熱(這對於 PyTorch 的某些元素是惰性初始化的,這一點很重要),設定執行緒池大小以便比較是“蘋果對蘋果”,並在必要時同步非同步加速器函式。

  2. 專注於副本

    在測量程式碼,特別是複雜核心/模型時,執行-執行變化是一個重要的混淆因素。預計所有測量都應包含副本以量化噪聲並允許計算中位數,中位數比平均值更穩健。為此,此類偏離了 timeit API,在概念上合併了 timeit.Timer.repeattimeit.Timer.autorange。(確切的演算法在方法文件字串中討論。)在不希望使用自適應策略的情況下,複製了 timeit 方法。

  3. 可選元資料

    在定義 Timer 時,可以選擇指定 labelsub_labeldescriptionenv。(稍後定義)這些欄位包含在結果物件的表示中,並且 Compare 類用於按組顯示結果以進行比較。

  4. 指令計數

    除了掛鐘時間之外,Timer 還可以執行 Callgrind 下的語句並報告執行的指令數。

直接對應於 timeit.Timer 建構函式引數

stmtsetuptimerglobals

PyTorch Timer 特定建構函式引數

labelsub_labeldescriptionenvnum_threads

引數
  • stmt (str) – 要在迴圈中執行和計時的一段程式碼。

  • setup (str) – 可選的設定程式碼。用於定義 stmt 中使用的變數。

  • global_setup (str) – (僅限 C++) 位於檔案頂層的程式碼,用於例如 #include 語句。

  • timer (Callable[[], float]) – 返回當前時間的呼叫物件。如果 PyTorch 構建時沒有加速器,或者不存在加速器,則此引數預設為 timeit.default_timer;否則,它將在測量時間之前同步加速器。

  • globals (Optional[dict[str, Any]]) – 在執行 stmt 時定義全域性變數的字典。這是為 stmt 所需的變數提供的另一種方法。

  • label (Optional[str]) – 總結 stmt 的字串。例如,如果 stmt 是 “torch.nn.functional.relu(torch.add(x, 1, out=out))”,則可以將 label 設定為 “ReLU(x + 1)” 以提高可讀性。

  • sub_label (Optional[str]) –

    提供補充資訊,以區分具有相同 stmt 或 label 的測量。例如,在上面的示例中,sub_label 可能是 “float” 或 “int”,這樣就可以輕鬆區分:“ReLU(x + 1): (float)”

    “ReLU(x + 1): (int)” 在列印 Measurements 或使用 Compare 進行彙總時。

  • description (Optional[str]) –

    用於區分具有相同 label 和 sub_label 的測量的字串。 description 的主要用途是向 Compare 表明資料的列。例如,您可以根據輸入大小設定它,以建立如下表所示的表格

                            | n=1 | n=4 | ...
                            ------------- ...
    ReLU(x + 1): (float)    | ... | ... | ...
    ReLU(x + 1): (int)      | ... | ... | ...
    

    使用 Compare。在列印 Measurement 時也會包含它。

  • env (Optional[str]) – 此標籤表示在不同環境中執行的、否則相同的任務,因此不等價,例如在對核心更改進行 A/B 測試時。Compare 將具有不同 env 指定的 Measurement 視為不同的,以便合併重複執行。

  • num_threads (int) – 執行 stmt 時 PyTorch 執行緒池的大小。單執行緒效能很重要,因為它既是關鍵的推理工作負載,也是內在演算法效率的良好指標,因此預設設定為一。這與預設的 PyTorch 執行緒池大小(嘗試利用所有核心)相反。

adaptive_autorange(threshold=0.1, *, min_run_time=0.01, max_run_time=10.0, callback=None)[source]#

類似於 blocked_autorange,但還檢查測量值的變異性,並重復直到 iqr/median 小於 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
引數
  • threshold (float) – 停止的 iqr/median 閾值。

  • min_run_time (float) – 檢查 threshold 之前所需的總執行時間。

  • max_run_time (float) – 所有測量值的總執行時間,無論 threshold 如何。

返回

一個 Measurement 物件,包含測量到的執行時間和重複次數,可用於計算統計量(平均值、中位數等)。

返回型別

Measurement

blocked_autorange(callback=None, min_run_time=0.2)[source]#

測量許多副本,同時將計時器開銷降至最低。

總的來說,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 變數。塊大小的選擇對於測量質量很重要,必須在兩個相互競爭的目標之間進行權衡

  1. 較小的塊大小會導致更多的副本,通常具有更好的統計資料。

  2. 較大的塊大小能更好地分攤 timer 呼叫的成本,並導致更少的偏差測量。這一點很重要,因為加速器同步時間並非微不足道(數量級為微秒級別),否則會使測量產生偏差。

blocked_autorange 透過執行預熱期來設定 block_size,增加 block_size 直到計時器開銷小於總計算時間的 0.1%。然後將此值用於主測量迴圈。

返回

一個 Measurement 物件,包含測量到的執行時間和重複次數,可用於計算統計量(平均值、中位數等)。

返回型別

Measurement

collect_callgrind(number: int, *, repeats: None, collect_baseline: bool, retain_out_file: bool) CallgrindStats[source]#
collect_callgrind(number: int, *, repeats: int, collect_baseline: bool, retain_out_file: bool) tuple[torch.utils.benchmark.utils.valgrind_wrapper.timer_interface.CallgrindStats, ...]

使用 Callgrind 收集指令計數。

與掛鐘時間不同,指令計數是確定性的(除了程式本身的非確定性和 Python 直譯器產生的小量抖動)。這使得它們非常適合詳細的效能分析。此方法在一個單獨的程序中執行 stmt,以便 Valgrind 可以對程式進行儀表化。由於儀表化,效能會嚴重下降,但由於少量迭代通常足以獲得良好的測量結果,這得到了緩解。

要使用此方法,必須安裝 valgrindcallgrind_controlcallgrind_annotate

由於呼叫者(此程序)和 stmt 執行之間存在程序邊界,因此 globals 不能包含任意記憶體中資料結構。(與計時方法不同)相反,globals 僅限於內建函式、nn.Modules 和 TorchScripted 函式/模組,以減少序列化和後續反序列化的意外因素。GlobalsBridge 類對此主題提供了更多詳細資訊。請特別注意 nn.Modules:它們依賴於 pickle,您可能需要將 import 新增到 setup 中才能使它們正確傳輸。

預設情況下,將收集並快取一個空語句的配置檔案,以指示驅動 stmt 的 Python 迴圈產生了多少指令。

返回

一個 CallgrindStats 物件,提供指令計數以及一些基本分析和操作結果的工具。

timeit(number=1000000)[source]#

映象 timeit.Timer.timeit() 的語義。

執行主語句(stmtnumber 次。https://docs.python.club.tw/3/library/timeit.html#timeit.Timer.timeit

返回型別

Measurement

class torch.utils.benchmark.Measurement(number_per_run, raw_times, task_spec, metadata=None)[source]#

Timer 測量結果。

此類儲存給定語句的一個或多個測量值。它是可序列化的,併為下游消費者提供了幾個方便的方法(包括詳細的 __repr__)。

static merge(measurements)[source]#

合併副本的便捷方法。

Merge 會將時間外推到 number_per_run=1,並且不會轉移任何元資料(因為它可能因副本而異)。

返回型別

list[‘Measurement’]

property significant_figures: int#

近似有效數字估計。

此屬性旨在提供一種方便的方法來估算測量的精度。它僅使用四分位間距來估算統計量,以儘量減少尾部偏斜,並使用靜態 z 值 1.645,因為它不打算用於 n 的小值,因此 z 可以近似 t

有效數字估計與 trim_sigfig 方法結合使用,以提供更易於人類理解的資料摘要。__repr__ 不使用此方法;它只顯示原始值。有效數字估計旨在用於 Compare

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)[source]#

Timer 收集的 Callgrind 結果的頂級容器。

通常透過呼叫 CallgrindStats.stats(…) 來進行操作,這會得到 FunctionCounts 類。還提供了幾個便捷方法;其中最重要的是 CallgrindStats.as_standardized()

as_standardized()[source]#

剝離函式字串中的庫名稱和一些字首。

在比較兩組不同的指令計數時,一個障礙可能是路徑字首。Callgrind 在報告函式時包含完整的檔案路徑(它應該如此)。然而,這在 diff 配置檔案時可能會導致問題。如果兩份配置檔案中的關鍵元件(如 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(...)

剝離字首可以透過規範化字串來緩解此問題,並在 diff 時更好地消除等效呼叫站點。

返回型別

CallgrindStats

counts(*, denoise=False)[source]#

返回執行的總指令數。

denoise 引數的解釋請參見 FunctionCounts.denoise()

返回型別

int

delta(other, inclusive=False)[source]#

diff 兩個計數集。

收集指令計數的一個常見原因是確定特定更改對執行某些工作單元所需的指令數的影響。如果更改增加了該數字,下一個合乎邏輯的問題是“為什麼”。這通常涉及檢視程式碼的哪個部分指令數增加了。此函式可以自動化此過程,以便可以輕鬆地在包含和排除的基礎上 diff 計數。

返回型別

FunctionCounts

stats(inclusive=False)[source]#

返回詳細的函式計數。

在概念上,返回的 FunctionCounts 可以被視為 (count, path_and_function_name) 元組的元組。

inclusive 匹配 callgrind 的語義。如果為 True,則計數包括子項執行的指令。inclusive=True 有助於識別程式碼中的熱點;inclusive=False 有助於減少 diff 兩次執行計數時的噪聲。(有關更多詳細資訊,請參閱 CallgrindStats.delta(…))

返回型別

FunctionCounts

class torch.utils.benchmark.FunctionCounts(_data, inclusive, truncate_rows=True, _linewidth=None)[source]#

用於操作 Callgrind 結果的容器。

它支援
  1. 加法和減法以組合或 diff 結果。

  2. 類似元組的索引。

  3. denoise 函式,它會剝離已知的非確定性且非常嘈雜的 CPython 呼叫。

  4. 兩個高階方法(filtertransform)用於自定義操作。

denoise()[source]#

移除已知嘈雜的指令。

CPython 直譯器中的一些指令非常嘈雜。這些指令涉及 Unicode 到字典的查詢,Python 使用它們來對映變數名。FunctionCounts 通常是內容無關的容器,但是,為了獲得可靠的結果,這一點足夠重要,值得破例。

返回型別

FunctionCounts

filter(filter_fn)[source]#

僅保留應用 filter_fn 到函式名後返回 True 的元素。

返回型別

FunctionCounts

transform(map_fn)[source]#

map_fn 應用於所有函式名。

這可以用於規範化函式名(例如,剝離檔案路徑的無關部分),透過將多個函式對映到同一名稱來合併條目(在這種情況下,計數會相加),等等。

返回型別

FunctionCounts

class torch.utils.benchmark.Compare(results)[source]#

用於在格式化表中顯示多個測量結果的幫助類。

表格式基於 torch.utils.benchmark.Timer 中提供的資訊欄位(descriptionlabelsub_labelnum_threads 等)。

表可以直接使用 print() 列印,或轉換為 str

有關使用此類功能的完整教程,請參閱:https://pytorch.com.tw/tutorials/recipes/recipes/benchmark.html

引數

results (list[torch.utils.benchmark.utils.common.Measurement]) – 要顯示的 Measurement 列表。

colorize(rowwise=False)[source]#

為格式化表著色。

預設按列著色。

extend_results(results)[source]#

將結果追加到已儲存的結果中。

所有新增的結果都必須是 Measurement 的例項。

highlight_warnings()[source]#

在構建格式化表時啟用警告高亮。

print()[source]#

列印格式化表

trim_significant_figures()[source]#

在構建格式化表時啟用有效數字修剪。