Hical 性能剖析实战:perf + 火焰图定位 QPS 瓶颈

Hical 性能剖析实战:perf + 火焰图定位 QPS 瓶颈 在 C++ 框架性能实测中,Hical 的 Hello World QPS(~27K)远低于 Cinatra(165K)和 Drogon(161K)。静态链接 + strip 验证后确认瓶颈不在链接方式。本文记录用 perf record + 火焰图精确定位 CPU 热点的全过程。 目录 1. 背景与动机 2. Profiling 环境搭建 3. 数据采集 4. 火焰图分析 5. 优化方向 6. 复现指南 1. 背景与动机 1.1 已排除的因素 在本次 profiling 之前,已经通过对照实验排除了以下因素: 假设 验证方式 结论 动态链接 Boost 有性能损耗 改为 Boost 静态链接,重跑压测 QPS 无显著变化(27K → 27K) strip 影响性能 strip vs 不 strip 对比 无影响(符号表不参与运行时) 二进制体积(icache 压力) 7.8M(strip) vs 9.3M(不strip) QPS 在噪声范围内,非瓶颈 排除结论:性能瓶颈在框架运行时架构,需要 profiling 定位具体热点函数。 ...

May 9, 2026 · 4 min · 687 words

Hical 踩坑实录五部曲(三):自研日志系统的 8 个血泪教训

Hical 踩坑实录五部曲(三):自研日志系统的 8 个血泪教训 引言 Hical 没有用 spdlog、glog 或任何第三方日志库——整套日志系统完全自研,覆盖 20+ 个源文件:格式化、Sink 后端、文件轮转、异步双缓冲、通道分流、HTTP 集成、运行时调级。 自研日志的好处在开发心得里聊过了。这篇只聊坑——从"能跑"到"能在生产环境跑"的过程中,踩过的 8 个真实问题。 目录 Hical 踩坑实录五部曲(三):自研日志系统的 8 个血泪教训 引言 目录 坑 1:异步写盘的背压——日志不应该成为延迟来源 坑 2:双缓冲析构时丢日志 坑 3:多线程 Sink 分发的锁竞争——COW 模式的引入 坑 4:日志注入——一条恶意日志伪造十行告警 坑 5:LogAdmin 的审计致盲攻击 坑 6:TRACE 日志在 Release 构建中的隐性开销 坑 7:trace-id 生成的性能瓶颈——OpenSSL 全局锁 坑 8:文件轮转的误删风险 总结:自研日志系统的检查清单 坑 1:异步写盘的背压——日志不应该成为延迟来源 现象:压测时发现 P99 延迟间歇性飙高。排查发现不是业务逻辑慢,而是日志写入阻塞了请求线程。 根因:早期版本的日志直接在调用线程同步 fwrite。高并发下磁盘 I/O 成为瓶颈,日志调用从微秒级退化为毫秒级。 引入异步 AsyncFileSink 后,新的问题出现——如果日志产生速度远超写盘速度(比如某个 bug 触发了大量 ERROR 日志),前端缓冲区会无限增长直到 OOM。 解决方案:背压保护——缓冲区超限时主动丢弃,而非阻塞或 OOM: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // AsyncFileSink.cpp — write() 中的背压丢弃 void AsyncFileSink::write(std::string_view formattedLine) { std::lock_guard<std::mutex> lock(m_bufMutex); // 背压保护:缓冲区过大时丢弃(防止内存爆炸) if (m_curBuf.size() > m_opts.backpressureLimit) { m_dropped.fetch_add(1, std::memory_order_relaxed); return; // 丢弃这条日志,但不阻塞调用线程 } m_curBuf.append(formattedLine.data(), formattedLine.size()); // 缓冲区接近满时通知后台线程 if (m_curBuf.size() >= m_opts.bufferSize) { m_cond.notify_one(); } } 关键细节:丢弃计数不是默默吞掉的——后台线程在每次刷盘时检查丢弃计数,并将统计写入日志文件: ...

May 9, 2026 · 8 min · 1702 words

2026 年 C++ Web 框架横评:Hical vs Drogon vs Cinatra vs Crow vs Oat++

2026 年 C++ Web 框架横评:Hical vs Drogon vs Cinatra vs Crow vs Oat++ 如果你在 2026 年启动一个需要 C++ Web 服务的项目,面前摆着 Drogon、Cinatra、Crow、Oat++ 和 Hical 五个选择。该怎么选?本文从架构设计、异步模型、内存管理、功能完整度、开发体验五个维度做一次横向对比,帮你快速定位最适合的框架。 一句话概括 框架 一句话定位 Drogon 久经考验的高性能全栈框架,TechEmpower 榜单常客 Cinatra header-only 的 C++20 协程 HTTP 框架,阿里 yalantinglibs 生态成员 Crow 极简轻量的微框架,Express.js 风格,上手最快 Oat++ 零依赖、内置 Swagger 的 API 框架,嵌入式友好 Hical 自研 HTTP/WS 栈 + PMR 内存池 + C++26 反射的现代全栈框架 核心对比表 Hical Drogon Cinatra Crow Oat++ C++ 标准 C++20(C++26 就绪) C++17 / C++20 C++20 C++14 / C++17 C++11+ 异步模型 协程(co_await 全链路) 回调 + 协程混合 协程(async_simple::Lazy) 回调 自研异步 API 内存管理 PMR 三层内存池 默认分配器 默认分配器 默认分配器 默认分配器 HTTP 解析 picohttpparser(自研栈) 自研(Trantor) 自研 自研 自研 SSL/TLS 编译期模板分支 运行时分支 运行时配置 运行时分支 运行时分支 路由 哈希表 O(1) + 参数线性 基数树 字符串匹配 + 正则 前缀树 Controller 映射 中间件 洋葱模型(协程链) Filter 链 AOP 切面 基础 Interceptor WebSocket 内置(自研 RFC 6455) 内置 内置 内置 内置 Cookie / Session 内置(RFC 6265) 内置 有限 有限 有限 文件上传 内置(DoS 防护) 内置 内置 需手动 内置 静态文件 内置(ETag/304) 内置 内置 需手动 有限 ORM 协程化 DB 中间件(MySQL) 内置(PG/MySQL/SQLite) 无(生态有 ormpp) 无 模块化(PG/SQLite/Mongo) OpenAPI/Swagger 内置(自动生成 + Swagger UI) 第三方 无 无 内置 日志系统 内置(6 级 + 异步双缓冲 + 通道路由) 自带(简易) 基础 无 自带(loggers) CORS 内置中间件 内置 需手动 需手动 内置 HTTP/2 不支持 支持 不支持 不支持 不支持 反射/自动序列化 C++26 双轨(原生 + 宏) 无 生态有 struct_json/struct_pack 无 宏 DTO 系统 HTTP 客户端 无 内置 内置(协程化) 无 内置 外部依赖 Boost.Asio + OpenSSL + zlib Trantor + jsoncpp + … 无(可选 OpenSSL) Asio 零依赖 License MIT MIT MIT BSD-3 Apache-2.0 深度对比 1. 异步模型 这是选框架时最该关注的维度,因为它决定了你写业务逻辑的方式。 ...

May 8, 2026 · 4 min · 820 words

Hical Linux 开发环境搭建

Hical Linux 开发环境(实操记录) 概述 将 Hical C++20 Web 框架的日常开发环境从 Windows 本地迁移到 Linux 虚拟机,通过 Oracle VM VirtualBox 运行 Ubuntu 24.04.3 LTS Server 虚拟机,使用 Tabby Terminal + VS Code Remote SSH 进行开发。 为什么迁移 痛点 Linux 解决 MSYS2/vcpkg 路径混乱、ABI 不兼容 原生 apt 包管理,toolchain 统一 liburing/io_uring 仅 Linux 可用 原生支持,解锁异步文件 I/O 最优路径 CI 环境是 Ubuntu,本地是 Windows 开发即 CI,减少"本地能跑 CI 挂"的问题 clang-format/clang-tidy 版本对齐困难 直接装 LLVM APT 源的 clang-20 Sanitizer(ASan/UBSan)Windows 支持差 Linux 原生完美支持 宿主机配置 项目 值 处理器 Intel Core i7-11700K @ 3.60GHz(8核16线程) 内存 32 GB 操作系统 Windows 10 Enterprise LTSC 2021 VM 存放磁盘 D:\ (900 GB,SSD) 一、VirtualBox 虚拟机创建 1.1 下载 软件 版本 下载地址 VirtualBox 7.1+ https://www.virtualbox.org/wiki/Downloads Ubuntu ISO ubuntu-24.04.3-live-server-amd64.iso(3.1 GiB) https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/24.04/ 选择 Server 版(无桌面 GUI),减少资源占用。国内推荐清华镜像下载更快。 ...

May 8, 2026 · 17 min · 3460 words

Hical 踩坑实录五部曲(二):MSVC / GCC / Clang 三平台 C++20 编译差异

Hical 踩坑实录五部曲(二):MSVC / GCC / Clang 三平台 C++20 编译差异 引言 Hical 从第一天起就要求在 GCC 14+、Clang 20+、MSVC 2022+ 三个编译器上通过 CI。框架大量使用了 C++20 新特性:Concepts、co_await 协程、PMR 内存池、std::format、__VA_OPT__ 递归宏。 三平台兼容的代价就是踩三倍的坑。 这篇文章记录了开发 Hical 过程中遇到的编译器差异踩坑——每个坑按统一结构展开:现象 → 最小复现 → 根因 → 解决方案。 目录 Hical 踩坑实录五部曲(二):MSVC / GCC / Clang 三平台 C++20 编译差异 引言 目录 坑 1:模板参数推导差异——GCC 过、MSVC 报错 坑 2:Concepts 约束检查时机差异 坑 3:__VA_OPT__ 宏展开行为差异 坑 4:PMR allocator 传播行为差异 坑 5:std::format 可用性与行为差异 坑 6:协程 promise_type 与异常处理差异 坑 7:链接顺序敏感——Windows 特有的 ws2_32 问题 经验总结:三平台兼容清单 编译器设置 C++20 特性 协程 一般性 坑 1:模板参数推导差异——GCC 过、MSVC 报错 现象:一段在 GCC 14 上完美编译的透明哈希代码,在 MSVC 上报 C2672: no matching overloaded function found。 ...

May 8, 2026 · 7 min · 1357 words

VM 编译运行 Hical Benchmark 流程:不走 Docker 的本地压测方案

VM 直接编译运行 Hical Benchmark 流程 不走 Docker,直接在 VM 上编译项目源码 + bench_server,用 wrk 压测。 适用于需要验证本地代码改动的场景(如性能优化后的 A/B 对比)。 前置条件 VM 里已有 Hical 项目源码:~/projects/Hical/ 已安装编译依赖(GCC 14+、CMake 3.20+、Boost 1.82+、OpenSSL) 挂接点 /mnt/hical_host/ 对应宿主机 d:/hical/Hical/ 一、同步宿主机代码改动到 VM 如果在宿主机上修改了源码,需要先拷贝到 VM 项目目录: 1 2 3 4 5 # 示例:拷贝 3 个改动文件 cp /mnt/hical_host/src/core/HttpServer.h \ /mnt/hical_host/src/core/HttpServer.cpp \ /mnt/hical_host/src/core/HttpSessionImpl.cpp \ ~/projects/Hical/src/core/ 或者整体同步 src 目录: 1 rsync -av /mnt/hical_host/src/ ~/projects/Hical/src/ 二、编译 1 2 3 4 5 6 7 8 9 10 cd ~/projects/Hical # 清理旧构建(可选,首次或 CMake 配置变更时执行) rm -rf build # 配置:Release + 编译 bench_server cmake -B build -DCMAKE_BUILD_TYPE=Release -DHICAL_BUILD_BENCH=ON # 编译(-j 自动取 CPU 核数) cmake --build build -j$(nproc) 编译产物:build/bench_server(直接链接本地 hical_core 库,代码改动即生效)。 ...

May 8, 2026 · 6 min · 1193 words

Hical 踩坑实录五部曲(一):Boost.Asio 协程开发的 N 个坑

Hical 踩坑实录五部曲(一):Boost.Asio 协程开发的 N 个坑 引言 Hical 的所有异步 I/O 都基于 Boost.Asio 协程(co_await + boost::asio::use_awaitable)。路由处理器返回 Awaitable<HttpResponse>,中间件用洋葱模型 co_await next(req),连接池用 co_await timer.async_wait() 做非阻塞等待。 协程消除了回调地狱,但引入了一套全新的陷阱。这篇记录的每一个坑,都是在压测或线上环境中真实触发过的。 目录 Hical 踩坑实录五部曲(一):Boost.Asio 协程开发的 N 个坑 引言 目录 坑 1:co_await 后 this 悬挂——对象已析构 坑 2:协程异常传播——catch 里不能 co_await 坑 3:steady_timer 当协程信号量的技巧 坑 4:jthread vs thread——精准匹配停止信号 坑 5:多线程 io_context + 协程的线程安全陷阱 坑 6:detached 协程的异常黑洞 坑 7:io_context::stop() 不等于安全退出 总结:协程安全编程检查清单 坑 1:co_await 后 this 悬挂——对象已析构 现象:压测时低概率崩溃,堆栈指向 TcpServer 的 accept 循环,访问了已释放的内存。 最小复现: 1 2 3 4 5 6 7 8 9 10 11 // ❌ 危险的写法 Awaitable<void> TcpServer::acceptLoop() { while (running_) { auto socket = co_await acceptor_.async_accept(use_awaitable); // ⚠️ 如果在 co_await 期间 TcpServer 被析构, // this 已经是悬空指针! this->createConnection(std::move(socket)); // 💥 use-after-free } } 根因:协程帧通过 co_spawn(io_context, coroutine, detached) 提交到 io_context。协程帧的生命周期由 io_context 管理,与创建协程的对象完全分离。 ...

May 7, 2026 · 8 min · 1586 words

C++ Web 服务日志最佳实践:Hical 日志系统完全指南

C++ Web 服务日志最佳实践:Hical 日志系统完全指南 引子:生产环境 printf 调试?该升级了 不少 C++ 服务器项目在早期会这样写日志: 1 2 printf("[INFO] user login: uid=%d\n", uid); fprintf(stderr, "[ERROR] db connect failed\n"); 这没什么问题——直到你的服务跑到生产环境,遇到以下场景: 日志文件膨胀:跑了三天,单个 app.log 已经 8GB,grep 一下需要等几分钟 性能抖动:每次写日志都 fwrite + fflush,高并发时 I/O 成为瓶颈 信息不够:出了问题只知道"某某接口报错",不知道是哪个请求、哪个用户 无法动态调级:想临时开 DEBUG 排查问题,必须重启服务 日志散落各处:访问日志、审计日志、业务日志混在同一个文件里,难以分析 Hical 的日志系统正是为了解决这五个问题而设计的。本文从最简用法出发,逐步覆盖文件轮转、异步写盘、结构化日志、通道分流、HTTP 集成到运行时调级,每个场景都给出可直接复制的代码。 1. 快速上手:三种 API 对比 Hical 日志提供三种书写风格,适用不同场景: std::format 风格(首选) 1 2 3 4 5 #include <hical/Log.h> HICAL_LOG_INFO("server started on port={}", 8080); HICAL_LOG_WARN("connection pool low, available={}", pool.available()); HICAL_LOG_ERROR("db query failed: sql={} err={}", sql, ec.message()); 格式字符串在编译期校验(借助 std::format_string<Args...>),参数类型不匹配直接报错,不会等到运行时才崩溃。这是最常见的用法。 ...

May 6, 2026 · 6 min · 1208 words

Hical 协程入门:告别回调地狱,用 co_await 写异步 C++

Hical 协程入门:告别回调地狱,用 co_await 写异步 C++ 传统 C++ 异步编程离不开回调嵌套、状态机、手动生命周期管理——代码写得像意大利面。C++20 协程从根本上改变了这一切:异步代码写起来和同步一样直观,编译器帮你管理暂停与恢复。本文从零讲解如何在 Hical 框架中使用协程,不需要你懂 Boost.Asio 底层。 什么是协程?30 秒版本 传统回调式: 1 2 3 4 5 6 7 8 9 10 11 12 // 回调嵌套——"回调地狱" socket.async_read(buffer, [&](error_code ec, size_t n) { if (!ec) { socket.async_write(buffer, [&](error_code ec2, size_t) { if (!ec2) { socket.async_read(buffer, [&](error_code ec3, size_t) { // 继续嵌套... }); } }); } }); 协程式: 1 2 3 4 // 同样的逻辑,协程版——像写同步代码一样 auto n = co_await socket.async_read(buffer, use_awaitable); co_await socket.async_write(buffer, use_awaitable); auto n2 = co_await socket.async_read(buffer, use_awaitable); co_await 会暂停当前函数,等 I/O 完成后自动恢复执行。没有回调,没有嵌套,错误用 try/catch 处理。 Hical 对协程做了什么封装? Hical 在 Coroutine.h 中提供了三个核心工具: ...

May 5, 2026 · 4 min · 762 words

C++ 也能优雅写 Web?5 分钟用 Hical 搭建 REST API

C++ 也能优雅写 Web?5 分钟用 Hical 搭建 REST API 提到 C++ 写 Web 服务,你脑海中浮现的可能是满屏的模板报错、手动解析 HTTP 报文、以及回调嵌套到看不清缩进的代码。但在 2026 年,C++20 协程 + PMR 内存池 + C++26 反射的组合,已经让 C++ Web 开发体验发生了质变。本文用 Hical 框架带你体验:10 行代码启动 HTTP 服务器,40 行代码搞定完整 REST API。 10 行代码,启动 HTTP 服务器 1 2 3 4 5 6 7 8 9 10 11 12 13 #include "core/HttpServer.h" using namespace hical; int main() { HttpServer server(8080); server.router().get("/", [](const HttpRequest&) -> HttpResponse { return HttpResponse::ok("Hello, hical!"); }); server.start(); } 1 2 curl http://localhost:8080/ # Hello, hical! 没有工厂类,没有 Builder 链,没有 XML 配置。创建服务器、注册路由、启动 —— 三步完事。 ...

May 4, 2026 · 4 min · 666 words