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

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.dtcms.com/a/347168.html

相关文章:

  • [新启航]白光干涉仪与激光干涉仪的区别及应用解析
  • ubuntu 新登录修改root密码
  • 【攻防世界】Web_php_include
  • 力扣热题之动态规划
  • CryptSIPVerifyIndirectData函数分析
  • 鸿蒙开发进阶(HarmonyOS)
  • STM32 外设驱动模块八:红外反射式光电模块
  • 【大语言模型 15】因果掩码与注意力掩码实现:深度学习中的信息流控制艺术
  • 2-5.Python 编码基础 - 键盘输入
  • 2025钉钉十周年新品发布会,新品 “蕨”命名,到底是什么?
  • vue3 - 组件间的传值
  • nodejs和vue安装步骤记录
  • 【Golang】有关任务窃取调度器和抢占式调度器的笔记
  • 机器人 - 无人机基础(5) - 飞控中的传感器(ing)
  • 【大语言模型 16】Transformer三种架构深度对比:选择最适合你的模型架构
  • 云原生俱乐部-k8s知识点归纳(8)
  • 资深产品经理个人能力提升方向:如何系统化进阶并考取高价值证书?
  • 资深产品经理个人能力提升方向:如何系统化进阶与考证规划
  • 可视化-模块1-HTML-02
  • Node.js特训专栏-实战进阶:23. CI/CD流程搭建
  • 国产轻量级桌面GIS软件Snaplayers从入门到精通(21)
  • 复杂街景误检率↓79%!陌讯动态融合算法在街道垃圾识别的边缘计算优化​​
  • Text2SQL、ChatBI简介
  • AI agent开发与大模型工程师面试复习纲要与高频面试题答案(4)-- AI agent系统设计与项目实践
  • Rust系统编程实战:驾驭内存安全、无畏并发与WASM跨平台开发
  • Go语言实战案例-Redis连接与字符串操作
  • python 字典有序性的实现和OrderedDict
  • 字节跳动开源Seed-OSS:36B参数模型以512K上下文与可控思考预算重新定义AI实用主义
  • Linux:进程间通信(IPC)-SystemV
  • MiMo-VL 技术报告