首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >GCC未能优化对齐的std::数组,如C数组

GCC未能优化对齐的std::数组,如C数组
EN

Stack Overflow用户
提问于 2017-04-27 08:00:15
回答 1查看 4.5K关注 0票数 25

下面是GCC 6和7在使用std::array时未能优化的一些代码

代码语言:javascript
复制
#include <array>

static constexpr size_t my_elements = 8;

class Foo
{
public:
#ifdef C_ARRAY
    typedef double Vec[my_elements] alignas(32);
#else
    typedef std::array<double, my_elements> Vec alignas(32);
#endif
    void fun1(const Vec&);
    Vec v1{{}};
};

void Foo::fun1(const Vec& __restrict__ v2)
{
    for (unsigned i = 0; i < my_elements; ++i)
    {
        v1[i] += v2[i];
    }
}

g++ -std=c++14 -O3 -march=haswell -S -DC_ARRAY编译上面的代码会产生很好的代码:

代码语言:javascript
复制
    vmovapd ymm0, YMMWORD PTR [rdi]
    vaddpd  ymm0, ymm0, YMMWORD PTR [rsi]
    vmovapd YMMWORD PTR [rdi], ymm0
    vmovapd ymm0, YMMWORD PTR [rdi+32]
    vaddpd  ymm0, ymm0, YMMWORD PTR [rsi+32]
    vmovapd YMMWORD PTR [rdi+32], ymm0
    vzeroupper

这基本上是通过256位寄存器一次添加四个双倍的两次展开迭代。但是,如果您不使用-DC_ARRAY进行编译,就会从以下几个方面产生很大的混乱:

代码语言:javascript
复制
    mov     rax, rdi
    shr     rax, 3
    neg     rax
    and     eax, 3
    je      .L7

在本例中生成的代码(使用的是std::array而不是普通的C数组)似乎检查输入数组的对齐性--尽管它被指定为对齐为32个字节。

GCC似乎不明白std::array的内容与std::array本身是一致的。这打破了使用std::array而不是C数组不会导致运行时成本的假设。

我错过了什么简单的东西可以解决这个问题吗?到目前为止,我想出了一个丑陋的黑客:

代码语言:javascript
复制
void Foo::fun2(const Vec& __restrict__ v2)
{
    typedef double V2 alignas(Foo::Vec);
    const V2* v2a = static_cast<const V2*>(&v2[0]);

    for (unsigned i = 0; i < my_elements; ++i)
    {
        v1[i] += v2a[i];
    }
}

还要注意:如果my_elements是4而不是8,那么问题就不会发生。如果使用Clang,则不会出现问题。

你可以在这里看到它:https://godbolt.org/g/IXIOst

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-04-27 20:20:15

有趣的是,如果用v1[i] += v2a[i]; (显然不是可移植的)代替v1._M_elems[i] += v2._M_elems[i];,gcc成功地优化了std::数组以及C数组的情况。

可能的解释:在gcc转储(-fdump-tree-all-all)中,在C数组中可以看到MEM[(struct FooD.25826 *)this_7(D) clique 1 base 0].v1D.25832[i_15],在std::MEM[(const value_typeD.25834 &)v2_7(D) clique 1 base 1][_1]中可以看到数组。也就是说,在第二种情况下,gcc可能忘记了这是Foo类型的一部分,并且只记得它正在访问一个double。

这是一个抽象的代价,它来自于为了最终查看数组访问而必须经过的所有内联函数。Clang仍然能够很好地向量化(即使在移除对齐之后!)。这可能意味着clang在不考虑对齐的情况下向量化,实际上它使用的指令(如vmovupd )不需要对齐地址。

您找到的哈克(强制转换为Vec )是另一种让编译器在处理内存访问时看到正在处理的类型对齐的方法。对于常规的std::operator[],内存访问发生在std::*this的成员函数中,这完全不知道*this是对齐的。

Gcc还有一个内置功能,让编译器了解对齐情况:

代码语言:javascript
复制
const double*v2a=static_cast<const double*>(__builtin_assume_aligned(v2.data(),32));
票数 18
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43651923

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档