ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

Swoole 在 ARM64 上的协程调度性能实测与调优 第 3 章 Swoole 在 ARM64 上的协程调度性能实测与调优3.1 为什么要单独测 ARM64 上的 Swoole大白话Swoole 的协程调度器底层用了boost.context一个汇编写的上下文切换库x86 和 ARM 的实现完全不同。在 ARM64 上有三个特有问题协程切换开销更大ARM 的寄存器保存/恢复比 x86 多内存屏障指令更频繁ARM 是弱内存序需要显式dmb指令同步某些版本的 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|grepc-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.gzcdswoole-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-caresmakecleanmake-j$(nproc)sudomakeinstall# 3. 调整内核参数协程数上万时必需sudotee-a/etc/sysctl.conf/dev/nullEOF # 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-p3.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(){$count100000;// 协程数$rounds10;// 每个协程切换次数echo创建{$count}个协程每个切换{$rounds}次...\n;$startmicrotime(true);for($i0;$i$count;$i){Coroutine::create(function()use($rounds){for($j0;$j$rounds;$j){$sum0;for($k0;$k100;$k){$sum$k;// 简单计算}Coroutine::sleep(0);// 主动让出 CPU触发切换}});}$usedmicrotime(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;$servernewServer(0.0.0.0,9501,SWOOLE_PROCESS);$server-set([worker_numswoole_cpu_num()*2,// worker 数 CPU 核心数 * 2reactor_numswoole_cpu_num(),// reactor 数 CPU 核心数max_coroutine100000,// 单个 worker 最大协程数enable_coroutinetrue,max_request0,// worker 不自动重启open_tcp_nodelaytrue,open_cpu_affinitytrue,// ARM 上开启 CPU 亲和性减少缓存失效log_levelSWOOLE_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([messageHello World,timetime()]));return;}// 场景 2模拟数据库查询测协程 I/O 调度if($request-server[request_uri]/db){go(function()use($response){// 模拟异步查询延迟 5msSwoole\Coroutine::sleep(0.005);$data[id1,nametest,created_atdate(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[];$wgnewSwoole\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][statusok,datarand(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|grep95013.5 压测命令与指标解读使用wrk在 ARM64 上编译git clone https://github.com/wg/wrk cd wrk make# 场景 1纯内存测峰值 QPSwrk-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 99thP99 延迟应接近 5ms若大于 20ms 说明调度卡顿# 场景 3并发聚合测多协程协作wrk-t4-c1000-d30s--latencyhttp://127.0.0.1:9501/aggregate# 关注Non-2xx responses应为 0Latency 99th应接近 10msARM64 上的典型结果鲲鹏 920 8 核场景QPSP50 延迟P99 延迟CPU 使用率/hello纯计算~15 万0.8ms2.1ms95%/db模拟 I/O~8 万6ms12ms60%/aggregate并发~5 万12ms25ms70%如果你的结果远低于上表排查点CPU 亲和性未开启open_cpu_affinity false会导致进程在核心间频繁迁移ARM 的 L1 缓存失效率高reactor_num 设太大ARM 核心间通信比 x86 慢reactor 不是越多越好 CPU 核心数即可内存频率低部分飞腾平台用的 DDR4-2400而鲲鹏能上 DDR4-3200内存带宽差距会影响协程切换时的栈拷贝速度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-marcharmv8-acrc./configure --with-php-config/usr/local/php8/bin/php-config\--enable-openssl --enable-sockets --enable-mysqlnd --enable-caresmakecleanmake-j$(nproc)sudomakeinstall调优 2调整协程栈大小默认 2MBARM 上可降至 256KB// 在 Server-set() 里加上coroutine[stack_size256*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--cpunodebind0--membind0/usr/local/php8/bin/php swoole-http-server.php也可以在 systemd service 里固定[Service] ExecStart/usr/bin/numactl --cpunodebind0 --membind0 /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 reboot3.7 监控脚本实时看协程调度状态sudotee/usr/local/bin/swoole-monitor.sh/dev/nullEOF #!/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 接口 coroN/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 EOFsudochmodx /usr/local/bin/swoole-monitor.sh# 使用swoole-monitor.sh更精准的协程数监控在 Swoole Server 里加个/stats路由if($request-server[request_uri]/stats){$statsSwoole\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获取实时协程数。
返回列表