(大模型训练)高性能网络(InfiniBand/RoCE) 详细学习笔记 第六章:【重难点】性能调优(上):系统与网卡参数
第六章摘要:性能调优的关键框架与实践 性能调优需遵循"调优金字塔"模型: 操作系统层是基础,需优化CPU亲和性与中断绑定(解决NUMA跨节点访问问题)、关闭irqbalance守护进程、禁用透明大页(THP)以避免延迟抖动; 网卡驱动层需匹配硬件特性; 上层应用层需适配底层优化。本章重点讲解OS层调优,通过NUMA感知的中断绑定脚本和THP禁用等手段,为RDMA创造低干扰、高性
第六章:【重难点】性能调优(上):系统与网卡参数
第五章的基准测试给了我们一把精确的尺子,现在是时候用这把尺子来衡量并优化我们系统的每一个细节了。如果你的ib_write_bw没有跑满线速,或者ib_write_lat的延迟高于预期,甚至nccl-tests的busbw不尽人-意,那么问题很可能就隐藏在本章将要探讨的系统和网卡参数之中。
性能调优是一门科学,更是一门艺术。它不是随机地修改配置,而是基于对系统架构深刻理解的系统性工程。本章将为你建立一个清晰的调优框架——“性能调优金字塔”,并从最底层、影响最广泛的操作系统层面开始,逐步深入到网卡和驱动层面,为你揭示那些能够显著影响RDMA性能的核心参数和“开关”。
6.1 调优金字塔:一个系统性的调优框架
在开始任何调优之前,我们必须建立一个正确的思维模型。一个常见的错误是直接跳到最细枝末节的参数进行修改,而忽略了更高层面的、更具决定性的因素。我们推荐遵循一个自下而上的“调优金字塔”模型:
-
第一层(地基):操作系统 (OS) 层面
- 描述: 这是最基础、影响最广的一层。操作系统的调度策略、中断处理、内存管理和电源管理等行为,为上层所有应用设定了性能的“天花板”。一个“嘈杂”或配置不当的OS,会让任何上层调优都收效甚微。
- 目标: 为RDMA流量创造一个低干扰、高效率、可预测的执行环境。
-
第二层(承重墙):网卡/驱动 (NIC/Driver) 层面
- 描述: 这一层直接控制着数据包如何被硬件处理和传输。这里的参数(如MTU、缓冲区大小、中断合并策略)直接决定了网络I/O的效率。
- 目标: 使网卡和驱动的配置与我们的网络环境(IB/RoCE)和应用负载(高吞吐/低延迟)相匹配。
-
第三层(屋顶):协议与上层应用层面
- 描述: 这是最顶层,涉及RDMA协议本身的参数,以及像Kubernetes、Docker、NCCL这些上层应用的配置。
- 目标: 确保上层应用能够正确、高效地利用底下两层已经优化好的RDMA能力。
本章将重点关注金字塔的第一层和第二层,因为它们是后续所有性能表现的基础。
6.2 OS 层面调优:为性能打造一块宁静的基石
操作系统为了通用性和公平性,其默认配置往往不适合需要极致性能和低抖动的专用场景(如AI训练)。我们的任务就是关掉这些“通用性”的设置,为RDMA开辟一条VIP通道。
6.2.1 【重难点】CPU 亲和性 (Affinity) 与 IRQ 中断绑定
这是OS层面最重要、最有效的调优手段,没有之一。其核心思想是解决NUMA(Non-Uniform Memory Access)架构带来的性能问题。
-
NUMA 到底是什么?为什么它如此重要?
现代多核服务器通常是NUMA架构。这意味着服务器内部有多个“节点”(通常一个CPU插槽及其直连的内存就是一个NUMA节点)。一个CPU核心访问其“本地”(Local)NUMA节点的内存和PCIe设备(比如我们的网卡)速度极快,而访问“远程”(Remote)NUMA节点的资源则需要跨越处理器之间的互联总线(如Intel的QPI或AMD的Infinity Fabric),这个跨越会带来显著的延迟增加。 -
性能陷阱:
当你的高性能网卡收到一个数据包时,它会产生一个硬件中断(IRQ)来通知CPU处理。如果操作系统将这个IRQ随机调度到一个远程NUMA节点的CPU核心上,那么这个CPU核心在处理数据时,所有对网卡内存(如Ring Buffer)的访问都将是缓慢的跨节点访问。这会极大地增加延迟,并降低吞-吐量。 -
调优目标:
我们必须将处理网卡中断的CPU核心,“钉死”在与网卡物理上处于同一个NUMA节点的那些核心上。 -
【实践笔记】如何将网卡中断绑在正确的 NUMA 节点上?
-
第一步:确定网卡所在的NUMA节点
# 找到你的网卡接口名,例如 ens1f0np0 # 查看其设备信息中的 numa_node 文件 cat /sys/class/net/ens1f0np0/device/numa_node这个命令会输出一个数字,比如
0或1,这就是你的网卡所在的NUMA节点ID。如果输出-1,说明系统不支持或无法确定,但对于现代服务器这很少见。 -
第二步:确定该NUMA节点上有哪些CPU核心
lscpu | grep "NUMA node"输出会类似:
NUMA node0 CPU(s): 0-23,48-71 NUMA node1 CPU(s): 24-47,72-95现在你知道了,如果你的网卡在NUMA node 0上,你应该使用0-23或48-71这些核心来处理它的中断。
-
第三步:找到网卡对应的所有IRQ号
# ens1f0np0是接口名,mlx5_core是NVIDIA网卡的驱动模块名 grep ens1f0np0 /proc/interrupts # 或者更通用地,grep mlx5_core /proc/interrupts你会看到一长串列表,第一列就是IRQ号。现代网卡为了并行处理,会有多个中断队列,因此会对应多个IRQ号。
110: ... IRQ-PCI-MSI-X mlx5_comp0@pci:0000:81:00.0 111: ... IRQ-PCI-MSI-X mlx5_comp1@pci:0000:81:00.0 ... 125: ... IRQ-PCI-MSI-X mlx5_comp15@pci:0000:81:00.0 -
第四步:编写脚本,将这些IRQ绑定到目标CPU核心
手动一个个绑定非常繁琐。一个简单的脚本可以一劳永逸。#!/bin/bash # set_irq_affinity.sh # 你的网卡接口名 IF="ens1f0np0" # 第一步:找到NUMA节点 NUMA_NODE=$(cat /sys/class/net/$IF/device/numa_node) if [ $NUMA_NODE -lt 0 ]; then echo "Warning: Could not determine NUMA node for $IF." # 可以选择一个默认节点,例如 node 0 NUMA_NODE=0 fi # 第二步:获取该NUMA节点的CPU列表,并转换为smp_affinity可以识别的掩码 CPU_MASK=$(numactl -H | grep "node $NUMA_NODE cpus" | awk -F': ' '{print $2}' | sed 's/ / /g') echo "Binding IRQs for $IF (NUMA node $NUMA_NODE) to CPUs: $CPU_MASK" # 第三步:找到所有相关的IRQ号 IRQ_LIST=$(grep $IF /proc/interrupts | awk -F':' '{print $1}') # 第四步:循环绑定 for IRQ in $IRQ_LIST do CPU_AFFINITY_MASK=$(cpulist-to-mask $CPU_MASK) # 需要安装 'numactl' 包来获取 cpulist-to-mask 工具 echo "Binding IRQ $IRQ to mask $CPU_AFFINITY_MASK" echo $CPU_AFFINITY_MASK > /proc/irq/$IRQ/smp_affinity done echo "Done." ``` 将这个脚本设为可执行,并以root权限运行。运行后,你可以通过`cat /proc/irq/IRQ号/smp_affinity`来验证掩码是否已正确写入。
-
6.2.2 关闭 irqbalance:让你的手动配置生效
irqbalance 是一个Linux系统中的守护进程,它的作用是周期性地检查系统负载,并自动地将IRQ中断重新分配到各个CPU核心上,以求达到负载均衡。
这在通用服务器上是一个很好的功能,但对于我们精心进行了手动IRQ绑定的高性能计算节点来说,它就是个捣乱者。它会定期覆盖掉我们设置好的smp_affinity,让我们的努力付诸东流。
实践抓手:
必须无情地关闭并禁用它。
sudo systemctl stop irqbalance
sudo systemctl disable irqbalance
6.2.3 透明大页 (Transparent Huge Pages - THP) 的影响
THP是Linux内核的一项内存管理功能,它试图在后台自动地将标准的4KB内存页合并成2MB的“大页”。
- 优点: 对于需要访问大块连续内存的应用程序,使用大页可以减少TLB(Translation Lookaside Buffer,地址转换后备缓冲器)的缓存未命中次数,从而提升内存访问性能。
- 【重难点】缺点: THP的“透明”和“自动”是有代价的。内核中有一个名为
khugepaged的线程,它会定期扫描进程的内存空间,寻找可以合并的页。这个扫描和合并的过程会消耗CPU,更糟糕的是,它可能会在合并时临时锁定内存页,导致应用程序出现不可预测的、长达毫秒级的延迟停顿(Latency Spike)。
对于RDMA这种对延迟极其敏感的应用,这种不可预测的停顿是致命的。一次毫秒级的停顿,足以让ib_write_lat的t_max值飙升,也会严重影响NCCL的同步效率。
实践结论与抓手:
对于追求极致低延迟和稳定性的AI训练集群,强烈建议禁用THP。
# 临时禁用
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 永久禁用 (添加到 /etc/rc.local 或通过grub内核参数)
# 在GRUB_CMDLINE_LINUX中添加 "transparent_hugepage=never"
你可以通过cat /sys/kernel/mm/transparent_hugepage/enabled来验证,输出应该是always madvise [never]。
6.3 网卡/驱动层面调优:拧紧硬件的每一颗螺丝
当操作系统这个“地基”被打牢之后,我们就可以开始对网卡和驱动这栋“承重墙”进行精细化调整了。
6.3.1 关键参数:MTU (再次强调)
我们在第五章已经提到了MTU对带宽的重要性。这里我们深入探讨“为什么”以及“如何选择”。
- 为什么IB用4096?
InfiniBand协议本身定义了多种MTU尺寸,如256, 512, 1024, 2048, 4096字节。4096字节(4K)是其中最大的标准尺寸。选择它有几个好处:- 高效率: 更大的MTU意味着更小的包头开销占比。发送同样多的数据,需要的包数量更少,处理的包头和中断也更少。
- 与内存页对齐: 4KB恰好是x86架构下标准的内存页面大小。这使得数据在内存和网卡之间的DMA传输可以更高效地对齐。
- 为什么RoCE用1064/1500/9000?
RoCEv2的MTU选择更复杂,因为它需要考虑底层以太网的限制和封装开销。- RoCEv2封装开销: 一个RoCEv2数据包=
以太网头(14B)+IP头(20B)+UDP头(8B)+IB BTH(12B)+ … +Payload+CRC(4B)。总开销大约在50字节以上。 1500(标准以太网MTU): 如果你的RoCE流量需要通过不支持巨型帧(Jumbo Frames)的标准网络设备,你就只能使用1500。此时,RoCE的有效载荷(Payload)大小会被限制在1500 - 开销左右。9000(巨型帧): 这是高性能RoCE网络的首选。将以太网MTU设为9000,可以极大地提高载荷效率,显著提升吞吐量。1064等奇怪的值: 这些值通常是为了兼容一些特定的、MTU受限的设备或云环境。在自建的AI集群中,应尽量避免使用这些较小的非标准MTU。
- RoCEv2封装开销: 一个RoCEv2数据包=
调优建议:
- IB: 保持默认的
4096。 - RoCE: 在所有服务器和交换机上,统一配置并启用9000字节的巨型帧。
6.3.2 关键参数:调整 Ring Buffer 大小 (ethtool -g)
- 什么是Ring Buffer?
Ring Buffer(环形缓冲区),也叫RX/TX Descriptor Queues,是位于主机内存中的一块区域。驱动程序将待发送(TX)或已接收(RX)数据包的描述符(地址、长度等信息)放入这个队列,网卡硬件通过DMA直接读写这个队列,从而实现CPU与网卡之间的高效解耦。 - 性能陷阱:
如果Ring Buffer的尺寸太小,在网络流量突发(Burst)时,它可能会被迅速填满。对于RX(接收)队列,如果CPU处理速度跟不上数据包到达速度,新来的包会因为没有描述符空间而被网卡驱动直接丢弃。这会导致上层协议(即使是无损网络)出现丢包,引发重传和性能下降。 - 实践抓手:
使用ethtool来检查和调整Ring Buffer的大小。-
检查当前设置:
ethtool -g ens1f0np0输出示例:
Ring parameters for ens1f0np0: Pre-set maximums: RX: 8192 TX: 8192 Current hardware settings: RX: 1024 TX: 1024Pre-set maximums是驱动和硬件支持的最大值,Current hardware settings是当前生效的值。在这个例子中,当前值远小于最大值,有很大的提升空间。 -
增大Ring Buffer:
# 将RX和TX都设置为最大值8192 sudo ethtool -G ens1f0np0 rx 8192 tx 8192
在进行高吞吐量、高PPS(每秒包数)的测试或应用前,将RX/TX Ring Buffer的大小尽量设置为硬件支持的最大值,可以有效地防止因突发流量造成的丢包,提升网络的稳定性。 -
6.3.3 关键参数:中断合并 (Interrupt Coalescing) 的取舍
-
什么是中断合并?
如前所述,网卡每收到一个包就中断一次CPU,开销巨大。中断合并是一种优化技术,它允许网卡在产生中断前“等待”一下,要么等累计收到一定数量的包(rx-frames),要么等过了一段固定的时间(rx-usecs),然后才向CPU发起一次中断,一次性处理多个包。 -
【重难点】性能的取舍:高吞吐 vs. 低延迟
中断合并是一个典型的性能权衡:- 提高合并程度(
rx-frames和rx-usecs值设得更大):- 优点: 显著降低中断频率,减轻CPU负担。这使得CPU可以将更多周期用于处理数据本身,而不是响应中断,从而提高最大吞吐量。
- 缺点: 数据包在网卡缓冲区中等待的时间更长,导致延迟增加。
- 降低合并程度(值设得更小,甚至为0):
- 优点: 数据包一到达就立即通知CPU,延迟最低。
- 缺点: 中断频率极高,大量消耗CPU资源,在极高流量下可能导致CPU成为瓶颈,反而限制最大吞吐量。
- 提高合并程度(
-
实践抓手与调优建议:
使用ethtool -c检查,ethtool -C设置。# 检查当前中断合并设置 ethtool -c ens1f0np0 # 找到类似 rx-usecs, rx-frames, tx-usecs, tx-frames 的参数调优决策表:
| 你的主要目标 | 调优策略 | 示例命令 (ethtool -C ...) |
|---|---|---|
| 极致的低延迟 | 关闭或最小化中断合并。 | rx-usecs 0 或 rx-frames 1 |
| 最大的吞吐量 | 增大中断合并参数。 | rx-usecs 128 rx-frames 32 (具体值需实验) |
| 平衡(AI训练) | 启用自适应中断合并(Adaptive Coalescing) | adaptive-rx on adaptive-tx on |
自适应中断合并是现代NVIDIA网卡的一项优秀功能。启用后,驱动会根据网络流量的动态模式(是小包为主还是大包为主,是稀疏还是密集)来自动调整中断合并的程度。对于AI训练这种既有小消息同步(要求低延迟)又有大块数据传输(要求高吞-吐量)的混合负载,开启自适应中断合并通常是最佳的起点和默认配置。
本章小结:
我们已经深入到系统性能调优的核心腹地。通过遵循“调优金字塔”模型,我们首先从操作系统层面解决了最影响性能稳定性的三大问题:通过IRQ绑定克服了NUMA陷阱,通过禁用irqbalance保障了配置的持久性,通过禁用THP消除了不可预测的延迟抖动。接着,我们深入到网卡和驱动层面,精细化地调整了三个关键参数:为RoCE选择了最优的MTU(9000),通过增大Ring Buffer增强了抗突发能力,并理解了中断合并中“吞吐量”与“延迟”的永恒权衡,最终为AI训练场景找到了最佳实践——启用自-适应合并。
完成了本章的调优后,你应该重新运行第五章的基准测试。在绝大多数情况下,你会看到带宽更接近线速,延迟更低且更稳定。但这还不是终点,下一章,我们将登上调优金字塔的顶端,探讨协议和上层应用(Kubernetes, NCCL)的专属调优技巧。
(大模型训练)高性能网络(InfiniBand/RoCE) 详细学习笔记 其它章节 链接
以下是目前找到的全部章节,点击章节标题即可跳转阅读,可直接访问:
- (大模型训练)高性能网络(InfiniBand/RoCE) 详细学习笔记 第一章:导论 - 为什么标准以太网(TCP/IP)“喂不饱” GPU?
- (大模型训练)高性能网络(InfiniBand/RoCE) 详细学习笔记 第二章:技术选型对比:InfiniBand vs. RoCE (v1/v2)
- (大模型训练)高性能网络(InfiniBand/RoCE) 详细学习笔记 第三章:【实践】环境搭建:驱动与基础组件(以 Mellanox/NVIDIA 为例)
- (大模型训练)高性能网络(InfiniBand/RoCE) 详细学习笔记 第四章:【实践】RoCE 的“命门”:无损网络配置 (PFC & ECN)
- (大模型训练)高性能网络(InfiniBand/RoCE) 详细学习笔记 第五章:【实践】基准性能测试:你搭的环境真的“快”吗?
- (大模型训练)高性能网络(InfiniBand/RoCE) 详细学习笔记 第六章:【重难点】性能调优(上):系统与网卡参数
- (大模型训练)高性能网络(InfiniBand/RoCE) 详细学习笔记 第七章:性能调优(下):协议与上层应用(K8s/PyTorch)
更多推荐



所有评论(0)