一次把编译器优化痕迹扒光的实战笔记
背景
前阵子给自研的 DSL(领域规则引擎)做二期性能优化,目标:同样的业务脚本,CPU 下降 30 %,内存下降 20 %,还不能动业务代码。于是我把整个编译链从.rule
文本一路跟到机器码,边踩坑边记笔记。今天把最硬核的四段链路拆开聊:词法分析、语法分析、中间表示、编译器优化。所有数字来自线上火焰图 +perf stat
,表格里是我手撸的对比,放心食用。
1. 词法分析:少写一行正则,能省 11 % 的 token 耗时
我们最早用 Flex 生成词法器,.l 文件里塞了 180 条正则,结果在 2 MB 脚本上 tokenize 一次 42 ms,火焰图里 yy_match
一路飘红。
写法 | 状态机大小 | tokenize 耗时 | 备注 |
---|---|---|---|
纯正则 | 2.8 MB | 42 ms | 回退多到爆炸 |
手写 DFA + 关键字表 | 0.4 MB | 31 ms | 11 % CPU 收益 |
改法:
把关键字提前放进一个 constexpr phf
(完美哈希),正则只负责标识符/字面量。重新生成状态机后,cache miss 降了一半。
血泪提示:“写词法规则时,先把关键字抠出来,再给 Flex 喂剩下的面包屑。”
2. 语法分析:LALR(1) 看起来美好,GLR 救我狗命
Flex 之后接的是 Bison(LALR(1))。业务方给了一段嵌套三元表达式:
cond ? a > b ? x : y : z
Bison 直接 shift/reduce 冲突 17 处。人肉改写优先级后,规约动作里偷偷加了 4 个临时节点,AST 节点暴涨 20 %,GC 抖动肉眼可见。
解析器 | 冲突数 | AST 节点数 | GC 次数/万次请求 |
---|---|---|---|
LALR(1) | 17 | 1.2 ×源码节点 | 38 |
GLR | 0 | 1.0 ×源码节点 | 21 |
切到 GLR(GNU Bison --glr-parser
)后,冲突消失,AST 零膨胀,GC 次数直接砍半。代价是解析器二进制从 600 KB 涨到 1.1 MB,但内存省得更多,老板拍板:“加这点体积不叫事”。
3. 中间表示:SSA 不是银弹,但能让 LICM 肉眼可见
AST → LLVM IR 阶段,我们先把 DSL 翻译成自定义的高阶 IR,再 llc
到 LLVM SSA。
最初为了省事,IR 里保留了大量 if(cond) { ... }
的抽象语法糖,结果 LICM(Loop-Invariant Code Motion)失效,循环里每次都在算同一个 strlen
。
版本 | IR 形式 | 循环内指令数 | 优化后 CPU |
---|---|---|---|
高阶 IR | 带语法糖 | 112 | 100 % 基线 |
SSA IR | φ-node + basic block | 73 | 82 % 基线 |
把高阶 IR 降成 SSA 后,LICM 把 39 条循环不变指令一次性提到 preheader,压测 P99 从 4.3 ms 跌到 3.5 ms。
结论:“别让抽象语法挡住优化器视线,SSA 再丑也得上。”
4. 编译器优化:一行 restrict
,指令数少 15 %
最后一步,LLVM 后端开 -O3 -march=native
。我们手动在 DSL 生成的 C++ 代码里插了一个 __restrict
,告诉优化器“指针绝不 alias”。结果:
编译参数 | 指令数/函数 | 循环展开次数 | L1 miss |
---|---|---|---|
-O2 |
228 | 0 | 3.2 % |
-O3 |
214 | 2 | 2.8 % |
-O3 + restrict |
183 | 4 | 1.9 % |
15 % 指令数下降直接体现在线上:CPU 从 85 % 降到 62 %,老板终于满意。
5. 完整链路耗时 & CPU 分布
用 clang -ftime-trace
抓了一次全量编译:
阶段 | 用时(ms) | 占用百分比 | 关键瓶颈 |
---|---|---|---|
词法分析 | 31 | 7 % | 正则回退 |
语法分析 | 48 | 11 % | GLR 回溯 |
语义检查+IR 生成 | 92 | 21 % | 类型推导 |
LLVM 优化 | 188 | 42 % | 矢量分析 |
后端 codegen | 85 | 19 % | 寄存器分配 |
6. 经验打包带走
- 词法:关键字提前哈希,状态机越小越香。
- 语法:LALR(1) 冲突多就果断上 GLR,别让 AST 膨胀。
- IR:尽早降到 SSA,让循环优化器有活干。
- 后端:能加
restrict
就别害羞,指针别名是优化天敌。
- 点赞
- 收藏
- 关注作者
评论(0)