上一版测完总觉得哪里不对,于是改了配置又跑了一遍——这次结果老实多了。

前言:为什么重新测了一遍

这篇文章其实是第二版了。上一篇发出来之后我越想越不对劲——当时每个容器只给了 512MB 内存,fd 上限也没显式调高,跑 10K 并发的时候实际上根本没撑到一万个连接。wrk 报了一堆 connect 8983 错误,真正建立的有效连接也就一千出头,所谓的"高并发 10K"测试本质上测的还是千级并发。那组数据里 Hical 高并发"遥遥领先"的结论,现在看有水分。

这次我把配置拉满了:每容器 1024MB 内存nofile 上调到 65536,VM 内核参数也做了对应调优(somaxconntcp_max_syn_backlog、端口范围等),确保 wrk 发起的一万个连接能真正建立起来。同时精简了文章的对比范围——压测还是 6 个框架一起跑的(数据完整保留在文章末尾),但本文只聚焦 Hical、Drogon、Cinatra 三个第一梯队选手做深入分析。这三个在上一版常规场景里已经拉开了和其余框架的差距,继续带着 91 QPS 的 cpp-httplib 同框只会让图表失真。

结果确实和上次不一样,尤其是高并发部分的排名翻了个个儿。先把结论放在前面:常规负载下三个框架打得很近,但各自有各自的强项——Hical 在 JSON 和中间件场景领先,Cinatra 在纯吞吐和 JSON Echo 上更快,Drogon 在高并发场景下表现最好。


测试环境

宿主机硬件

项目 配置
处理器 Intel Core i7-11700K @ 3.60GHz(8核16线程)
内存 32 GB
存储 SSD(900 GB)
宿主系统 Windows 10 Enterprise LTSC 2021

虚拟化环境

项目 配置
虚拟化平台 Oracle VM VirtualBox 7.1(半虚拟化接口: KVM)
Guest OS Ubuntu 24.04.3 LTS Server
VM 分配 16 GB RAM / 8 CPU
Docker Engine 29.4.3 + Compose v5.1.3
每框架容器 4 CPU + 1GB RAM,nofile=65536
压测工具 wrk 4.1.0(独立容器),4 线程
默认并发 100 连接
持续时间 30 秒
编译器 GCC 14.2(Ubuntu 24.04)
优化级别 Release(-O2)
采样方式 每场景连续跑 3 轮,取算术平均值

注:所有框架运行在同一台 VM 的 Docker 容器中,wrk 也在同一 Docker 网络内发起请求(容器间通信,无宿主机网络栈介入)。VirtualBox 虚拟化会引入一定开销,绝对 QPS 数值会低于裸机,但各框架的相对排名通常稳定。

框架版本

框架 版本 构建方式 I/O 模型 JSON 库
Hical v2.6.3 复制源码编译 C++20 协程 (Boost.Asio) Boost.JSON
Drogon v1.9.8 源码编译 回调 (Trantor) JsonCpp
Cinatra latest FetchContent C++20 协程 (自有实现) iguana

基础场景:Hello World / JSON

先看最简单的纯文本和 JSON 响应,这基本测的是框架自身的"底层开销"——HTTP 解析、路由匹配、响应序列化这些。

场景 Hical Drogon Cinatra
Hello World (GET /) 153,871 140,536 165,717
JSON 响应 (GET /api/status) 141,499 95,090 128,391
JSON Echo (POST /api/echo) 123,585 69,343 131,440
路径参数 (GET /users/42) 140,285 86,487 125,262

单位:Requests/sec(三轮平均值)

几个观察:

  1. Hello World:三者都在 14w~17w 的水平,Cinatra 166k 略高,Hical 154k,Drogon 141k。Cinatra 和 Hical 差距 8%,属于同一档;Drogon 稍低一些。

  2. JSON 序列化(GET /api/status):Hical 141k 领先,Cinatra 128k 紧随,Drogon 95k 落后较多。这里有个重要背景:Drogon 用的是 JsonCpp 库,而 Hical 用 Boost.JSON,Cinatra 用 iguana。JSON 库本身的性能差异直接体现在这里。

  3. JSON Echo(POST 反序列化+序列化):Cinatra 131k 略快于 Hical 124k,iguana 的反序列化确实快。Drogon 只有 69k,差距比较明显,大概率是 JsonCpp 的开销。

  4. 路径参数:Hical 140k 最高,Cinatra 125k 次之,Drogon 86k 偏低。

小结:不涉及 JSON 的话 Cinatra 最快,一旦碰 JSON 就是 Hical 的地盘。Drogon 吃了 JsonCpp 的亏,不过 Hello World 纯吞吐也就比前两个低 10% 左右,不至于掉队。


中间件场景:真正的分水岭

中间件测试是这次最有意思的部分。为了公平,测试分了两种模式:

  • 原生中间件:Hical 用 RouteGroup 洋葱链,Drogon 用 HttpFilter,Cinatra 因为没有运行时中间件机制,用 std::function 调用链模拟等价开销。
  • Hical SyncMW:Hical 独有的同步中间件快速路径(全 Sync 时零协程帧,纯栈调用),其他框架继续用各自的原生机制。
场景 Hical Drogon Cinatra
0 层中间件 140,837 106,789 132,121
3 层(原生) 136,302 105,171 131,595
10 层(原生) 93,402 104,942 137,830
3 层(Hical SyncMW) 141,160 94,588 129,701
10 层(Hical SyncMW) 139,685 100,652 127,711

单位:Requests/sec(三轮平均值)

这里有个很值得讨论的现象:

Hical 的原生异步中间件在 10 层时性能下降明显(从 0 层的 141k 掉到 93k,降幅 34%),而 Drogon 和 Cinatra 几乎不受影响。为什么?因为 Hical 的洋葱模型中间件每层都是一个 co_await,10 层就是 10 次协程挂起/恢复 + 10 个协程帧的堆分配。Drogon 的 HttpFilter 是回调链(不走协程),Cinatra 的测试代码是普通函数调用链——人家根本没有协程帧开销。

但这也是为什么 Hical 提供了 SyncMW 快速路径的原因。当所有中间件都使用 SyncBeforeHandler 且 handler 本身也是同步签名时,RouteGroup 会直接注册为 SyncRouteHandler,走 dispatchSync() 路径——零协程帧,整条链路从中间件到 handler 都是纯栈上函数调用,和 Drogon/Cinatra 的回调/函数调用模型站在同一起跑线上。QPS 从 93k 恢复到 140k,完全消除了协程帧开销,和 0 层中间件的 141k 持平——因为 SyncMW 路径连路由分发本身的协程帧都省了。

公平性说明:Cinatra 在中间件测试中使用的是 std::function 调用链模拟,而不是框架原生的中间件机制(Cinatra 的 middleware 是编译时模板,不支持运行时动态添加)。这意味着 Cinatra 的中间件数字测的是"函数调用链的开销",而非框架中间件调度本身的开销。这是测试设计上的限制——不同框架的中间件机制差异太大,很难做到 100% 等价对比。


高并发:1000 和 10000 连接

这是最能体现框架"韧性"的测试。

并发连接数 Hical Drogon Cinatra
100 137,522 145,663 159,519
1,000 63,077 81,646 69,797
10,000 59,451 100,534 63,971

单位:Requests/sec(三轮平均值)

注:10,000 并发时 Cinatra 三轮均出现 timeout(808~2719 个),Hical 第一轮有 1112 个 timeout 后两轮无错误,Drogon 第一轮有 4022 个 timeout 但后两轮几乎为零。

这组数据很有意思:

  • 100 并发:Cinatra 160k 略高,Drogon 146k,Hical 138k。三者差距在 15% 以内。
  • 1000 并发:所有框架都有较大衰减。Drogon 82k 表现最好,Cinatra 70k 次之,Hical 63k。
  • 10000 并发:Drogon 101k 一骑绝尘,是 Hical(59k)和 Cinatra(64k)的 1.6~1.7 倍。

这组结果我没预料到。跑之前我以为 Hical 的 SO_REUSEPORT + 多 io_context 架构在高并发下会有优势,结果被 Drogon 的回调模型按在地上。两个 C++20 协程框架(Hical 和 Cinatra)在 10K 连接下都掉到了 60k 附近,而 Drogon 稳在 101k。协程模型下每个连接维护一个协程帧,一万个帧的管理和调度本身就是开销,Drogon 的回调模型不存在这个问题。

另外要提一嘴:Cinatra 在 10K 时三轮都有数百到数千个 timeout,这也会拖低 QPS。Hical 除了第一轮外零错误但 QPS 同样低,说明瓶颈不在连接管理而在协程调度本身。


延迟对比

除了 QPS,延迟也很重要:

场景 Hical Avg Drogon Avg Cinatra Avg
Hello World (c=100) 1.91 ms 1.71 ms 1.77 ms
高并发 1000 14.94 ms 12.28 ms 15.37 ms
高并发 10000 157.22 ms 65.86 ms 89.80 ms

数据来自 wrk 的 Avg Latency 输出,三轮平均值。

c=100 时三者都在 1.7~1.9ms,没啥好说的。往上拉就有意思了:

  • c=1000:Drogon 12ms,Hical 15ms,Cinatra 15ms——还在一个量级
  • c=10000:Drogon 66ms,Cinatra 90ms,Hical 157ms——Hical 直接拉胯,是 Drogon 的 2.4 倍

和 QPS 数据对得上,高并发就是 Hical 当前最大的短板,后面得好好优化。


资源占用

指标 Hical Drogon Cinatra
空载内存 200.4 MB 93.5 MB 56.6 MB
满载内存 197.0 MB 93.5 MB 55.8 MB
二进制大小 2.1 MB 1.9 MB 528 KB
代码行数(bench) 149 行 234 行 182 行

Hical 空载就吃了 200 MB,这是 PMR 内存池预分配的代价。好处是运行时几乎不再向系统要内存,坏处是启动就占这么大一坨。Drogon 94 MB,Cinatra 57 MB 最省。

有意思的是三者满载和空载内存几乎没变化——100 并发的压力下连接本身基本不额外吃内存。


综合评价

维度 Hical Drogon Cinatra
常规吞吐 (c=100) 优秀 良好 优秀
JSON 处理 快 (Boost.JSON) 慢 (JsonCpp) 快 (iguana)
中间件 (少量/SyncMW) 最快 良好 优秀
中间件 (大量,异步) 有衰减 稳定 稳定
高并发 (c=1000+) 一般 最好 良好
高并发延迟 偏高 最低 中等
内存占用 高 (200MB) 中 (94MB) 低 (57MB)
生态成熟度 新框架 成熟 中等

几点坦诚的说明

利益相关:我是 Hical 的作者,这个测试从设计到执行都是我一个人干的。Docker 隔离、相同资源限制、wrk 统一配置——这些我尽力做到了,但屁股决定脑袋,测试设计上的视角偏差肯定有。不服的话欢迎自己跑一遍打我脸。

中间件测试其实不太公平:三个框架的中间件机制完全不一样——Hical 是运行时洋葱链、Drogon 是 HttpFilter 链、Cinatra 压根没有运行时中间件。我尽量模拟等价语义了,但严格说测的不是同一个东西。

关于数据波动:每个场景跑了 3 轮取平均。wrk 在 Docker 里的波动大概 5%~10%,三轮能抹平大部分偶然因素,但不可能完全干净。原始数据附在文末,你可以自己看各轮之间的差异有多大。

双层虚拟化的影响:VirtualBox VM 里跑 Docker,绝对 QPS 肯定比裸机低。但大家都在同一个环境里跑,相对排名应该是稳定的。裸机 Linux 上数字会更好看,不过排名大概率不会变。

选框架不能只看 QPS:Drogon 有完整的 ORM、WebSocket、HTTP 客户端;Cinatra 轻巧好上手;Hical 在中间件和 JSON 这块有些自己的想法。性能测试只是选型的一个参考维度,生态、文档、社区活跃度这些才是长期用下去绕不开的。


复现方法

所有测试代码、Docker 配置和压测脚本均已开源在仓库的 benchmark/ 目录下,详见仓库 benchmark/README.md,包含完整的构建、启动、压测、采集和清理流程。

git clone https://github.com/hical61/hical.git
cd hical/benchmark
# 后续步骤参照 benchmark/README.md

注:只需三步就能在你自己的机器上跑出结果。如果和我的数据有明显差异,欢迎开 issue 讨论。


TL;DR

  • 三个框架在 100 并发下性能相近,都在 14w~17w QPS
  • JSON 处理:Hical (Boost.JSON) 和 Cinatra (iguana) 明显快于 Drogon (JsonCpp)
  • 中间件:Hical SyncMW 零协程帧路径最快,但原生异步中间件层数增多时有衰减
  • 高并发 (1000+):Drogon 的回调模型表现最稳健,协程框架都有不同程度下降
  • 内存占用:Cinatra 最低 (57MB),Hical 最高 (200MB,PMR 预分配)
  • 选框架不能只看 QPS,生态和维护同样重要

附录:三轮原始数据

以下是三轮测试的原始 QPS 数据,供交叉验证。每轮为独立的 30 秒 wrk 测试,三轮连续执行,未重启容器。

第一轮(2026-05-25 10:08)

场景 Hical Drogon Cinatra
Hello World (c=100) 156,796.12 127,980.96 163,774.61
JSON 响应 (c=100) 137,491.36 74,466.33 127,707.87
JSON Echo (c=100) 111,098.91 62,833.71 135,691.72
路径参数 (c=100) 150,121.59 88,839.01 118,966.68
中间件 0 层 (c=100) 153,207.23 112,848.48 136,976.94
中间件 3 层原生 (c=100) 136,898.55 110,966.38 138,247.89
中间件 10 层原生 (c=100) 90,629.82 90,728.35 144,136.85
SyncMW 3 层 (c=100) 136,901.79 101,765.38 124,463.02
SyncMW 10 层 (c=100) 141,379.19 99,782.52 125,834.13
高并发 (c=100) 110,907.90 125,923.51 166,994.13
高并发 (c=1000) 62,416.43 82,525.51 73,922.75
高并发 (c=10000) 59,184.07 98,286.46 70,999.25

高并发 10K Socket Errors: Hical timeout=1112, Drogon timeout=4022, Cinatra timeout=2719

第二轮(2026-05-25 10:55)

场景 Hical Drogon Cinatra
Hello World (c=100) 165,965.45 168,045.72 165,510.87
JSON 响应 (c=100) 145,883.87 100,499.66 135,568.09
JSON Echo (c=100) 131,825.18 81,832.00 141,118.30
路径参数 (c=100) 130,455.35 77,424.95 126,874.50
中间件 0 层 (c=100) 138,603.05 99,891.09 121,208.64
中间件 3 层原生 (c=100) 131,093.74 90,841.49 126,053.21
中间件 10 层原生 (c=100) 93,569.44 114,919.03 141,083.12
SyncMW 3 层 (c=100) 149,265.85 80,593.01 127,425.23
SyncMW 10 层 (c=100) 135,342.89 91,646.87 133,061.49
高并发 (c=100) 140,643.56 149,802.87 149,170.97
高并发 (c=1000) 65,478.39 86,809.88 71,551.21
高并发 (c=10000) 59,258.94 94,281.89 62,137.10

高并发 10K Socket Errors: Hical 无错误, Drogon 无错误, Cinatra timeout=919

第三轮(2026-05-25 11:42)

场景 Hical Drogon Cinatra
Hello World (c=100) 138,851.55 125,580.99 167,866.51
JSON 响应 (c=100) 141,122.19 110,304.60 121,898.13
JSON Echo (c=100) 127,830.71 63,364.29 117,510.76
路径参数 (c=100) 140,279.31 93,197.92 129,945.28
中间件 0 层 (c=100) 130,700.88 107,628.47 138,178.82
中间件 3 层原生 (c=100) 140,912.53 113,704.01 130,484.50
中间件 10 层原生 (c=100) 96,007.00 109,177.63 128,268.96
SyncMW 3 层 (c=100) 137,311.12 101,405.52 137,215.18
SyncMW 10 层 (c=100) 142,333.72 110,527.95 124,236.71
高并发 (c=100) 161,013.19 161,261.49 162,390.92
高并发 (c=1000) 61,336.68 75,602.27 63,918.27
高并发 (c=10000) 59,910.86 109,031.90 58,776.93

高并发 10K Socket Errors: Hical 无错误, Drogon timeout=7, Cinatra timeout=1062


测试日期:2026-05-25 | 宿主机:i7-11700K / 32GB | VM:VirtualBox 7.1 + Ubuntu 24.04 (16GB/8CPU) | 容器:Docker 29.4,4 CPU / 1GB per container,nofile=65536 | 工具:wrk 4.1.0 | 每场景 3 轮取平均值

后续更新:v2.6.7 重测(2026-07-15)

一个半月过去,Hical 发了几个版本,到 v2.6.7 的时候高并发这块做了几项针对性优化。于是拿同一套环境、同样的 wrk 参数重新跑了三轮。不过有个重要变化得先交代——benchmark 代码这次也改了,不是 Hical 这边改的,是 Drogon 的中间件实现方式换掉了。所以结果跟上一版相比变化挺大,高并发尤其明显,但中间件场景的数据对比也需要重新理解。下面细说。

从 v2.6.3 到 v2.6.7 改了什么

从 v2.6.3 发到现在这版,中间经过了 v2.6.4、v2.6.5、v2.6.6 四个版本。每个版本都有 Performance 条目,加起来快 20 项了,全列出来太啰嗦,按版本梳理一下跟 HTTP 吞吐直接相关的:

v2.6.4(发 v2.6.3 之后五天就出了)主要干了几件事:IdleScanner 集中式空闲扫描替代了每个连接独立 steady_timer 协程——以前每个连接一个 timer 协程在那蹲着等超时,现在是每个 io_context 一个定时器扫一遍侵入式链表,省掉了海量协程帧和 epoll_ctl 调用。EventLoopPool 的负载分发从 round-robin 改成了最少连接(挑 connectionCount_ 最小的 loop),连接分布更均匀。GenericConnection 的 writeLoop drain 加了 kMaxDrainBatch = 256 上限,防止突发队列饿死 io_context 上的其他任务。还有个响应前缀模板化——连接级预构建了 Server/Connection/Date 三个通用头的 wire bytes(约 90B),keep-alive 请求直接 memcpy,省了三次 HeaderMap::insert + 序列化循环。

v2.6.5的重点是 ReadBufferPool——thread_local 无锁的 8KB 缓冲池,BufferHandle RAII 借还。空闲连接不再霸着 8KB 读缓冲,来了请求借一块、写完了还回去。加上 GenericConnection::inputBuffer_ 从普通 string 改成了 optional<PmrBuffer> 懒分配(readLoop 第一次进来才创建),空闲连接从 17.44 KB 降到了约 8 KB。另外还有 HeaderMap::toLower()Router::urlDecode() 两个热路径都换成了编译期 256 查表,HTTP 解析时顺手把 Connection 头抓出来省了后面单独扫一遍。

v2.6.6 改了 keep-alive 的空闲等待方式——原来是 async_wait(wait_read),每次都要调 epoll_ctl(MOD) 注册可读事件;改成 256B 栈缓冲 async_read_some 投机读,利用 Asio 内部的投机路径,空闲连接完全不走 epoll_ctl。strace 验证过,10 秒内的 epoll_ctl 调用从 32,563 降到 9,173(−71.8%),CPU 时间占比从 26.51% 降到 5.57%。AsioEventLoopconcurrency_hint 也暴露出来可配了,默认设成 1,跟 Hical 一线程一 io_context 的模型匹配,epoll 走单线程优化路径少调几次 epoll_ctl。另外 setHeader 参数从 const std::string& 改成了 std::string_view,字符串字面量传参不再隐式构造临时 std::string,per-request 少一次堆分配。

v2.6.7(就是这次压测的版本)改了三个热路径:乐观同步写——小响应先拿 write_some 试一把同步写,写完直接 co_return,不挂协程不进 reactor 完成队列。只有 would_block(内核发送缓冲区满了)或者只写了一部分的情况才回退 async_write。有个坑踩了半天——Asio 内部有两套 non_blocking 标志位,sync_send 只检查 user_set_non_blocking,要不是 @hyird 在 issue #11 里指出来,我都不知道 handleSession 入口得显式 socket.non_blocking(true),否则 write_some 在 EAGAIN 时可能掉进 poll() 卡死 io 线程。第二个是 readBuf 提前归还——响应写完立刻 release() 还给 ReadBufferPool,别等到协程末尾析构,10K 连接在途的高并发场景下峰值缓冲从 80MB 砍到约 40MB。第三个是消除转发协程帧——中间件包装 lambda 从 co_return co_await mw(r, next) 改成 return mw(r, next)(类型推导一致),lambda 退化成普通函数,10 层中间件从每请求 20 帧降到 10 帧。剩下还有几个细碎的:HttpRequest attributes 换透明哈希(string_view 零分配查找)、preparePayload 幂等化、WebSocket 帧缓冲复用。总体效果是万连接吞吐 +58%、内存 −30%、中间件链 +7.6%。

这几个版本累计下来,一条请求从 accept 到响应发完的热路径上,能省的堆分配基本都省了、能不走协程挂起的就不挂、能不走 epoll_ctl 的就不走。

Benchmark 本身也修了一个口径问题

在开始看数据之前,得交代一件事——这次重测不只是 Hical 版本变了,benchmark 代码也有一个重要改动:Drogon 的中间件实现方式换了

上一版(v2.6.3)压测时,Drogon 的 /middleware/3/middleware/10 用的是 HttpFilterHttpFilter 是什么?是在同一个 EventLoop 线程上做纯同步回调递归——nextCb() 就是普通函数调用,不建协程帧、不挂起不恢复。而 Hical 的异步中间件是 co_await next(req),每层建一个协程帧、每层挂起恢复一次。这两者根本不是一个机制——用回调链跟协程洋葱比"谁层数增加衰减更少",就像拿自行车和汽车比谁更省油,没什么意义。

所以这次把口径对齐了(issue #12):Drogon 的 /middleware/* 端点换成了 HttpCoroMiddleware——Drogon v1.9.8 原生支持的协程中间件,语法就是标准的 co_await,每层挂起+恢复,跟 Hical 的异步中间件语义严格对齐。同时 /sync-filter/* 两端点(原 /sync-middleware/*,名字也改了,因为"同步中间件"不够准确,这就是同步过滤链)继续用 HttpFilter,跟 Hical 的 SyncBeforeHandler 零协程帧快速路径对齐。

简单说:上一版中间件测试是"苹果比橘子",v2.6.7 修成了"苹果比苹果、橘子比橘子"——协程洋葱跟协程洋葱比,同步过滤跟同步过滤比。

这意味着 v2.6.7 里 Drogon 的中间件数据跟 v2.6.3 不可直接对比,因为测的就不是同一个东西了。其他五个框架的 benchmark 代码没变(只统一改了端点路径名)。

Hical 自身前后对比

同一套环境,Hical 版本从 v2.6.3 换到 v2.6.7。Crow/Oat++/cpp-httplib/Cinatra 的版本和 benchmark 代码都没变,Drogon 的版本没变但中间件 benchmark 代码有上述修改。

场景 v2.6.3 v2.6.7 变化
Hello World (c=100) 153,871 130,413 −15.2%
JSON 响应 (c=100) 141,499 141,285 −0.2%
JSON Echo (c=100) 123,585 132,890 +7.5%
路径参数 (c=100) 140,285 140,555 +0.2%
中间件 0 层 (c=100) 140,837 135,362 −3.9%
中间件 3 层 (c=100) 136,302 137,465 +0.9%
中间件 10 层 (c=100) 93,402 103,909 +11.2%
SyncMW 3 层 (c=100) 141,160 135,341 −4.1%
SyncMW 10 层 (c=100) 139,685 138,332 −1.0%
高并发 1000 63,077 91,620 +45.3%
高并发 10000 59,451 96,916 +63.0%

低并发场景有小幅下滑(Hello World 降了 15%)。具体是谁拖的没有做逐项 profiling,不好下定义——v2.6.3 到 v2.6.7 之间 HttpSessionImpl.cpp 的改动很大,加了好几条新分支(chunked body 检查、SSE 路径检测、IdleScanner Entry 注册),readBuf 也从连接级复用改成了每请求借还。这些在低并发 100 连接的场景下,单条请求本身已经够快了,任何一点点额外开销都会被放大。后面如果要追回这 15%,需要拿 perf 逐个排除。

但高并发是另一回事。1000 并发 +45%,10000 并发 +63%——这个幅度跑出来之前我自己也没想到。

中间件 10 层 +11%,提过消除转发协程帧把每请求 20 帧降到了 10 帧,但没单独对这个场景做 profiling,不确定是不是唯一的贡献因素。

v2.6.7 六框架重赛

下面是 2026-07-15 跑的三轮完整数据(平均)。跟上一版不同的是这版把六框架全都列出来了,包括 Crow、Oat++、cpp-httplib。

场景 Hical Drogon Crow Oat++ cpp-httplib Cinatra
Hello World (c=100) 130,413 166,821 112,295 18,887 90 156,623
JSON 响应 (c=100) 141,285 100,758 101,720 16,597 90 140,979
JSON Echo (c=100) 132,890 78,583 75,736 13,156 90 156,924
路径参数 (c=100) 140,555 92,162 88,135 16,920 90 154,054
中间件 0 层 (c=100) 135,362 115,639 95,089 17,949 91 146,131
中间件 3 层 (c=100) 137,465 106,197 94,106 17,425 91 148,269
中间件 10 层 (c=100) 103,909 122,389 87,398 16,949 90 162,361
SyncMW 3 层 (c=100) 135,341 103,341 86,923 16,694 90 138,882
SyncMW 10 层 (c=100) 138,332 107,707 88,263 15,786 90 143,571
高并发 1000 91,620 91,348 71,506 7,409 92 81,495
高并发 10000 96,916 114,866 59,738 3,479 89 61,346

单位:Requests/sec,三轮平均值。加粗为该项最高。

延迟情况

场景 Hical Drogon Cinatra
Hello World (c=100) 1.94ms 1.66ms 1.68ms
高并发 1000 10.87ms 11.04ms 12.73ms
高并发 10000 59.83ms 62.69ms 81.37ms

Avg Latency,三轮平均值。加粗为最低。

Hical 的 10K 延迟从上一版的 157ms 降到了 60ms(−62%),在三个框架里反而是最低的。1000 并发也是最低的 10.9ms。

高并发稳定性(10K timeout)

框架 第 1 轮 第 2 轮 第 3 轮
Hical 0 0 0
Drogon 1,640 0 0
Cinatra 2,079 812 604
Crow 0 0 0
Oat++ 11,530 9,593 15,434
cpp-httplib 24* 24* 24*

*cpp-httplib 的 24 个 timeout 是其连接数上限所致,c=100 时一样有 24 个,跟高并发无关。

Hical 三轮全零 timeout 是这个版本最让我满意的地方。Drogon 第一轮炸了 1640 个,后两轮稳住了。Cinatra 三轮都有错误。Crow 是个意外惊喜:虽然 QPS 只有 60K,但三轮全零错误,比 Cinatra 稳。

这次的数据跟上一版能对上吗

基础场景(Hello World、JSON、路径参数、高并发)和 sync-filter 场景可以对上——Drogon 和 Cinatra 的版本没变,基础场景的 benchmark 代码也没变。Drogon 10K 上版 100K 这版 115K,波动在正常范围,说明测试环境本身没出幺蛾子。

但中间件场景(/middleware/3/middleware/10不能直接对比——上一版 Drogon 用的是 HttpFilter(同步回调链),这一版换成了 HttpCoroMiddleware(协程洋葱)。不是同一个测试,不能拿上一版的 Drogon 中间件数字跟这版比。Hical 自身的中间件提升(10 层 +11%)是同一套代码、同一个端点、只换版本,可比。

关于上一版结论的修正

回头看,上一版说"协程模型在高并发下有固有开销,Drogon 的回调模型更稳健"——v2.6.7 的数据表明这个结论下早了。优化完热路径之后,Hical 的 10K 延迟从 157ms 降到 60ms,反过来成了三个框架里最低的。至于哪些优化贡献了多少,没逐项 profiling 没法量化。

当然 Hello World 下滑 15% 这个没法回避,但原因不是单一因素能解释的,需要逐项 profiling 才能定位。后面拿 perf 排查一下,不过这个优先级不高——低并发场景 140K 还是 130K 对实际业务影响微乎其微,高并发场景的收益才是实打实的。

更新版总结

维度 Hical v2.6.7 Drogon Cinatra
常规吞吐 良好 优秀 优秀
JSON 处理 快 (Boost.JSON) 慢 (JsonCpp) 快 (iguana)
中间件 (SyncMW) 最快 良好 模拟数据不可比*
中间件 (协程洋葱) 10 层有衰减 稳定 模拟数据不可比*
高并发 1000 最好 持平 良好
高并发 10000 良好 最好 有衰减
高并发延迟 最低 60ms 63ms 81ms
高并发稳定性 全零 timeout 首轮有波动 三轮均有错误
内存占用 高 (200MB) 中 (94MB) 低 (57MB)

*Cinatra 的中间件测试用的是 std::function 调用链模拟,不是框架原生的中间件机制,数字仅供参考。

v2.6.7 三轮原始数据

第一轮(2026-07-15 03:30)
场景 Hical Drogon Crow Oat++ cpp-httplib Cinatra
Hello World (c=100) 134,420 175,877 108,685 20,375 90 158,187
JSON 响应 (c=100) 137,390 109,967 102,350 20,007 91 148,464
JSON Echo (c=100) 134,367 86,709 77,803 14,797 92 159,359
路径参数 (c=100) 146,481 100,818 95,053 19,647 92 159,819
中间件 0 层 (c=100) 142,329 117,764 99,669 19,467 91 148,070
中间件 3 层 (c=100) 146,008 118,947 102,599 20,133 91 148,891
中间件 10 层 (c=100) 102,936 121,190 92,445 18,579 89 164,013
SyncMW 3 层 (c=100) 140,262 102,412 76,653 17,125 91 137,787
SyncMW 10 层 (c=100) 144,414 116,962 93,215 16,132 91 153,012
高并发 1000 88,866 92,894 73,605 7,919 91 78,686
高并发 10000 93,166 114,537 53,354 2,644 90 58,717
第二轮(2026-07-15 04:08)
场景 Hical Drogon Crow Oat++ cpp-httplib Cinatra
Hello World (c=100) 139,198 163,214 116,451 17,990 88 155,403
JSON 响应 (c=100) 144,283 100,428 96,150 15,186 90 138,290
JSON Echo (c=100) 138,493 72,901 78,326 11,918 89 153,243
路径参数 (c=100) 142,118 85,468 71,255 15,908 90 157,474
中间件 0 层 (c=100) 132,482 119,612 90,079 16,370 91 139,029
中间件 3 层 (c=100) 135,869 78,170 79,170 14,233 90 148,299
中间件 10 层 (c=100) 110,697 115,655 79,551 15,849 90 173,214
SyncMW 3 层 (c=100) 139,376 114,377 94,720 18,013 89 144,794
SyncMW 10 层 (c=100) 134,893 108,657 83,868 15,939 89 144,766
高并发 1000 89,268 85,628 79,274 7,253 92 79,392
高并发 10000 93,334 112,561 66,056 4,330 87 62,351
第三轮(2026-07-15 04:42)
场景 Hical Drogon Crow Oat++ cpp-httplib Cinatra
Hello World (c=100) 117,620 161,374 111,750 18,295 91 156,280
JSON 响应 (c=100) 142,182 91,880 106,660 14,598 90 136,183
JSON Echo (c=100) 125,811 76,137 71,078 12,753 89 158,169
路径参数 (c=100) 133,065 90,199 98,096 15,204 89 144,869
中间件 0 层 (c=100) 131,275 109,542 95,521 18,011 92 151,296
中间件 3 层 (c=100) 130,518 121,474 100,550 17,908 91 147,616
中间件 10 层 (c=100) 98,095 130,323 90,199 16,420 89 149,857
SyncMW 3 层 (c=100) 126,384 93,234 89,396 14,943 90 134,065
SyncMW 10 层 (c=100) 135,688 97,501 87,705 15,288 90 132,935
高并发 1000 96,725 95,522 61,639 7,054 91 86,406
高并发 10000 104,246 117,500 59,803 3,463 88 62,971

更新于 2026-07-15 | Hical v2.6.7(含 #11 优化) | 其余框架版本不变 | 测试环境与 2026-05-25 完全一致

更多推荐