当编译器让你惊喜

Lobsters Hottest 新闻

摘要

Matt Godbolt 探讨了编译器优化如何将 O(n) 求和循环转换为 O(1) 的闭式解,突出了 Clang 和 GCC 如何采用循环展开和数学简化等复杂技术来大幅提升代码性能。

<p><a href="https://lobste.rs/s/vugypt/when_compilers_surprise_you">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/04/20 14:44

# 编译器令人惊喜的时刻 — Matt Godbolt 的博客 来源:https://xania.org/202512/24-cunning-clang 作者:我,由 LLM 校对。详细信息见文末。 编译器时不时会用一些真正聪明的技巧让我惊叹。当我第一次看到这个优化时,几乎不敢相信。我在研究循环优化时写了这个简单的函数,它用来求和到给定值的所有数字: 到目前为止看起来还不错:GCC 做了一些初步检查,然后进入一个循环,使用 `lea` 高效地求和(我们之前见过这个(https://xania.org/202512/02-adding-integers))。但仔细看循环,我们会发现一些不寻常的东西: ``` .L3: lea edx, [rdx+1+rax*2] ; result = result + 1 + x*2 add eax, 2 ; x += 2 cmp edi, eax ; x != value jne .L3 ; keep looping ``` 编译器巧妙地意识到它可以一次处理两个数字¹(https://xania.org/202512/24-cunning-clang#fn:check),利用了这样一个事实:我们会加上 `x` *和* `x + 1`,这等于加上 `x*2 + 1`。非常巧妙,我同意! 如果你把优化器调到 `-O3`,你会看到编译器更加努力地使用并行加法来向量化循环。非常聪明。 这都是 GCC 的表现。让我们看看 clang 对我们代码的处理: 这是我差点从椅子上摔下来的地方:**根本没有循环**!Clang 检查 `value` 是否为正,如果是,它执行: ``` lea eax, [rdi - 1] ; eax = value - 1 lea ecx, [rdi - 2] ; ecx = value - 2 imul rcx, rax ; rcx = (value - 1) * (value - 2) shr rcx ; rcx >>= 1 lea eax, [rdi + rcx] ; eax = value + rcx dec eax ; --eax ret ``` 对我来说,这到底在干什么根本不明显。通过回推数学,这等价于: ``` v + ((v - 1)(v - 2) / 2) - 1; ``` 展开括号: ``` v + (v² - 2v - v + 2) / 2 - 1 ``` 重新整理一下: ``` (v² - 3v + 2) / 2 + (v - 1) ``` 将 `(v - 1)` 乘以 2 / 2: ``` (v² - 3v + 2) / 2 + (2v - 2)/2 ``` 合并这些项并化简: 简化和因式分解得到 `v(v - 1) / 2`,这就是"整数和"的闭式解!真是令人惊叹²(https://xania.org/202512/24-cunning-clang#fn:why)——我们从按照编写方式的 O(n) 算法,转变成了 O(1) 的算法! 我喜欢尽管和编译器打了二十多年交道,它们仍然能让我惊喜和欣喜。多年来为了让编译器优秀而投入的经验和工作确实令人谦卑,也令人鼓舞。 我们快到这个系列的尾声了——还有很多要说,但那得等下一次了。明天会有点不同:到时见! *观看伴随这篇文章的视频(https://youtu.be/V9dy34slaxA)。* --- *这篇文章是编译器优化降临节 2025(https://xania.org/AoCO2025-archive) 的第 24 天,一个 25 天的系列,探索编译器如何转换我们的代码。* *← 有点变化(https://xania.org/202512/23-switching-it-up) | 谢谢(https://xania.org/202512/25-thank-you) →* *这篇文章由人类(Matt Godbolt(https://xania.org/MattGodbolt))撰写,由 LLM 和人类审阅和校对。* *在 Patreon(https://patreon.com/c/mattgodbolt) 或 GitHub(https://github.com/sponsors/compiler-explorer) 上支持 Compiler Explorer,或在 Compiler Explorer Shop(https://shop.compiler-explorer.com/) 购买 CE 产品。* 发布于 2025 年 12 月 24 日 CST 06:00:00。

相似文章

信任你的编译器:现代C++

Hacker News Top

本文对比了旧的C++性能技巧与现代编译器的能力,表明编译器现在能够将朴素代码优化得比手工调整的技巧更好。包含在AMD Zen 5上使用Clang 21的基准测试。

查询循环:编译器谋杀之谜

Lobsters Hottest

一位 Ferrocene/Rust 编译器工程师详细描述了一场为期一周的调试历程,该崩溃由查询循环引起,最终揭示了三个相互作用的错误,导致了OOM和无限循环。

为什么ML/OCaml适合编写编译器(1998)

Lobsters Hottest

这篇1998年的文章认为,ML和OCaml非常适合编写编译器,因为它们具有垃圾收集、尾递归优化以及带有模式匹配的代数数据类型等特性,这些特性简化了复杂编译器数据结构的处理。

优化CPU密集型Go热路径的笔记

Hacker News Top

本文讨论了CPU密集型Go代码的性能优化技术,指出了泛型和接口抽象因无法内联而产生的局限性,并主张在热路径中使用代码复制。文章通过一个Brotli移植示例和深入基准测试进行了说明。