如何清除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

当然,我不想用“and”或_mm256_insertf128_si256()之类的词。

最佳答案

更新:现在有一个__m128i _mm256_zextsi128_si256(__m128i)内在的;参见Agner Fog's answer。下面的其他答案只与不支持此内在特性的旧编译器相关,并且没有高效、可移植的解决方案。
不幸的是,理想的解决方案将取决于您正在使用的编译器,并且在其中一些编译器上,没有理想的解决方案。
我们可以通过以下几种基本方法来编写:
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位,这样它们中的任何一个都可以安全地使用。但是哪个是最理想的呢?好吧,那要看你用的是哪个编译器了…
合同一般条款:
版本A:根本不受支持,因为GCC缺少_mm256_set_m128i内部函数。(当然可以模拟,但可以使用“b”或“c”中的一种形式来完成。)
版本B:编译成低效代码。习语是不可识别的,而内部函数则被逐字翻译成机器代码指令。使用VPXOR将临时ymm寄存器归零,然后使用VPBLENDD将其与输入ymm寄存器混合。
C版:理想。尽管代码看起来有点吓人和低效,但所有支持avx2代码生成的gcc版本都认识到这一点。您将得到预期的VMOVDQA xmm?, xmm?指令,该指令隐式清除高位。
喜欢C版!
叮当声:
版本A:编译成低效代码。使用VPXOR将临时ymm寄存器归零,然后使用VINSERTI128将其插入临时ymm寄存器(或浮点形式,取决于版本和选项)。
版本B&C:也被编译成低效的代码。临时ymm寄存器再次归零,但在这里,它使用VPBLENDD与输入ymm寄存器混合。
什么都不理想!
国际商会:
A版:理想。生成预期的VMOVDQA xmm?, xmm?指令。
版本B:编译成低效代码。将临时ymm寄存器归零,然后将零与输入ymm寄存器混合(VPBLENDD)。
C版:也被编译成低效的代码。将临时ymm寄存器归零,然后使用VINSERTI128将零插入临时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相同(可在2个端口上运行的2个UOP的1C吞吐量)。
另请参见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和intel上都是最理想的,但是我们已经知道了,在clang或msvc上,它似乎都不是一个选项,除非它们的代码生成器被改进以识别上述习惯用法中的一个,或者为了这个精确的目的添加了一个额外的内部函数。
幸运的是,VPBLENDD在amd和intel cpu上至少和VINSERTI128一样好。在英特尔处理器上,VPBLENDDVINSERTI128有显著改进。(事实上,它几乎和VMOVDQA一样好,在很少的情况下,后者不能重命名,除了需要全零向量常量。)如果您不能哄编译器使用VPBLENDD的话,最好选择导致VMOVDQA指令的内部函数序列。
如果需要浮点__m256__m256d版本,则选择更困难。在Ryzen上,VBLENDPS的吞吐量为1C,但VINSERTF128的吞吐量为0.67C。在所有其他CPU(包括AMD推土机系列)上,VBLENDPS等于或优于。它在intel上要好得多(和integer一样)。如果您是专门为amd优化的,那么您可能需要做更多的测试,以查看在您的特定代码序列中哪个变体最快,否则就需要混合。对Ryzen来说只差一点点。
总之,我们可以针对通用x86并支持尽可能多的不同编译器:
#if (defined _MSC_VER)

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

#elif (defined __INTEL_COMPILER)

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

#elif (defined __GNUC__)

    // Intended to cover GCC and Clang.
    ymm = _mm256_inserti128_si256(_mm256_setzero_si256(),
                                  _mm256_castsi256_si128(ymm),
                                  0);

#else
    #error "Unsupported compiler: need to figure out optimal sequence for this compiler."
#endif

请分别查看此版本和版本a、b和con the Godbolt compiler explorer
也许您可以在此基础上定义自己的基于宏的内在特性,直到有更好的东西出现。

关于c - 如何清除__m256值的高128位?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21384879/

10-11 00:25