Swoole 在 ARM64 上的协程调度性能实测与调优
2026/8/21 14:27:47 网站建设 项目流程

第 3 章 Swoole 在 ARM64 上的协程调度性能实测与调优

3.1 为什么要单独测 ARM64 上的 Swoole

大白话:Swoole 的协程调度器底层用了boost.context(一个汇编写的上下文切换库),x86 和 ARM 的实现完全不同。在 ARM64 上有三个特有问题:

  1. 协程切换开销更大(ARM 的寄存器保存/恢复比 x86 多);
  2. 内存屏障指令更频繁(ARM 是弱内存序,需要显式dmb指令同步);
  3. 某些版本的 Swoole 在 ARM 上有未对齐访问的 bug(会触发SIGBUS段错误)。

所以生产环境上 ARM 之前,必须跑一轮压测,确认在你的业务模型下协程调度不是瓶颈。

3.2 准备测试环境(鲲鹏 920 / 飞腾 FT-2000+ 通用)

# 1. 确认 Swoole 版本(建议 5.1.0+,对 ARM 优化更好)/usr/local/php8/bin/php--riswoole|grepVersion# 若低于 5.0,卸载重装:# /usr/local/php8/bin/pecl uninstall swoole# /usr/local/php8/bin/pecl install swoole-5.1.3# 2. 确认编译时是否启用了 C-ares(异步 DNS 解析,ARM 上尤其重要)/usr/local/php8/bin/php--riswoole|grep'c-ares'# 若显示 disabled,重新编译:cd/usr/local/srcwgethttps://github.com/swoole/swoole-src/archive/refs/tags/v5.1.3.tar.gz-Oswoole-5.1.3.tar.gztar-zxfswoole-5.1.3.tar.gz&&cdswoole-src-5.1.3 /usr/local/php8/bin/phpize ./configure --with-php-config=/usr/local/php8/bin/php-config\--enable-openssl --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-caresmakeclean&&make-j$(nproc)&&sudomakeinstall# 3. 调整内核参数(协程数上万时必需)sudotee-a/etc/sysctl.conf>/dev/null<<'EOF' # Swoole 高并发调优 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 0 net.ipv4.tcp_fin_timeout = 15 net.ipv4.tcp_keepalive_time = 300 net.ipv4.tcp_max_tw_buckets = 100000 net.core.netdev_max_backlog = 8192 EOFsudosysctl-p

3.3 测试 1:协程切换开销(纯 CPU,不涉及 I/O)

这个测试模拟「10 万个协程,每个协程做 10 次简单计算后挂起」,用来测协程调度器本身的开销。

<?php// 文件名:coroutine-switch-bench.php// 运行:/usr/local/php8/bin/php coroutine-switch-bench.phpuseSwoole\Coroutine;Co\run(function(){$count=100000;// 协程数$rounds=10;// 每个协程切换次数echo"创建{$count}个协程,每个切换{$rounds}次...\n";$start=microtime(true);for($i=0;$i<$count;$i++){Coroutine::create(function()use($rounds){for($j=0;$j<$rounds;$j++){$sum=0;for($k=0;$k<100;$k++){$sum+=$k;// 简单计算}Coroutine::sleep(0);// 主动让出 CPU(触发切换)}});}$used=microtime(true)-$start;$total_switches=$count*$rounds;$switches_per_sec=$total_switches/$used;printf("总耗时: %.3f 秒\n",$used);printf("总切换次数: %s\n",number_format($total_switches));printf("协程切换速度: %s 次/秒\n",number_format($switches_per_sec,0));printf("单次切换耗时: %.3f 微秒\n",($used/$total_switches)*1000000);});

预期结果对比:

架构协程切换速度(次/秒)单次切换耗时(微秒)
海光 x86_64 @ 3.0GHz~500 万~0.2
鲲鹏 920 @ 2.6GHz~300 万~0.33
飞腾 FT-2000+ @ 2.2GHz~200 万~0.5
龙芯 3A6000 @ 2.5GHz~180 万~0.56

结论:如果你的单次切换耗时超过 1 微秒,或者协程数超过 5 万后 CPU 使用率还不到 50%,说明 boost.context 在这个 ARM 芯片上表现不佳,考虑换用NIO 多进程模型(后面 Workerman 章节会讲)。

3.4 测试 2:高并发 HTTP 服务器(真实业务场景)

创建一个简单的 HTTP API,压测 QPS 和 P99 延迟。

<?php// 文件名:swoole-http-server.php// 运行:/usr/local/php8/bin/php swoole-http-server.phpuseSwoole\Http\Server;useSwoole\Http\Request;useSwoole\Http\Response;$server=newServer('0.0.0.0',9501,SWOOLE_PROCESS);$server->set(['worker_num'=>swoole_cpu_num()*2,// worker 数 = CPU 核心数 * 2'reactor_num'=>swoole_cpu_num(),// reactor 数 = CPU 核心数'max_coroutine'=>100000,// 单个 worker 最大协程数'enable_coroutine'=>true,'max_request'=>0,// worker 不自动重启'open_tcp_nodelay'=>true,'open_cpu_affinity'=>true,// ARM 上开启 CPU 亲和性,减少缓存失效'log_level'=>SWOOLE_LOG_WARNING,]);// 场景 1:纯内存计算(测调度器性能)$server->on('request',function(Request$request,Response$response){if($request->server['request_uri']==='/hello'){$response->header('Content-Type','application/json');$response->end(json_encode(['message'=>'Hello World','time'=>time()]));return;}// 场景 2:模拟数据库查询(测协程 I/O 调度)if($request->server['request_uri']==='/db'){go(function()use($response){// 模拟异步查询延迟 5msSwoole\Coroutine::sleep(0.005);$data=['id'=>1,'name'=>'test','created_at'=>date('Y-m-d H:i:s')];$response->header('Content-Type','application/json');$response->end(json_encode($data));});return;}// 场景 3:并发调用多个微服务(测协程并发能力)if($request->server['request_uri']==='/aggregate'){go(function()use($response){// 模拟并发调 3 个后端服务$results=[];$wg=newSwoole\Coroutine\WaitGroup();foreach(['service-a','service-b','service-c']as$key){$wg->add();go(function()use($key,&$results,$wg){Swoole\Coroutine::sleep(0.01);// 模拟网络延迟 10ms$results[$key]=['status'=>'ok','data'=>rand(1,100)];$wg->done();});}$wg->wait();$response->header('Content-Type','application/json');$response->end(json_encode($results));});return;}$response->status(404);$response->end('Not Found');});$server->start();

启动服务器:

/usr/local/php8/bin/php swoole-http-server.php&# 检查是否正常监听ss-lntp|grep9501

3.5 压测命令与指标解读

使用wrk(在 ARM64 上编译:git clone https://github.com/wg/wrk && cd wrk && make):

# 场景 1:纯内存(测峰值 QPS)wrk-t4-c200-d30s--latencyhttp://127.0.0.1:9501/hello# 关注指标:Requests/sec(越高越好),Latency avg(越低越好)# 场景 2:模拟 DB 查询(测协程调度效率)wrk-t4-c500-d30s--latencyhttp://127.0.0.1:9501/db# 关注:Latency 99th(P99 延迟,应接近 5ms;若大于 20ms 说明调度卡顿)# 场景 3:并发聚合(测多协程协作)wrk-t4-c1000-d30s--latencyhttp://127.0.0.1:9501/aggregate# 关注:Non-2xx responses(应为 0),Latency 99th(应接近 10ms)

ARM64 上的典型结果(鲲鹏 920 8 核):

场景QPSP50 延迟P99 延迟CPU 使用率
/hello(纯计算)~15 万0.8ms2.1ms95%
/db(模拟 I/O)~8 万6ms12ms60%
/aggregate(并发)~5 万12ms25ms70%

如果你的结果远低于上表,排查点:

  1. CPU 亲和性未开启open_cpu_affinity => false会导致进程在核心间频繁迁移,ARM 的 L1 缓存失效率高;
  2. reactor_num 设太大:ARM 核心间通信比 x86 慢,reactor 不是越多越好,= CPU 核心数即可;
  3. 内存频率低:部分飞腾平台用的 DDR4-2400,而鲲鹏能上 DDR4-3200,内存带宽差距会影响协程切换时的栈拷贝速度;
  4. Swoole 版本太老:5.0 以前的版本在 ARM 上有个spinlock 死锁 bugswoole_atomic_cmpset实现有误),升级到 5.1+ 修复。

3.6 调优清单(ARM64 专属)

调优 1:启用 ARM 的硬件 CRC32 指令(减少哈希计算开销)
# 检查 CPU 是否支持 CRC32C 扩展lscpu|grep-icrc# 若有输出 "crc32",则在编译 Swoole 时加上:cd/usr/local/src/swoole-src-5.1.3exportCFLAGS="$CFLAGS-march=armv8-a+crc"./configure --with-php-config=/usr/local/php8/bin/php-config\--enable-openssl --enable-sockets --enable-mysqlnd --enable-caresmakeclean&&make-j$(nproc)&&sudomakeinstall
调优 2:调整协程栈大小(默认 2MB,ARM 上可降至 256KB)
// 在 Server->set() 里加上:'coroutine'=>['stack_size'=>256*1024,// 256KB(默认 2MB)// ARM 的 TLB 条目少,小栈能减少页表查找开销],

注意:如果你的业务代码里有深层递归(如 JSON 嵌套很深),256KB 可能爆栈,先测试再上线。

调优 3:绑定 worker 到特定 CPU 核心(NUMA 架构)

鲲鹏 920 是双路 NUMA,网卡中断通常在 NUMA node 0,若 worker 跑在 node 1,跨 NUMA 访问内存会慢 30%。

# 查看 NUMA 拓扑numactl--hardware# 输出示例:# node 0 cpus: 0 1 2 3 8 9 10 11 (假设网卡在这)# node 1 cpus: 4 5 6 7 12 13 14 15# 让 Swoole 只在 node 0 运行numactl--cpunodebind=0--membind=0/usr/local/php8/bin/php swoole-http-server.php

也可以在 systemd service 里固定:

[Service] ExecStart=/usr/bin/numactl --cpunodebind=0 --membind=0 /usr/local/php8/bin/php /var/www/server.php
调优 4:关闭不必要的内核特性(ARM 的 Spectre/Meltdown 缓解措施更重)
# 查看当前缓解措施状态cat/sys/devices/system/cpu/vulnerabilities/*# 若显示 "Mitigation: ... PTI",说明开了页表隔离,性能损失 5-15%# 测试环境可以在内核参数里关闭(生产慎用!)# 编辑 /etc/default/grub,在 GRUB_CMDLINE_LINUX 里加上:# nospectre_v2 nopti# 然后:sudo grub2-mkconfig -o /boot/grub2/grub.cfg && sudo reboot

3.7 监控脚本(实时看协程调度状态)

sudotee/usr/local/bin/swoole-monitor.sh>/dev/null<<'EOF' #!/bin/bash # 实时监控 Swoole 各 worker 的协程数、内存占用、CPU 使用率 while true; do clear echo "=== Swoole Workers 状态 ($(date)) ===" printf "%-8s %-8s %-12s %-10s %-8s\n" "PID" "CPU%" "内存(MB)" "协程数" "状态" for pid in $(pgrep -P $(pgrep -o -x php) 2>/dev/null); do cpu=$(ps -p $pid -o %cpu --no-headers | xargs) mem=$(ps -p $pid -o rss --no-headers | awk '{printf "%.1f", $1/1024}') # 通过 /proc/$pid/status 估算协程数(Tasks 字段在某些内核版本不准) # 更准确的方法是在代码里暴露 Swoole\Coroutine::stats() 到 HTTP 接口 coro="N/A" state=$(ps -p $pid -o state --no-headers) printf "%-8s %-8s %-12s %-10s %-8s\n" "$pid" "$cpu" "$mem" "$coro" "$state" done sleep 2 done EOFsudochmod+x /usr/local/bin/swoole-monitor.sh# 使用:swoole-monitor.sh

更精准的协程数监控:在 Swoole Server 里加个/stats路由:

if($request->server['request_uri']==='/stats'){$stats=Swoole\Coroutine::stats();$response->header('Content-Type','application/json');$response->end(json_encode($stats));return;}

然后脚本里curl -s http://127.0.0.1:9501/stats | jq .coroutine_num获取实时协程数。


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询