【问题标题】:Error in AVX loop vectorizationAVX 循环矢量化错误
【发布时间】:2016-06-18 09:41:59
【问题描述】:

当我尝试使用 AVX 获取数据时,出现运行时错误 - 分段错误:

int i = 0;
const int sz = 9;
size_t *src1 = (size_t *)_mm_malloc(sz*sizeof(size_t), 32);
size_t *src2 = (size_t *)_mm_malloc(sz*sizeof(size_t), 32);
size_t *dst  = (size_t *)_mm_malloc(sz*sizeof(size_t), 32);


__m256 buffer  = _mm256_load_si256(&src1[i]);
__m256 buffer2 = _mm256_load_si256(&src2[i+1]); //Segmentation fault in this line

//Something...

_mm256_store_si256(dst[i], buffer);


_mm_free(src1);
_mm_free(src2);
_mm_free(dst);

我通过使用 '_mm256_loadu_si256' 内在函数来解决问题。有人知道为什么会这样吗?

【问题讨论】:

  • 如果你的缓冲区是一个小的常数大小,不要malloc他们!将它们声明为本地数组。在 C99 或 GNU C++ 中,可变长度的局部数组也很有效,只要您知道大小足够小而不会耗尽堆栈。

标签: c++ memory-management vectorization intrinsics avx


【解决方案1】:

_mm*_load_* 内部函数仅适用于 对齐 数据,而 _mm*_loadu_* 内部函数允许您使用 未对齐 数据(但会降低性能)。

分段错误告诉您,您尝试从内存加载到 AVX 寄存器的值未在正确的边界上对齐。对于 256 位版本,值必须在 32 字节边界上对齐。

如果您不想支付加载未对齐值的性能损失,那么您需要确保这些值在 32 字节边界上正确对齐。您可以通过插入填充或使用强制对齐的注释来做到这一点。注释是特定于编译器的——在 GCC 上,你会使用类似 __attribute__((aligned(32))) 的东西,而在 MSVC 上,你会使用类似 __declspec(align(32)) 的东西。

这里的问题在于,您在第二次加载时的数组索引会强制从未对齐的内存位置加载。这不能通过属性/注释来解决。您将不得不填充这些值。使用size_t 作为指针类型可能是第一个错误。该类型应为 32 字节宽。

【讨论】:

  • 但我在 32 字节边界上与 _mm_malloc(sz*sizeof(size_t), 32); 正确对齐,这就是为什么我不明白发生了什么。
  • 刚刚更新了我的答案。这就是为什么你的第一次加载工作正常。但是,第二次加载不会,因为您将第一个位置增加了sizeof(size_t),这不是 32 字节的倍数。
  • 从 C++11 开始存在 alignas specifier,它允许以标准方式指定对齐要求。例如:alignas(128) char data[32] 会将内存中的数据数组对齐到 128 字节。
  • loadu 在用于对齐的数据时具有完整的性能。因此,当数据几乎总是对齐时,只需使用 loadu 而不是通过额外处理未对齐的罕见情况来减慢对齐的情况。这是利用对未对齐负载的良好硬件支持的好方法:它们并不昂贵,以至于您需要减慢对齐的情况以避免它们。
  • @peter 感谢您提及这一点。我其实并不知道。我想你只是因为使用未对齐的版本而支付了处理费用。令人印象深刻的是,设计师们能够完成这个特技。 一直都是这样,或者你知道吗? (换句话说,您的说法是否仅针对 AVX,还是可以追溯到最早的 SSE 时代?)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-24
  • 1970-01-01
  • 2020-10-02
  • 2021-03-04
  • 1970-01-01
  • 1970-01-01
  • 2016-02-11
相关资源
最近更新 更多