优化写出循环

时间:2017-10-23 10:40:48

标签: c++ optimization g++

尽管存在__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更差(假设大小不是一个小数字),虽然我还没有测量过。

我做出这个假设是对的吗?

我认为两种情况下的可观察行为都应该相同。

3 个答案:

答案 0 :(得分:3)

这似乎是由__restrict函数上缺少f_original限定符引起的。 __restrict is a GCC extension;目前尚不清楚如何在C ++中表现出来。也许这是一个编译器错误(错过优化),它似乎在内联后消失。

答案 1 :(得分:0)

这两种方法并不相同。在第一个中,v的值在执行期间被多次更新。这可能是你想要的,也可能不是你想要的,但它与第二种方法不同,所以编译器不能将自己定义为可能的优化。

答案 2 :(得分:0)

restrict关键字表示没有其他任何别名,实际上就像值是本地的一样(并且没有本地对它有任何引用)。

在第二种情况下,v没有外部可见效果,因此不需要存储它。

在第一种情况下,某些外部可能会看到它,编译器此时并不知道没有可以改变它的线程,但它知道它不必读取它因为它既不原子也不易变。而d[]另一个外部可见变量的更改使得存储成为必要。

如果编译器编写者推理,那么dv都不是易失性的,也不是原子的,所以我们可以使用'as-if'来完成所有这些,然后编译器必须确保没有人可以完全触摸v。我很确定这会出现在新版本之一,因为在返回之前没有同步,所有情况下99%的情况都是如此。然后,程序员必须将volatile或atomic放在已更改的变量上,我认为我可以使用它。