SIMD 聚集导致的分段错误?

Posted

技术标签:

【中文标题】SIMD 聚集导致的分段错误?【英文标题】:Segmentation fault caused by SIMD gather? 【发布时间】:2020-12-03 12:37:00 【问题描述】:

我的项目使用 SIMD 收集来加速表查找。以下是简化版,但足以说明我遇到的问题。

#include <x86intrin.h>
#include <stdio.h>

alignas(32) static int a[256][8] =  0 ;

int main()
    // initialize 32 bytes (as a __m256i)
    int *s = (int*)_mm_malloc(32, 4);
    for(int i=0; i<8; i++)
        s[i] = i;

    __m256i *t = (__m256i*)s;
    // do table lookup task using SIMD gather
    for(int i=0; i<100000; i++)
        int *addr = a[i % 256];
        t[0] = _mm256_i32gather_epi32(addr, t[0], 4);
    

    // print out the result
    for(int i=0; i<8; i++)
        printf("%d ", s[i]);
    printf("\n");

编译执行

user@server:~/test$ g++ -O3 -mavx2 gather.cpp 
user@server:~/test$ ./a.out
Segmentation fault (core dumped)

其实还有一个使用 SIMD shuffle 和 __m128i 的替代版本,可以正常工作。有人知道吗?

【问题讨论】:

你应该edit你的帖子让你的代码sn-p变成minimal reproducible example。您当前的代码无法编译,因为它忽略了 a 的初始化值。 谢谢。我重新编辑了代码。 Ubuntu 20.04,g++ 9.3.0,无法确认分段错误,输出>0 0 0 0 0 0 0 0 您的处理器类型是什么? 它是 Intel(R) Xeon(R) CPU E5-2699 v3 @ 2.30GHz 【参考方案1】:

_mm_malloc (size_t size, size_t align) - 您只需对齐 4,然后对 __m256i* 执行对齐要求的取消引用。据推测,当_mm_malloc(32, 4) 碰巧返回未按 32 对齐的内存时,会出现段错误。

只需像普通人一样使用_mm256_set_epi32(7,6,5,4,3,2,1,0);,或者可以在循环中初始化的本地数组alignas(32)。 (和/或您可以使用_mm256_loadu_si256 进行未对齐的加载)。

可以使用_mm_malloc(32,32) 修复您的代码,但不要这样做。动态分配(然后泄漏)一个您只想供本地使用的单个 32 字节对象是非常愚蠢的。


当所有数据都来自一个或两个 32 字节的块时,优先使用 shuffle 而非收集

就缓存访问而言,一个 8 元素的收集成本大约为 8 个标量或向量负载,加上其他执行单元的一些工作。 (https://uops.info/ 和 https://agner.org/optimize/)。不幸的是,当多个元素来自同一个缓存行时,Gather 不会变得更有效率。

在您的情况下,您甚至不需要随机播放,只需从a[][] 的一部分加载 32 字节。

int *addr = a[i % 256]; 获得一个指向 32 字节对齐的int [8] 的指针,您可以从中获得_mm256_load_si256((const __m256i*)addr)。这为您提供了您想要的 0..7 本机顺序的元素。

如果您确实需要 0..7 以外的订单,请使用 AVX2 vpermd (_mm256_permutevar8x32_epi32) 和您用作收集索引的相同随机控制向量常数。

【讨论】:

经过多次尝试,我认为这是正确的答案。谢谢。

以上是关于SIMD 聚集导致的分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

使用结果浮点数时的 SSE SIMD 分段错误

为啥我的字符串分配会导致分段错误?

为啥释放内存会导致分段错误?

确定导致分段错误的代码行?

带有 ifort 的 tracebackqq() 导致分段错误

为啥重新声明 std::cout 会导致分段错误?