循环外写的优化
Optimisation of a write out of a loop
尽管存在__restrict关键字,但将成员变量移动到局部变量可以减少此循环中的写入次数。这是使用GCC-O3。Clang和MSVC在这两种情况下都优化了写入[注意,自从这个问题发布以来,我们观察到在调用函数中添加__restrict会导致GCC也将存储移出循环。请参阅下面的godbolt链接和注释]
class X
{
public:
void process(float * __restrict d, int size)
{
for (int i = 0; i < size; ++i)
{
d[i] = v * c + d[i];
v = d[i];
}
}
void processFaster(float * __restrict d, int size)
{
float lv = v;
for (int i = 0; i < size; ++i)
{
d[i] = lv * c + d[i];
lv = d[i];
}
v = lv;
}
float c{0.0f};
float v{0.0f};
};
对于gcc-O3,第一个有一个内部循环,看起来像:
.L3:
mulss xmm0, xmm1
add rdi, 4
addss xmm0, DWORD PTR [rdi-4]
movss DWORD PTR [rdi-4], xmm0
cmp rax, rdi
movss DWORD PTR x[rip+4], xmm0 ;<<< the extra store
jne .L3
.L1:
rep ret
这里的第二个:
.L8:
mulss xmm0, xmm1
add rdi, 4
addss xmm0, DWORD PTR [rdi-4]
movss DWORD PTR [rdi-4], xmm0
cmp rdi, rax
jne .L8
.L7:
movss DWORD PTR x[rip+4], xmm0
ret
请参阅https://godbolt.org/g/a9nCP2以获取完整的代码。
为什么编译器不在这里执行lv优化?
我假设每个循环的3次内存访问比2次更糟糕(假设大小不是一个小数字),尽管我还没有测量到这一点。
我这样做对吗?
我认为在这两种情况下,可观察到的行为应该是相同的。
这似乎是由f_original
函数上缺少__restrict
限定符引起的。CCD_ 3是GCC的扩展;目前还不太清楚它在C++中的表现。也许是编译器错误(错过了优化)导致它在内联后消失。
这两种方法不完全相同。在第一种情况下,v
的值在执行期间被多次更新。这可能是也可能不是你想要的,但它与第二种方法不同,因此编译器不能自行决定是否进行优化。
restrict关键字表示没有与其他任何东西的别名,实际上就像该值是本地的一样(并且没有本地对它的任何引用)。
在第二种情况下,v
没有外部可见效果,因此不需要存储
在第一种情况下,某些外部可能会看到它,编译器此时并不知道不会有线程可以更改它,但它知道不必将其读取为既不是原子性的也不是易失性的。而d[]
的变化又是另一个外部可见的变量,使得存储成为必要。
如果编译器编写者进行推理,那么d
和v
都不是易失性的,也不是原子性的,所以我们可以使用"好像"来完成这一切,那么编译器必须确保没有人能接触到v
。我很确定这将出现在新版本中,因为在返回之前没有同步,无论如何,99%以上的情况都是这样。然后,程序员将不得不将volatile或atomic放在更改的变量上,我认为我可以接受这一点。
- gcc 如何优化此循环?
- 循环比较(优化)
- 为什么 std::chrono 在测量循环和编译器优化的并行 OpenMP 的执行时间时不起作用?
- 优化在网格图中查找哈密尔循环的函数?
- 未能优化看似明显的循环不变量(但volatile限定符发挥了神奇的作用)
- 在循环条件中调用const vector size()似乎缺少优化
- GCC 优化了基于固定范围的 for 循环,就好像它具有更长的可变长度一样
- 通过循环展开和阻塞进行优化
- 为什么 CLang++ 不优化循环,而 G++ 优化循环?
- 嵌套循环 C++ 的优化
- 如何在卷积程序的 c++ 中优化嵌套循环
- G 4.8.5带负数组索引的循环优化错误
- 使用 OpenMP 优化外循环并减少
- C是否优化了循环的检查部分
- 如何分析优化的代码并加快循环
- 如何优化循环/if语句的C
- 使用for_each标准或提升工具优化循环
- 在模板专门化中优化循环和避免代码重复
- 矢量化/优化循环,用于宽寄存器(特别是Xeon Phi)的未对齐数据访问
- 使用GCC优化C/ c++循环中的嵌套if语句