如何用 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 ymm
; vmovdqu ymm
更短,并且完全做同样的事情,with no penalty for mixing with EVEX-encoded instructions。如果编译器想要加载到 ymm16..31 中,则始终可以使用 vmovdqu32
或 64
,否则您希望它使用较短的 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”未在此范围内声明
为啥 gcc -O3 处理 avx256 的内在比较与 gcc -O0 和 clang 不同?