GCC,std :: array和for {range}-based for

时间:2017-09-05 10:41:55

标签: c++ arrays c++11 gcc optimization

我试图使用(大型)结构数组,每个结构只包含一个小std::array。 特别是K == 1案例应该得到很好的支持(见代码)。 但是,在大多数情况下,GCC似乎无法正确优化这些阵列,尤其是在使用基于范围的数据时。 Clang生成的代码我不能完全理解,但似乎已经很好地优化了(并且使用了SSC,而GCC没有这样做。)

#include <vector>
#include <array>

template<int K>
struct Foo {
    std::array<int, K> vals;

    int bar() const {
        int sum = 0;
#if 0 // Foo Version A
        for (auto f : vals)
            sum += f;
#else // Foo Version B
        for (auto i = 0; i < K; ++i)
            sum += vals[i];
#endif
        return sum;
    }
};

int test1(std::vector<Foo<1>> const& foos)
{
    int sum = 0;
    for (auto const& f : foos)
        sum += f.bar();
    return sum;
}

// Version C
int test2(std::vector<std::array<int, 1>> const& foos)
{
    int sum = 0;
    for (auto const& f : foos)
        for (auto const& v : f)
            sum += v;
    return sum;
}

// Version D
int test3(std::vector<std::array<int, 1>> const& foos)
{
    int sum = 0;
    for (auto const& f : foos)
        for (auto i = 0; i < f.size(); ++i)
            sum += f[i];
    return sum;
}

Godbolt Code,gcc 7.2,标记-O2 -std=c++11 -march=native。较旧的gcc版本表现相似。

如果我没有弄错,那么所有四个版本都应该具有相同的语义。

此外,我希望所有版本都能编译成大约相同的程序集。 程序集应该只有一个常用的条件跳转(用于迭代向量)。

但是,会发生以下情况:

  • 版本A(基于范围的,array-in-struct):3个条件跳转,一个用于处理零长度向量。然后一个为矢量(这没关系)。但是然后另一个迭代数组?为什么?它具有恒定的大小1.

  • 版本B(手册for,array-in-struct):这里,GCC实际上认识到长度为1的数组可以优化,装配看起来很好。

  • 版本C(基于范围的直接数组):数组上的循环没有被优化掉,所以循环再次进行两次条件跳转。另外:这个版本包含更多我认为需要的内存访问。

  • 版本D(手册,直接阵列):这是唯一一个看起来很健康的版本。 11条说明。

Clang创建了更多的汇编代码(相同的标志),但它对于所有版本几乎相同,并且包含大量循环展开和SSE。

这是与海湾合作委员会有关的问题吗?我应该提交错误吗?这是我应该/可以修复的C ++代码中的东西吗?

编辑:由于版本B中的修复而更新了godbolt网址。行为现在与版本D相同,这使得这是一个纯粹的手动版本与范围问题。

0 个答案:

没有答案