通过run_cpu脚本优化Intel® Xeon®上的CPU性能¶
Created On: Jun 25, 2024 | Last Updated: Aug 03, 2024 | Last Verified: Nov 05, 2024
有多种配置选项会影响在Intel® Xeon®可扩展处理器上执行PyTorch推理时的性能。为获得最佳性能,提供了名为``torch.backends.xeon.run_cpu``的脚本,用于优化线程和内存管理的配置。对于线程管理,该脚本配置线程亲和性并预加载Intel® OMP库;对于内存管理,它配置NUMA绑定并预加载优化的内存分配库,比如TCMalloc和JeMalloc。此外,脚本为计算资源分配提供了可调参变量,适用于单实例和多实例场景,帮助用户尝试针对特定工作负载的最佳资源利用协调。
您将学习到的内容¶
如何利用诸如``numactl``、
taskset
、Intel® OpenMP运行时库以及优化的内存分配器(如``TCMalloc``和``JeMalloc``)等工具来提升性能。如何配置CPU资源和内存管理,以最大化Intel® Xeon®处理器上的PyTorch推理性能。
优化介绍¶
应用NUMA访问控制¶
在单个插槽内可用的CPU核心数量增加对用户有利,因为提供了更多计算资源。但这也会导致对内存访问的竞争,从而因内存繁忙导致程序停滞。为解决这个问题,引入了非一致性内存访问(NUMA)。与统一内存访问(UMA)不同,NUMA将内存组织为多个组。某些内存直接连接到某个插槽的集成内存控制器中,成为该插槽的局部内存。局部内存访问比远程内存访问快得多。
用户可以在Linux上使用``lscpu``命令获取CPU信息,以了解机器中有多少核心和插槽。此外,此命令还提供了NUMA信息,例如CPU核心的分布。以下是使用Intel® Xeon® CPU Max 9480机器执行``lscpu``的示例:
$ 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调度或内存放置策略运行进程。如上所述,核心在一个插槽内共享高速缓存,因此避免跨插槽计算是一个好主意。从内存访问的角度来看,限制在本地内存访问比访问远程内存快得多。最新的Linux分发版中应该已经安装``numactl``命令。如果缺失,您可以通过安装命令手动安装,例如在Ubuntu上:
$ apt-get install numactl
在CentOS上,您可以运行以下命令:
$ yum install numactl
Linux中的``taskset``命令是另一个强大的实用程序,它允许您设置或检索正在运行的进程的CPU亲和性。大多数Linux分发版中预安装了``taskset``,如果没有,在Ubuntu上您可以通过以下命令安装:
$ apt-get install util-linux
在CentOS上,您可以运行以下命令:
$ yum install util-linux
使用Intel® OpenMP运行时库¶
OpenMP是一种多线程实现,通过它一个主线程(一系列连续执行的指令)派生出指定数量的子线程,系统将任务分配给它们并行执行。子线程同时运行,运行时环境将线程分配给不同的处理器。用户可以通过一些环境变量设置控制OpenMP行为来适合其工作负载,这些设置由OMP库读取并执行。默认情况下,PyTorch使用GNU OpenMP库(GNU libgomp)进行并行计算。在Intel®平台上,Intel® OpenMP Runtime Library(libiomp)提供OpenMP API规范支持。与libgomp相比,它通常带来更多性能提升。
您可以通过以下命令安装Intel® OpenMP运行时库:
$ pip install intel-openmp
或
$ conda install mkl
选择优化的内存分配器¶
内存分配器从性能的角度来看也起着重要作用。更高效的内存使用可减少不必要的内存分配或销毁的开销,从而带来更快的执行。根据实践经验,在深度学习工作负载中,``TCMalloc``或``JeMalloc``通过最大限度地复用内存,可以比默认的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核心(仅使用核心#0)上运行单实例推理:
$ python -m torch.backends.xeon.run_cpu --ninstances 1 --ncores-per-instance 1 <program.py> [program_args]
在单个CPU节点(NUMA插槽)上运行单实例推理:
$ python -m torch.backends.xeon.run_cpu --node-id 0 <program.py> [program_args]
运行多实例推理,112核CPU上每个实例使用14个核心,共8个实例:
$ python -m torch.backends.xeon.run_cpu --ninstances 8 --ncores-per-instance 14 <program.py> [program_args]
以吞吐量模式运行推理,每个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 |
帮助 |
---|---|
|
要启动的程序/脚本的完整路径。 |
|
要启动的程序/脚本的输入参数。 |
选项解释¶
通用选项设置(选项)包括以下内容:
knob |
类型 |
默认值 |
帮助 |
---|---|---|---|
|
显示帮助信息并退出。 |
||
|
更改每个进程以将启动脚本解释为Python模块,行为类似于“python -m”。 |
||
|
布尔值 |
False |
避免在程序前加上“python”,直接执行它。适用于脚本不是Python脚本的情况。 |
|
字符串 |
|
指定日志文件目录。默认路径为``''``,表示禁用文件日志记录。 |
|
字符串 |
|
日志文件名的前缀。 |
用于应用或禁用优化的选项有:
knob |
类型 |
默认值 |
帮助 |
---|---|---|---|
|
布尔值 |
False |
启用``TCMalloc``内存分配器。 |
|
布尔值 |
False |
启用``JeMalloc``内存分配器。 |
|
布尔值 |
False |
使用默认内存分配器。既不使用``TCMalloc``也不使用``JeMalloc``。 |
|
布尔值 |
False |
默认情况下,如果安装了Intel® OpenMP库将被使用。设置此标志将禁用Intel® OpenMP的使用。 |
备注
内存分配器会影响性能。如果用户未指定所需的内存分配器,``run_cpu``脚本将按TCMalloc > JeMalloc > PyTorch默认内存分配器的顺序搜索是否有安装的,选择第一个匹配项。
控制实例数量和计算资源分配的选项有:
knob |
类型 |
默认值 |
帮助 |
---|---|---|---|
|
整数 |
0 |
实例数量。 |
|
整数 |
0 |
每个实例使用的核心数。 |
|
整数 |
-1 |
用于多实例的节点ID,默认使用所有节点。 |
|
字符串 |
|
指定核心列表为``'core_id, core_id, ….'``或核心范围为``'core_id-core_id'``。默认使用所有核心。 |
|
布尔值 |
False |
默认情况下仅使用物理核心。指定此标志将启用逻辑核心使用。 |
|
布尔值 |
False |
防止工作负载在跨NUMA节点的核心上执行。 |
|
整数 |
-1 |
为排名指定实例索引,以分配ncores_per_instance;否则ncores_per_instance将顺序分配给实例。 |
|
布尔值 |
False |
快速设置以在多插槽CPU服务器上调动多实例工作负载。 |
|
布尔值 |
False |
快速设置以延迟模式启动基准测试,所有物理核心被使用,每个实例使用4个核心。 |
|
布尔值 |
False |
快速设置以吞吐量模式启动基准测试,所有物理核心被使用,每个实例使用1个NUMA节点。 |
|
布尔值 |
False |
默认情况下使用``numactl``命令来控制NUMA访问。设置此标志将禁用该功能。 |
|
布尔值 |
False |
禁用``taskset``命令的使用。 |
备注
此脚本将设置的环境变量包括以下内容:
环境变量 |
值 |
---|---|
LD_PRELOAD |
根据您设置的选项,<lib>/libiomp5.so、<lib>/libjemalloc.so、<lib>/libtcmalloc.so 可能会被添加到 LD_PRELOAD。 |
KMP_AFFINITY |
如果预加载了 libiomp5.so,KMP_AFFINITY 将被设置为 |
KMP_BLOCKTIME |
如果预加载了 libiomp5.so,KMP_BLOCKTIME 将被设置为 “1”。 |
OMP_NUM_THREADS |
|
MALLOC_CONF |
如果预加载了 libjemalloc.so,MALLOC_CONF 将被设置为 |
请注意,该脚本会尊重预先设置的环境变量。例如,如果您在运行脚本之前设置了上述环境变量,这些变量的值将不会被脚本覆盖。
结论¶
在本教程中,我们探讨了一系列用于优化 PyTorch 在 Intel® Xeon® 可扩展处理器上的推理性能的高级配置和工具。通过使用 torch.backends.xeon.run_cpu
脚本,我们演示了如何通过微调线程和内存管理来实现最佳性能。我们涵盖了关键概念,例如 NUMA 访问控制、优化内存分配器(如 TCMalloc
和 JeMalloc
),以及使用 Intel® OpenMP 来实现高效的多线程。
此外,我们还提供了实用的命令行示例,以指导您设置单实例和多实例场景,从而确保针对特定工作负载的最佳资源利用率。通过理解和应用这些技术,用户可以显著提高 PyTorch 应用程序在 Intel® Xeon® 平台上的效率和速度。
另请参见: