我试图使用(大型)结构数组,每个结构只包含一个小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相同,这使得这是一个纯粹的手动版本与范围问题。