基准工具 - 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.org/tutorials/recipes/recipes/benchmark.html
PyTorch 计时器基于 timeit.Timer(实际上内部使用 timeit.Timer),但有几个关键区别:
- 运行时感知:
定时器将执行预热(对于 PyTorch 中的一些元素是懒加载的,因此非常重要),设置线程池大小以确保比较是公平的,并在必要时同步异步 CUDA 函数。
- 专注于重复:
在测量代码时,尤其是复杂内核/模型时,运行间的变化是一个重要的混杂因素。预期所有测量都应该包括重复以量化噪声并允许计算中位数,这比平均值更稳健。为此,此类从概念上合并了 timeit API 的 timeit.Timer.repeat 和 timeit.Timer.autorange。具体的算法在方法文档字符串中讨论。当不需要自适应策略时,timeit 方法会进行重复。
- 可选元数据:
定义计时器时,可以可选地指定标签、子标签、描述和环境。(稍后定义)这些字段包含在结果对象的表示中,并由 Compare 类用于对结果进行分组和比较显示。
- 指令计数
除了墙时,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]]) – 一个字典,用于定义在执行 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)”
“ReLU(x + 1): (整数)”当使用打印测量值或使用比较进行总结时。
描述(可选[str]) –
用于区分具有相同标签和子标签的测量值的字符串。描述的主要用途是向比较信号数据列。例如,可以根据输入大小设置它,创建如下形式的表格:| n=1 | n=4 | ... ------------- ... ReLU(x + 1): (float) | ... | ... | ... ReLU(x + 1): (int) | ... | ... | ...
使用比较。打印测量值时也会包含它。
env(可选[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)[source][source]¶
与 blocked_autorange 类似,但还会检查测量值的可变性,并重复执行,直到 iqr/median 小于阈值或达到 max_run_time。
在高层次上,自适应自动范围执行以下伪代码:
`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
- 参数:
阈值(浮点数)- iqr/中位数阈值的值,用于停止
最小运行时间(浮点数)- 在检查阈值之前所需的总运行时间
最大运行时间(浮点数)- 不论阈值如何,所有测量的总运行时间
- 返回:
包含测量运行时间和重复次数的测量对象,可用于计算统计数据(平均值、中位数等)。
- 返回类型:
- blocked_autorange(callback=None, min_run_time=0.2)[source][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。块大小的选择对测量质量很重要,必须平衡两个相互竞争的目标:
小块大小会产生更多的重复样本,并且通常能得到更好的统计数据。
大块大小能更好地分摊计时器调用的成本,并导致测量结果更少偏差。这很重要,因为 CUDA 同步时间是非平凡的(数量级为单到低双位数微秒),否则会偏置测量结果。
blocked_autorange 通过运行预热周期来设置 block_size,直到计时开销小于整体计算的 0.1%。然后使用这个值进行主测量循环。
- 返回:
一个包含测量运行时间和重复次数的测量对象,可以用来计算统计数据(平均值、中位数等)。
- 返回类型:
- collect_callgrind(number:int, *, repeats=None, collect_baseline=bool, retain_out_file=bool) → valgrind_timer_interface.CallgrindStats[source][source]
- collect_callgrind(number: int, *, repeats: int, collect_baseline: bool, retain_out_file: bool) tuple[valgrind_timer_interface.CallgrindStats, ...]
使用 Callgrind 收集指令计数。
与墙时不同,指令计数是确定的(除程序本身的非确定性和小量抖动外)。这使得它们非常适合详细性能分析。此方法在单独进程中运行 stmt,以便 Valgrind 可以对该程序进行仪器化。由于仪器化,性能会严重下降,但通常只需要少量迭代就可以获得良好的测量结果。
为了使用此方法,必须安装 valgrind、callgrind_control 和 callgrind_annotate。
因为调用者(此进程)与 stmt 执行之间存在进程边界,全局变量不能包含任意的内存数据结构。(与计时方法不同)相反,全局变量仅限于内置的、nn.Modules 的以及 TorchScripted 函数/模块,以减少序列化和后续反序列化带来的意外因素。《GlobalsBridge》类提供了更多关于此主题的详细信息。特别小心对待 nn.Modules:它们依赖于 pickle,你可能需要为它们添加导入以正确传输,设置时可能需要。
默认情况下,将收集并缓存空语句的配置文件,以指示 stmt 由 Python 循环驱动的指令数量。
- 返回:
提供指令计数和一些基本分析及操作结果功能的一个 CallgrindStats 对象。
- timeit(number=1000000)[source][source]
反映 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)[source][source]¶
定时器测量的结果。
此类存储给定语句的一个或多个测量值。它是可序列化的,并为下游消费者提供几个便利方法(包括详细的 __repr__)。
- 静态合并测量值(measurements)[源][源]
合并复制的便利方法。
合并将外推时间到 number_per_run=1,并且不会传输任何元数据。(因为它们可能在重复之间有所不同)
- 返回类型:
list[torch.utils.benchmark.utils.common.Measurement]
- 属性 significant_figuresint ¶
近似有效数字估计。
该属性旨在提供一种方便的方式来估计测量的精度。它仅使用四分位数区域来估计统计量,以尝试减轻尾部偏差,并使用静态的 z 值 1.645,因为它不期望用于 n 值较小的情况,所以 z 可以近似 t。
与 trim_sigfig 方法结合使用的显著数字估计,以提供更易于人类解释的数据摘要。__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)[source][source]¶
Timer 收集的 Callgrind 结果的顶层容器。
通常使用 FunctionCounts 类进行操作,该类通过调用 CallgrindStats.stats(…)获取。还提供了几个便捷方法;其中最重要的是 CallgrindStats.as_standardized()。
- as_standardized()[source][source]
从函数字符串中去除库名称和一些前缀。
当比较两组不同的指令计数时,路径前缀可能会成为一个难题。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(...)
去除前缀可以通过规范化字符串并在比较时更好地消除等效调用站点来改善这个问题。
- 返回类型:
- counts(*, denoise=False)[source][source]¶
返回执行的总指令数。
请参阅 FunctionCounts.denoise() 以了解 denoise 参数的解释。
- 返回类型:
- delta(other, inclusive=False)[来源][来源] ¶
比较两个计数集。
收集指令计数的一个常见原因是为了确定特定更改对执行某些工作单元所需指令数量的影响。如果这个数字增加了,下一个合乎逻辑的问题就是“为什么”。这通常涉及到查看代码中指令计数增加的部分。此函数自动化了这个过程,使得可以轻松地以包含和排除为基础比较计数。
- 返回类型:
- class torch.utils.benchmark.FunctionCounts(_data, inclusive, truncate_rows=True, _linewidth=None)[source][source]¶
处理 Callgrind 结果的容器。
- 它支持:
加法和减法以组合或比较结果。
类似元组的索引。
一种去除已知非确定性和噪声的 CPython 调用的去噪函数。
两种高级方法(过滤和转换)用于自定义操作。
- denoise()[source][source]
移除已知的噪声指令。
CPython 解释器中的许多指令相当嘈杂。这些指令涉及 Unicode 到字典的查找,这是 Python 用来映射变量名的方式。FunctionCounts 通常是一个内容无关的容器,然而,这对于获得可靠的结果来说非常重要,因此需要例外处理。
- 返回类型:
- class torch.utils.benchmark.Compare(results)[source][source]¶
用于显示多个测量结果的格式化表格的辅助类。
表格格式基于在
torch.utils.benchmark.Timer
中提供的字段信息(描述、标签、子标签、线程数等)。表格可以直接使用
print()
打印,或者转换为 str 类型。想要了解如何使用这个类的完整教程,请参阅:https://pytorch.org/tutorials/recipes/recipes/benchmark.html
- 参数:
结果(列表[torch.utils.benchmark.utils.common.Measurement])- 要显示的测量列表。