< 比 <= 在 C++ 中更快吗?用实例破除迷思

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:一个流传数十年的迷思

\n\n

如果你曾经浏览过 Stack Overflow 的 C++ 板块,很可能见过这个问题:“< 比 <= 更快吗?”。这个问题积累了数千次浏览和数十页的讨论。这并不奇怪——代码优化 这个话题一直让各个水平的程序员都为之着迷。

\n\n

这个逻辑看似坚不可摧:运算符 < 比较两个数并返回结果,而 <= 做同样的事情,但 额外检查了相等性。因此,< 应该运行得更快,对吗?尤其是在 循环 中,比较操作要执行数百万次。

\n\n

今天,我们将一劳永逸地澄清这个迷思。你将了解到:

\n
    \n
  • 这些运算符在处理器层面究竟是如何工作的。
  • \n
  • 为什么编译器是你最好的朋友(或者如果你不信任它,它也可能是你的敌人)。
  • \n
  • 什么时候 微优化 有意义,什么时候是浪费时间。
  • \n
\n\n

< 和 <= 运算符在汇编层面是如何工作的?

\n\n

要理解是否存在差异,我们需要“深入引擎盖”——看看编译器将你的 C++ 代码转换成了什么。考虑两个简单的函数:

\n\n
bool less_than(int a, int b) {\n    return a < b;\n}\n\nbool less_or_equal(int a, int b) {\n    return a <= b;\n}
\n\n

我们用 g++ 编译它们(不进行优化,使用 -O0 标志)。以下是我们在汇编清单(x86-64)中看到的内容:

\n\n
less_than(int, int):\n    cmp    edi, esi      ; 比较 a 和 b\n    setl   al            ; 如果 a < b(小于),则设置 al=1\n    ret\n\nless_or_equal(int, int):\n    cmp    edi, esi      ; 比较 a 和 b\n    setle  al            ; 如果 a <= b(小于或等于),则设置 al=1\n    ret
\n\n

看到了吗?唯一的区别是指令 setl(如果小于则设置)和 setle(如果小于或等于则设置)。两条指令都只需要 一个 CPU 时钟周期 来执行。在现代 CPU(Intel Core i7, AMD Ryzen)上,它们具有相同的延迟——1 个周期。

\n\n

结论 #1: 在机器指令层面,< 和 <= 之间没有性能差异。到此为止。

\n\n

那循环呢?这里总该有区别了吧!

\n\n

大多数争论都围绕着 循环 展开。比较以下两种方法:

\n\n
// 方法 1:使用 <\nfor (int i = 0; i < n; ++i) {\n    // 执行某些操作\n}\n\n// 方法 2:使用 <=\nfor (int i = 0; i <= n - 1; ++i) {\n    // 执行相同的操作\n}
\n\n

直觉告诉我们:第二种方法多了一次计算 n - 1。但让我们看看在 编译器优化 之后会发生什么。

\n\n

我们用 g++ -O2 对 n = 1 000 000 000 运行一个基准测试:

\n\n
#include <iostream>\n#include <chrono>\n\nint main() {\n    const long long n = 1'000'000'000;\n    volatile long long sink = 0; // 防止编译器优化掉循环\n\n    auto start = std::chrono::high_resolution_clock::now();\n    \n    for (long long i = 0; i < n; ++i) {\n        sink += i;\n    }\n    \n    auto end = std::chrono::high_resolution_clock::now();\n    std::cout << "时间 (<): " \n              << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()\n              << " ms\\n";\n\n    start = std::chrono::high_resolution_clock::now();\n    \n    for (long long i = 0; i <= n - 1; ++i) {\n        sink += i;\n    }\n    \n    end = std::chrono::high_resolution_clock::now();\n    std::cout << "时间 (<=): " \n              << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()\n              << " ms\\n";\n\n    return 0;\n}
\n\n

在我的机器(i7-107

博客

书籍推荐