• 教程 >
  • 使用 run_cpu 脚本优化 Intel® Xeon® 上的 CPU 性能
快捷方式

使用 run_cpu 脚本优化 Intel® Xeon® 上的 CPU 性能

在 Intel® Xeon® 可扩展处理器上执行时,有多种配置选项会影响 PyTorch 推理的性能。为了获得峰值性能,提供了 torch.backends.xeon.run_cpu 脚本,该脚本优化了线程和内存管理的配置。对于线程管理,该脚本配置线程亲和性和 Intel® OMP 库的预加载。对于内存管理,它配置 NUMA 绑定并预加载优化的内存分配库,如 TCMalloc 和 JeMalloc。此外,该脚本提供了可调参数,用于在单实例和多实例场景中分配计算资源,帮助用户尝试针对特定工作负载的最佳资源利用率协调。

你将学到什么

  • 如何利用 numactltaskset、Intel® OpenMP 运行时库和优化内存分配器(如 TCMallocJeMalloc)等工具来提高性能。

  • 如何配置 CPU 资源和内存管理,以最大限度地提高 Intel® Xeon® 处理器上的 PyTorch 推理性能。

优化的介绍

应用 NUMA 访问控制

在一个插槽内为用户提供越来越多的 CPU 内核是有益的,因为这提供了更大的计算资源。但是,这也导致了对内存访问的竞争,这会导致程序因内存繁忙而停滞。为了解决这个问题,引入了非一致性内存访问 (NUMA)。与一致性内存访问 (UMA) 不同,在 UMA 中所有内存对所有内核都可平等访问,NUMA 将内存组织成多个组。一定数量的内存直接连接到一个插槽的集成内存控制器,成为该插槽的本地内存。本地内存访问比远程内存访问快得多。

用户可以使用 Linux 上的 lscpu 命令获取 CPU 信息,以了解机器上存在多少个内核和插槽。此外,此命令提供 NUMA 信息,例如 CPU 内核的分布情况。以下是执行 lscpu 命令在一个配备 Intel® Xeon® CPU Max 9480 的机器上的示例。

$ lscpu
...
CPU(s):                  224
  On-line CPU(s) list:   0-223
Vendor ID:               GenuineIntel
  Model name:            Intel (R) Xeon (R) CPU Max 9480
    CPU family:          6
    Model:               143
    Thread(s) per core:  2
    Core(s) per socket:  56
    Socket(s):           2
...
NUMA:
  NUMA node(s):          2
  NUMA node0 CPU(s):     0-55,112-167
  NUMA node1 CPU(s):     56-111,168-223
...
  • 检测到两个插槽,每个插槽包含 56 个物理内核。启用超线程后,每个内核可以处理 2 个线程,从而导致每个插槽有 56 个逻辑内核。因此,机器总共有 224 个 CPU 内核在运行。

  • 通常,物理内核在逻辑内核之前被索引。在这种情况下,内核 0-55 是第一个 NUMA 节点的物理内核,内核 56-111 是第二个 NUMA 节点的物理内核。

  • 逻辑内核随后被索引:内核 112-167 对应于第一个 NUMA 节点的逻辑内核,内核 168-223 对应于第二个 NUMA 节点的逻辑内核。

通常,在运行具有计算密集型工作负载的 PyTorch 程序时,应避免使用逻辑内核以获得良好的性能。

Linux 提供了一个名为 numactl 的工具,允许用户控制进程或共享内存的 NUMA 策略。它以特定的 NUMA 调度或内存放置策略运行进程。如上所述,内核在一个插槽中共享高速缓存,因此避免跨插槽计算是一个好主意。从内存访问的角度来看,将内存访问限制在本地比访问远程内存快得多。 numactl 命令应该已经安装在最新的 Linux 发行版中。如果它不存在,您可以使用安装命令手动安装它,例如在 Ubuntu 上

$ apt-get install numactl

在 CentOS 上,您可以运行以下命令

$ yum install numactl

Linux 中的 taskset 命令是另一个强大的实用程序,允许您设置或检索正在运行的进程的 CPU 亲和性。 taskset 预先安装在大多数 Linux 发行版中,如果它不存在,您可以在 Ubuntu 上使用以下命令安装它

$ apt-get install util-linux

在 CentOS 上,您可以运行以下命令

$ yum install util-linux

使用 Intel® OpenMP 运行时库

OpenMP 是多线程的实现,它是一种并行化方法,其中主线程(连续执行的一系列指令)会派生出指定数量的子线程,系统会将任务分配给这些子线程。然后,这些线程并发运行,运行时环境将线程分配给不同的处理器。用户可以使用一些环境变量设置来控制 OpenMP 行为,以适合他们的工作负载,这些设置由 OMP 库读取和执行。默认情况下,PyTorch 使用 GNU OpenMP 库 (GNU libgomp) 进行并行计算。在 Intel® 平台上,Intel® OpenMP 运行时库 (libiomp) 提供 OpenMP API 规范支持。与 libgomp 相比,它通常会带来更多性能优势。

可以使用以下命令之一安装 Intel® OpenMP 运行时库

$ pip install intel-openmp

$ conda install mkl

选择一个优化的内存分配器

内存分配器从性能角度也起着重要作用。更有效的内存使用减少了不必要的内存分配或销毁的开销,从而导致更快的执行速度。从实际经验来看,对于深度学习工作负载, TCMallocJeMalloc 可以通过尽可能地重用内存来获得比默认 malloc 操作更好的性能。

您可以通过在 Ubuntu 上运行以下命令来安装 TCMalloc

$ apt-get install google-perftools

在 CentOS 上,您可以通过运行以下命令来安装它

$ yum install gperftools

在 conda 环境中,也可以通过运行以下命令来安装它

$ conda install conda-forge::gperftools

在 Ubuntu 上,可以使用以下命令安装 JeMalloc

$ apt-get install libjemalloc2

在 CentOS 上,可以通过运行以下命令来安装它

$ yum install jemalloc

在 conda 环境中,也可以通过运行以下命令来安装它

$ conda install conda-forge::jemalloc

快速入门示例命令

  1. 要在 1 个 CPU 内核上使用 1 个线程运行单实例推理(仅使用内核 #0)

$ python -m torch.backends.xeon.run_cpu --ninstances 1 --ncores-per-instance 1 <program.py> [program_args]
  1. 要在单个 CPU 节点(NUMA 插槽)上运行单实例推理

$ python -m torch.backends.xeon.run_cpu --node-id 0 <program.py> [program_args]
  1. 要运行多实例推理,在一个 112 核 CPU 上,每个实例使用 14 个内核,总共运行 8 个实例

$ python -m torch.backends.xeon.run_cpu --ninstances 8 --ncores-per-instance 14 <program.py> [program_args]
  1. 要在吞吐量模式下运行推理,在此模式下,每个 CPU 节点中的所有内核都会设置一个实例

$ python -m torch.backends.xeon.run_cpu --throughput-mode <program.py> [program_args]

注意

此处的“实例”不指云实例。此脚本作为单个进程执行,该进程调用多个“实例”,这些实例是由多个线程组成的。在此上下文中,“实例”是线程组的代名词。

使用 torch.backends.xeon.run_cpu

可以使用以下命令显示参数列表和使用指南

$ python -m torch.backends.xeon.run_cpu –h
usage: run_cpu.py [-h] [--multi-instance] [-m] [--no-python] [--enable-tcmalloc] [--enable-jemalloc] [--use-default-allocator] [--disable-iomp] [--ncores-per-instance] [--ninstances] [--skip-cross-node-cores] [--rank] [--latency-mode] [--throughput-mode] [--node-id] [--use-logical-core] [--disable-numactl] [--disable-taskset] [--core-list] [--log-path] [--log-file-prefix] <program> [program_args]

上面的命令具有以下位置参数

knob

help

program

要启动的程序/脚本的完整路径。

program_args

要启动的程序/脚本的输入参数。

选项说明

通用选项设置(旋钮)包括以下内容

knob

type

默认值

help

-h, --help

显示帮助消息并退出。

-m, --module

将每个进程更改为将启动脚本解释为 Python 模块,并以与“python -m”相同的行为执行。

--no-python

bool

False

避免在程序前面加上“python” - 直接执行它。当脚本不是 Python 脚本时很有用。

--log-path

str

''

指定日志文件目录。默认路径为 '',这意味着禁用将日志记录到文件。

--log-file-prefix

str

“run”

日志文件名的前缀。

用于应用或禁用优化的旋钮是

knob

type

默认值

help

--enable-tcmalloc

bool

False

启用 TCMalloc 内存分配器。

--enable-jemalloc

bool

False

启用 JeMalloc 内存分配器。

--use-default-allocator

bool

False

使用默认内存分配器。既不会使用 TCMalloc 也不会使用 JeMalloc

--disable-iomp

bool

False

默认情况下,如果安装了 Intel® OpenMP 库,则会使用它。设置此标志将禁用使用 Intel® OpenMP。

注意

内存分配器会影响性能。如果用户没有指定所需的内存分配器,则 run_cpu 脚本将按 TCMalloc > JeMalloc > PyTorch 默认内存分配器的顺序搜索是否安装了任何分配器,并使用第一个匹配的分配器。

用于控制实例数量和计算资源分配的旋钮是

knob

type

默认值

help

--ninstances

int

0

实例数量。

--ncores-per-instance

int

0

每个实例使用的内核数量。

--node-id

int

-1

要用于多实例的节点 ID,默认情况下将使用所有节点。

--core-list

str

''

指定内核列表,例如 'core_id, core_id, ....' 或内核范围,例如 'core_id-core_id'。默认情况下将使用所有内核。

--use-logical-core

bool

False

默认情况下仅使用物理内核。指定此标志将启用逻辑内核的使用。

--skip-cross-node-cores

bool

False

防止工作负载在跨 NUMA 节点的内核上执行。

--rank

int

-1

指定要为其分配 ncores_per_instance 的实例索引;否则,ncores_per_instance 将按顺序分配给实例。

--multi-instance

bool

False

一个快速设置,用于在多插槽 CPU 服务器上调用工作负载的多个实例。

--latency-mode

bool

False

一个快速设置,用于以延迟模式调用基准测试,在此模式下,将使用所有物理内核,每个实例使用 4 个内核。

--throughput-mode

bool

False

一个快速设置,用于以吞吐量模式调用基准测试,在此模式下,将使用所有物理内核,每个实例使用 1 个 NUMA 节点。

--disable-numactl

bool

False

默认情况下, numactl 命令用于控制 NUMA 访问。设置此标志将禁用它。

--disable-taskset

bool

False

禁用 taskset 命令的使用。

注意

此脚本将设置的环境变量包括以下内容

环境变量

LD_PRELOAD

根据您设置的旋钮,<lib>/libiomp5.so、<lib>/libjemalloc.so、<lib>/libtcmalloc.so 可能被附加到 LD_PRELOAD。

KMP_AFFINITY

如果 libiomp5.so 已预加载,则 KMP_AFFINITY 可以设置为 "granularity=fine,compact,1,0"

KMP_BLOCKTIME

如果 libiomp5.so 已预加载,则 KMP_BLOCKTIME 设置为“1”。

OMP_NUM_THREADS

ncores_per_instance 的值

MALLOC_CONF

如果 libjemalloc.so 已预加载,则 MALLOC_CONF 将设置为 "oversize_threshold:1,background_thread:true,metadata_thp:auto"

请注意,该脚本会尊重预先设置的环境变量。例如,如果您在运行脚本之前设置了上面提到的环境变量,则脚本不会覆盖这些变量的值。

结论

在本教程中,我们探讨了各种旨在优化 Intel® Xeon® 可扩展处理器上 PyTorch 推理性能的高级配置和工具。通过利用 torch.backends.xeon.run_cpu 脚本,我们演示了如何微调线程和内存管理以实现峰值性能。我们涵盖了重要的概念,例如 NUMA 访问控制、优化的内存分配器(例如 TCMallocJeMalloc)以及 Intel® OpenMP 的使用,以实现高效的多线程。

此外,我们还提供了实用的命令行示例,指导您设置单实例和多实例场景,确保针对特定工作负载进行优化资源利用。通过了解和应用这些技术,用户可以显著提高其 PyTorch 应用程序在 Intel® Xeon® 平台上的效率和速度。

另请参阅

文档

访问 PyTorch 的全面开发者文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并获得问题的解答

查看资源