如何用 gcc 或 clang 模拟 _mm256_loadu_epi32?

Posted

技术标签:

【中文标题】如何用 gcc 或 clang 模拟 _mm256_loadu_epi32?【英文标题】:How to emulate _mm256_loadu_epi32 with gcc or clang? 【发布时间】:2020-01-08 15:43:20 【问题描述】:

英特尔的内在指南列出了内在的_mm256_loadu_epi32

_m256i _mm256_loadu_epi32 (void const* mem_addr);
/*
   Instruction: vmovdqu32 ymm, m256
   CPUID Flags: AVX512VL + AVX512F
   Description
       Load 256-bits (composed of 8 packed 32-bit integers) from memory into dst.
       mem_addr does not need to be aligned on any particular boundary.
   Operation
   a[255:0] := MEM[mem_addr+255:mem_addr]
   dst[MAX:256] := 0
*/

但是 clang 和 gcc 不提供这个内在的。相反,他们只提供(在文件 avx512vlintrin.h 中)被屏蔽的版本

_mm256_mask_loadu_epi32 (__m256i, __mmask8, void const *);
_mm256_maskz_loadu_epi32 (__mmask8, void const *);

归结为相同的指令vmovdqu32。我的问题:我如何模仿_mm256_loadu_epi32:

 inline _m256i _mm256_loadu_epi32(void const* mem_addr)
 
      /* code using vmovdqu32 and compiles with gcc */
 

无需编写程序集,即仅使用可用的内在函数?

【问题讨论】:

由于您不需要遮罩(因此元素大小无关紧要),您可以使用_mm256_loadu_si256 @PaulR 这比_mm256_maskz_epi32(0xffu,ptr)好吗?您会将此评论提升为答案吗? 是的,这样更好。如果编译器想要加载到 ymm16..31 中,它总是可以使用 AVX512 编码,否则你希望它使用更短的vmovdqu。相关:What is the difference between _mm512_load_epi32 and _mm512_load_si512? 请注意,使用_mm256_loadu_si256,您需要将输入指针转换为const __m256i*(所以这不是一个坏主意,将其封装到内联函数中) 【参考方案1】:

只需像普通人一样使用_mm256_loadu_si256。 AVX512 内在函数为您提供的唯一东西是更好的原型(const void* 而不是const __m256i*)。

@chtz 建议您可能仍想自己编写一个包装函数来获得void* 原型。但是不要叫它_mm256_loadu_epi32;某些未来的 GCC 版本可能会添加它以与 Intel 的文档兼容并破坏您的代码。


当你没有屏蔽时,你甚至不希望编译器发出vmovdqu32 ymmvmovdqu ymm 更短,并且完全做同样的事情,with no penalty for mixing with EVEX-encoded instructions。如果编译器想要加载到 ymm16..31 中,则始终可以使用 vmovdqu3264,否则您希望它使用较短的 VEX 编码的 AVX1 vmovdqu

我很确定 GCC 对 _mm256_maskz_epi32(0xffu,ptr) 的处理与 _mm256_loadu_si256((const __m256i*)ptr) 完全相同,并且无论您使用哪一个,都生成相同的 asm。它可以优化 0xffu 掩码并简单地使用未掩码的负载,但您的源代码中不需要这种额外的复杂性。

但不幸的是,当启用 AVX512VL(例如 -march=skylake-avx512)时,即使您编写 _mm256_loadu_si256,当前的 GCC 也会悲观到 vmovdqu32 ymm0, [mem]。这是一个错过的优化,GCC Bug 89346

只要没有掩码,您使用哪个 256 位加载内在函数(对齐与未对齐除外)都没有关系。

相关:

error: '_mm512_loadu_epi64' was not declared in this scope What is the difference between _mm512_load_epi32 and _mm512_load_si512?

【讨论】:

“正常人”是主观的。 @JL2210:这种措辞是一种幽默的方式,表明_mm256_loadu_si256 是您可以在很多代码中找到的正常/标准方式,或者至少这就是我的意图。这也意味着这样做没有缺点,因为我推荐它。 (答案的其余部分更详细地解释了缺乏不利因素)。我还暗示,一旦您了解混合 AVX 和 AVX512 内在函数和/或指令不是问题,它也是显而易见的解决方案。我认为这不太可能给人以粗鲁的印象,但如果我错了,请纠正我。 好的,所以我做了_mm256_loadu_si256((const __m256i*)(k)),然后clang告诉我:warning: cast from 'const std::int32_t *' (aka 'const int *') to 'const __m256i *' increases required alignment from 4 to 32 @Walter:这很奇怪,你有我可以查看的 Godbolt MCVE 链接吗?因为这正是您在这些 void* 内在函数可用之前在 AVX1 / AVX2 代码中所做的事情。取消引用__m256i* 就像load 而不是loadu,所以是的,它确实增加了对齐要求,但是将它传递给loadu 并没有这样做。只要您从不直接取消引用该指针,该警告就是虚假的。

以上是关于如何用 gcc 或 clang 模拟 _mm256_loadu_epi32?的主要内容,如果未能解决你的问题,请参考以下文章

在 GCC 10.3.0 中找不到 _mm256_rem_epu64 内在函数

gcc 标头错误:“_mm256_set_m128d”未在此范围内声明

带有 -mavx 标志的链接器错误 GCC7

为啥 gcc -O3 处理 avx256 的内在比较与 gcc -O0 和 clang 不同?

如何通过英特尔 OpenCL SVML 使用 _mm256_log_ps?

模拟器clang上的Xcode构建错误:错误:没有这样的文件或目录:'__entitlements'