第一个版本通过将一个值从内存移动到一个本地变量中进行优化。第二个版本没有这样做。
我原以为编译器会在这里选择进行localValue优化,而不是在循环的每次迭代中读写内存中的值。为什么它不这样做呢?
class Example
{
public:
void processSamples(float * x, int num)
{
float localValue = v1;
for (int i = 0; i < num; ++i)
{
x[i] = x[i] + localValue;
localValue = 0.5 * x[i];
}
v1 = localValue;
}
void processSamples2(float * x, int num)
{
for (int i = 0; i < num; ++i)
{
x[i] = x[i] + v1;
v1 = 0.5 * x[i];
}
}
float v1;
};
processSamples组装成的代码如下:
.L4:
addss xmm0, DWORD PTR [rax]
movss DWORD PTR [rax], xmm0
mulss xmm0, xmm1
add rax, 4
cmp rax, rcx
jne .L4
将processSamples2转换为这样:
.L5:
movss xmm0, DWORD PTR [rax]
addss xmm0, DWORD PTR example[rip]
movss DWORD PTR [rax], xmm0
mulss xmm0, xmm1
movss DWORD PTR example[rip], xmm0
add rax, 4
cmp rax, rdx
jne .L5
由于编译器不必担心线程(v1不是原子的),它是否可以假设没有其他内容会查看此值并继续将其保留在寄存器中,而循环正在旋转?
请参见https://godbolt.org/g/RiF3B4获取完整的汇编代码和可供选择的编译器!
v1
时,它是未初始化的。这是未定义行为,并会导致gcc和clang在优化时出现奇怪的问题。 - Richard Hodges