一个循环中写出的优化

13

将成员变量移动到本地变量可以减少循环中的写入次数,尽管存在__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次更糟糕(假设size不是一个小数字),尽管我还没有测量过。

我的假设正确吗?

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


可能与别名有关。将X的成员(数据和函数)设为全局似乎有所帮助。 - Marc Glisse
{btsdaf} - JCx
{btsdaf} - JCx
{btsdaf} - kabanus
{btsdaf} - JCx
显示剩余3条评论
3个回答

3
这似乎是由于f_original函数缺少__restrict限定符引起的。 __restrict是GCC的扩展;在C++中它的行为不太清楚。也许这是一个编译器bug(错过的优化),在内联后似乎消失了。

0
这两种方法并不相同。在第一种方法中,在执行过程中会多次更新v的值。这可能是你想要的,也可能不是,但它与第二种方法不同,因此编译器不能自行决定它作为可能的优化。

5
但是在此期间,唯一读取v的更新值的是process函数本身,所以编译器可以自由地将其放入寄存器中。需要注意的是,多线程并不起作用,因为在缺乏任何同步原语的情况下,无法保证对v的更新何时对其他线程可见。 - Thomas
{btsdaf} - JCx
@Thomas:我会这样说:由于缺乏“volatile”和线程同步,根据as-if规则,这两种方法是等效的。 - Arne Vogel

0

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

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

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

如果编译器作者的推理是,既然dv都不是易失性的也不是原子的,所以我们可以使用“as-if”来完成所有操作,那么编译器必须确保没有人能够接触v。我相信这将在新版本中的其中一个版本中出现,因为在返回之前没有同步,这在99%以上的情况下都是如此。程序员将不得不在更改的变量上放置易失性或原子性,我认为我可以接受这种方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接