Skip to content

性能真相与本实验的局限

全书从第 0 章就在警告:这套环境不能用来测性能。 最后一篇把这句话讲透 —— 为什么不能,以及如果真要测该怎么测。

顺便,这一篇里所有数字都是在本环境实测的。它们衡量的不是"VPP 能跑多快", 而是不同做法之间的相对差异,那个是有意义的。

先看一个能说明问题的数字

bash
vppctl clear runtime
# 空闲 2 秒
vppctl show runtime
text
             Name           State        Calls      Vectors   Packet-Clocks  Vectors/Call
af-packet-input           polling       8565925           0         1.87e2          0.00

两秒内调用了 856 万次,处理了 0 个包。

这就是 polling 模式:CPU 不停地问网卡"有包吗",没包也在问。 一个核就这么烧掉了。第 0 章说"polling 会吃满一个 CPU 核", 这行输出是它的量化形式。

Vectors/Call:VPP 性能的核心指标

回顾第 0 章讲的向量处理:VPP 的性能优势来自一批包共享一次代码加载。 这个"一批"有多大,就是 Vectors/Call

text
  Vectors/Call = 1     每次只处理一个包,完全没有批处理收益
  Vectors/Call = 256   满载,i-cache 加载成本被摊薄到极致

实测本环境在约 10 万 pps 下:

text
             Name           State      Calls      Vectors   Packet-Clocks  Vectors/Call
ip4-input                 active       36699      254342         3.62e2         6.93
nat44-ed-in2out           active       36531      200006         6.15e2         5.47

只有 5~7。 离 256 差得很远。

原因就在 AF_PACKET:它是 Linux 的通用抓包接口,逐帧交付 (我们还特意选了 TPACKET v2 来降时延,见第 0 章), 一次 poll 拿不到多少包,自然攒不出大批次。

真实部署用 DPDK 或 AF_XDP 时,一次可以从网卡队列里批量取走上百个包, Vectors/Call 能上到几十甚至逼近 256。这是本环境和生产最本质的性能差距。

这个指标怎么用

调优 VPP 时,先看 Vectors/Call

text
  持续接近 1     → 收包路径有问题,或者流量本来就稀疏
  上升到几十     → 批处理在起作用,性能正常
  接近 256       → 满载,该考虑加 worker 了

它比 pps 更能说明"VPP 有没有在好好干活"。

快路径 vs 慢路径:47 倍的差距

第 1 章说过慢路径比快路径贵得多。具体贵多少, show runtimePacket-Clocks 列给了答案。

实验 A:同一条流打 20 万包(全部命中会话,走快路径)

text
  vector rates in 1.0608e5
ip4-input                 36699     254342     3.62e2      6.93
nat44-ed-in2out           36531     200006     6.15e2      5.47

实验 B:2 万条新流(每个包都新建会话,全部走慢路径)

text
  vector rates in 2.9189e4
nat44-ed-in2out           15052      20001     1.42e3      1.33
nat44-ed-in2out-slowpath  15051      20000     7.03e3      1.33

并排看:

text
  ┌────────────────┬──────────────────┬──────────────────┐
  │                │ 已建立的流         │ 每包新建会话       │
  ├────────────────┼──────────────────┼──────────────────┤
  │ 吞吐            │ 106,080 pps      │ 29,189 pps       │
  │ in2out 时钟     │ 615 clocks/包     │ 1,420 clocks/包   │
  │ slowpath 时钟   │ —                │ 7,030 clocks/包   │
  │ 单包总成本       │ 615              │ 8,450            │
  │ Vectors/Call   │ 5.47             │ 1.33             │
  └────────────────┴──────────────────┴──────────────────┘

  新建会话比转发已有会话贵 8450 / 615 ≈ 14 倍
  实际吞吐掉到 27%

评估 NAT 设备要看 CPS,不是 bps

这组数据解释了一个反直觉的事实:NAT 设备的瓶颈通常不是带宽,是新建会话速率。

text
  一条视频流:几百 Mbps,但只有一条会话      → NAT 几乎没有负担
  一万个用户刷网页:带宽不大,但每秒新建几万条会话 → NAT 压力巨大

所以选型和压测时,CPS(Connections Per Second)比吞吐更关键。 只测 iperf 打满带宽,测不出 NAT 的真实瓶颈。

Vectors/Call 在慢路径场景下掉到 1.33 也印证了这一点 —— 建会话是串行化的工作,批处理帮不上忙。

polling vs interrupt:4 倍时延差

本实验底座默认开 polling,代价是吃满一个核。这个代价换来了什么?

bash
vppctl set interface rx-mode host-eth1 polling      # 或 interrupt
# ping 30 次
text
  ┌────────────┬──────────────────────────────┬──────────────┐
  │ 收包模式     │ RTT min/avg/max (ms)         │ CPU          │
  ├────────────┼──────────────────────────────┼──────────────┤
  │ polling    │ 0.077 / 0.088 / 0.193        │ 吃满一个核     │
  │ interrupt  │ 0.330 / 0.424 / 0.597        │ 空闲时几乎为零  │
  └────────────┴──────────────────────────────┴──────────────┘

  平均时延差 4.8 倍

adaptive 模式在 af_packet 上不可用

bash
vppctl set interface rx-mode host-eth1 adaptive
text
set interface rx-mode: unable to set rx-mode on interface host-eth1 queue-id 4294967295.

adaptive(低负载中断、高负载轮询)在支持的驱动上是很好的折中, 但 AF_PACKET 不支持。

这也解释了第 3 章 HA 底座为什么刻意不开 polling —— 两台 VPP 各吃一个核,4 核的实验机扛不住,而 HA 实验不看时延。

为什么这些数字不能当生产参考

三层损耗叠在一起:

text
  ┌──────────────────┬────────────────────────┬──────────────────────────┐
  │ 环节              │ 本实验                  │ 生产部署                  │
  ├──────────────────┼────────────────────────┼──────────────────────────┤
  │ 收包路径          │ AF_PACKET(内核拷贝)    │ DPDK / AF_XDP(零拷贝)    │
  │ 批处理效率        │ Vectors/Call ≈ 5~7     │ 可达几十到 256            │
  │ 链路              │ veth + bridge          │ 物理网卡                  │
  │ 内核参与          │ 每个包都过内核           │ 绕过内核                  │
  │ worker 线程       │ 1(vpp_main)          │ 多个,绑核,NUMA 亲和      │
  │ CPU              │ 与其他容器共享 4 核       │ 独占核,隔离中断           │
  └──────────────────┴────────────────────────┴──────────────────────────┘

差距不是百分之几十,是数量级。本环境跑 10 万 pps, 同样的 VPP 配 DPDK 和多 worker 在物理机上跑到千万 pps 级别是常态。

千万别拿本教程的数字去做容量规划

本环境的数字只有一个用途:比较不同配置之间的相对差异

text
  ✓ 有意义    慢路径比快路径贵 14 倍
  ✓ 有意义    polling 比 interrupt 时延低 4.8 倍
  ✓ 有意义    开 affinity 后负载均衡分布从 9:11 变成 20:0

  ✗ 无意义    "VPP NAT 能跑 10 万 pps"
  ✗ 无意义    "NAT 转发时延 0.088ms"

真要测性能该怎么做

环境

text
  · 物理机,不要虚拟化嵌套
  · 独占 CPU 核,isolcpus + VPP 的 corelist-workers 绑定
  · 关闭超线程,或者至少不要把 worker 和它的 HT 兄弟核一起用
  · NUMA 亲和:网卡、内存、worker 在同一个 NUMA 节点
  · DPDK 或 AF_XDP,不要 AF_PACKET

打流工具

text
  · TRex(Cisco 开源)—— 事实标准,能精确控制 CPS 和流数量
  · DPDK-pktgen
  · 商业测试仪(IXIA / Spirent)

  不要用 iperf / netperf 测 NAT —— 它们建立少量长连接,
  测出来的是快路径吞吐,完全掩盖了 CPS 瓶颈。

必须分开测的两个指标

text
  ① 吞吐(已建立会话)
     少量长流打满带宽,看 pps 和 bps
     → 衡量快路径效率

  ② 新建会话速率(CPS)
     大量短流,每条只有几个包
     → 衡量慢路径效率,这通常才是真瓶颈

  两者的结果可能差一个数量级,只报一个是不完整的。

要一起记录的辅助数据

bash
vppctl show runtime          # Vectors/Call 和 Packet-Clocks
vppctl show nat44 summary    # 会话数水位
vppctl show errors           # 有没有丢包

压测时 Vectors/Call 应该上去

如果压到很高 pps 但 Vectors/Call 还是个位数,说明瓶颈不在 VPP, 而在收包路径或者打流端。这时候增加 worker 也没用。

本环境能可靠测量的东西

说了这么多不能测的,也说说能测的 —— 这些都是行为而非性能, 不受环境损耗影响:

text
  ✓ 功能正确性        地址改成了什么、会话怎么建的
  ✓ 相对差异          A 配置 vs B 配置,谁快谁慢
  ✓ 状态开销的量级     慢路径贵多少倍
  ✓ 错误行为          什么条件下丢包、丢在哪个节点
  ✓ 配额与限制        端口块大小、会话上限、每用户配额
  ✓ 插件交互          谁和谁冲突、怎么冲突
  ✓ 弱网下的行为       用 nsim 注入时延和丢包(见下一篇)

整本教程做的都是这类观察。这也是为什么所有实验的自检脚本 断言的都是现象("源地址变成了 X"、"计数器出现了 Y"), 而不是数值("时延小于 1ms")—— 后者换台机器就不成立了。

小结

核心指标Vectors/Call,本环境 5~7,生产可达几十到 256
慢路径成本8450 clocks vs 615,约 14 倍;吞吐掉到 27%
选型要看CPS 而不是 bps,NAT 瓶颈通常在新建会话
polling 代价时延低 4.8 倍,代价是吃满一个核
本环境瓶颈AF_PACKET + veth + 共享 CPU,比生产差数量级
能可靠测的功能、相对差异、状态开销量级、错误行为
不能测的任何绝对性能数字

还有一件本环境能做的事

上面说的都是"不能测性能"。但有一类实验它其实能做得很好: 注入可控的网络损伤,观察时延和丢包对应用的影响。

下一篇nsim 插件把链路变差, 测出"2% 丢包让 TCP 吞吐掉 96%"这类结论 —— 并且据此重新审视本篇的性能数字。


全书到此结束

七章走下来,从"一个包被改了哪几个字节"到"运营商级 NAT 怎么做溯源":

text
  第 0 章  上手           为什么 VPP · 搭环境 · 第一个 NAT
  第 1 章  原理           改了什么 · 会话表 · EI vs ED · 端口与老化
  第 2 章  NAT44-ED       伪装 · 地址池 · 静态映射 · identity · LB · twice-NAT · VRF · 超时
  第 3 章  NAT44-EI       行为画像 · 会话高可用 · 选型
  第 4 章  CGN            DET44 算法映射 · 可追溯性
  第 5 章  IPv6 过渡      NAT64 · NAT66 · 464XLAT
  第 6 章  其他翻译器      PNAT · CNAT
  第 7 章  排错与性能      读 trace · 故障速查 · 性能真相 · 弱网模拟

四个实验底座、十四个可复现的实验步骤, 文档里贴的每一段命令输出都是从这套环境里真实抓下来的。

如果只带走三件事,我希望是这三件:

一、会话表是理解一切的钥匙。 会话键里有什么字段,决定了 EI/ED 的全部差异、 决定了端口能不能复用、决定了 P2P 能不能打洞、决定了删会话要不要带 external-host

二、show trace 值得成为肌肉记忆。 它能回答"这个包经历了什么"这个问题, 而这个问题的答案往往就是故障的答案。内核转发给不了你这份逐跳清单。

三、计数器干净不等于没问题。 七章里遇到的最难查的五个故障, 共同点都是 NAT 计数器一片干净 —— 因为问题不在 NAT 逻辑, 而在路由、邻居解析或插件残留上。遇到这种情况, 第一反应应该是"把注意力从 VPP 移开"。

想继续深入

本教程覆盖的是 VPP NAT 的使用。再往下走的方向:

  • VPP API —— CLI 只适合调试,生产用 govpp / vppapi 编程配置
  • 多 worker 调优 —— handoff、RSS、会话在 worker 间的分布
  • 写插件 —— VPP 的 graph node 模型对自定义转发逻辑非常友好
  • Calico/VPP —— 看 cnat 在真实控制面驱动下怎么工作

实验环境基于 netlab + containerlab + VPP 26.06