将 __m256i 设置为两个 __m128i 值的值

Posted

技术标签:

【中文标题】将 __m256i 设置为两个 __m128i 值的值【英文标题】:Setting __m256i to the value of two __m128i values 【发布时间】:2015-09-17 12:36:12 【问题描述】:

因此,AVX 有一个来自immintrin.h 的函数,它应该允许将两个__m128i 值的串联存储到一个__m256i 值中。功能是

__m256i _mm256_set_m128i (__m128i hi, __m128i lo)

但是,当我使用它时,就像这样:

__m256i as[2]; __m128i s[4]; as[0] = _mm256_setr_m128i(s[0], s[1]);

我得到一个编译错误:

error: incompatible types when assigning to type ‘__m256i’ from type ‘int’

我真的不明白为什么会发生这种情况。非常感谢任何帮助!

【问题讨论】:

你确定这个函数在那个头文件中吗?当我尝试使用#include <immintrin.h> 之后的函数时,我收到警告消息“_mm256_set_m128i 的隐式声明”。在 C 中,隐式声明意味着编译器假定函数将返回 int,这也将解释编译器错误。 【参考方案1】:

似乎并非所有编译器都在immintrin.h 中定义了_mm256_setr_m128i,甚至_mm256_set_m128i。所以我通常只根据需要定义宏,用合适的#ifdefs 括起来,用于测试编译器和版本:

#define _mm256_set_m128i(v0, v1)  _mm256_insertf128_si256(_mm256_castsi128_si256(v1), (v0), 1)

#define _mm256_setr_m128i(v0, v1) _mm256_set_m128i((v1), (v0))

Intel ICC 11.1 及更高版本同时具有_mm256_set_m128i_mm256_setr_m128i

MSVC 2012 及更高版本只有_mm256_set_m128i

gcc/clang 似乎也没有,虽然我还没有检查最近的版本是否已经修复。

【讨论】:

GCC 似乎缺少很多“复合”内在函数。我最近发现_mm256_loadu2_m128 和 family 是从内存中进行转置的最佳方式,因为(在 ICC 上)它编译为两条不使用向量洗牌端口的指令。在 GCC 上,内联汇编是获得相同效果的唯一方法。对于 AVX512,GCC 缺少所有归约内在函数。 @Mysticial:嗯,好的,我明白了你在说的问题。您必须确保所有内存引用都通过loadu,否则编译器可能会在-O0 使用movaps 来完成它们。解决方案:使用 gcc 4.9.2 或更新版本,然后loadu 确实折叠成 vinsertf128。或者对于 gcc 4.8:始终至少使用 -Og:“优化调试”,并保留在 -O0 编译时会出错的代码。 goo.gl/eBPD5a。请注意,clang 有两个有问题的内在函数,_mm256_set_m128i_mm256_loadu2_m128 @PeterCordes 哦,太好了!我确实在使用 GCC 4.8 进行测试。谢谢! 我刚碰到这个。我想vbroadcastf128 一个 pshufb 常量进入 ymm 寄存器的两个通道。但是 gcc6.3.1 在我尝试的所有事情上都很糟糕。您的宏是最糟糕的情况,编译为vmovdqa+vinserti128。 (clang 编译为 256b 向量)。使用 gcc 的其他尝试:const __m128 tmp = _mm_castsi128_ps(shufmask128);_mm256_castps_si256(_mm256_broadcast_ps(&tmp)) 编译为常量的 FP 128b 加载,FP 128b 存储到堆栈,然后是刚刚存储的副本中的 vinsertf128。如果你能相信的话,指针转换会更糟。 痛,痛,痛。它在 gcc 8.1 上。它不在 gcc 7.3 上。请参阅github.com/gcc-mirror/gcc/blob/master/gcc/config/i386/… 的源代码。 _mm256_set_m128i 在那里。【参考方案2】:

我们遇到了同样的问题并使用宏来解决它。

#ifdef __GNUC__ 
#if __GNUC__ < 8
#define _mm256_set_m128i(xmm1, xmm2) _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1), _mm256_castsi128_si256(xmm2), 2)
#define _mm256_set_m128f(xmm1, xmm2) _mm256_permute2f128_ps(_mm256_castps128_ps256(xmm1), _mm256_castps128_ps256(xmm2), 2)
#endif
#endif

【讨论】:

_mm256_insertf128_si256 总是至少和_mm256_permute2f128_ps 一样便宜,或者至少是相应的 asm 指令。我没有检查如何编译常量与运行时变量。

以上是关于将 __m256i 设置为两个 __m128i 值的值的主要内容,如果未能解决你的问题,请参考以下文章

将 16 位值的 __m256i 打包(饱和)到 8 位值的 __m128i?

将 __m256i 寄存器转换为 uint64_t 位掩码,以便每个字节值处的值是输出中的设置位

从四个 __m128i 变量的 64 个高位或低位初始化 __m256i

如何在 MSVC 中高效地将两个 __m128d 转换为一个 __m128i?

将 __m256i 存储为整数

将 __m256i 存储为整数