Skip to content

lua-yar 性能压测报告

测试时间:2026-07-24 压测脚本:test/benchmark_matrix.lua / test/benchmark_cosocket.lua / test/benchmark_cext.lua 测试样本:{ method = "add", params = { 1, 2 }, provider = "p", token = "t" }


〇、压测环境与配置

0.1 硬件

项目 规格
CPU Apple M4 (10 核, arm64)
内存 32 GB
OS macOS 26.5.1 (Build 25F80)

0.2 运行时

运行时 版本 安装方式
Lua 5.1 5.1.5 luaver
Lua 5.3 5.3.6 luaver
Lua 5.5 5.5.0 Homebrew (/opt/homebrew/bin/lua)
LuaJIT 2.1.1784360928 Homebrew (/opt/homebrew/bin/luajit)
OpenResty 1.31.1.1 (nginx + LuaJIT, clang 17.0.0) Homebrew (/opt/homebrew/opt/openresty)

0.3 C 扩展与依赖

扩展 版本 安装路径 说明
lua-cjson 2.1.0.10-1 /opt/homebrew/lib/luarocks/rocks-5.5 JSON 编解码 C 扩展
lua-cmsgpack 0.4.0-0 /opt/homebrew/lib/luarocks/rocks-5.5 MessagePack 编解码 C 扩展
luasocket 3.1.0-1 (LuaSocket 3.0.0) /opt/homebrew/lib/luarocks/rocks-5.5 TCP 服务端(bench_server.lua 使用)

Lua 5.1/5.3 的 C 扩展通过 luarocks --lua-dir --tree 独立编译安装至各自版本目录。LuaJIT 与 Lua 5.1 ABI 兼容,共享 .so。OpenResty 的 cmsgpack 从 Lua 5.1 树复制至 /Users/frank/.luarocks-resty/lib/lua/5.1/

0.4 传输器配置

传输器 说明 适用场景
mock cosocket 内存缓冲区模拟 cosocket receive/send 接口,零 I/O 开销 隔离协议处理性能(所有运行时)
真实 cosocket OpenResty ngx.socket.tcp() 连接 127.0.0.1:9600,TCP 往返 真实 I/O 吞吐(仅 OpenResty)
bench_server luasocket TCP server, 127.0.0.1:9600, keepalive 模式 真实 cosocket 压测的服务端

0.5 压测方法论

配置项 说明
预热迭代 min(n, 1000) 消除 JIT 预热影响,确保热点路径已编译
计时器 os.clock() CPU 时间,排除 I/O 等待(mock cosocket 场景)
codec 迭代数 100,000 序列化器裸编解码
protocol 迭代数 50,000 render/parse 全链路
handle_connection 迭代数 50,000 mock cosocket 全链路
真实 cosocket new-conn 迭代数 2,000 限制以避免 TCP TIME_WAIT 端口耗尽
真实 cosocket keepalive 迭代数 10,000 单连接多次请求
报告指标 ops/s 每秒操作数 = 迭代数 / elapsed

一、矩阵总览:handle_connection 全链路

全链路:receive_message → Protocol.parse → dispatch → Protocol.render → send mock cosocket(内存缓冲,零 I/O 开销),隔离纯协议处理性能

打包器 Lua 5.1 Lua 5.3 Lua 5.5 LuaJIT OpenResty
纯 Lua JSON 30K 32K 34K 95K 122K
纯 Lua Msgpack 36K 38K 42K 148K 143K
cjson (C 扩展) 30K 32K 34K 95K 121K
cmsgpack (C 扩展) 37K 39K 42K 146K 146K

关键发现

  1. LuaJIT 是最大性能杠杆:纯 Lua 在 LuaJIT 下获得 3.2-4.1x 加速(JSON 30K→95K,Msgpack 36K→148K)
  2. C 扩展在全链路中几乎无收益:cjson vs 纯 Lua JSON ≈ 1.0x,cmsgpack vs 纯 Lua Msgpack ≈ 1.0x——协议开销(header/framing/dispatch)主导,编解码器非瓶颈
  3. Msgpack 优于 JSON:纯 Lua 下 1.2x(全链路),LuaJIT 下 1.5x——二进制格式更紧凑且解析更快
  4. OpenResty ≈ 独立 LuaJIT:JSON 122K vs 95K(OpenResty 略快 28%),Msgpack 143K vs 148K(LuaJIT 略快 3%)

二、序列化器裸编解码

2.1 JSON

操作 Lua 5.1 Lua 5.3 Lua 5.5 LuaJIT OpenResty C 扩展
Json.pack (纯Lua) 125K 131K 132K 625K 569K
Json.pack (cjson) 830K 804K 785K 811K 805K cjson
cjson 加速比 6.6x 6.1x 5.9x 1.3x 1.4x
Json.unpack (纯Lua) 67K 71K 79K 235K 246K
Json.unpack (cjson) 693K 677K 699K 975K 997K cjson
cjson 加速比 10.4x 9.5x 8.9x 4.1x 4.0x

2.2 Msgpack

操作 Lua 5.1 Lua 5.3 Lua 5.5 LuaJIT OpenResty C 扩展
Msgpack.pack (纯Lua) 134K 138K 149K 1,053K 923K
Msgpack.pack (cmsgpack) 1,174K 1,155K 973K 1,352K 1,295K cmsgpack
cmsgpack 加速比 8.8x 8.4x 6.5x 1.3x 1.4x
Msgpack.unpack (纯Lua) 174K 180K 188K 571K 527K
Msgpack.unpack (cmsgpack) 835K 796K 842K 1,320K 1,317K cmsgpack
cmsgpack 加速比 4.8x 4.4x 4.5x 2.3x 2.5x

关键发现

  • LuaJIT 纯 Lua Msgpack pack(1.05M ops/s)≈ cmsgpack(1.35M ops/s):JIT 编译器几乎消除了 C 扩展优势
  • cjson unpack 在 OpenResty 下最快(997K ops/s):OpenResty 的 cjson 针对其 LuaJIT 优化
  • 标准 Lua 三版本(5.1/5.3/5.5)性能几乎一致:纯 Lua 无 JIT,版本差异 < 5%

三、Protocol 全链路 render/parse

3.1 render(打包请求:header + framing + body 序列化)

打包器 Lua 5.1 Lua 5.3 Lua 5.5 LuaJIT OpenResty
render JSON (纯Lua) 132K 139K 141K 586K 606K
render JSON (cjson) 271K 294K 303K 741K 829K
render Msgpack (纯Lua) 131K 134K 147K 690K 811K
render Msgpack (cmsgpack) 274K 293K 304K 881K 1,019K

3.2 parse(解析响应:framing + header 解包 + body 反序列化)

打包器 Lua 5.1 Lua 5.3 Lua 5.5 LuaJIT OpenResty
parse JSON (纯Lua) 67K 71K 78K 191K 273K
parse JSON (cjson) 155K 162K 181K 680K 819K
parse Msgpack (纯Lua) 106K 112K 121K 417K 445K
parse Msgpack (cmsgpack) 161K 170K 190K 782K 913K

关键发现

  • render 比 parse 快:render 是顺序写入(table.concat),parse 需要逐字节状态机解析
  • LuaJIT 下 cjson parse(680K)≈ cmsgpack parse(782K):两者在 JIT 下趋于同水平
  • 标准 Lua 下 C 扩展 render 加速 2.0-2.1x,parse 加速 1.6-2.3x:header/framing 固定开销稀释了序列化加速

四、Framing I/O 层

操作 Lua 5.1 Lua 5.3 Lua 5.5 LuaJIT OpenResty
receive_exact (8B) 1.9M 2.0M 2.1M 11.4M 11.7M
receive_exact (82B) 1.7M 1.7M 1.8M 11.2M 10.9M
receive_exact (90B) 1.6M 1.6M 1.8M 10.0M 9.6M
receive_message (JSON) 153K 159K 176K 725K 886K

关键发现

  • LuaJIT Framing 10-11M ops/s:I/O 层在 JIT 下极快,永远不是瓶颈
  • mock cosocket 零 I/O 开销:纯内存缓冲 string.sub 操作,隔离协议处理开销

五、真实 cosocket handle_connection 往返压测

OpenResty 环境下,启动 test/bench_server.lua(luasocket TCP server, 127.0.0.1:9600, keepalive 模式), cosocket 客户端完整往返:connect → send(Yar request) → Framing.receive_message(response) → close

5.1 mock cosocket vs 真实 cosocket 对比

场景 ops/s vs mock cosocket 说明
mock cosocket handle_connection (JSON) 122K 基线 纯协议开销,零 I/O
真实 cosocket (new conn each) 23K 5.3x slower 每次新建 TCP 连接 + 往返 + 关闭
真实 cosocket (keepalive) 89K 1.4x slower 单连接多次请求,省去 connect/close
mock cosocket handle_connection (Msgpack) 143K 基线 纯协议开销
真实 cosocket Msgpack (keepalive) 89K 1.6x slower 单连接多次 Msgpack 往返

5.2 keepalive vs new-conn 开销分解

模式 ops/s 对比
new conn each (JSON) 23K 基线
keepalive (JSON) 89K 3.9x faster
keepalive (Msgpack) 89K 3.9x faster (vs Msgpack new-conn 估算)

5.3 全打包器 keepalive 往返收敛

打包器 keepalive 往返 ops/s mock cosocket ops/s 差距
纯 Lua JSON 89K 122K 1.4x
纯 Lua Msgpack 89K 143K 1.6x
cjson 87K 121K 1.4x
cmsgpack 88K 146K 1.7x

关键发现

  1. TCP connect/close 是最大 I/O 开销:new-conn 模式比 keepalive 慢 3.9x,连接建立开销远大于数据传输
  2. keepalive 模式下所有打包器收敛到 ~87-89K ops/s:真实 I/O 开销主导,编解码器差异被稀释
  3. mock cosocket 高估性能 1.4-5.3x:mock 适合隔离协议开销分析,但不能代表真实吞吐
  4. 生产环境必须使用连接池:cosocket setkeepalive 归池或 TCP persistent 连接复用

六、C 扩展加速比矩阵(全链路 handle_connection)

打包器组合 Lua 5.1 Lua 5.3 Lua 5.5 LuaJIT OpenResty
cjson vs 纯Lua JSON 1.0x 1.0x 1.0x 1.0x 1.0x
cmsgpack vs 纯Lua Msgpack 1.0x 1.0x 1.0x 1.0x 1.0x
cmsgpack vs 纯Lua JSON 1.2x 1.2x 1.2x 1.5x 1.2x
cmsgpack vs cjson 1.2x 1.2x 1.2x 1.5x 1.2x

关键发现

  • 全链路加速比(1.0x)远低于裸编解码加速比(2-10x):header 打包/解包 + framing + dispatch 是固定开销,编解码器选择对全链路性能几乎无影响
  • cmsgpack ≈ cjson:在全链路中两者性能持平,选择 Msgpack 的理由是二进制紧凑而非速度
  • Msgpack 格式本身优于 JSON:纯 Lua Msgpack vs 纯 Lua JSON 全链路 1.2-1.5x,无需 C 扩展即有优势

七、开销分解

7.1 协议解析 vs I/O framing 占比

维度 ops/s 占比
Protocol.parse only (no I/O) 191-445K
receive_message + Protocol.parse 153-886K
I/O framing 开销 15-24%
Protocol parse 占比 76-85%

7.2 全链路开销层级

handle_connection 全链路开销分解(LuaJIT, mock cosocket)

┌─────────────────────────────────────────────────────┐
│  Protocol.parse (body 反序列化)         ~45%         │
│  Protocol.render (body 序列化)           ~25%         │
│  Framing.receive_message (帧读取)       ~15%         │
│  Header unpack/pack                     ~10%         │
│  dispatch (method lookup + pcall)        ~3%         │
│  send (mock, 零开销)                     ~2%         │
└─────────────────────────────────────────────────────┘

关键发现

  • 协议解析(parse + render)占全链路 ~70%:优化序列化器是唯一有效手段
  • Framing 层占 ~15%receive_exacttable.concat 循环已足够高效
  • dispatch 占比极低(~3%):方法表 memoize 查找 + pcall 开销可忽略

八、综合评估

8.1 性能等级矩阵

配置 标准 Lua (5.1/5.3/5.5) LuaJIT / OpenResty 评级
纯 Lua JSON 全链路 30-34K ops/s 95-122K ops/s B- / B+
纯 Lua Msgpack 全链路 36-42K ops/s 143-148K ops/s B / A-
cjson 全链路 30-34K ops/s 95-121K ops/s B- / B+
cmsgpack 全链路 37-42K ops/s 146K ops/s B / B+
Framing I/O (mock) 153-176K ops/s 725-886K ops/s A / A+
真实 cosocket (keepalive) 87-89K ops/s B+

8.2 生产推荐

场景 运行时 打包器 传输器 预期吞吐
OpenResty 高并发 OpenResty LuaJIT cmsgpack cosocket + 连接池 ~88K ops/s (真实 I/O)
OpenResty 零依赖 OpenResty LuaJIT 纯 Lua Msgpack cosocket + 连接池 ~89K ops/s (真实 I/O)
OpenResty 协议峰值 OpenResty LuaJIT cmsgpack mock cosocket ~146K ops/s (协议上限)
标准 Lua 高并发 Lua 5.1/5.3 cmsgpack luasocket ~39K ops/s (协议上限)
标准 Lua 零依赖 Lua 5.1/5.3 纯 Lua Msgpack luasocket ~36K ops/s (协议上限)
嵌入式低资源 Lua 5.1 纯 Lua JSON 自定义 ~30K ops/s

8.3 核心结论

  1. 运行时是第一性能杠杆:LuaJIT vs 标准 Lua = 3.2-4.1x(全链路),远超 C 扩展的 1.0x
  2. C 扩展在全链路中几乎无收益(1.0x):协议开销(header/framing/dispatch)主导,编解码器非瓶颈;仅在裸编解码层有 2-10x 加速
  3. Msgpack 优于 JSON:纯 Lua 下 1.2x,LuaJIT 下 1.5x(全链路),且二进制更紧凑
  4. cjson ≈ cmsgpack:在全链路中两者持平,Msgpack 的优势是格式紧凑而非速度
  5. Framing 层零瓶颈:LuaJIT 下 10-11M ops/s,占全链路 < 5% 开销
  6. OpenResty ≈ 独立 LuaJIT:cosocket 环境不引入额外框架开销(5% 以内)
  7. 真实 cosocket I/O 是吞吐瓶颈:keepalive 模式下 ~89K ops/s,所有打包器收敛——I/O 开销主导
  8. 连接复用是生产必备:new-conn vs keepalive = 3.9x 差距,cosocket setkeepalive 或 TCP persistent 必须启用

附录:压测脚本与局限性

A.1 压测脚本

脚本 维度 运行方式
test/benchmark_matrix.lua 5 运行时 × 4 打包器 × 2 传输器 矩阵 lua/luajit/resty 通用
test/benchmark_cosocket.lua cosocket 传输层专项 + 真实 I/O 往返 resty (OpenResty)
test/benchmark_cext.lua C 扩展注入 vs 纯 Lua 编解码 lua/luajit/resty 通用
test/bench_server.lua 压测用最小 TCP 服务端(luasocket, keepalive 模式) lua test/bench_server.lua

A.2 局限性

  • 单线程服务端bench_server.lua 使用 luasocket 顺序 accept,未测多连接并发场景
  • localhost I/O:真实 cosocket 往返走 127.0.0.1,网络延迟接近零,实际网络环境下 I/O 开销更高
  • 无 PHP 互通:未测试与 PHP Yar Server 的跨语言互通性能