SSE,AVX不缺少?

SOD*_*IMM 9 x86 sse simd avx

这是我的想象力,还是PNOTSSE和AVX失踪的指令?也就是说,翻转向量中的每一位的指令.

如果是,是否有比PXOR使用所有1的向量更好的模拟方法?非常烦人,因为我需要设置所有1的向量来使用该方法.

Pau*_*l R 10

对于诸如此类的情况,查看编译器将生成什么是有益的.

例如,对于以下功能:

#include <immintrin.h>

__m256i test(const __m256i v)
{
  return ~v;
}
Run Code Online (Sandbox Code Playgroud)

gcc和clang似乎都生成了相同的代码:

test(long long __vector(4)):
        vpcmpeqd        ymm1, ymm1, ymm1
        vpxor   ymm0, ymm0, ymm1
        ret
Run Code Online (Sandbox Code Playgroud)

  • 谢谢保罗.这是一个非常好的表明我认为没有更好的东西. (2认同)

Pet*_*des 7

AVX512F vpternlogd/_mm512_ternarylogic_epi32(__m512i a, __m512i b, __m512i c, int imm8)最终提供了一种无需任何额外常量即可实现 NOT 的方法,使用可在 Skylake-avx512 上的任何矢量 ALU 端口上运行的单个指令。

对于 AVX512VL,也适用于 128 和 256 位向量,而不会弄脏 ZMM 的上部。(除 Xeon Phi 之外的所有 AVX512 CPU 均具有 AVX512VL)。

在 Intel CPU 上,它可以在端口 0、1 或 5 中的任何一个上运行,因此 128 和 256 位版本的吞吐量为 3/时钟。或者像往常一样,512 位向量为 2/时钟,因为当任何 512 位微指令运行时,端口 1 会关闭。 https://www.uops.info/html-instr/VPTERNLOGD_XMM_XMM_XMM_I8.html)。


vpternlogd zmm,zmm,zmm, imm8有 3 个输入向量和 1 个输出,可就地修改目的地。使用正确的立即数,您仍然可以在不同的寄存器中实现复制与非,但它将对输出寄存器有“错误”依赖(这vpxord dst, src, all-ones不会)。

TL:DR:可能仍然使用全 1 的异或作为循环的一部分,除非你用完了寄存器。 vpternlog如果稍后需要输入,可能会花费额外的vmovdqa寄存器复制指令。

在循环之外,编译器首先创建全 1 的 512b 向量的最佳选择vpternlogd zmm,zmm,zmm, 0xff,因为 AVX512 比较指令比较掩码 ( ),因此与全 1 的异或可能已经涉及,或者可能是广播常数从内存中,对于 512 位向量。或者是 128 或 256 位的深度破坏 ALU uop 。k0-k7vpternlogdvpcmpeqd same,same


对于每个位位置i,输出位为imm[ (DEST[i]<<2) + (SRC1[i]<<1) + SRC2[i]],其中imm8被视为 8 元素位图。

因此,如果我们希望结果仅取决于 SRC2(即操作zmm/m512/m32bcst数),我们应该选择重复 1,0 的位图,1在偶数位置(由 选定src2=0)。

vpternlogd  zmm1,zmm1, zmm2,  01010101b  ; 0x55  ; false dep on zmm1
Run Code Online (Sandbox Code Playgroud)

如果你幸运的话,编译器会为你优化,_mm512_xor_epi32(v, _mm512_set1_epi32(-1))如果vpternlogd它有利可图的话。

// To hand-hold a compiler into saving a vmovdqa32 if needed:
__m512i tmp = something earlier;
__m512i t2 = _mm...(tmp);
// use-case: tmp is dead, t2 and ~t2 are both needed.
__m512i t2_inv = _mm512_ternarylogic_epi32(tmp, t2, t2, 0b01010101);
Run Code Online (Sandbox Code Playgroud)

如果您不确定这是一个好主意,只需保持简单并为所有 3 个输入使用相同的变量即可:

__m512i t2_inv = _mm512_ternarylogic_epi32(t2, t2, t2, 0b01010101);
Run Code Online (Sandbox Code Playgroud)


Mar*_*tin 6

如果您使用 Intrinsics,您可以使用这样的内联函数来单独进行 not 操作。

 inline __m256i _mm256_not_si256 (__m256i a){    
     //return  _mm256_xor_si256 (a, _mm256_set1_epi32(0xffffffff));
     return  _mm256_xor_si256 (a, _mm256_cmpeq_epi32(a,a));//I didn't check wich one is faster   
 }
Run Code Online (Sandbox Code Playgroud)

  • 好的编译器通常会将 `_mm256_set1_epi32(-1)` 优化为 `vpcmpeqd same,same`。我想对于 AVX 来说,尝试“欺骗”它发出这种信号(如果它通常不会发出这种信号)可能不会伤害编译器。(使用 SSE 可能会花费额外的 MOVDQA 指令,但 AVX 3 操作数编码解决了这个问题。) (3认同)