如何清除__m256值的高128位?

时间:2014-01-27 15:33:36

标签: c x86 simd avx avx2

如何清除m2的高128位:

__m256i    m2 = _mm256_set1_epi32(2);
__m128i    m1 = _mm_set1_epi32(1);

m2 = _mm256_castsi128_si256(_mm256_castsi256_si128(m2));
m2 = _mm256_castsi128_si256(m1);

不起作用 - 英特尔针对_mm256_castsi128_si256内在函数的文档说“结果向量的高位未定义”。 同时我可以在汇编中轻松完成:

VMOVDQA xmm2, xmm2  //zeros upper ymm2
VMOVDQA xmm2, xmm1

当然,我不想使用“和”或_mm256_insertf128_si256()等。

3 个答案:

答案 0 :(得分:5)

不幸的是,理想的解决方案取决于您使用的是哪种编译器,而其中一些编译器 并不是理想的解决方案。

我们可以用几种基本方法来写这个:

版本A

ymm = _mm256_set_m128i(_mm_setzero_si128(), _mm256_castsi256_si128(ymm));

版本B

ymm = _mm256_blend_epi32(_mm256_setzero_si256(),
                         ymm,
                         _MM_SHUFFLE(0, 0, 3, 3));

版本C

ymm = _mm256_inserti128_si256(_mm256_setzero_si256(),
                              _mm256_castsi256_si128(ymm),
                              0);

这些中的每一个都正是我们想要的,清除256位YMM寄存器的高128位,因此可以安全地使用它们中的任何一个。但哪个是最优的?那么,这取决于您使用的编译器......

<强> GCC

版本A:完全不支持,因为GCC缺少_mm256_set_m128i内在函数。 (当然可以模拟,但这可以使用&#34; B&#34;或&#34; C&#34;中的一种形式完成。)

版本B:编译为效率低下的代码。习语不被识别,内在因素被非常字面地翻译成机器代码指令。使用VPXOR将临时YMM寄存器归零,然后使用VPBLENDD将其与输入YMM寄存器混合。

版本C:理想。虽然代码看起来有点可怕和低效,但支持AVX2代码生成的所有GCC版本都能识别这个习惯用法。您将获得预期的VMOVDQA xmm?, xmm?指令,该指令隐式清除高位。

首选版本C!

<强>锵

版本A:编译为效率低下的代码。临时YMM寄存器使用VPXOR归零,然后使用VINSERTI128(或浮点形式,根据版本和选项)将其插入临时YMM寄存器。

版本B&amp; C:还编译成效率低下的代码。临时YMM寄存器再次归零,但在这里,它使用VPBLENDD与输入YMM寄存器混合。

没什么理想的!

<强> ICC

版本A:理想。生成预期的VMOVDQA xmm?, xmm?指令。

版本B:编译为效率低下的代码。将临时YMM寄存器归零,然后将零与输入YMM寄存器(VPBLENDD)混合。

版本C:也编译为效率低下的代码。将临时YMM寄存器归零,然后使用VINSERTI128将0插入临时YMM寄存器。

首选版本A!

<强> MSVC

版本A和C:编译为效率低下的代码。将临时YMM寄存器归零,然后使用VINSERTI128(A)或VINSERTF128(C)将零插入临时YMM寄存器。

版本B:也编译为效率低下的代码。将临时YMM寄存器归零,然后使用VPBLENDD将其与输入YMM寄存器混合。

没什么理想的!

总而言之,如果使用正确的代码序列,则可以让GCC和ICC发出理想的VMOVDQA指令。但是,我无法通过任何方式让Clang或MSVC安全地发出VMOVDQA指令。这些编译器缺少优化机会。

因此,在Clang和MSVC上,我们可以选择XOR + blend和XOR + insert。哪个更好?我们转向Agner Fog's instruction tables(电子表格版本also available):

关于AMD的Ryzen架构:(推土机系列与AVX __m256相当于这些,以及挖掘机上的AVX2类似:

  Instruction   | Ops | Latency | Reciprocal Throughput |   Execution Ports
 ---------------|-----|---------|-----------------------|---------------------
   VMOVDQA      |  1  |    0    |          0.25         |   0 (renamed)
   VPBLENDD     |  2  |    1    |          0.67         |   3
   VINSERTI128  |  2  |    1    |          0.67         |   3

Agner Fog似乎错过了他桌子Ryzen部分的一些AVX2指令。请参阅this AIDA64 InstLatX64 result以确认VPBLENDD ymm与Ryzen上的VPBLENDW ymm执行相同,而不是与VBLENDPS ymm相同(1个吞吐量来自2个uop,可在2个端口上运行)

另请参阅an Excavator / Carrizo InstLatX64,表明VPBLENDDVINSERTI128在那里具有相同的效果(2个周期延迟,每个周期吞吐量1个)。 VBLENDPS / VINSERTF128也是如此。

在英特尔架构上(Haswell,Broadwell和Skylake):

  Instruction   | Ops | Latency | Reciprocal Throughput |   Execution Ports
 ---------------|-----|---------|-----------------------|---------------------
   VMOVDQA      |  1  |   0-1   |          0.33         |   3 (may be renamed)
   VPBLENDD     |  1  |    1    |          0.33         |   3
   VINSERTI128  |  1  |    3    |          1.00         |   1

显然,VMOVDQA在AMD和英特尔上都是最佳的,但我们已经知道,在它们的代码生成器被改进以识别一个之前,它似乎不是Clang或MSVC的选项为了这个确切的目的,增加了上述习语或附加内在词。

幸运的是,VPBLENDD在AMD和Intel CPU上至少与VINSERTI128一样好。在英特尔处理器上,VPBLENDDVINSERTI128提高了重要。 (实际上,在极少数情况下它不能重命名,它几乎与VMOVDQA一样好,除非需要一个全零向量常量。)更喜欢导致{的内在函数序列{1}}如果您不能哄骗编译器使用VPBLENDD

如果您需要浮点VMOVDQA__m256版本,则选择更加困难。在Ryzen上,__m256d具有1c吞吐量,但VBLENDPS具有0.67c。在所有其他CPU(包括AMD Bulldozer系列)上,VINSERTF128相等或更好。它在英特尔上更好更好(与整数相同)。如果您专门针对AMD进行了优化,则可能需要进行更多测试,以查看特定代码序列中哪个变体最快,否则混合。 Ryzen只会稍微糟糕一点。

总之,然后,针对通用x86并支持尽可能多的不同编译器,我们可以这样做:

VBLENDPS

分别查看此内容以及版本A,B和C on the Godbolt compiler explorer

也许你可以在此基础上定义自己的基于宏的内在,直到更好的东西降下来。

答案 1 :(得分:5)

已添加新的内部函数来解决此问题:

m2 = _mm256_zextsi128_si256(m1);

https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_zextsi128_si256&expand=6177,6177

如果已知上半部分为零,则此函数不会产生任何代码,只是确保不会将上半部分视为未定义。

答案 2 :(得分:2)

查看编译器为此生成的内容:

__m128i m1 = _mm_set1_epi32(1);
__m256i m2 = _mm256_set_m128i(_mm_setzero_si128(), m1);

或者这个:

__m128i m1 = _mm_set1_epi32(1);
__m256i m2 = _mm256_setzero_si256();
m2 = _mm256_inserti128_si256 (m2, m1, 0);

我在这里的clang版本似乎为(vxorps + vinsertf128)生成相同的代码,但是YMMV。