首页 / 资讯中心 / 文章详情

PCIe性能调优:MPS与MRRS参数详解与实战

PCIe性能调优:MPS与MRRS参数详解与实战 ★ FEATURED ARTICLE
1. 从一个被忽视的“隐形瓶颈”说起如果你做过存储阵列、高速采集卡或者多卡互联的服务器平台大概率遇到过这种怪事链路协商速率明明跑到了 Gen4 x16理论带宽接近 32GB/s可实测的 DMA 吞吐就是上不去卡在某个不上不下的数字上怎么调驱动、换内存、绑核都没用。我第一次碰到这个问题是在一块自研的采集卡上当时折腾了整整两天最后发现问题出在两个 BIOS 里藏得很深的参数上——MPS和MRRS。这两个缩写展开分别是Max Payload Size最大有效载荷和Max Read Request Size最大读请求它们都属于 PCIe 总线事务层Transaction Layer的可配置参数直接决定了每一个 TLPTransaction Layer Packet能携带多少数据。听起来很底层但它的影响非常直接MPS 决定了写请求一次能搬多少字节MRRS 决定了读请求一次能要多少字节。这两个值如果配小了链路上就会塞满大量“小包裹”协议开销、包头开销、ACK/NAK 往返全部被放大有效带宽自然被吃掉一大截。这篇内容适合谁看如果你在做服务器平台调优、FPGA 高速接口、NVMe 存储性能压榨、多 GPU 互联、或者自研 PCIe 板卡的驱动开发那 MPS/MRRS 是你绕不开的一课。我会从原理讲到实操把参数怎么算、BIOS 怎么设、Linux 下怎么查、踩过哪些坑全部摊开讲清楚。哪怕你之前没接触过 PCIe 事务层看完也能上手调。先说结论在绝大多数高吞吐场景下把 MPS 和 MRRS 都拉到 4096 字节或平台允许的最大值往往能带来 10%~30% 的带宽提升而且不需要改一行代码。但前提是——你得知道整条链路上所有设备的“木桶短板”在哪否则设了也白设甚至可能触发兼容性问题。2. MPS 与 MRRS 到底是什么把 PCIe 想象成快递系统2.1 用“快递包裹”理解 TLP 与有效载荷PCIe 上的数据传输不是“一根线一直流”而是把数据切成一个个**数据包TLP**来发送的。你可以把它想象成寄快递每个包裹外面都要贴一张运单Header运单上写着收件地址、包裹大小、事务类型等信息包裹里面装的才是真正的货物Payload有效载荷。MPS 说的就是一个写请求Memory Write包裹里最多能装多少字节的货物。MRRS 说的是一个读请求Memory Read包裹里我一次最多向对方“订购”多少字节的货物。这两个参数的单位都是字节PCIe 规范允许的取值是 128、256、512、1024、2048、4096 这几档。注意MPS 和 MRRS 是每个 PCIe 设备Endpoint 或 Root Port各自独立配置的不是全链路统一的一个值。2.2 为什么包裹越大越划算开销占比的算术关键点在于运单Header的大小基本是固定的。一个 Memory Write TLP 的 Header 是 12 或 16 字节加上链路层的序列号2 字节和 LCRC4 字节再算上物理层的帧开销一个 TLP 的固定开销大概在 20~30 字节量级。我们来算一笔账。假设传输 1MB 数据当 MPS 128 字节时需要 1048576 / 128 8192 个 TLP。每个 TLP 固定开销按 24 字节算光开销就是 8192 × 24 ≈ 196KB相当于有效数据的 19% 被开销吃掉。当 MPS 4096 字节时只需要 256 个 TLP开销 256 × 24 ≈ 6KB占比不到 0.6%。差距一目了然。这还没算上链路层 ACK/NAK 协议、Flow Control 信用更新等额外的往返开销——TLP 数量越多这些控制流量也越密集进一步挤占有效带宽。提示MPS 影响的是写方向Memory Write、Completion 等MRRS 影响的是读方向Memory Read Request。很多人只调了 MPS 就以为万事大吉结果读密集型负载比如从内存读数据到设备一点没变快就是因为漏了 MRRS。2.3 一个容易混淆的点MPS 与 Completion 的关系这里有个细节必须讲清楚否则你调参时会一头雾水。读请求Memory Read本身不携带数据它只是“下单”。真正把数据送回来的是Completion TLP。而 Completion 的大小受什么限制受发起读请求的那一侧的 MPS限制而不是 MRRS。所以完整的逻辑链是这样的MRRS 决定“我一次下单要多少”MPS 决定“对方回给我的每个 Completion 包裹能装多少”。两个参数配合才能把读方向的效率拉满。这也是为什么调优时两个都要设只调一个效果会打折。3. 参数选型背后的博弈为什么不是无脑拉满3.1 木桶效应全链路最小值才是实际生效值PCIe 规范有一条硬性规则MPS 的实际生效值是链路上所有设备从发起方到目标方包括中间所有 Switch所配置 MPS 的最小值。MRRS 同理取路径上各设备的最小值。这意味着什么假设你的 Root Port 设了 4096但中间挂了一个 PCIe Switch它的 MPS 只支持 256那么整条路径的实际 MPS 就是 256。你在 Root Port 上设 4096 完全是白费力气。所以调优的第一步不是改参数而是摸清整条链路上每个设备的 MPS/MRRS 能力。这也是我踩过的第一个大坑当时只改了 CPU Root Port 的 BIOS 设置忘了中间还有一颗 Switch结果性能纹丝不动白白浪费了半天。3.2 兼容性与稳定性大包裹的代价既然大包裹这么香为什么厂商不默认全设 4096原因有几个老设备兼容性一些早期的 PCIe 设备尤其是 Gen1/Gen2 时代的硬件上只支持到 128 或 256 字节的 MPS。如果上游设备发来一个 4096 的 TLP它可能直接无法解析导致链路错误甚至设备掉线。缓冲区资源接收端需要为每个可能的 TLP 预留缓冲区Receive Buffer。MPS 越大单个缓冲区越大对硬件 SRAM 的占用越高。低成本设备为了省资源往往把 MPS 做小。延迟敏感场景大包裹意味着单个 TLP 传输时间长如果链路上有高优先级的实时流量大包裹会造成“队头阻塞”增加最坏情况延迟。对于某些工业控制、音视频同步场景小 MPS 反而更合适。注意调 MPS/MRRS 之前务必确认链路上所有设备的数据手册里标注的支持范围。盲目拉满可能让系统在压力测试下出现偶发的链路降速或设备重连这种问题极难排查。3.3 一个实用的决策原则我的经验是分场景处理场景类型MPS 建议MRRS 建议理由纯吞吐型存储、采集、GPU互联拉到链路最小值允许的最大同 MPS吞吐优先开销占比最小延迟敏感型实时控制、音视频256~512256~512控制单包传输时间降低抖动混合型通用服务器1024~20481024~2048折中兼顾吞吐与兼容老设备混插环境以最老设备为准同左稳定性优先避免链路错误这张表不是教条而是一个起点。真正落地时还是要以实测数据为准。4. 实操从 BIOS 到 Linux 的完整调优流程4.1 第一步摸清链路拓扑与设备能力在改任何参数之前先把“地图”画出来。Linux 下用lspci就能看到拓扑# 查看 PCIe 树状拓扑 lspci -t # 查看某个设备的详细能力包括 MPS/MRRS 支持范围 lspci -vvv -s 01:00.0 | grep -i -A2 DevCap在lspci -vvv的输出里你会看到DevCap:这一行里面有MaxPayload和MaxReadReq字段这就是该设备硬件支持的最大值。同时看DevCtl:行里面的MaxPayload和MaxReadReq是当前实际生效的配置值。# 示例输出片段 DevCap: MaxPayload 256 bytes, PhantFunc 0 ... DevCtl: ... MaxPayload 128 bytes, MaxReadReq 512 bytes上面这个例子就说明设备硬件支持 256 字节 MPS但当前只跑在 128 字节明显有优化空间。4.2 第二步BIOS 侧设置以常见服务器平台为例MPS/MRRS 的初始配置通常在 BIOS/UEFI 里。不同厂商的菜单路径不一样但关键词是固定的你在 BIOS 里搜这几个词就能找到PCIe Max Payload SizePCIe Max Read Request SizeMPS、MRRS常见选项值就是 128/256/512/1024/2048/4096有的平台还有Auto。建议手动指定不要用 Auto因为 Auto 往往保守地选 128 或 256。设置时的顺序很重要先设下游设备Endpoint再设上游Switch、Root Port。因为实际生效值取最小值如果上游先设了 4096下游还是 128那实际就是 128你得反复确认。提示有些平台的 BIOS 里 MPS 和 MRRS 是分开在不同菜单的MPS 可能在“PCIe Configuration”下MRRS 可能在“Advanced Chipset”里。找不到就用 BIOS 的搜索功能大部分现代 UEFI 都支持。4.3 第三步Linux 运行时验证与调整BIOS 改完重启后用lspci确认实际生效值。如果发现某个设备还是小值可能是 BIOS 没生效或者被 OS 覆盖了。Linux 下可以用setpci在运行时直接改配置空间需要 root且要谨慎# 查看设备的 PCIe Capability 寄存器偏移 lspci -vvv -s 01:00.0 | grep -i Express # 用 setpci 读取 Device Control 寄存器MPS/MRRS 在这里 # 假设 Capability 偏移是 0xa0Device Control 在偏移 0x08 setpci -s 01:00.0 0xa8.w # 修改 MPS 为 4096bit 7:5 101MRRS 为 4096bit 14:12 101 # 需要先读出原值按位操作后再写回说实话setpci直接改寄存器风险不小改错了可能导致设备异常。更稳妥的做法是通过 BIOS 设置或者用内核启动参数。Linux 内核有一个参数可以影响 MPS 行为# 在 GRUB 配置中加入 pcipcie_bus_perf这个参数会让内核尝试把 MPS 设为链路允许的最大值。相关的还有pcie_bus_tune_off不调整、pcie_bus_safe保守、pcie_bus_peer2peer。pcie_bus_perf是吞吐优先的选择但前提是硬件都支持大 MPS。4.4 第四步实测验证用数据说话改完参数必须用实测数据验证。我常用的方法是写方向测试用设备向主机内存做 DMA 写测吞吐。读方向测试主机内存向设备做 DMA 读测吞吐。对比测试分别在 MPS128 和 MPS4096 下跑同一套负载记录带宽、CPU 占用、链路错误计数。链路错误计数可以从lspci -vvv里的CEStaCorrectable Error Status和UEStaUncorrectable Error Status看。如果调大 MPS 后错误计数飙升说明有设备扛不住得退回去。# 查看链路错误统计 lspci -vvv -s 01:00.0 | grep -i -A5 CESta\|UESta5. 常见问题与排查技巧实录5.1 改了 BIOS 但 lspci 显示没变这是最高频的问题。原因通常有三个一是 BIOS 设置没保存成功有些平台需要按 F10 确认二是链路上有设备不支持你设的值系统自动降级了三是 OS 启动时又把它改回去了。排查顺序先确认 BIOS 保存再用lspci -vvv逐个设备看DevCap和DevCtl找出那个“拖后腿”的设备。如果所有设备 DevCap 都支持 4096 但 DevCtl 还是 128那可能是内核参数在起作用检查 GRUB 里有没有pcie_bus_safe之类的保守设置。5.2 调大后性能反而下降这种情况少见但确实存在。我遇到过一次把 MPS 拉到 4096 后某个采集卡的吞吐反而掉了 15%。后来发现是那块卡的接收缓冲区设计得比较小大 TLP 导致缓冲区频繁溢出触发了重传。退回到 1024 后恢复正常。所以大不一定好合适才是好。遇到性能下降先降一档再测找到那个“甜点值”。5.3 多设备环境下的相互影响一台服务器上往往挂着好几块 PCIe 设备它们可能共享同一个 Root Port 或 Switch。这时候 MPS 的设置会相互影响——因为 Switch 的上行端口 MPS 要同时满足所有下行设备。我的做法是先找出所有设备里支持的最小 MPS以此为基准统一设置。如果某块老卡只支持 256那整条路径就都设 256别为了其他设备强行拉高否则老卡会出问题。5.4 常见问题速查表现象可能原因排查方法解决思路带宽上不去lspci 显示 MPS128BIOS 默认保守查 DevCap 支持范围BIOS 手动设为最大值改了 Root Port 无效中间 Switch 限制lspci -t看拓扑逐级确认 Switch 设置读快写慢MRRS 没调对比读写测试同步调整 MRRS调大后偶发链路错误设备缓冲区不足看 CESta/UESta降一档 MPS重启后设置丢失OS 覆盖查内核启动参数移除保守参数或加 pcie_bus_perf多卡环境互相干扰共享 Switch 上行查拓扑关系以最小支持值为准5.5 几个只有踩过才知道的细节MPS 和 MRRS 不一定同步调。有些场景下 MPS 拉满、MRRS 保持中等反而更好因为读请求太大会增加内存控制器的压力。虚拟化环境要额外注意。如果设备直通给虚拟机Guest OS 里的 MPS 设置可能和 Host 不一致导致性能异常。建议在 Host 侧统一配置。固件版本会影响支持范围。同一块卡刷了新固件后 MPS 支持范围可能变化调优前先确认固件版本。测试要跑足够长时间。有些兼容性问题只在长时间高负载下才暴露短时间跑分看不出来。我一般至少跑 30 分钟压力测试。6. 一个完整的调优案例复盘6.1 场景描述某平台需要做高速数据采集链路上有一块自研采集卡Endpoint、一颗 PCIe Switch、以及 CPU 的 Root Port。初始状态下采集卡 DMA 写带宽只有约 6.2GB/s而链路是 Gen4 x8理论带宽约 16GB/s利用率不到 40%。6.2 排查过程第一步lspci -vvv查看各设备 MPSRoot PortDevCap 支持 4096DevCtl 实际 128Switch 上行口DevCap 支持 2048DevCtl 实际 128采集卡DevCap 支持 4096DevCtl 实际 128问题很清楚全链路都跑在 128 字节严重浪费。第二步确认 Switch 是短板只支持 2048所以整条路径的 MPS 上限是 2048。第三步BIOS 里把 Root Port、Switch、采集卡全部设为 2048MRRS 同样设为 2048。6.3 调优结果重启后lspci确认全部生效为 2048。重新跑 DMA 写测试调优前6.2GB/s调优后11.8GB/s提升幅度约 90%CPU 占用也从原来的 35% 降到了 18%因为 TLP 数量减少后中断和协议处理的开销大幅下降。6.4 复盘要点这个案例里最大的收益来自把全链路从 128 统一提到 2048。如果只改 Root Port 而不改 Switch实际生效值还是 128一点用没有。所以再次强调先摸拓扑找短板再统一设置。另外MRRS 的调整对读方向同样重要。这个案例里读带宽从 5.8GB/s 提到了 10.9GB/s几乎翻倍。读写都调才是完整的优化。7. 一些延伸思考与个人体会MPS 和 MRRS 只是 PCIe 性能调优的冰山一角。往深了走还有Relaxed Ordering、No Snoop、Flow Control 信用配置、ASPM 电源管理等一堆参数可以折腾。但我的经验是先把 MPS/MRRS 这两个基础项调好往往就能拿到 80% 的收益剩下的细节属于锦上添花。还有一点值得说很多性能问题其实不是 PCIe 本身的锅而是内存带宽、NUMA 亲和性、DMA 描述符设计等上层因素。调 PCIe 参数之前先用perf、numactl这些工具排除掉其他瓶颈别一头扎进寄存器里出不来。最后分享一个我常用的验证小技巧调完参数后除了看带宽数字一定要看链路错误计数和CPU 占用。带宽涨了但错误计数也涨了说明设置不稳带宽涨了且 CPU 占用降了才是真正的健康优化。这个判断标准帮我避开了好几次“看起来很美”的假优化。
阅读完成 · 觉得有帮助?
咨询建站