惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Hacker News
The Hacker News
C
Cisco Blogs
P
Privacy & Cybersecurity Law Blog
Cloudbric
Cloudbric
S
Security Affairs
PCI Perspectives
PCI Perspectives
The Last Watchdog
The Last Watchdog
AWS News Blog
AWS News Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
N
News and Events Feed by Topic
W
WeLiveSecurity
T
Tenable Blog
L
LINUX DO - 最新话题
T
Tor Project blog
Help Net Security
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
P
Proofpoint News Feed
爱范儿
爱范儿
O
OpenAI News
Hacker News - Newest:
Hacker News - Newest: "LLM"
Y
Y Combinator Blog
I
Intezer
C
Check Point Blog
Stack Overflow Blog
Stack Overflow Blog
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
S
Securelist
P
Privacy International News Feed
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
V
Vulnerabilities – Threatpost
Schneier on Security
Schneier on Security
量子位
SecWiki News
SecWiki News
L
Lohrmann on Cybersecurity
T
Threat Research - Cisco Blogs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
M
MIT News - Artificial intelligence
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Scott Helme
Scott Helme
H
Help Net Security
Vercel News
Vercel News
云风的 BLOG
云风的 BLOG
Spread Privacy
Spread Privacy
Know Your Adversary
Know Your Adversary
I
InfoQ
TaoSecurity Blog
TaoSecurity Blog
Blog — PlanetScale
Blog — PlanetScale
N
News | PayPal Newsroom
小众软件
小众软件
C
CERT Recently Published Vulnerability Notes

Watermelonabc的Blog

福州大学单片机原理与应用(跨学科)复习 福州大学毛概期末复习 x86 汇编语言详解 - 2 CCB 2026 Half-Final Guangzhou 视频也可以含恶意代码吗 Hello,2026 2025 福州大学算法与数据结构复习 2025 福州大学习概期末复习 Frida 入门 记一场贴吧交流 虚拟机保护 2025 强网杯 S9 Reverse 复现 你如何在 FZU 取到快递 2025 福州大学离散数学笔记 2025 福州大学数字逻辑笔记 C# 逆向入门 安卓刷机一点通 Z3-Solver 教程 出题人的 “恶意” CSAPP Lab2 Note Hash 系列算法 程序脱壳 Linux 简单教程 安卓逆向介绍 IDA 类型修复 到旗山了
编译器为我做了什么
Watermelonabc · 2025-07-19 · via Watermelonabc的Blog

编译器为我做了什么

Backstory

考虑一下不同代码写法会不会影响性能?例如:

写法 A

1
2
3
4
5
6
7
8
9
int sum(const std::vector<int> &v)
{
int result = 0;
for(size_t i = 0; i < v.size(); ++i)
{
result += v[i];
}
return result;
}

写法 B

1
2
3
4
5
6
7
8
9
int sum(const std::vector<int> &v)
{
int result = 0;
for(int x : v)
{
result += x;
}
return result;
}

这两种写法在功能上等效。但如果想真正确认哪一种在性能上取胜,则需要使用基准测试 (benchmark)。在这里,写法 B 的性能更好。

与此同时,我们还想知道自己能否承担这种性能提升的代价,这时需要查看编译器输出的汇编结果了。

不要只看汇编结果。我们看不完那么多汇编指令,而且这还是经过编译器优化修改过的产物。如果需要比较,请一并做好基准测试。

x86_64 Assembly 101

寄存器

x86_64 架构下的寄存器有:

  • rax, rbx, rcx, rdx, rsp, rbp, rsi, rdi, r8-r15
  • xmm0-xmm15
  • rdi, rsi, rdx 等用来存储变量
  • rax 用于存储返回值

在 x86_64 架构中,rax 的长度是 64 位,eax 的长度是 32 位。当数据写入 eax 中时,实际上是将 rax 的高位的 32 个位清 0,仅在低位的 32 个位写入数据。这是一个寄存器相关的技巧。

指令格式

我们使用 Intel 语法格式:

1
2
3
4
op
op dest
op dest, src
op dest, src1, src2

这里的 op 指代任意汇编指令,比如 mov, calldestsrc 是寄存器或者内存指针,使用 “基址 + 偏移” 表示,即 base + [reg1] + [reg2 * (1, 2, 4 or 8)]

汇编指令与伪 C 代码的对照

汇编

1
2
3
4
5
6
mov eax, DWORD PTR [r14]
add rax, rdi
add eax, DWORD PTR [r14+4]
sub eax, DWORD PTR [r14+4*rbx]
lea rax, [r14+4*rbx]
xor edx, edx

伪 C 代码

1
2
3
4
5
6
int eax = *r14 
rax += rdi;
eax += r14[1];
eax -= r14[rbx];
int *rax = &r14[rbx];
edx = 0;

有人会将 edx = 0 肉编译为 mov edx, 0,但这里却是用 xor edx, edx。有两个原因:1)mov 中使用的 0 必须以 4 字节存储,体积要大得多;2)指令集已经明白 xor 有清 0 的作用,因此进行对应优化 —— 换句话说,xor 更偏向 “重置”。

Compiler Explorer

最初的 Compiler Explorer 是一个本地 shell 脚本,主要功能部分长这样:

1
2
3
g++ /tmp/test.cc -O2 -c -S -o --masm=intel \
| c++filt \
| grep -vE '\s+\.'

然后用 Unix 工具 watch 持续运行脚本,得到近实时编译结果:

1
2
3
4
5
6
7
8
sum(std::vector<int, std::allocator<int> > const&):
.LFB786:
mov rcx, QWORD PTR [rdi]
mov rax, QWORD PTR 8[rdi]
sub rax, rcx
shr rax, 2
mov rsi, rax

后面部署到线上,有了 GUI,可以在 Compiler Explorer 上探索。

通过示例,我们可以清晰地看到写法 A 和写法 B 的汇编区别:写法 B 的汇编明显更短。

本文的示例使用 x86-64 gcc 15.1 -O2 编译这些函数,而原作者使用 x86-64 gcc 8.1 编译,得到的结果会有差异。

通过引入 numeric 库,我们还有写法 C:

1
2
3
4
int sum(const std::vector<int> &v)
{
return accumulate(begin(v), end(v), 0);
}

写法 C 的汇编几乎和 B 相同,只有两个指令的差异 —— 不过是位置的变化罢了。