当前位置: 首页 > 知识库问答 >
问题:

为什么编译器优化不会为从1开始的整数求和生成循环..n?

柯冯浩
2023-03-14

为了更好地理解编译器,特别是汇编语言,我一直在尝试一段简单的代码,其中计算前N个数字的总和,这应该产生N(N 1)/2N(N-1)/2

如代码所示,有两个功能:

#include <cstdint>


// Once compiled with optimization, the generated assembly has a loop

uint64_t sum1( uint64_t n ) {  
    uint64_t sum = 0;
    for ( uint64_t j=0; j<=n; ++j ) {
        sum += j;
    }
    return sum;
}

// Once compiled with optimization, the generated assembly of the following has no loop

uint64_t sum2( uint64_t n ) {  
    uint64_t sum = 0;
    for ( uint64_t j=0; j<n; ++j ) {
        sum += j;
    }
    return sum;
}

在第一个函数中,我从O循环到N,即j

我的理解/观察:

>

  • 对于第一个函数sum1,生成的程序集有一个循环,而对于第二个函数sum2assembly则没有循环。然而,一旦我删除了编译器优化,即-O3,那么您最终可以看到程序集中第二个函数的循环。

    要查看使用编译器优化生成的程序集,请参阅此优化。

    要查看未经过编译器优化的生成的程序集,请参见这个未优化的。

    编译器是x86-64 clang

    问题:为什么编译器优化不显示汇编中的另一个循环?


  • 共有1个答案

    乐修远
    2023-03-14

    这是因为编译器非常非常聪明,它知道可以用一个简单的数学公式而不是循环来计算从0到n的所有值的总和。

    但是,你的C编译器也弄清楚了这个数学公式不能用在

     类似资料:
    • 为了更好地理解编译器,特别是汇编语言,我一直在实验一段简单的代码,其中计算第一个数字的总和,这应该导致或. 如代码所示,有两个功能: 在第一个函数中,I从O循环到N,即

    • 在 C 或 C 中,如果编译器遇到一个 循环,其中计数器从 计数到 n, n 是一个变量(不是函数调用,也不是常量),编译器是否会通过检查变量 (绑定变量)是否会在循环期间更改(访问写入, 例如: 可以是循环前计算的字符串的长度),通过优化这里,我的意思是将其值复制到寄存器以避免内存访问? 下面是一个示例: 编译器会注意到这一点并对其进行优化吗?

    • 这是CppCon谈话中的一个例子https://www.youtube.com/watch?v=F6Ipn7gCOsY 目标是首先从A打印Hello,然后允许线程B启动。很明显,应该避免繁忙等待,因为它占用大量CPU。 作者说, 循环可以由编译器进行优化(通过将 的值放入寄存器中),因为编译器看到 从不Hibernate,因此永远不会被更改。但是,即使线程从不Hibernate,另一个线程仍然可以

    • 在我的系统上,行-主顺序访问平均花费(试用),而列-主顺序访问在我的系统上花费(试用),这是相当重要的。 从表面上看,这应该是一件非常简单的事情来优化。 为什么现代编译器不优化这些场景?

    • 我经常注意到gcc在可执行文件中将乘法转换为移位。当将与相乘时,可能会发生类似的情况。例如,可能只是将的指数增加1,从而节省一些周期。如果有人要求编译器这样做(例如,通过),编译器通常会这样做吗? 编译器通常是否足够聪明来执行此操作,还是我需要自己使用或函数系列来执行此操作?

    • (这个问题与此密切相关,但它是一个更具体的问题,我希望能就此得到答案)