• Docs >
  • Benchmark Utils - torch.utils.benchmark
Shortcuts

基准测试工具 - 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)[源代码]

用于测量 PyTorch 语句执行时间的辅助类。

有关如何使用此类的完整教程,请参阅: https://pytorch.org/tutorials/recipes/recipes/benchmark.html

PyTorch Timer 基于 timeit.Timer(实际上在内部使用了 timeit.Timer),但有几个关键区别:

  1. Runtime aware:

    计时器将执行预热(这很重要,因为PyTorch的某些元素是延迟初始化的),设置线程池大小以便进行公平比较,并在必要时同步异步CUDA函数。

  2. Focus on replicates:

    在测量代码时,特别是复杂的内核/模型,运行间的变化是一个重要的混杂因素。预期所有测量都应包括重复以量化噪声并允许计算中位数,这比平均值更为稳健。为此,此类在概念上将timeit.Timer.repeattimeit.Timer.autorange合并,从而偏离了timeit API。(确切的算法在方法文档字符串中讨论。)timeit方法被复制用于不需要自适应策略的情况。

  3. Optional metadata:

    在定义一个计时器时,可以选择性地指定标签子标签描述环境。(稍后定义)这些字段包含在结果对象的表示中,并通过比较类用于分组和显示比较结果。

  4. Instruction counts

    除了墙钟时间,Timer 还可以在 Callgrind 下运行语句并报告执行的指令数。

timeit.Timer构造函数参数直接类似:

stmt, setup, timer, globals

PyTorch 计时器特定的构造函数参数:

标签, 子标签, 描述, 环境, 线程数

Parameters
  • stmt (str) – 要在循环中运行并计时的代码片段。

  • 设置 (str) – 可选的设置代码。用于定义在stmt中使用的变量

  • global_setup (str) – (仅限C++) 放置在文件顶层的代码,用于诸如#include语句。

  • 计时器 (可调用[[], 浮点数]) – 返回当前时间的可调用对象。如果 PyTorch 是构建时没有 CUDA 或者没有 GPU 存在,这默认是 timeit.default_timer;否则它将在测量时间之前同步 CUDA。

  • globals (可选[字典[字符串, 任意]]) – 一个字典,定义了当stmt被执行时的全局变量。这是提供stmt所需变量的另一种方法。

  • 标签 (可选[字符串]) – 用于总结stmt的字符串。例如,如果stmt是 “torch.nn.functional.relu(torch.add(x, 1, out=out))” 可以将其标签设置为“ReLU(x + 1)”以提高可读性。

  • 子标签 (可选[字符串]) –

    提供补充信息以消除具有相同语句或标签的测量的歧义。例如,在我们的示例中,子标签可能是“浮点数”或“整数”,以便于区分:

    “ReLU(x + 1): (浮点数)”

    ”ReLU(x + 1): (整数)”

    当打印测量结果或使用比较进行汇总时。

  • 描述 (可选[字符串]) –

    用于区分具有相同标签和子标签的测量的字符串。描述的主要用途是向比较信号数据列。例如,可以根据输入大小设置它,以创建如下形式的表格:

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

    使用比较。在打印测量时也会包含它。

  • env (可选[字符串]) – 此标签表示在不同环境中运行的相同任务,因此它们并不等同,例如在进行内核更改的A/B测试时。比较将在合并重复运行时将具有不同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/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
Parameters
  • 阈值 (浮点数) – 用于停止的iqr/中位数阈值

  • min_run_time (float) – 在检查 threshold 之前所需的总运行时间

  • max_run_time (float) – 总运行时间,无论 threshold 如何

Returns

一个包含测量运行时间和重复次数的测量对象,可用于计算统计数据(如均值、中位数等)。

Return type

测量

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。块大小的选择对测量质量至关重要,并且必须在两个相互竞争的目标之间取得平衡:

  1. 较小的块大小会导致更多的副本,并且通常会得到更好的统计结果。

  2. 较大的块大小更好地分摊了计时器调用的成本,并导致测量结果的偏差较小。这一点很重要,因为CUDA同步时间并非微不足道(通常为单到低双位数的微秒级),否则会偏差测量结果。

blocked_autorange 通过运行一个预热期来设置 block_size,增加块大小直到计时器开销小于总体计算的 0.1%。然后使用此值进行主测量循环。

Returns

一个包含测量运行时间和重复次数的测量对象,可用于计算统计数据(如均值、中位数等)。

Return type

测量

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可以对程序进行检测。由于检测,性能会严重下降,但通过少量迭代通常足以获得良好测量的这一事实得到了缓解。

为了使用此方法,必须安装 valgrindcallgrind_controlcallgrind_annotate

因为调用者(此进程)和stmt执行之间存在进程边界,globals不能包含任意的内存数据结构。(与计时方法不同)相反,全局变量被限制为内置函数、nn.Modules以及TorchScripted函数/模块,以减少从序列化和后续反序列化带来的意外因素。GlobalsBridge类提供了关于此主题的更多详细信息。特别要注意nn.Modules:它们依赖于pickle,您可能需要为它们添加一个导入到setup中,以确保它们能够正确传输。

默认情况下,将收集并缓存一个空语句的配置文件,以指示有多少指令来自驱动stmt的Python循环。

Returns

一个 CallgrindStats 对象,提供指令计数以及一些用于分析和操作结果的基本功能。

timeit(number=1000000)[源代码]

镜像了 timeit.Timer.timeit() 的语义。

执行主语句 (stmt) number 次。 https://docs.python.org/3/library/timeit.html#timeit.Timer.timeit

Return type

测量

class torch.utils.benchmark.Measurement(number_per_run, raw_times, task_spec, metadata=None)[源代码]

计时器测量的结果。

此类存储给定语句的一个或多个测量结果。它是可序列化的,并为下游消费者提供了几个便利方法(包括详细的 __repr__)。

static merge(measurements)[源代码]

合并重复样本的便捷方法。

合并将外推时间至number_per_run=1,并且不会传输任何元数据。(因为它们可能在重复之间有所不同)

Return type

列表[测量]

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)[源代码]

用于存储由计时器收集的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(...)

去除前缀可以通过规范化字符串并导致在差异比较时更好地消除等效调用站点来改善这个问题。

Return type

CallgrindStats

counts(*, denoise=False)[源码]

返回执行的指令总数。

参见 FunctionCounts.denoise() 以了解 denoise 参数的解释。

Return type

int

delta(other, inclusive=False)[源代码]

比较两组计数。

收集指令计数的一个常见原因是确定特定更改对执行某些工作单元所需的指令数量的影响。如果更改增加了该数量,下一个合乎逻辑的问题是“为什么”。这通常涉及查看代码的哪一部分在指令计数上有所增加。此函数自动化了该过程,以便可以轻松地在包含和排除的基础上进行计数差异比较。

Return type

函数计数

stats(inclusive=False)[源代码]

返回详细的函数计数。

从概念上讲,返回的 FunctionCounts 可以被视为一个元组,其中包含 (count, path_and_function_name) 元组。

inclusive 匹配 callgrind 的语义。如果为 True,计数包括子进程执行的指令。inclusive=True 有助于识别代码中的热点;inclusive=False 在比较两次不同运行的计数时有助于减少噪音。(有关更多详细信息,请参阅 CallgrindStats.delta(…))

Return type

函数计数

class torch.utils.benchmark.FunctionCounts(_data, inclusive, truncate_rows=True, _linewidth=None)[源代码]

用于操作Callgrind结果的容器。

It supports:
  1. 加法和减法来合并或区分结果。

  2. 类似元组的索引。

  3. 一个去噪函数,它会去除已知为非确定性和噪声较大的CPython调用。

  4. 两个高阶方法(filtertransform)用于自定义操作。

denoise()[源码]

移除已知的噪声指令。

CPython 解释器中的几条指令相当嘈杂。这些指令涉及 Python 用于映射变量名的 unicode 到字典查找。FunctionCounts 通常是一个内容无关的容器,然而这对于获得可靠结果非常重要,值得作为一个例外处理。

Return type

函数计数

filter(filter_fn)[源码]

仅保留应用 filter_fn 到函数名称返回 True 的元素。

Return type

函数计数

transform(map_fn)[源代码]

map_fn 应用于所有函数名称。

这可以用于规范化函数名称(例如,去除文件路径中不相关的部分),通过将多个函数映射到同一个名称来合并条目(在这种情况下,计数会相加),等等。

Return type

函数计数