
“吞吐量”这个词干网络和干系统的应该都不陌生——面试喜欢问产品宣传喜欢写可真到了线上排查问题的时候你会发现理论和现实差距大到让人怀疑人生。尤其这两天群里又在聊 XDMA 实际吞吐量很低、千兆网卡跑到 100Mbps 就上不去的案例。很多刚接触高性能网络或者 PCIe DMA 的兄弟上来就被一堆“线速”、“包转发率”、“BDP”这些词砸懵了。吞吐量到底怎么定义理论值怎么算实测值为什么总是上不去这篇文章我就从基本概念讲起把带宽、吞吐量、实际有效速率这些关系捋清楚再结合千兆以太网和 XDMA 这两个典型场景聊聊瓶颈到底卡在哪、要怎么定位。1. 吞吐量到底是什么很多人把吞吐量和带宽混着用这在日常聊天没什么问题但一旦到了性能调优或者写压测报告的时候就必须较真了。1.1 带宽、吞吐量、速率的区别带宽Bandwidth描述的是链路或设备的额定传输能力是理论极限值。比如千兆以太网带宽就是 1000MbpsPCIe Gen3 x4 链路单向带宽约 3.94GB/s。吞吐量Throughput描述的是在特定条件下系统实际能传输的有效数据量单位同样常用 bps 或 B/s。两者之间差着一大截因为真实传输过程要付出大量额外开销包头、确认包、重传、排队、中断处理、内存拷贝、协议栈处理……每一项都在蚕食理论带宽。还有两个容易混淆的概念——速率和吞吐量。速率Rate通常指瞬时测量值比如某一秒内传输了多少比特吞吐量更偏向持续、稳态的平均值通常要跑满数秒甚至更久才能得出可信结果。拿测速来说一瞬间可能冲到 980Mbps但持续 10 秒后稳定在 940Mbps这个稳定值才是吞吐量。再往下拆吞吐量还有几个细分的观察维度网络吞吐量物理链路端到端的有效数据传输速率磁盘/存储吞吐量存储系统持续读写数据的速率总线/DMA吞吐量数据经过总线或 DMA 引擎搬运的速率这些维度看似不同但核心逻辑完全一致理论带宽扣除一切开销之后的真实有效速率就是吞吐量。1.2 吞吐量为什么永远到不了理论带宽先说一个简单的例子。千兆以太网跑 TCP很多人都能测到 940Mbps 左右但为什么不是 1000Mbps以太网帧有固定开销前导码和定界符占 8 字节以太网头占 14 字节帧尾 FCS 占 4 字节帧间隙IFG占 12 字节MTU 默认 1500 字节TCP 负载最多 1460 字节扣除 TCP 头 20 字节 IP 头 20 字节也就是说一个完整线速帧在物理链路上实际占用的开销至少是 1538 字节1500 14 4 12 8真正传输的用户数据只有 1460 字节。有效比例大约是 1460 / 1538 94.9%。这样算下来千兆以太网即使跑到线速理论有效吞吐也只有 949Mbps。这还只是以太网层面的开销跑 TCP 还有三次握手、确认包、重传、拥塞控制等额外损耗。再加上网卡中断、驱动处理、系统协议栈开销实测稳定值通常在 930~940Mbps 就很不错了——如果你测得的数值接近这个区间说明系统状态非常健康。注意经常有人把 MBps 和 Mbps 混用。1 MBps 8 Mbps千兆网 940Mbps 换算一下是 117.5MB/s。要是看到测试工具里显示 100MB/s不要立刻觉得有问题先确认单位。1.3 影响吞吐量的核心因素把影响吞吐量的因素整理起来大致分五类影响因素说明典型例子链路物理带宽硬件决定的额定极限千兆网卡 vs 万兆网卡协议开销封装头、控制帧、校验TCP/IP 头、以太网帧头延迟与往返时间高 RTT 会严重限制吞吐跨地域传输处理能力CPU、内存、总线、DMA 引擎软中断打满 CPU缓冲与流量控制窗口大小、队列深度TCP 接收窗口、DMA 描述符数量其中最关键也最容易踩坑的是延迟和缓冲。TCP 吞吐量有一条经典公式TCP 吞吐上限 ≈ 窗口大小 / 往返时延RTT假设 RTT 是 10ms接收窗口是 64KB那么最大吞吐量就是 64KB / 10ms 6.4MB/s约 51Mbps。这就是为什么很多人用千兆网传跨省数据实际吞吐低得可怜——不是链路不行而是窗口和延迟算死了。2. 千兆网卡实测只有 100Mbps回到文章开头那个热搜问题千兆以太网吞吐量只有 100Mbps。首先必须先区分一种情况——网卡协商成了百兆模式这是最常见的低级错误。2.1 网卡协商成了百兆模式网卡和交换机之间通过自动协商确定工作速率。如果网线是四芯线比如某些劣质或老旧线缆、水晶头接触不良、交换机端口配置成强制百兆都会导致链路协商失败或降级到 100Mbps。排查方法很简单ethtool eth0看 Speed 字段如果是 1000Mb/s 就没问题如果显示 100Mb/s说明物理链路就不对先换线换口再说。我遇到过最离谱的情况是一整批网线看着没问题拿测试仪测 8 芯全通但就是协商不到千兆。最后发现是线缆用的铝芯线高频衰减太严重千兆根本跑不稳定。换铜芯线后立刻恢复。2.2 协商千兆但吞吐只有 100Mbps如果 ethtool 显示千兆但 iperf3 实测只有 90~100Mbps情况就复杂了。这类问题通常在以下三个层面第一个层面驱动和中断处理问题网卡收到数据包后会触发中断如果中断频率过高CPU 会被软中断打满处理不过来的包只能丢弃。很多网卡驱动默认关闭了中断合并Interrupt Coalescing导致每个包都产生一次中断高 PPS每秒包数场景下 CPU 直接成为瓶颈。解决办法是调大合并参数。以 Intel 网卡为例ethtool -C eth0 rx-usecs 125 ethtool -C eth0 rx-frames 32这样把中断频率降下来CPU 占用率能降好几个百分点吞吐经常能提升 10%~20%。第二个层面CPU 亲和性和多队列单队列网卡在高负载下很容易被打爆。如果网卡支持多队列RSS一定要把队列绑定到不同 CPU 核心上否则所有队列中断都挤在一个核上其他核闲着性能自然上不去。中断绑核用系统工具设置。简单做法是修改/proc/irq/下的smp_affinity列表把各个队列中断分散到不同核心。绑完核之后用mpstat观察各个核心的软中断占用率如果只有一个核打满其他核很闲那基本就是绑核没做好。第三个层面PCIe 链路带宽不足这个不常见但确实存在。千兆网卡如果插在 PCIe 1.0 x1 插槽上可用带宽只有 250MB/s 双向网卡跑满 125MB/s 时看起来没问题但如果同一总线上还挂了别的设备或者使用了某些桥接芯片实际可用带宽会被抢占。用lspci -vvv看一眼网卡的 LnkSta 字段确认是不是跑在完整的链路宽度和速率上。千兆网卡很少因为 PCIe 带宽不够而掉到 100Mbps但一旦涉及万兆网卡或者多张千兆网卡同时跑满PCIe 链路宽度不够导致吞吐上不去的案例并不少见。2.3 实测吞吐量的正确姿势用 iperf3 测吞吐量不能上来就一条命令跑完有几个参数必须调iperf3 -c 10.0.0.2 -t 30 -w 4M -l 64K -P 4-w 4M设置 TCP 窗口为 4MB-l 64K调整 buffer 大小-P 4用 4 条并发流。单流的情况下受限于单核 TCP 处理能力和接收窗口很难跑满高速链路。我见过不少 10G 环境单流只能跑 2~3Gbps加几路并发之后轻松跑满的。测出数值之后怎么判断正不正常对照这张表链路TCP 实测参考值说明千兆以太网930~940Mbps健康千兆以太网100~500Mbps需要排查万兆以太网6~9.5Gbps单流通常难跑满XDMA PCIe Gen3 x42500~3500 MB/s视传输块大小而定低数值不一定代表有问题要结合现场链路状况来判断。但如果同一个环境里别人能跑 940你只能跑 100那一定存在可优化的点绝不要用“线路质量差”这个理由糊弄过去。3. XDMA 实际吞吐量为什么很低XDMAXilinx DMA是 FPGA 和主机之间做高速数据传输的常用方案尤其在国产 FPGA、软件定义存储、高速数据采集领域用得很多。很多人刚开始调 XDMA 的时候都挺兴奋觉得 PCIe 带宽那么大随便传传就是几个 GB/s结果一测实际吞吐低得让人怀疑 IP 核配错了。这其实是整个系统链路共同作用的结果不单纯是 IP 核配置的问题。3.1 理解 XDMA 的数据路径XDMA 本质上是一个 PCIe Endpoint DMA 引擎它通过描述符Descriptor告诉 DMA 引擎“数据从哪来、到哪去、传多少”。主机驱动准备好描述符后写寄存器通知 XDMA 启动传输XDMA 搬运完成后通过中断或者轮询状态位通知驱动回收描述符。数据传输有几个关键环节任何一个环节掉链子都会拖累整体吞吐驱动申请内存并填充描述符写寄存器触发 DMA 传输PCIe 总线执行 TLP 读写事务中断或者轮询通知完成驱动处理完成事件并回收描述符整套流程里有一个隐藏的公式有效吞吐量 单次传输的数据量 / 数据搬运时间 启动开销 完成开销启动开销和完成开销如果占比很大哪怕传输引擎本身再快实际吞吐也上不去。3.2 传输块太小导致吞吐暴跌这是 XDMA 吞吐低最普遍的原因。假设一次 DMA 传输的数据量只有 512 字节而一次传输的启动和完成开销是 2 微秒那么即使 PCIe 理论带宽是 8GB/s单次传输搬运 512 字节只花了 0.064 微秒但总耗时是 2.064 微秒有效吞吐只有 248MB/s。如果把单次传输块增大到 1MB一次搬运耗时 125 微秒总耗时 127 微秒有效吞吐约 8.25GB/s——效率差了 30 多倍。所以 XDMA 调优的第一原则尽可能增大单次传输块大小。建议至少达到 64KB 以上理想是 1MB 以上。我见过某个项目的 XDMA 代码里驱动每次只传输 4KB测出来只有 500MB/s 左右后来改成 1MB 大块传输直接冲到 3GB/s 以上。这个优化过程几乎没有改任何硬件逻辑只改了软件的数据组织方式。3.3 描述符数量和内存对齐描述符是控制 DMA 传输的灵魂。XDMA 驱动通过描述符告诉硬件每一次传输的源地址、目的地址和长度。两点值得注意一是描述符的深度。如果描述符队列太短比如只有 16 个驱动需要不停地等待硬件消费完描述符才能补充新的经常会出现“DMA 引擎空转等活干”的情况。合理配置描述符队列深度让它能覆盖传输过程中的所有 pending 请求效果会好很多。二是内存对齐。DMA 传输对内存对齐要求很高特别是跨 4KB 页面边界的 buffer硬件可能需要拆分成多个 TLP 处理白白增加开销。驱动中申请 DMA buffer 时建议按 4KB 或者更大的边界对齐必要时用大页内存HugePage一步到位。举一个对比配置项低吞吐配置高吞吐配置单次传输块4KB/次1MB/次描述符队列深度16256内存对齐不要求4KB/2MB 对齐完成通知方式每个描述符中断一次批量完成、中断聚合很多 XDMA 驱动默认实现是“每传完一个描述符就产生一次中断”高 PPS 场景下 CPU 直接被打满。正确的做法是开启中断聚合或者干脆用轮询模式——在持续高速传输场景中轮询模式配合大块传输往往比中断模式效果好得多。3.4 实测数据和一个调优案例我这边之前在 FPGA 板上跑过一个 XDMA 基准测试反复调整参数后拿到了这样的数据PCIe Gen3 x4 链路理论 3.94GB/s 单向单次传输大小实测吞吐写方向CPU 占用4KB480MB/s65%64KB1.8GB/s30%1MB3.2GB/s12%4MB3.4GB/s10%从 480MB/s 到 3.4GB/s只是改了软件层的数据聚合方式和中断处理逻辑硬件一句代码都没动。这就是“吞吐量瓶颈往往不在链路而在系统设计”的最好注脚。还有一个案例是写方向的性能总是比读方向高一截。这个在很多 PCIe 设备上都是正常的原因在于读操作是“请求-完成”模式主机发读请求后必须等设备返回数据整个事务中间存在往返延迟而写操作是“一锤子买卖”只要数据送出去就完事流水线效率能拉满。知道这个特性之后在需要高吞吐的场景尽量设计成 DMA 写而不是 DMA 读。4. 吞吐量排查与调优实战前面讲了两个具体场景这部分把通用的排查思路和调优方法整理成一套可以照着做的方法论。因为不管是网络还是总线还是存储底层的逻辑都是通路上的瓶颈分析。4.1 吞吐量排查流程图思路版先说排查的整体思路。吞吐量问题排查从来都不是单点问题而是三步走的逻辑第一步确认瓶颈在哪个层次如果是网络网卡→驱动→协议栈→应用如果是 DMA硬件 IP→驱动→内存→PCIe 链路可以用排除法快速定位。跑一个本机回环测试loopback如果本机回环吞吐本身就低问题大概率在协议栈或应用层如果回环正常再接物理链路测重点查网卡、驱动、中断。第二步用工具量化关键指标top/mpstat看 CPU 哪些核忙软中断占比高不高ethtool -S eth0看丢包计数、队列分发情况perf top看协议栈热点和驱动热点iostat/sar -d看存储侧是否成为瓶颈如果是 XDMA在驱动里加计数统计每次传输的启动到完成的平均耗时工具数据比感觉可靠得多。不要一上来就怀疑硬件先把系统级数据抓全再逐层往下查。第三步逐个排除并验证每改动一个参数就重新跑一次测试记录下来。最好绘制一张“参数变化 vs 吞吐量”的变化表很多时候性能是多个瓶颈叠加后的结果——解决一个瓶颈另一个瓶颈会随即暴露出来这恰恰是性能调优的正常过程。4.2 网络吞吐量场景的调优顺序如果是网卡方向的问题我一般按照以下顺序调确认链路速率和双工模式ethtool eth0检查网卡队列数量ethtool -l eth0调大 ring buffer 深度ethtool -G eth0 rx 4096 tx 4096开启中断合并ethtool -C eth0绑核并设置 RPSReceive Packet Steering确认 MTU 设置有条件就上 jumbo frame最后才考虑协议栈相关参数sysctl里的tcp_rmem、tcp_wmem、net.core.rmem_max等这几个步骤操作成本都不高但效果通常很显著。之前调过一个双网卡 bonding 的环境绑定方式从主备模式改成了 802.3ad 负载均衡之后整机吞吐从 900Mbps 提升到 1.7Gbps这里的关键是网卡和交换机都必须在同一链路聚合模式下。4.3 XDMA / PCIe 吞吐量场景的调优顺序XDMA 场景特殊性更强调优重点集中在软件怎么把数据高效喂给 DMA 引擎优先级操作预期效果1增大单次传输块建议 ≥1MB可能几倍提升2减小完成中断频率降低 CPU 占用提升吞吐3增加描述符队列深度保证 DMA 引擎不断流4使用大页/对齐内存减少拆分提升稳定性5多通道并发传输挖掘 PCIe 多队列潜力6减少寄存器读写次数直接减少启动开销操作的核心原则是让 DMA 引擎始终处于“有活干”的状态同时让 CPU 的开销尽量低。这是软件视角对吞吐量的理解你不需要提升硬件的物理上限只需要把软件和硬件之间的配合做到严丝合缝。4.4 一个通用的带宽—延迟理解方法无论网络还是 PCIe都要记住一个概念——带宽延迟积BDP。它表示“链路上正在飞行中的数据量上限”等于带宽乘以延迟。简单理解如果链路带宽是 1Gbps延迟是 1ms那么链路里最多同时存在 1Mbps × 0.001s 1Mb ≈ 125KB 的数据。如果系统窗口/缓冲只有 64KB发送方发 64KB 之后就得停下来等待确认链路利用率只有一半。想要跑满千兆窗口至少要和 BDP 持平再预留一些余量。生活化类比你是一条流水线每次只能往皮带上放固定数量的零件皮带走完一圈需要的时间是延迟。如果皮带上有 100 个零件位你一次只能发 50 个那另一半皮带一直空着——这就像吞吐量上不去的常见原因有效利用的“带宽”不到物理上限的一半。5. 常见问题速查与避坑心得最后整理一个速查表把吞吐量场景中最典型的问题、原因和排查方向放进一张表里方便现场排障时参考。现象可能原因排查/解决办法千兆网实测只有 100Mbps 且协商百兆网线劣质/四芯线/端口强制百兆换线、查交换机配置协商千兆但吞吐不到 200Mbps中断打满单核、驱动参数未调开多队列、绑核、中断聚合用 iperf3 单流跑不满带宽窗口大小不足、单流受限加-P 4并发调-w窗口XDMA 4KB 块传输吞吐极低启动开销占比过高增大传输块到 1MB 以上DMA 传输中断频繁、CPU 打满每描述符产生中断开中断聚合或改轮询读方向吞吐只有写方向一半PCIe 读事务往返延迟高尽量设计成写方向传输传输过程中掉速抖动内存碎片化导致地址不连续用大页/预分配缓冲池再强调几个经常踩中的坑这是常规文档里不会写但也最容易卡住人的地方第一不要盲目改参数而不复测。调优是一个实证过程不记录基线就动手改完也不知道有没有效果。每一次调优都应该先保存当前数据再改动一个参数重新测试记录结果。第二万兆网卡插到 PCIe 2.0 x1 插槽基本等于废了。装硬件前先用lspci -vvv看实际协商到的链路宽度和速率不要等测试发现吞吐上不去了才回过来查槽位。第三万兆环境记得看 PCIe 速率上限。PCIe 2.0 x8 只有约 4GB/s 的可用带宽跑万兆网卡满速 1250MB/s 刚好卡在极限附近如果同时还有存储 IO 抢总线带宽吞吐数据会很不稳定。这时候需要优先保证关键数据的通路质量。第四测吞吐量一定含 TCP 层的时候要区分一个细节——UDP 测试工具比如 iperf3 -u的结果通常高于 TCP但不能替代真实业务。因为真实业务几乎跑的都是 TCP 或者有确认机制的自定义协议UDP 的“高吞吐”只是一个理论参照不代表系统处理真实业务的能力。我自己在实际调吞吐量问题时最深的感受是性能问题几乎没有一次是单一瓶颈解决之后就彻底解决的它经常是一个瓶颈接着另一个瓶颈层层剥开才接近真实上限。所以别指望找到一个“银弹”参数就一劳永逸。务实的做法是建立一套“数据采集 → 假设 → 验证 → 再采集”的循环用工具量化每一步。思路对了绝大多数吞吐量问题离解决就不远了。