为了更好地理解编译器,特别是汇编语言,我一直在尝试一段简单的代码,其中计算前N
个数字的总和,这应该产生N(N 1)/2
或N(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
,生成的程序集有一个循环,而对于第二个函数sum2
assembly则没有循环。然而,一旦我删除了编译器优化,即-O3
,那么您最终可以看到程序集中第二个函数的循环。
要查看使用编译器优化生成的程序集,请参阅此优化。
要查看未经过编译器优化的生成的程序集,请参见这个未优化的。
编译器是x86-64 clang
问题:为什么编译器优化不显示汇编中的另一个循环?
为了更好地理解编译器,特别是汇编语言,我一直在实验一段简单的代码,其中计算第一个数字的总和,这应该导致或. 如代码所示,有两个功能: 在第一个函数中,I从O循环到N,即
在 C 或 C 中,如果编译器遇到一个 循环,其中计数器从 计数到 n, n 是一个变量(不是函数调用,也不是常量),编译器是否会通过检查变量 (绑定变量)是否会在循环期间更改(访问写入, 例如: 可以是循环前计算的字符串的长度),通过优化这里,我的意思是将其值复制到寄存器以避免内存访问? 下面是一个示例: 编译器会注意到这一点并对其进行优化吗?
这是CppCon谈话中的一个例子https://www.youtube.com/watch?v=F6Ipn7gCOsY 目标是首先从A打印Hello,然后允许线程B启动。很明显,应该避免繁忙等待,因为它占用大量CPU。 作者说, 循环可以由编译器进行优化(通过将 的值放入寄存器中),因为编译器看到 从不Hibernate,因此永远不会被更改。但是,即使线程从不Hibernate,另一个线程仍然可以
在我的系统上,行-主顺序访问平均花费(试用),而列-主顺序访问在我的系统上花费(试用),这是相当重要的。 从表面上看,这应该是一件非常简单的事情来优化。 为什么现代编译器不优化这些场景?
我经常注意到gcc在可执行文件中将乘法转换为移位。当将与相乘时,可能会发生类似的情况。例如,可能只是将的指数增加1,从而节省一些周期。如果有人要求编译器这样做(例如,通过),编译器通常会这样做吗? 编译器通常是否足够聪明来执行此操作,还是我需要自己使用或函数系列来执行此操作?
(这个问题与此密切相关,但它是一个更具体的问题,我希望能就此得到答案)