《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

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

Linux 性能分析与优化实战指南:perf / 火焰图 / Heaptrack 全流程

Linux 性能分析与优化实战指南 基于 Hical 项目的 Ubuntu 24.04 VM 环境(VirtualBox,8 CPU / 16GB RAM)。 前置条件:已完成 Hical-Linux开发环境 和 VM编译运行Hical-Benchmark流程 的环境搭建。 目录 零、工具安装 一、perf stat:硬件计数器分析 二、perf record + 火焰图:CPU 热点定位 三、Heaptrack:内存分配分析 四、缓存层次与 cache line 五、实战:Hical 性能分析全流程 六、速查卡 零、工具安装 0.1 一键安装所有性能工具 1 2 3 4 5 6 7 8 9 10 11 # perf(必须匹配内核版本) sudo apt install -y linux-tools-$(uname -r) linux-tools-generic # heaptrack(内存分配分析) sudo apt install -y heaptrack heaptrack-gui # FlameGraph(火焰图生成脚本) git clone --depth 1 https://github.com/brendangregg/FlameGraph.git ~/FlameGraph # 辅助工具 sudo apt install -y valgrind strace sysstat hwloc 0.2 内核参数调整(perf / heaptrack 权限) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # ── perf 权限 ── # 查看当前值(默认通常是 4,限制很严) cat /proc/sys/kernel/perf_event_paranoid # 临时放开(重启失效) sudo sysctl -w kernel.perf_event_paranoid=-1 sudo sysctl -w kernel.kptr_restrict=0 # ── ptrace 权限(heaptrack --pid 运行时附着需要) ── # 查看当前值(默认 1,禁止非父进程 ptrace) cat /proc/sys/kernel/yama/ptrace_scope # 临时放开(重启失效) sudo sysctl -w kernel.yama.ptrace_scope=0 # ── 永久生效(写入配置文件) ── cat << 'EOF' | sudo tee /etc/sysctl.d/99-perf.conf kernel.perf_event_paranoid = -1 kernel.kptr_restrict = 0 kernel.yama.ptrace_scope = 0 EOF sudo sysctl --system 各级别含义: ...

May 15, 2026 · 25 min · 5176 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

火焰图对比分析:自研 HTTP 栈 vs Beast HTTP 栈

火焰图对比分析:自研 HTTP 栈 vs Beast HTTP 栈 Hical v2.6.0 完成了从 Beast HTTP 到自研零拷贝 HTTP 栈的迁移。本文通过两份火焰图的逐项对比,用数据量化"去 Beast"到底省了什么、省了多少,以及当前性能瓶颈到底在哪里。 目录 火焰图对比分析:自研 HTTP 栈 vs Beast HTTP 栈 目录 1. 测试环境与采集方式 2. 总体热度分布对比 flame.svg(自研路径)— 总计 ~299 亿 samples flame1.svg(Beast 路径)— 总计 ~411 亿 samples 3. HTTP 解析:picohttpparser vs Beast parser 4. Header 存储:栈数组 vs 链表堆分配 5. 响应序列化:FixedBuffer vs Beast serializer 自研路径 Beast 路径 6. 发送路径:sendto vs sendmsg 7. 协程与调度开销 8. 内核瓶颈:loopback softirq 的天花板 epoll_ctl 已不是瓶颈 9. strace 佐证:系统调用频率 10. 结论与下一步 量化收益:去 Beast 到底省了多少 当前性能分布总结 下一步优化方向 最终结论 1. 测试环境与采集方式 项目 配置 环境 Ubuntu VM (Docker 内),GCC 14,-O2 -g 压测工具 wrk,4 线程,keep-alive 采集 perf record -F 99 -g -p <pid> → FlameGraph 生成 SVG 辅助 strace -c -f -p <pid> 统计系统调用频率 对比目标 flame.svg(自研路径 v2.6.0)vs flame1.svg(Beast 路径) 两份火焰图采集条件一致,唯一区别是 HTTP 处理栈的实现路径。 ...

May 12, 2026 · 5 min · 918 words