填充__m128i参数的正确方法是啥,从基本类型(例如short)到与_mm256_broadcast_epi(例如_mm_broadcastw_epi16)一起使用
Posted
技术标签:
【中文标题】填充__m128i参数的正确方法是啥,从基本类型(例如short)到与_mm256_broadcast_epi(例如_mm_broadcastw_epi16)一起使用【英文标题】:What is the correct way to fill a __m128i parameter, from basic type (such as short), to use with _mm256_broadcast_epi (such as _mm_broadcastw_epi16)填充__m128i参数的正确方法是什么,从基本类型(例如short)到与_mm256_broadcast_epi(例如_mm_broadcastw_epi16)一起使用 【发布时间】:2020-11-04 10:23:39 【问题描述】:所有四个 _mm256_broadcastb_epi8、_mm_broadcastw_epi16、_mm256_broadcastd_epi32 和 _mm256_broadcastq_epi64 函数是 VPBROADCASTB、VPBROADCASTW、VPBROADCASTD 和 VPBROADCASTQ 指令的内在函数。 根据英特尔的文档:"Intel® Advanced Vector Extensions Programming Reference", 这些指令可能会相应地接收 8 位、16 位、32 位、64 位内存位置。 第 5-230 页:
源操作数是8位,16位32位, 64位内存位置或XMM寄存器中的低8位、16位、32位、64位数据
但是,这些指令的内部 API(Intel、MSVS 和 gcc)接收 __m128i 参数。 现在,如果我有一个基本类型的变量,据说是“短”,那么最有效和跨平台的方式是什么(至少在 MSVS 和 gcc 之间) 将该变量传递给相应的广播内在函数(_mm_broadcastw_epi16 在短的情况下)?
例如:
void func1(uint8_t v)
__m256i a = _mm256_broadcastb_epi8(<convert_to__m128i>(v));
...
void func1(uint16t v)
__m256i a = _mm256_broadcastw_epi16(<convert_to__m128i>(v));
...
void func1(uint32_t v)
__m256i a = _mm256_broadcastd_epi32(<convert_to__m128i>(v));
...
void func1(uint64_t v)
__m256i a = _mm256_broadcastq_epi64(<convert_to__m128i>(v));
...
以 MSVS 为例:
void func1(uint16t v)
__m128i vt;
vt.m128_u16[0] = v;
__m256i a = _mm256_broadcastw_epi16(vt);
...
但如果没有优化,它可以先加载一个 xmm 寄存器,然后才能在 VPBROADCASTW 中使用它。 当进行优化时,它可以直接使用 v 的内存位置。 它也只对 MSVS 有效。
【问题讨论】:
_mm256_set1_epi8
和朋友们。
仅供参考,如果您知道指令的助记符并想知道与其相关的内在函数,那么进行这样的搜索非常有用:software.intel.com/sites/landingpage/IntrinsicsGuide/…
_mm_broadcastb_epi8
产生__m128i
结果,而不是__m256i
。你通常不想要它,只要让编译器为 _mm256_set1_epi8
内在函数发出广播指令,如果你有一个标量值开始,而不是 __m128i
。
【参考方案1】:
已经有序列/复合内在函数可以完全满足您的需求:
_mm256_set1_epi8/16/32/64
来自 Intels 内在函数指南:
Broadcast 8-bit integer a to all elements of dst. This intrinsic may generate the vpbroadcastb.
使用那些您应该能够信任编译器生成最佳代码的代码。
在执行此类操作时,我会使用 Intel Intrinsics Guide,这很有帮助,因为您可以从助记符进行反向搜索(在这种情况下,您知道您最终需要 vpbroadcastb),它会告诉您哪些内在函数与之相关。
【讨论】:
确实我错过了_m256_set1_epi,它看起来正是我想要的。因此,_mm256_broadcast 似乎适用于您有一个 128 位寄存器 (xmm) 开始的情况。【参考方案2】:如果您有一个标量值开始,而不是 __m128i
,则让编译器为 _mm256_set1_epi8(scalar)
内在函数发出广播指令。
但您通常也不想要 _mm_broadcastb_epi8
(__m128i
结果)或 __m256i _mm256_broadcastb_epi8(__m128i)
,除非您已经有一个 __m128i
开头并想要低元素。
如果您不关心低 dword 的高 2 或 3 个字节的高位,那么在编译器不浪费指令的情况下将标量放入 __m128i
可能是 8 位或 16 位标量的实际问题;它可能会将movzx
加载到一个整数寄存器中,然后使用vmovd
或类似的东西。
【讨论】:
我在示例中错误地将 _mm_broadcastb 而不是 _mm256_broadcast。确实我错过了 _m256_set1_epi,它看起来正是我想要的。以上是关于填充__m128i参数的正确方法是啥,从基本类型(例如short)到与_mm256_broadcast_epi(例如_mm_broadcastw_epi16)一起使用的主要内容,如果未能解决你的问题,请参考以下文章
打印 _mm_cmpeq_epi8 的输出(__m128i 类型)
如何将 16 字节的内存加载到 Rust __m128i 中?
SSE:如何将 _m128i._i32[4] 减少到 _m128i._i8