让树莓派4 WireGuard 性能从 400Mbps 飞涨到 1Gbps
背景
由于学校机房依然需要网络认证,因此我采用了使用 WireGuard 从别处接入网络的方式。为了评估放在学校机房的软路由需要什么样的性能才足够跑满千兆大包 WireGuard ,上周末写了个 WireGuard Benchmark 脚本 来评估 CPU 以及 Kernel 网络栈处理 WireGuard 的性能。为了方便评估以取得更多的结果,我采用 network namspace 在本机创建 WireGuard 隧道,然后使用 iperf3 评估性能。以我实际使用的经验来看,至少 OpenWRT + MT7981 以及 OpenWRT + MT7986 的路由器上该方法评测的性能都与现实 WireGuard 下行到 LAN 的速率大差不差。 然而实际测试中发现,就连4核 A53@2GHz 的 MT7986 SoC 的路由器都能跑 818Mbps ,而4核 A72@1.8GHz 的树莓派4则只能跑 394Mbps 。
检查过程
这个结果显然不能让我信服,我怀疑是软件相关配置影响了性能。我首先检查了以下问题:
- chacha20和poly1305是否使用neon向量加速:是(通过lsmod观察到chacha20_neon等模块)
- 防火墙规则是否清空:是(nft list ruleset -a)
- perf record -a -g 是否发现执行时间异常的 Kernel 加密解密函数:否
在这些检查无果后,我无意中将树莓派重启到了基于 archlinux arm 的 PiKVM 系统,由于向下兼容原因该系统迄今依然采用 armv7 的内核和用户态软件。结果测试发现,使用该发行版自带的6.1内核速率可以大幅提升至 665Mbps,甚至这只是32位的 armv7 跑出来的结果。
基于上述的发现我大致推测,问题应该出现在内核的相关选项上。然后我就想,既然 4xA53@2GHz 的路由器是使用 OpenWRT 23.05 达成的,要不试试看对应的发行版的树莓派镜像。因为通常而言相同的发行版会采用类似的内核配置以及一些sysctl优化选项。
事实证明我的猜想是对的,在树莓派上运行 OpenWRT 23.05 后,测试直接达到了 1.1Gbps,相比 Raspbian 提升了接近3倍。
那么,问题出在哪呢?于是我将 OpenWRT 使用的内核 .config 1 2 移花接木到树莓派官方的内核 fork 上,我们还需要打开 CONFIG_DEVTMPFS
以保证 rootfs 能使用 /dev 路径的方式找到,同时打开 CONFIG_AUDIT
保证 systemd 能够启动。结果发现,使用 OpenWRT 内核参数的 Raspbian 依然能轻松跑到 1.1Gbps的带宽。由此,我们就排除了编译内核使用的编译器等差异带来的问题,问题只出在内核的 .config 中了。
那么接下来就是愉快的二分查找了,不过这里大可以跳过内核中驱动的部分,我排除了很多问题,包括但不限于 OpenWRT 没有打开的 CPU Erratum 配置,最终发现和两个配置有关:
- CONFIG_PREEMPT_NONE
- CONFIG_IRQSOFF_TRACER
CONFIG_PREEMPT_NONE
其中, CONFIG_PREEMPT_NONE
的内核 Kconfig 帮助信息如下:
This is the traditional Linux preemption model, geared towards
throughput. It will still provide good latencies most of the
time, but there are no guarantees and occasional longer delays
are possible.
我们可以看出,它不允许内核执行中采用抢占调度,在桌面系统或者实时性要求高的场景下可能会带来更高的延迟,但有助于提高吞吐量,该选项被 OpenWRT 默认使用。
然而, Raspbian 配置的 CONFIG_PREEMPT
却是:
This option reduces the latency of the kernel by making
all kernel code (that is not executing in a critical section)
preemptible. This allows reaction to interactive events by
permitting a low priority process to be preempted involuntarily
even if it is in kernel mode executing a system call and would
otherwise not be about to reach a natural preemption point.
This allows applications to run more 'smoothly' even when the
system is under load, at the cost of slightly lower throughput
and a slight runtime overhead to kernel code.
这样的选项虽然有助于降低内核任务的延迟,然而由于抢占调度会导致上下文切换次数增加,导致由调度导致更高的性能开销。
仅仅将 Raspbian 的内核使用的 bcm2711_defconfig
基础上增加 CONFIG_PREEMPT_NONE=y
后,速度就能提升到 669Mbps 。
CONFIG_IRQSOFF_TRACER
在经过一段时间的测试 Kernel 的 General Setup 以及 General architecture-dependent options 以及 Memory Management options 无果后,我决定浏览一下 Kernel Hacking 中的调试选项,我开始怀疑是内核中的各种调试相关选项产生了性能开销。
结果我发现,只要把内核的 CONFIG_TRACE
关闭, WireGuard 就能跑上 1.1Gbps ,由于这里的 Trace 选项很多,我开始继续拿开启的选项的列表开始二分,最终果然找到了问题的根源 CONFIG_IRQSOFF_TRACER
。
内核的帮助信息如下:
This option measures the time spent in irqs-off critical
sections, with microsecond accuracy.
这个选项显然是用于内核的 Debug 的用途,然而如果所使用的内核模块大量使用了关中断的行为,这样一种 Trace 会带来开销。
在调研了手边能 ssh 的机器,分别使用 Debian 和 archlinux ,其自带的内核均关闭了该选项,我决定提交一个 PR 给树莓派 Kernel 在默认配置中关闭该选项,于是就有了:
以及,我的 Debug 中间结果也记录在了这里:
树莓派5
soha 为我提供了一个树莓派5的 SSH ,我也修改了 Kernel 相关选项进行测试,结果发现,树莓派5上的 WireGuard 性能可以从 1.13Gbps 提升到 3.08Gbps,比例相似。
BTW, RISC-V 呢?
本人有个习惯,遇到任何软硬件问题都会测试一下 RISC-V 是否存在类似问题。于是我测试了价格比较合理的 VisionFive 2 (JH7110) 和 Sipeed Lichee Pi 4A (TH1520)。好在,我给两款开发板使用的 defconfig 均基于主线的 RISC-V defconfig,默认就关闭了所有 Trace ,并对 Kernel 的调度使用了 CONFIG_PREEMPT_NONE
。因此,我不需要修改内核 config 就可以避免树莓派上遇到问题。
然而,由于内核里还没有任何针对 RISC-V 指令集优化实现,不像 aarch64 上已经有 neon 的 chacha20 和 poly1305 实现,导致两款 RISC-V 开发板的 WireGuard 性能都不佳,均在 400Mbps 左右,某种意义上来说,它确实接近了树莓派4目前官方内核的水平。好在目前 RVV 1.0 的 CPU 核已经有可购买的硬件了,相信会有开源开发者去改善(万一是你呢)。
相关的结果排名也可以在这里获取:https://github.com/cyyself/wg-bench
具體是要怎樣操作呢?謝謝
我的 PR 已经 Merge 到了树莓派官方内核的仓库,所以现在什么都不用操作了,升级到最新内核即可。