《C++ High Performance》学习总结:把性能优化从玄学变成方法论

《C++ High Performance》学习总结:把性能优化从玄学变成方法论 一句话评价:这本书不教你怎么"更快",而是教你怎么系统地发现慢在哪、为什么慢、以及怎么改。看完最大的收获不是某个技巧,而是一整套性能思维框架。 写在前面:为什么读这本书 说实话,做服务器开发这几年,性能优化基本靠"直觉 + 经验 + 试":感觉这里慢就优化这里,测一下看有没有变化。这种打法偶尔有效,但有两个致命问题: 没有量测就跑优化——经常优化了半天,发现改的根本不是热点(Amdahl 定律的教训:优化一个只占 5% 时间的函数,改出花来整体也就快 5%)。 不知道还有哪些工具——工具链一直在进化,perf、火焰图、Sanitizer、PGO,很多只知道名字没实际用过。 这本书正好把这两块补齐了。它不是零基础教程,需要你对 C++ 有一定基础(至少要懂 STL、模板、RAII),但它能把你已有的知识用性能的视角重新串一遍。 下面按书的脉络讲,但重点讲对我们服务器开发真正有用的部分,并且每条都尽量落到我们的实际场景。 这篇总结我写得比较长,因为它本质上是我重读这本书时给自己补的"教学设计":每一个之前只记结论的知识点,我都尽量补上"为什么"。读的时候你可以把它当成一条讲解链路:它是什么 → 它为什么这么设计(底层原理)→ 一个生活化的类比 → 配图或代码 → 落到我们服务器的实际场景。这样哪怕几个月后再回头看,也能靠推理而不是靠死记找回上下文。 一、先建立正确的性能思维(第 1-3 章) 1.1 值语义 vs 引用语义——很多人忽略的第一课 是什么 第 1 章从 C++ 最本质的特性讲起:C++ 默认是值语义。auto a = b 是拷贝,不是两个名字指向同一块内存。这一点和 Java、Python、C# 里"变量是引用"的直觉完全不同,也是很多新手刚从脚本语言转过来时栽的第一个跟头。 1 2 3 4 5 6 7 8 struct Player { int guid; int level; }; Player p1{10001, 60}; Player p2 = p1; // 拷贝:p2 拥有自己的内存,内容从 p1 复制 p2.level = 99; // 改 p2 不影响 p1 为什么(底层原理) ...

August 15, 2026 · 19 min · 4014 words

《深入理解计算机系统》(CSAPP)学习总结:从晶体管到网络协议的完整世界观

《深入理解计算机系统》(CSAPP)学习总结:从晶体管到网络协议的完整世界观 一句话评价:这本书解决的是"会写代码但不知道为什么"的困惑——它把从 CPU 到网络的所有中间层一次性打通。看完之后再看那些经典的 C++ 性能技巧、并发 bug、内存问题,全都"对上了号"。 写在前面:为什么服务器开发必须读这本书 做服务器开发,平时打交道最多的就是"程序为什么快/慢、为什么会崩、为什么内存涨"。这些问题你问一个纯应用层 C++ 开发者,他可能给你一堆经验;但这本书给的是底层原理——每个"经验"背后都有确切的硬件/操作系统机制支撑。 举个具体例子:我们写 std::vector 比 std::list 快,这个"经验"人尽皆知。但只有懂了缓存层次结构和 cache line(高速缓存行),你才真正明白为什么快、快多少、在什么极端情况下可能不成立。没有这个底层认知,性能优化永远是"背口诀"而不是"推导"。 这本书的三个核心理念贯穿始终: 程序员是系统的"中间层"——向上要满足应用需求,向下要理解硬件和 OS 如何配合。 抽象是管理复杂度的工具——指令集抽象 CPU,虚拟内存抽象物理内存,文件抽象 I/O,虚拟机和容器抽象整机。 “程序员的视野”——书中反复出现的经典表述:程序员看到的地址空间,是虚拟的;程序员看到的"文件",是内核在背后处理的字节流。 下面按书的 13 章讲,重点讲对服务器开发真正有用的部分,并尽量落到我们的实际场景。 一、计算机系统漫游(第 1 章) 第 1 章是全书总纲,用"hello world"程序的完整旅程串起整个系统:源文件 → 编译器 → 汇编器 → 链接器 → 可执行文件 → 加载器 → CPU 执行 → 标准输出。 1.1 一条 C 语句的一生 你写下 printf("hello, world\n"),它背后其实经历了"多国接力": 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 hello.c(文本) │ ① 预处理器:展开 #include、宏 ▼ hello.i(展开后的文本) │ ② 编译器 gcc:翻译成汇编 ▼ hello.s(汇编文本,mov/add/call 这些助记符) │ ③ 汇编器 as:把汇编翻译成机器指令(0101...) ▼ hello.o(目标文件,机器码,但地址还没定) │ ④ 链接器 ld:和库合并、分配最终地址 ▼ hello(可执行文件,躺在磁盘上) │ ⑤ 加载器:把它读进内存,从 main 开始执行 ▼ CPU 取指 → 译码 → 执行 → 访存 → 写回 ▼ 通过系统调用 write() 把字节交给操作系统 → 终端 注意一个细节:编译器的输出不是二进制,而是汇编——这是人和机器之间的一层"翻译手稿",后面第 3 章就是专门讲怎么读懂它。每一层都是下一层用"更接近机器的语言"重写一遍,直到变成 CPU 认识的 0/1。 ...

August 15, 2026 · 24 min · 5028 words

性能优化思路:从测量到代码的五层递进方法论

性能优化思路:从测量到代码的五层递进方法论 优化不靠直觉,靠数据。这篇文章是我在给 Hical 做性能优化时沉淀下来的一套方法论:从"用 profiler 找到真正的瓶颈",到架构选型、系统间交互、数据结构、再到最后的代码级微优化,一共五层,层层递进。每一层都有对应的工具、实战案例和"什么时候别优化"的提醒。看完你会发现,大部分性能问题根本轮不到写代码,先想清楚在哪一层出手,比埋头优化重要得多。 目录 第零层:测量——找到真正的瓶颈 性能优化五层模型 架构级性能优化 (子)系统间性能优化 数据结构与算法性能优化 代码级性能优化 性能回归检测:优化完了怎么守住 第零层:测量——找到真正的瓶颈 优化不是凭直觉猜,是数据驱动的工程决策。没有 profiling 数据就动手,相当于在黑屋子里开枪——打中算运气,打不中浪费子弹。我在 Hical 上踩过的第一个坑就是猜错了方向:光凭代码感觉觉得是协程帧太多,结果 perf 一跑,14.5% 的 CPU 其实耗在调度器上。所以,先量,再动。 工具链:每种工具回答一个问题 工具 回答什么问题 Hical 实战 perf record + 火焰图 CPU 时间花在哪了 perf record -g -F 99 → docker 内 10s wrk strace -c 系统调用频次和时间占比 epoll_ctl 31351 次占 25.92% GTest 性能用例 函数级性能断言 test_router_perf.cpp,100K 次 dispatch 计时 wrk / wrk2 端到端吞吐和延迟分布 wrk -c100 -t4 -d30s heaptrack / valgrind massif 内存分配热点 找意外的大分配 perf stat -d IPC、分支预测率、cache miss 看整体健康度 火焰图怎么看 拿 Hical 实际火焰图数据来说(docker 内 wrk -c10000 -d10s,perf record -g -F 99): ...

August 1, 2026 · 13 min · 2748 words

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

协程框架高并发翻车了?三个C++ Web框架实测,结果出乎意料 上一版测完总觉得哪里不对,于是改了配置又跑了一遍——这次结果老实多了。 前言:为什么重新测了一遍 这篇文章其实是第二版了。上一篇发出来之后我越想越不对劲——当时每个容器只给了 512MB 内存,fd 上限也没显式调高,跑 10K 并发的时候实际上根本没撑到一万个连接。wrk 报了一堆 connect 8983 错误,真正建立的有效连接也就一千出头,所谓的"高并发 10K"测试本质上测的还是千级并发。那组数据里 Hical 高并发"遥遥领先"的结论,现在看有水分。 这次我把配置拉满了:每容器 1024MB 内存、nofile 上调到 65536,VM 内核参数也做了对应调优(somaxconn、tcp_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 数值会低于裸机,但各框架的相对排名通常稳定。 ...

May 25, 2026 · 10 min · 2063 words

Hical 性能优化全记录

优化背景 Hical 是我写的 C++20/26 Web 框架,跑 Hello World 压测时起初只有 27K QPS,而同类框架(Cinatra 165K、Drogon 170K)差了将近一个数量级。目标很明确:追平 Cinatra/Drogon 的水平。 整个优化过程分 6 个阶段,不是拍脑袋乱改,每一步都是 perf + 火焰图定位瓶颈 → 想方案 → 写代码 → 跑压测验证 的循环。能看到数字变化才算数。 阶段 1:协程帧削减(v2.5.1-v2.5.2) 发现问题 perf 火焰图第一个大头:14.5% CPU 在 scheduler::wake_one_thread_and_unlock + pthread_cond_signal。 一开始以为是跨线程调度问题,仔细一看不是——是 Boost.Asio scheduler 每次 co_await resume 都要走的内部调度流程太重了。一个 Hello World 请求居然走了 4 个协程帧: 1 2 3 4 5 handleSession: co_await async_read → 帧 1(必需,I/O 等待) co_await router_.dispatch() → 帧 2(Router 本身是协程) co_await handler(req) → 帧 3(同步 handler 被包装成协程,不必要!) co_await async_write → 帧 4(必需,I/O 等待) 帧 1 和 4 是真正的 I/O 等待不可消除,但帧 2 和 3 完全是浪费——一个同步的 return HttpResponse("Hello") 被裹了两层协程。 ...

May 22, 2026 · 9 min · 1794 words

Hical 生产部署实践:从编译优化到 Kubernetes 容器化

Hical 生产部署实践:从编译优化到容器化 框架开发完了,测试也通过了——然后呢?“本地跑得好好的"和"线上稳定运行"之间,隔着编译优化、进程管理、反向代理、监控告警、容器编排一整套工程实践。这篇文章把 Hical 从开发环境搬到生产环境的完整链路走一遍,每个环节都给出可直接复用的配置模板。 目录 Hical 生产部署实践:从编译优化到容器化 目录 一、编译优化:榨干最后一点性能 1.1 Release 基础参数 1.2 LTO(链接时优化) 1.3 PGO(Profile-Guided Optimization) 1.4 静态链接 vs 动态链接 二、进程管理:别让服务裸奔 2.1 systemd 服务配置 2.2 信号处理与 Graceful Shutdown 2.3 多线程与多 acceptor(SO_REUSEPORT) 三、反向代理:Nginx 挡在前面 3.1 HTTP 反向代理 3.2 WebSocket 代理 3.3 SSL 终止策略 四、监控与可观测性 4.1 Prometheus 指标暴露 4.2 日志接入 ELK / Loki 4.3 健康检查端点 五、容器化部署 5.1 多阶段 Dockerfile 5.2 docker-compose 完整示例 5.3 Kubernetes 部署参考 六、性能调优检查清单 系统级 Hical 应用级 PMR 内存池 数据库连接池 日志系统 调优流程 一、编译优化:榨干最后一点性能 1.1 Release 基础参数 开发阶段用 Debug 方便调试,上线必须切 Release。区别不只是 -O2,还有 assert 消除、NDEBUG 定义(Hical 的 HICAL_LOG_TRACE 宏在 NDEBUG 下编译期完全消除): ...

May 17, 2026 · 15 min · 2992 words

Heaptrack:找出 C++ 程序中的无效内存分配

Heaptrack:找出 C++ 程序中的无效内存分配 你的火焰图上 malloc/free 占了 8% CPU。你知道分配太频繁了,但——是哪个函数在疯狂 new?每次 new 了多少字节?有没有更好的办法? 故事:每秒 17000 次 malloc,但只有 41 次是浪费的 对我的 C++20/26 Web 框架(Hical)做 Heaptrack 分析时发现:136K QPS 下每秒 17457 次堆分配,但临时分配(分配后很快释放)只有 41 次/秒——说明 PMR 内存池策略生效了。 但第一版代码没有 PMR 时,临时分配高达 13 万次/秒。Heaptrack 精确告诉了我哪些 std::string 和 std::vector 是罪魁祸首,逐个消灭后内存分配开销从 8% 降到 < 0.1%。 这篇教你用 Heaptrack 做同样的事——精确定位哪个函数在做无效分配,然后干掉它。 一、Heaptrack 是什么 Heaptrack 是一个堆内存分配追踪器,记录程序运行期间的每一次 malloc/new/free/delete,告诉你: 总共分配了多少次?多少字节? 哪个函数分配最多?(完整调用栈) 峰值内存使用在哪个时间点? 有没有泄漏(分配了但从未释放)? 临时分配有多少?(分配后很快释放——这是优化首要目标) 对比 Valgrind Massif Heaptrack Valgrind –tool=massif 性能开销 2~5x 减速 20~50x 减速 数据粒度 每次分配的完整调用栈 定期快照 GUI heaptrack_gui(丰富) ms_print(文本) 适用场景 日常分析(推荐) 极精确内存画像 一句话:Heaptrack 是 Valgrind Massif 的现代替代品,快 10 倍,信息更全。 ...

May 15, 2026 · 5 min · 873 words

perf + 火焰图:5 分钟定位 C++ 程序的 CPU 瓶颈

perf + 火焰图:5 分钟定位 C++ 程序的 CPU 瓶颈 你的服务器 CPU 跑满了,QPS 却上不去。top 告诉你"忙",但不告诉你忙在哪。怎么办? 故事:从 27K 到 136K QPS 我开发了一个 C++20/26 Web 框架(Hical),第一次压测只有 27K QPS,而同场景下 Drogon 和 Cinatra 都在 160K+。CPU 使用率 100%,top 没用,gdb 打断点太慢。 最终靠 perf record + 火焰图,5 分钟定位到瓶颈不在我的框架代码(仅占 2% CPU),而在 Boost.Asio 的调度层——跨线程 epoll_ctl 和 per-request timer 合计吃了 27% CPU。 优化后 QPS 从 27K → 136K。 这篇文章把我整套分析流程分享出来。不需要你用过 Hical,任何 C++ 服务器程序都适用。 一、工具安装(2 分钟搞定) 1 2 3 4 5 6 7 8 9 # perf(必须匹配内核版本) sudo apt install -y linux-tools-$(uname -r) linux-tools-generic # FlameGraph 脚本(Brendan Gregg 出品) git clone --depth 1 https://github.com/brendangregg/FlameGraph.git ~/FlameGraph # 放开 perf 权限(否则只能看到自己的进程) sudo sysctl -w kernel.perf_event_paranoid=-1 sudo sysctl -w kernel.kptr_restrict=0 验证: ...

May 15, 2026 · 5 min · 971 words

缓存行对 C++ 性能的影响有多大?实测告诉你

缓存行对 C++ 性能的影响有多大?实测告诉你 面试题:“遍历 vector 比遍历 list 快多少倍?"——答案不是 2 倍,是 10~100 倍。原因只有一个字:缓存。 故事:为什么 vector 存 20 个 HTTP 头比 unordered_map 还快 开发 Hical Web 框架时,我面临一个选择:HTTP 请求头用什么容器存? 直觉说 unordered_map<string, string> 查找 O(1),肯定比 vector<pair<string, string>> 的 O(n) 快。但实测结果打脸——vector 线性扫描 20 个头部,比 unordered_map 哈希查找还快 40%。 原因就是 cache line。这篇文章讲清楚这件事。 一、CPU 缓存:被忽视的性能悬崖 1.1 速度鸿沟 你的程序跑在 CPU 上,但数据存在内存里。两者之间有一道巨大的速度鸿沟: 1 2 3 4 5 6 7 8 9 10 11 ┌──────────┐ │ CPU 寄存器│ ~0.3 ns (1 cycle) ├──────────┤ │ L1 Cache │ ~1 ns (3-4 cycles) 32-48 KB / 核 ├──────────┤ │ L2 Cache │ ~4 ns (10-12 cycles) 256 KB-1 MB / 核 ├──────────┤ │ L3 Cache │ ~12 ns (30-40 cycles) 8-32 MB / 共享 ├──────────┤ │ 主内存 │ ~60-100 ns (150-300 cycles) └──────────┘ 关键数字:L1 和主内存的延迟差 100 倍。 ...

May 15, 2026 · 6 min · 1203 words

C++ 性能分析全景指南:从工具链到方法论

C++ 性能分析全景指南:从工具链到方法论 不要凭直觉猜瓶颈——人的直觉在性能问题上错误率极高。先量测,再优化。 —— 这一共识来自 Brendan Gregg《Systems Performance》与多位 CppCon 讲者的反复强调 写在前面 性能优化是 C++ 程序员的核心竞争力之一。但"性能优化"这四个字太大了——从微架构级的 cache line 对齐,到宏观的算法复杂度选择,中间跨越了多个抽象层次。 这篇文章不是某个工具的使用教程,而是试图建立一套完整的性能分析知识框架:遇到性能问题时,你该用什么工具、看什么指标、按什么思路排查。全文分为十二个部分: 核心思维 CPU Profiling 内存分析 编译优化分析 Benchmark 编写 并发与锁分析 Sanitizer 全家桶 优化决策方法论 工具选择与学习路线 Windows 平台工具链 Lua 层性能分析 游戏服务器专项性能分析 说明:前九节以 Linux 工具为例讲解通用原理(这些知识跨平台有效),第十节为 Windows/MSVC 专项,第十一、十二节针对本项目的 Lua 层与游戏服务器特性。 一、核心思维 1.1 性能问题的三种类型 所有性能问题,本质上只有三类: 类型 表现 典型原因 CPU-bound CPU 利用率高,但吞吐上不去 算法复杂度高、分支预测失败、指令级并行度低 Memory-bound CPU 利用率不高(在等数据),IPC 低 缓存未命中、TLB miss、false sharing、频繁堆分配 I/O-bound CPU 几乎空闲,程序却很慢 磁盘读写、网络等待、锁竞争(广义 I/O) 判断当前程序属于哪一类,是性能分析的第一步。用错了工具,你会在错误的方向上浪费大量时间。 1.2 Amdahl 定律的启示 优化一个占总耗时 5% 的函数,即使你把它优化到 0,整体也只快 5%。但优化一个占 60% 的函数,哪怕只快 20%,整体就快 12%。 ...

May 12, 2026 · 23 min · 4761 words