协程框架高并发翻车了?三个C++ Web框架实测,结果出乎意料

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

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

这篇文章其实是第二版了。上一篇发出来之后我越想越不对劲——当时每个容器只给了 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 OSUbuntu 24.04.3 LTS Server
VM 分配16 GB RAM / 8 CPU
Docker Engine29.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 库
Hicalv2.6.3复制源码编译C++20 协程 (Boost.Asio)Boost.JSON
Drogonv1.9.8源码编译回调 (Trantor)JsonCpp
CinatralatestFetchContentC++20 协程 (自有实现)iguana

基础场景:Hello World / JSON

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

场景HicalDrogonCinatra
Hello World (GET /)153,871140,536165,717
JSON 响应 (GET /api/status)141,49995,090128,391
JSON Echo (POST /api/echo)123,58569,343131,440
路径参数 (GET /users/42)140,28586,487125,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 时零协程帧,纯栈调用),其他框架继续用各自的原生机制。
场景HicalDrogonCinatra
0 层中间件140,837106,789132,121
3 层(原生)136,302105,171131,595
10 层(原生)93,402104,942137,830
3 层(Hical SyncMW)141,16094,588129,701
10 层(Hical SyncMW)139,685100,652127,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 连接

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

并发连接数HicalDrogonCinatra
100137,522145,663159,519
1,00063,07781,64669,797
10,00059,451100,53463,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 AvgDrogon AvgCinatra Avg
Hello World (c=100)1.91 ms1.71 ms1.77 ms
高并发 100014.94 ms12.28 ms15.37 ms
高并发 10000157.22 ms65.86 ms89.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 当前最大的短板,后面得好好优化。


资源占用

指标HicalDrogonCinatra
空载内存200.4 MB93.5 MB56.6 MB
满载内存197.0 MB93.5 MB55.8 MB
二进制大小2.1 MB1.9 MB528 KB
代码行数(bench)149 行234 行182 行

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

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


综合评价

维度HicalDrogonCinatra
常规吞吐 (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,包含完整的构建、启动、压测、采集和清理流程。

1
2
3
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)

场景HicalDrogonCinatra
Hello World (c=100)156,796.12127,980.96163,774.61
JSON 响应 (c=100)137,491.3674,466.33127,707.87
JSON Echo (c=100)111,098.9162,833.71135,691.72
路径参数 (c=100)150,121.5988,839.01118,966.68
中间件 0 层 (c=100)153,207.23112,848.48136,976.94
中间件 3 层原生 (c=100)136,898.55110,966.38138,247.89
中间件 10 层原生 (c=100)90,629.8290,728.35144,136.85
SyncMW 3 层 (c=100)136,901.79101,765.38124,463.02
SyncMW 10 层 (c=100)141,379.1999,782.52125,834.13
高并发 (c=100)110,907.90125,923.51166,994.13
高并发 (c=1000)62,416.4382,525.5173,922.75
高并发 (c=10000)59,184.0798,286.4670,999.25

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

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

场景HicalDrogonCinatra
Hello World (c=100)165,965.45168,045.72165,510.87
JSON 响应 (c=100)145,883.87100,499.66135,568.09
JSON Echo (c=100)131,825.1881,832.00141,118.30
路径参数 (c=100)130,455.3577,424.95126,874.50
中间件 0 层 (c=100)138,603.0599,891.09121,208.64
中间件 3 层原生 (c=100)131,093.7490,841.49126,053.21
中间件 10 层原生 (c=100)93,569.44114,919.03141,083.12
SyncMW 3 层 (c=100)149,265.8580,593.01127,425.23
SyncMW 10 层 (c=100)135,342.8991,646.87133,061.49
高并发 (c=100)140,643.56149,802.87149,170.97
高并发 (c=1000)65,478.3986,809.8871,551.21
高并发 (c=10000)59,258.9494,281.8962,137.10

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

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

场景HicalDrogonCinatra
Hello World (c=100)138,851.55125,580.99167,866.51
JSON 响应 (c=100)141,122.19110,304.60121,898.13
JSON Echo (c=100)127,830.7163,364.29117,510.76
路径参数 (c=100)140,279.3193,197.92129,945.28
中间件 0 层 (c=100)130,700.88107,628.47138,178.82
中间件 3 层原生 (c=100)140,912.53113,704.01130,484.50
中间件 10 层原生 (c=100)96,007.00109,177.63128,268.96
SyncMW 3 层 (c=100)137,311.12101,405.52137,215.18
SyncMW 10 层 (c=100)142,333.72110,527.95124,236.71
高并发 (c=100)161,013.19161,261.49162,390.92
高并发 (c=1000)61,336.6875,602.2763,918.27
高并发 (c=10000)59,910.86109,031.9058,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 轮取平均值 | Hical 版本:v2.6.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.3v2.6.7变化
Hello World (c=100)153,871130,413−15.2%
JSON 响应 (c=100)141,499141,285−0.2%
JSON Echo (c=100)123,585132,890+7.5%
路径参数 (c=100)140,285140,555+0.2%
中间件 0 层 (c=100)140,837135,362−3.9%
中间件 3 层 (c=100)136,302137,465+0.9%
中间件 10 层 (c=100)93,402103,909+11.2%
SyncMW 3 层 (c=100)141,160135,341−4.1%
SyncMW 10 层 (c=100)139,685138,332−1.0%
高并发 100063,07791,620+45.3%
高并发 1000059,45196,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。

场景HicalDrogonCrowOat++cpp-httplibCinatra
Hello World (c=100)130,413166,821112,29518,88790156,623
JSON 响应 (c=100)141,285100,758101,72016,59790140,979
JSON Echo (c=100)132,89078,58375,73613,15690156,924
路径参数 (c=100)140,55592,16288,13516,92090154,054
中间件 0 层 (c=100)135,362115,63995,08917,94991146,131
中间件 3 层 (c=100)137,465106,19794,10617,42591148,269
中间件 10 层 (c=100)103,909122,38987,39816,94990162,361
SyncMW 3 层 (c=100)135,341103,34186,92316,69490138,882
SyncMW 10 层 (c=100)138,332107,70788,26315,78690143,571
高并发 100091,62091,34871,5067,4099281,495
高并发 1000096,916114,86659,7383,4798961,346

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

延迟情况

场景HicalDrogonCinatra
Hello World (c=100)1.94ms1.66ms1.68ms
高并发 100010.87ms11.04ms12.73ms
高并发 1000059.83ms62.69ms81.37ms

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

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

高并发稳定性(10K timeout)

框架第 1 轮第 2 轮第 3 轮
Hical000
Drogon1,64000
Cinatra2,079812604
Crow000
Oat++11,5309,59315,434
cpp-httplib24*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.7DrogonCinatra
常规吞吐良好优秀优秀
JSON 处理快 (Boost.JSON)慢 (JsonCpp)快 (iguana)
中间件 (SyncMW)最快良好模拟数据不可比*
中间件 (协程洋葱)10 层有衰减稳定模拟数据不可比*
高并发 1000最好持平良好
高并发 10000良好最好有衰减
高并发延迟最低 60ms63ms81ms
高并发稳定性全零 timeout首轮有波动三轮均有错误
内存占用高 (200MB)中 (94MB)低 (57MB)

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

v2.6.7 三轮原始数据

第一轮(2026-07-15 03:30)

场景HicalDrogonCrowOat++cpp-httplibCinatra
Hello World (c=100)134,420175,877108,68520,37590158,187
JSON 响应 (c=100)137,390109,967102,35020,00791148,464
JSON Echo (c=100)134,36786,70977,80314,79792159,359
路径参数 (c=100)146,481100,81895,05319,64792159,819
中间件 0 层 (c=100)142,329117,76499,66919,46791148,070
中间件 3 层 (c=100)146,008118,947102,59920,13391148,891
中间件 10 层 (c=100)102,936121,19092,44518,57989164,013
SyncMW 3 层 (c=100)140,262102,41276,65317,12591137,787
SyncMW 10 层 (c=100)144,414116,96293,21516,13291153,012
高并发 100088,86692,89473,6057,9199178,686
高并发 1000093,166114,53753,3542,6449058,717

第二轮(2026-07-15 04:08)

场景HicalDrogonCrowOat++cpp-httplibCinatra
Hello World (c=100)139,198163,214116,45117,99088155,403
JSON 响应 (c=100)144,283100,42896,15015,18690138,290
JSON Echo (c=100)138,49372,90178,32611,91889153,243
路径参数 (c=100)142,11885,46871,25515,90890157,474
中间件 0 层 (c=100)132,482119,61290,07916,37091139,029
中间件 3 层 (c=100)135,86978,17079,17014,23390148,299
中间件 10 层 (c=100)110,697115,65579,55115,84990173,214
SyncMW 3 层 (c=100)139,376114,37794,72018,01389144,794
SyncMW 10 层 (c=100)134,893108,65783,86815,93989144,766
高并发 100089,26885,62879,2747,2539279,392
高并发 1000093,334112,56166,0564,3308762,351

第三轮(2026-07-15 04:42)

场景HicalDrogonCrowOat++cpp-httplibCinatra
Hello World (c=100)117,620161,374111,75018,29591156,280
JSON 响应 (c=100)142,18291,880106,66014,59890136,183
JSON Echo (c=100)125,81176,13771,07812,75389158,169
路径参数 (c=100)133,06590,19998,09615,20489144,869
中间件 0 层 (c=100)131,275109,54295,52118,01192151,296
中间件 3 层 (c=100)130,518121,474100,55017,90891147,616
中间件 10 层 (c=100)98,095130,32390,19916,42089149,857
SyncMW 3 层 (c=100)126,38493,23489,39614,94390134,065
SyncMW 10 层 (c=100)135,68897,50187,70515,28890132,935
高并发 100096,72595,52261,6397,0549186,406
高并发 10000104,246117,50059,8033,4638862,971

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