【问题标题】:clang c++17 std::vector over aligned types copy of elements SIGSEGV when compiled with -mavx使用-mavx编译时,c ++ 17 std::vector在元素SIGSEGV的对齐类型上的副本
【发布时间】:2018-08-24 08:27:08
【问题描述】:

根据question,我认为在 C++17 中,带有默认分配器的 std::vector 应该处理对齐的类型。但是,下面的代码

#include <iostream>
#include <iterator>
#include <array>
#include <vector>

template<typename T, size_t N, size_t Alignment>
struct alignas(Alignment) AlignedArray : public std::array<T, N>
{
    friend std::ostream& operator<<(std::ostream& o, const AlignedArray& a)
    {
        std::copy(a.cbegin(), a.cend(), std::ostream_iterator<T>(o, " "));
        return o;
    }
};

int main()
{
    using Array = AlignedArray<double, 24, 64>;
    std::vector<Array> v(10);
    for(const auto& e : v)
    {
        auto arr(e);
        std::cout << arr << std::endl;
    }
    return 0;
}

当我使用 clang 6.0.1 和 -mavx 编译 arr 时出现段错误。如果没有-mavx 开关,它运行良好(CPU 是 E5-2697 v2)。我用它编译了 clang++ -I&lt;path_to_libcxx&gt;/include/c++/v1 -g -mavx -std=c++17 main.cpp -stdlib=libc++ -lc++abi -o alignastest -L&lt;path_to_libcxx&gt;/lib -L&lt;path_to_libcxxabi&gt;/lib。 我在旧的 RHEL 6.9 上运行它,我在其中编译了 clang 6.0.1 和 libcxx、libcxxabi。 我在另一个系统(Ubuntu 18.10,gcc 8)上对其进行了测试,它没有任何问题。

子问题

关于对齐,我发现 std::aligned_alloc 在 libc++ 中的实现依赖于 C11 功能,该功能仅在最近的 glibc 版本 (__config.h) 中启用:

#if __GLIBC_PREREQ(2, 17)
#define _LIBCPP_HAS_C11_FEATURES
#endif

不幸的是,RHEL 6.9 只安装了ldd (GNU libc) 2.12alignas 是否也取决于 glibc 版本?

【问题讨论】:

  • 我在我的系统上对此进行了测试,适用于 g++ 8,不适用于 clang++ 6。我查看了编译后的程序,即使使用 -mavx 的 std::,g++ 也不使用 avx 指令:复制,而铿锵做。此外,指令错误是从第一个数组的地址到 ymm0 的 vmovaps(对齐的 256 位移动),希望这有助于调试。
  • 我无法重现此内容。如果我使用-std=c++17,我会得到一个正确对齐的内存块。
  • 看来 libc++ 版本 6 中还没有对过度对齐类型的支持。
  • 如何检查?
  • 查看标题 并搜索有关对齐的任何内容。当前版本有很多,但在第 6 版中几乎没有。

标签: c++ clang c++17


【解决方案1】:

我发现了编译代码的问题,但是,我还没有找到解决方案。但它接缝,这只是一个clang问题,使用g ++修复它。

最好通过显示一些生成的汇编代码来说明问题。 auto arr(e); 代码行被编译为一些移动指令,以将数据从向量复制到堆栈,clang 使用(使用 -mavx 编译时)avx2 指令,如下所示(AT&T 语法):

vmovaps 0xa0(%rax),%ymm0
vmovaps %ymm0,0x120(%rsp)
...

其中 %rax 是向量中当前数组的地址。目标 arr 位于 0x80(%rsp)。该程序将复制 32 字节的块(256 位 avx2 指令)。

但是,在我的调试测试中查看值时,问题变得清晰起来:%rax = 0x55555556be70。问题是,vmovaps(移动对齐打包单精度)到 256 位 avx2 寄存器期望目标和源在 256 位或 32 字节(0x20)边界对齐,但是 %rax 仅对齐 16 字节。在没有对齐的情况下编译时,clang 使用 vmovups(相同的指令,但不需要对齐数据)。

所以问题是,std::vector 的分配器不尊重对齐方式,也不在 64 字节边界对齐数组。 g++ 也不会将向量内的数组与 32 字节边界对齐,并且在不使用 -O[not 0] 时也不会使用 avx 指令。但是 g++ 总是使用 128 位 xmm 寄存器,只需要对齐到 16 字节,分配器将数据与两个编译器对齐。

编辑:

我刚刚意识到,我忘记使用 -std=c++17 进行编译。使用该标志,它适用于 clang++。代码看起来相同,但分配器正确地将代码对齐在 64 字节边界。所以我想这与一个旧图书馆有关。也许你可以把你的二进制文件发给我,然后我可以更详细地看一下。

【讨论】:

  • 奇怪的是用-mavx编译时使用了avx2指令。如果您使用 -std=c++17 进行编译,是否还会发生这种情况?感谢您的帮助,这是两个二进制文件的链接,一次带有 avx,一次没有 avx:dropbox.com/sh/8xjzqfmvjisb6k7/AAC1NvA7AR16DyuvH4RYODtJa?dl=0"
  • 你是在 libc++ 还是 libstdc++ 中使用 clang?
  • 我都试过了,用 libc++ 和 libstdc++ 编译。两者都在我使用 c++17 时工作,但只有 libstdc++ 在没有 c++17 的情况下会失败。但是带有std
  • @AlexanderDaum 在 C++17 之前,std::allocator::allocate 使用的 ::operator newstd::vector 默认使用)无法为过度对齐的类型分配内存。
  • 当我将它与 32 字节对齐一起使用时,我仍然会遇到同样的错误 :( 我猜它与这个 RHEL 6.9 系统上的其他一些旧库(可能是 glibc)有关。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-11
  • 1970-01-01
  • 2018-03-06
  • 2011-04-12
  • 2020-03-03
  • 2014-09-27
  • 1970-01-01
相关资源
最近更新 更多