当前位置: 首页 > web >正文

iperf2 vs iperf3:UDP 发包逻辑差异与常见问题

1. 问题引入
在日常网络测试中,大家经常会遇到这样的情况:iperf3 -u -c -b 1000M
结果显示:实际带宽只有 600~800 Mbps,远低于我们设置的 1000 Mbps。
于是问题来了:
  • 是链路本身达不到 1Gbps?
  • 还是 iperf3 没有按指令发足够多的 UDP 报文?
很多人会因此怀疑测试结果不准确。其实这并不是链路坏了,而是 iperf3 UDP 模式的设计决定了它不会“强行灌包”
2. 根本原因
造成这种现象的核心原因在于 iperf2 与 iperf3 的 UDP 发包逻辑不同
  • iperf2:强调“尽量贴合目标速率”。如果系统调度落后,会突发补发欠账的包,看起来速率接近 -b,但链路抖动大,丢包率可能高。
  • iperf3:强调“真实统计”。如果系统调度落后,就直接跳过“欠账”,只统计实际发出的字节数。结果可能低于 -b,但数值更真实,能反映链路的实际可用带宽。
换句话说:
  • iperf2 更像“压测工具” → 灌满链路,测试设备极限。
  • iperf3 更像“测量工具” → 精确统计,反映真实可用速率。
3. 发包逻辑对比
3.1 关键变量与基本公式
  • 目标带宽:B_target(bit/s),由 -b 指定
  • UDP 载荷长度:L_payload(Byte),由 -l 指定
  • 每包比特数:P_bits = L_payload * 8
  • 期望包速率:pps = B_target / P_bits(包/秒)
  • 理想包间隔:Δt = 1 / pps = P_bits / B_target(秒)
注意:iperf 的带宽统计通常以应用层负载为准(即 payload 大小),不含 UDP/IP/L2 头部;但链路“线速”会受实际报文总大小(含各层头部、IFG 等)影响。
示例(-b 1,000,000,000,-l 1470):
  • P_bits = 1470 * 8 = 11760
  • pps ≈ 1e9 / 11760 ≈ 85,034 包/秒
  • Δt ≈ 11.76 μs/包 这意味着:用户态要在 每 11.76 μs 精准触发一次 sendto(),对普通 OS 调度精度是非常苛刻的。
3.2 iperf2:允许“追赶”的节奏(更像压测)
思路
  • 累计目标字节数为节拍参考:target_bytes(now) = B_target * elapsed
  • 如果当前已发送字节 sent_bytes < target_bytes(now),则立即、连续多次 sendto() 直到追上进度
  • 这样会出现突发补发(burst),让统计值更贴近 -b,但链路侧抖动与丢包风险更高
流程
  • 周期性计算“理论上应该发出的累积字节数”
  • 如果“欠账”,就循环 sendto() 补发到“应发进度”
  • 否则按节拍 sleep_until()
  • 统计以“发送成功的字节数/窗口时间”为准(更接近目标,但存在突发)
伪代码
// iperf2-style UDP pacing (pseudo)
init_clock();
uint64_t sent_bytes = 0;
const uint64_t pkt_bytes = L_payload;
const double   Bps = B_target / 8.0;while (running) {double elapsed = now_seconds_since_start();double should_send_bytes = Bps * elapsed;// 欠账就追赶:可能一次性 send 多包while (sent_bytes + pkt_bytes <= should_send_bytes) {int rc = sendto(sock, buf, L_payload, 0, ...);if (rc < 0) {if (errno == EAGAIN || errno == EWOULDBLOCK) break; // socket 滑窗/队列满// 其它错误按需处理break;}sent_bytes += rc;}// 适度小睡,避免忙等sleep_until(elapsed + small_delta);
}
效果与代价
  • 更容易“报表接近 -b”
  • 可能把欠账一次性灌入内核/NIC 队列 → 链路侧看到 burst、抖动、甚至丢包上升
3.3 iperf3:不追赶的“到点发一包”(更像测量)
思路
  • 下一次发送的绝对时间戳为节拍:next_send_time
  • 到点就发一包;如果因调度/阻塞落后了,不会补发欠账,而是顺延到新的节拍
  • 统计“实际成功发出的字节数/真实时间”,因此当系统跟不上节拍时,显示带宽会低于 -b
流程
  • 预先计算 Δt,设置 next_send_time = start + Δt
  • 若 now < next_send_time → 睡到点;到点后只发一包
  • 若 now >= next_send_time(落后)→ 不补发欠账,直接把 next_send_time 顺延(常见做法是 next_send_time = now + Δt 或者按步进递增,落后太多时会跳过若干节拍)
  • 统计“实际发出去的字节/时间窗口”,更接近真实可用能力
伪代码
// iperf3-style UDP pacing (pseudo)
struct timespec next = start_time + Δt;
uint64_t sent_bytes = 0;while (running) {struct timespec now = clock_gettime(CLOCK_MONOTONIC);if (now < next) {sleep_until(next);               // 到点再发}// 到点只发一包,不追赶int rc = sendto(sock, buf, L_payload, 0, ...);if (rc > 0) {sent_bytes += rc;} else {if (errno == EAGAIN || errno == EWOULDBLOCK) {// socket 满了,说明系统跟不上节拍,不补发,直接顺延} else {// 其它错误按需处理}}// 计算下一个节拍;若落后较多,常见做法是直接“与 now 对齐”:now = clock_gettime(CLOCK_MONOTONIC);if (now > next + Δt) {// 说明错过多个节拍,直接对齐,避免累积补发next = now + Δt;} else {next = next + Δt;}
}
// 统计:实际成功发出的字节 / 真实窗口时间(不会“强行贴合 -b”)
效果与代价
  • 更真实地反映“系统+内核+NIC”整体可用发包能力
  • 当节拍跟不上目标时,显示带宽显著低于 -b(这是特性,不是 bug)
3.4 行为差异:一个“欠账”场景
  • 设定 Δt = 11.76 μs,next_send_time = t0 + Δt
  • 某次调度抖动导致 now = t0 + 100 μs 才醒来
    • iperf2:计算“应发 N 包”,立即循环 sendto() 补发到应发进度(burst)
    • iperf3:只发一包,然后把 next_send_time 挪到 now + Δt(或按步进顺延),不补发落下的 N−1 包
3.5 时序图
4. 伪代码对比
// iperf2 (简化示意)
for (;;) {target += pkt_bytes;while (sent < target) sendto(pkt);  // 允许“追赶”补发(可能突发)sleep_until(next_time);next_time += Δt;
}// iperf3 (简化示意)
for (;;) {if (now < next_time) sleep_until(next_time);sendto(pkt);                         // 到点才发,不补发“欠账”next_time += Δt;                     // 落后就顺延,不追赶
}
// 统计= 实际成功发送字节 / 时间窗口(不强行贴合 -b)
5. UDP 模式调优 Checklist
  • 提高 socket buffer
iperf3 -u -b 1000M -l 1470 --sndbuf 1M
  • 绑定 CPU,减少调度抖动
taskset -c 1 iperf3 -u ...
  • 调大内核参数
sysctl -w net.core.wmem_max=4194304
sysctl -w net.core.rmem_max=4194304
  • 测试高带宽场景建议
    • 真实性能评估 → 用 iperf3
    • 极限压力测试 → 可对比 iperf2
6. 常见 FAQ
Q: iperf3 为什么我指定了 -b 1000M,结果只有 700Mbps?
  • 并不是链路坏了,而是 iperf3 不会补发落后的包。
  • 这正是它的“真实统计”特性,结果反映的是 你机器实际能发出的速率,而不是强行贴合 -b。
Q: 如何区分瓶颈在 CPU 还是 NIC?
  • CPU 瓶颈:单核 100% 占用,速率上不去。
  • NIC 瓶颈:CPU 占用不高,但速率稳定在某个物理极限(如千兆口 ~940Mbps)。
Q: 如果我只想“灌满链路”,该怎么做?
  • 可以用 iperf2,它会追赶补发,更适合做极限压力测试。
http://www.xdnf.cn/news/18713.html

相关文章:

  • 力扣(组合)
  • 人工智能时代下普遍基本收入(UBI)试验的实践与探索——以美国硅谷试点为例
  • LeetCode Hot 100 第二天
  • Java—— 配置文件Properties
  • 【Java SE】抽象类、接口与Object类
  • 秋招面试准备
  • 设计模式详解
  • TypeScript变量声明讲解
  • 个人思考与发展
  • 快速了解命令行界面(CLI)的行编辑模式
  • docker:compose
  • 【PSINS工具箱】MATLAB例程,平面上的组合导航,观测量为位置、速度、航向角,共5维。状态量为经典的15维
  • ModbusTCP与EtherNet/IP协议转换:工控机驱动步进电机完整教程
  • 智慧矿山误报率↓83%!陌讯多模态融合算法在矿用设备监控的落地优化
  • 安装即是已注册,永久可用!
  • Sql server的行转列
  • 数据结构:顺序表
  • C# 项目“交互式展厅管理客户端“针对的是“.NETFramework,Version=v4.8”,但此计算机上没有安装它。
  • 玳瑁的嵌入式日记D24-0823(数据结构)
  • 【基础-判断】使用http模块发起网络请求时,必须要使用on(‘headersReceive’)订阅请求头,请求才会成功。
  • 游戏广告投放数据分析项目:拆解投放的“流量密码”
  • 图像边缘检测
  • qwen2.5vl(2):lora 微调训练及代码讲解
  • Android Studio下载gradle文件很慢的捷径之路
  • 个人禁食伴侣FastTrack
  • 数据库类型与应用场景全解析:从传统关系型到新兴向量数据库
  • MySQL深分页的处理方案
  • React学习(十一)
  • 深入理解 React useEffect
  • 三、Bpmnjs 核心组件与架构介绍