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 聚集导致的分段错误?的主要内容,如果未能解决你的问题,请参考以下文章