吞吐量不等于带宽:从千兆网到XDMA,实际性能为何腰斩?

发布时间:2026/10/7 17:02:02
吞吐量不等于带宽:从千兆网到XDMA,实际性能为何腰斩? 做网络和高速数据传输这行的朋友一定没少跟“吞吐量”这个数字较劲。接口标的是千兆、万兆业务模型里跑出来动辄腰斩FPGA 上的 PCIe DMA 设计得挺漂亮实际一测却低得让人怀疑人生。最近讨论度最高的两个问题“xdma 实际吞吐量很低”和“千兆网以太网吞吐量 100mbps”其实指向的是同一件事我们对吞吐量的理解还停留在“标称速率”那张纸上。这篇文章准备把吞吐量的基本概念一次讲透再拿千兆网和 XDMA 这两个典型场景做靶子说说实际吞吐量到底怎么算、为什么会掉链子、应该怎么定位和优化。适合正在调网络性能、做高速数据传输或者刚接触 FPGA 驱动开发的朋友读完至少能少走几趟弯路。1. 先把“吞吐量”这三个字掰开揉碎1.1 吞吐量不是带宽也不是速率很多新手会把带宽、速率、吞吐量混着用实际上它们表达的是不同层面的能力。带宽更像链路的一个静态属性描述的是信道“最多能塞多少位”比如千兆以太网带宽是 1000MbpsPCIe Gen3 x8 的单向带宽大约 7.88GB/s速率偏向于某一次传输的瞬时快慢而吞吐量衡量的是系统在单位时间内真正成功交付的数据量也就是说它是个带“结果”的指标。用公式表示就是吞吐量 成功交付的数据量 / 消耗的时间单位常见有两种写法bit/s 和 Byte/s。网络设备习惯用 bps存储和文件传输习惯用 B/s中间还有个 Byte 和 bit 的 8 倍关系。注意别把小写的 b 和 B 看错这个在后面千兆网那个例子里就是最大的坑。继续往深看吞吐量还可以分成链路层吞吐、TCP/UDP 吞吐、应用层 Goodput。链路层吞吐统计的是以太网帧里的数据部分TCP 吞吐统计的是 TCP 段携带的负载应用层 Goodput 则只算业务真正用到的那些字节比如你下载一个 100MB 的文件文件本身就是 GoodputTCP 重传、ACK、协议头全部不算。测同一个系统这三类数字各不相同所以比较吞吐量之前先要对齐口径。1.2 为什么数字总比标称值小只要是多层协议栈参与的数据传输实际吞吐量就一定会低于理论带宽。原因可以归结为四类开销第一是协议开销。以太网帧有帧头、CRC、帧间隔IP 和 TCP 各有头部。这些是硬开销只要走这个协议栈就省不掉。第二是确认与重传。TCP 要 ACK拥塞控制要窗口滑动一旦丢包还要重传这些都会占用链路能力。第三是处理瓶颈。数据从网卡到内核再到应用中间有中断、内存拷贝、上下文切换、锁竞争。CPU 一旦成为瓶颈就算链路是实的吞吐也上不去。第四是资源竞争。DMA 要和 CPU 争内存带宽多个 PCIe 设备要抢 Root Complex 的通道NUMA 架构下跨节点访问内存还会更慢。可以拿高速公路类比带宽是限速 120 的公路吞吐量是单位时间内真正通过收费站的车辆数。路再宽收费站排队慢、ETC 识别迟钝、旁边还有车加塞实际通过量就上不去。这个类比基本能套用在任何协议栈、任何通信链路上。所以判断一个系统“吞吐量正常与否”不能拿标称值当基准而要先算出该场景下的理论上限再和实测值对比。上限怎么算下面两个场景就是最好的演示。2. 千兆网场景100Mbps 这个数字到底怎么来的2.1 单位误读第一课Mbps、MB/s 和 MiB/s热搜里那句“千兆网以太网吞吐量 100mbps”我第一反应是单位写错了因为千兆网理论值是 1000Mbps实际有效吞吐也远高于 100Mbps。真正容易出现的实测数字是“100MB/s”也就是每秒 100 兆字节那才是千兆网文件拷贝时的常见速度。如果口径混了把 100MB/s 写成 100Mbps看起来就差了一个数量级。先把单位换算摆清楚1 Byte 8 bit 1 Mbps 0.125 MB/s 1000 Mbps 125 MB/s 1 MB/s 8 Mbps再提醒一个更隐蔽的坑网络速率一般用十进制千进制1000Mbps 125MB/s而存储容量和文件大小常用二进制 MiB1MiB 1048576 字节。很多人拿 1000Mbps 去除 1048576算出 119.2MiB/s又把 MB 和 MiB 混写最后数字对不上都不知道错在哪。建议在团队文档里统一注明或直接写成 Mbps、MB/s、MiB/s不要简化。2.2 千兆以太网的实际理论上限计算现在正经算一下千兆网能干到多少。千兆以太网物理层速率 1000Mbps链路层以帧为单位传输每个帧在链路上实际占用的时间不仅包括帧本身还包括前导码、SFD 和帧间间隔。按标准以太网最大帧来算前导码 SFD8 字节帧间间隔12 字节以太网帧本体目标 MAC 6 字节 源 MAC 6 字节 类型 2 字节 负载 1500 字节 FCS 4 字节 1518 字节链路上一个最大帧占用8 12 1518 1538 字节如果只算以太网负载最高效率是1500 / 1538 97.53%那么链路层理论上限大约是1000Mbps × 97.53% 975.3Mbps ≈ 121.9MB/s但业务走的是 TCP/IP。以太网负载里还要去掉 IP 头 20 字节和 TCP 头 20 字节真正能装应用数据的只有 1460 字节链路帧占用还是 1538 字节。因此 TCP 方向上限大约是1000 × 1460 / 1538 949.3Mbps ≈ 118.6MB/s这是单条 TCP 连接在理想无丢包、无 CPU 瓶颈下的模型值。实测千兆网单流 TCP 也就 110 到 118MB/s文件拷贝再算上磁盘和系统调用开销90 到 110MB/s 都是正常范围。所以当你看到千兆网“只跑满 100MB/s”这不叫故障这叫符合协议特性。2.3 实测时最容易踩的测速误区我见过不少同事用文件拷贝速度去判断链路好坏这是最容易误判的方式。文件拷贝至少要过一层文件系统小文件密集时吞吐可能掉到 30MB/s那根本反映不出网卡的线速能力。正规做法是打流工具最常用的是 iperf3。服务端iperf3 -s客户端iperf3 -c 192.168.1.10 -t 60测多流并发可以加-P 4测反向方向可以加-R测 UDP 可以加-u -b 1000M。注意一点单流和四流的测试结果差异很大因为单条 TCP 流受窗口和 CPU 单核限制多流能更好地压满链路。测完如果速度异常先用ethtool eth0看协商速率和双工模式再检查网线是否为六类及以上、两端是否有交换机限速策略、驱动是否启用了 offload。这些老问题被我列在排查表里后面统一说。3. XDMA 的“理论上限”和“实际惨状”3.1 XDMA 是什么理论带宽怎么算XDMA 是 Xilinx 官方提供的 PCIe DMA IP 核经常用在 FPGA 和主机之间做高吞吐数据搬运。它的典型架构是FPGA 逻辑通过 AXI 接口对接 XDMA主机端驱动维护描述符环CPU 准备好 DMA buffer 后把描述符写到环形队列XDMA 自动从内存读数据或把 FPGA 数据写回内存完成后通过中断或轮询通知软件。XDMA 的理论带宽取决于 PCIe 链路。以 PCIe Gen3 x8 为例每个 lane 速率是 8GT/s采用 128b/130b 编码实际有效数据速率约 7.88Gbps8 个 lane 合计单向大约 7.88GB/s双向加起来还要更高。Gen3 x4 减半大概 3.94GB/s。注意这里的单位是 GB/s不是 Gbps别又被 8 倍关系坑一次。但理论值终究是理论值。很多人配置好驱动跑官方自带的dma_to_device示例程序发现只有几百 MB/s甚至几十 MB/s第一反应是板子坏了。其实绝大多数不是硬件问题而是描述符机制、中断频率、内存访问路径这几样东西没配合好。3.2 实测吞吐量低的四个典型原因第一个原因是单次 DMA 传输长度太小。描述符机制有固定成本软件填一次描述符、硬件取一次描述符再怎么优化也要几十微秒的往返延迟。假设单次传输 4KB链路带宽 4GB/s纯传输只需要 1 微秒但描述符处理要 10 微秒此时吞吐瓶颈完全在软件侧实测可能不到 500MB/s。把单次传输长度提升到 128KB、1MB 甚至更大吞吐往往会翻好几倍。第二个原因是中断风暴。默认情况下每完成一笔 DMA 就产生一次中断如果单次传输很短中断频率会非常高CPU 大量时间耗在中断上下文切换上。最简单的验证方法是看/proc/interrupts会发现对应 MSI-X 中断计数飞快增长。解决思路是中断聚合把多笔完成合并成一次中断或者直接改成轮询模式让驱动忙等描述符完成。轮询模式在持续大流量场景下提升非常明显代价是占满一个 CPU 核。第三个原因是内存与页表问题。DMA buffer 如果 4KB 对齐做得不好一次大块传输会被拆成多个 SGL 条目硬件逐个处理效率自然下降。如果 buffer 分散在不同页上SGL 条目多描述符环消耗快。更麻烦的是 IOMMU 开启时每次 DMA 都可能要查页表或走 swiotlb 反弹缓冲吞吐会掉得厉害。IOMMU 如果业务上不需要可以先关掉对比测试。第四个原因是 PCIe 参数配置不合理。PCIe 的 Max Payload Size 和 Max Read Request Size 直接从寄存器里读出来看如果 MPS 是 128B、MRRS 是 128B那么每次读请求能返回的数据量很小读延迟比例放大读吞吐C2H即 FPGA 到主机方向会特别难看。这类参数通常在根端口和端点固件里协商FPGA 端需要设置合理的 MPS/MRRS 值最好到 256B 或 512B。3.3 用日志和数据定位 XDMA 瓶颈拿到一个“XDMA 实际吞吐量很低”的问题我的排查顺序是固定的。先用官方示例跑大块连续传输比如单次 1MB、循环 100 次记录总耗时算峰值吞吐。如果峰值都上不去说明不是应用层调度问题而是驱动、DMA 配置或链路问题。然后看传输块大小的翻转测试。分别跑 4KB、16KB、64KB、256KB、1MB把吞吐量拉成一条曲线。如果吞吐量随块大小增长而显著增长说明瓶颈在描述符处理如果到 256KB 之后不再增长则可能是 PCIe 链路本身、内存带宽或中断频率的问题。再打开大页或预分配固定 buffer对比关掉 IOMMU 前后的数字。这能快速判断内存地址转换是否在拖后腿。还可以用perf stat或perf top看 CPU 开销分布如果大部分时间在irq、tasklet、copy_user_enhanced_fast_string上说明数据路径中软件占比太大。工具层面lspci -vvv可以看链路状态、MPS、MRRS、中断情况cat /proc/interrupts看中断分布numactl --hardware看内存拓扑。配合这些信息基本能把瓶颈锁定到某一层。4. 吞吐量问题的通用排查路径4.1 分层排查思路不管是千兆网、万兆网、RDMA、PCIe DMA吞吐量问题的定位逻辑是相通的我习惯分成四层排查链路层看协商状态和能力是否正常。网卡要确认速率和双工PCIe 设备要确认 LinkSta 里的宽度和速率是不是降级成了x1或Gen1。很多所谓“吞吐低”其实是链路没训练到满配。协议层看封装开销和窗口限制。TCP 的窗口、拥塞控制、Nagle 算法、延迟 ACKUDP 的包长和接收缓冲区都会直接影响有效吞吐。比如 TCP 默认窗口太小单流带宽永远跑不满这时候调大 socket buffer 比换网卡有效得多。软件层看中断、拷贝、锁。中断频率是不是过高数据是否在内存中被拷贝了多次驱动里的锁是否串行化了大块传输。x86 平台上一次不必要的copy_from_user就可能吃掉几个 GB/s 的带宽。系统资源层看 CPU、内存带宽、NUMA。DMA buffer 和业务进程分配在不同 NUMA 节点跨节点访问内存会增加几十纳秒到上百纳秒的延迟吞吐敏感型应用立刻能感知多个 PCIe 设备同时工作还要考虑 Root Port 和内存控制器的总带宽上限。4.2 一条命令快速看状态的核心指标快速摸底一般用这几个命令# 网卡协商速率与丢包统计 ethtool eth0 # PCIe 链路宽度、速率和 MPS/MRRS 配置 lspci -vvv -s 01:00.0 # 中断分布情况 cat /proc/interrupts # CPU 占用热点 perf top这几个命令输出组合起来基本能把链路层和软件层问题暴露出来。比如ethtool显示 Speed: 100Mb/s那千兆网测出 100Mbps 就完全正常因为链路本来就只有百兆。再比如lspci里LnkSta: Speed 2.5GT/s, Width x1那 XDMA 吞吐低也别怪驱动先把链路带宽搞定。排查时记得每次只改一个变量保持其他条件不变。很多人一次同时改 MTU、关中断聚合、换 CPU 绑核测出数字变好了但根本说不清是哪个改动起了作用后续一回归又掉回去这种排查看似快实际浪费更多时间。5. 吞吐量优化网络与 XDMA 的通用打法5.1 网络传输优化检查单针对千兆网或类似以太网场景按以下清单逐项过多数吞吐问题能解决确认协商速率两端都支持千兆网线至少超五类最好六类。调大 MTU局域网场景可以开启巨帧MTU 9000。最直接的好处是减少帧数让 CPU 中断减少对 PCIe 网卡尤其明显。开启网卡 offloadTSO、GSO、GRO、LRO 这类卸载功能让大包在网卡侧或内核里合并处理降低 CPU 负担。增大 socket buffersysctl -w net.core.rmem_max16777216 sysctl -w net.core.wmem_max16777216多流并发iperf3 加-P 4业务上使用多线程或多连接绕开单流窗口和单核瓶颈。检查中断均衡多队列网卡让irqbalance把中断分散到多核或者在/etc/irqbalance配置里设置策略避免所有中断压在 CPU0。关闭 TCP Nagle对延迟敏感的短连接场景可考虑关闭 Nagle 合并减少小包的等待时间不过大吞吐场景影响相对小。每一步改动前后都记录实测数据尤其记下单位是 Mbps 还是 MB/s。我见过太多人拿两组单位不一致的数据对拍折腾半天才发现是单位写错。5.2 XDMA 优化配置实例XDMA 优化的优先顺序理论上应该遵循“先链路、再块大小、再中断、再内存”第一确认 PCIe 链路跑满宽度和速率。如果 LnkSta 显示 Gen1 x4先把 FPGA 里的 PCIe IP 配置改成 Gen3链路建不起来后面全白搭。第二把单次 DMA 长度调大。驱动示例程序里控制单次长度的参数是-l循环次数是-c。一瓶测下来如果-l 4096时吞吐只有 400MB/s而-l 1048576时能到 4GB/s说明描述符处理开销主导了瓶颈后续应用侧就尽量用 1MB 级别的块搬运。# 以官方参考驱动为例单次传输 1MB循环 100 次 ./dma_to_device -d /dev/xdma0_h2c_0 -l 1048576 -c 100第三处理中断。优先开启中断聚合或改轮询模式。驱动实现里如果支持每 N 个描述符产生一次完成中断或者用定时器聚合中断可以显著降低 CPU 使用率。轮询模式适合纯搬运场景实测能再拉高一段吞吐代价是独占 CPU。第四处理内存。用内核hugetlbfs预留大页确保 DMA buffer 物理连续且对齐或提前分配并固定核心 CPU 和内存节点例如numactl --cpunodebind0 --membind0让驱动、中断、buffer 都在同一 NUMA 节点上。第五调整 PCIe 的 MPS 和 MRRS。FPGA 端 AXI 侧带宽够的前提下把 MPS 配到 256BMRRS 配到 512B 或更高能明显改善读方向吞吐。写方向H2C主要看 MPS读方向C2H主要看 MRRS。这个参数如果只在 BIOS 里修改有时不会传递到端点设备FPGA 侧要主动宣告支持。5.3 常见问题速查表把这类项目里最常碰到的现象整理成一个速查表排查时可以直接对照现象大概率原因验证手段建议解法千兆网单流只有 90MB/s 左右协议栈开销 默认窗口限制iperf3 多流对比调大 socket buffer、多流并发千兆网协商为 100Mbps网线或端口协商异常ethtool eth0换六类网线、换交换机端口XDMA 小块传输吞吐低描述符处理瓶颈扫描 -l 参数增大单次 DMA 长度XDMA 大块传输也无法提升中断频繁或内存带宽限制/proc/interrupts、perf top开中断聚合/轮询、检查 NUMAC2H 读方向特别差MRRS 设置过小lspci -vvv 查看FPGA 端调大 MRRS开启 IOMMU 之后吞吐下降页表转换 / swiotlb 反弹关闭 IOMMU 对比业务无强需求时关闭或使用 IOMMU 直通多通道同时传输反而下降内存带宽或 PCIe Root Port 争抢监控内存带宽利用率分散到不同 NUMA 节点或控制并发通道数这个表不是金科玉律但覆盖了大多数“实际吞吐量很低”的情况。真遇到表中的现象至少能让你快速判断该往哪一层继续挖。6. 一点个人经验最后说点私货。我调过不少网络吞吐和 PCIe DMA 项目最大的感受是吞吐量问题九成出在“看数据的方式”上而不是硬件上。单位没统一、测试口径不清、理论基准算错这三件事引发的无效排查远比真实的硬件故障多。另一点是不要迷信某个神秘参数。很多文章喜欢强调“只要开了 DDIODMA 吞吐立刻起飞”但实际工程里 DDIO 只是把数据留在缓存、减少内存访问次数如果你的瓶颈根本不在内存延迟而在于描述符提交那改了也没意义。先定位再优化才是能复现、可回归的正路。如果你也正被“XDMA 实际吞吐量很低”或者“千兆网吞吐量只有 100 出头”折磨建议先按这篇文章把理论上限算一遍再拿 iperf3 或官方 DMA 示例做一个块大小扫描。数字不会骗人只要变量隔离得够干净瓶颈通常会在几分钟内自己浮出来。