2017-03-05 102 views
8

这是我的想象,还是一个PNOT指令缺少SSE和AVX?也就是说,翻转矢量中每一位的指令。是不是从SSE,AVX失踪?

如果是,是否有更好的方法来模拟它,而不是PXOR与所有1的向量?很烦人,因为我需要建立一个全1的矢量来使用这种方法。

+1

设置所有的向量'1's不特别困难:'[v] pcmpe [typesize] \t%[x/y] mmN,%[x/y] mmN [,%[x/y] mmN]'或大约。建立常数的单个指令看起来不太繁重。如果你对'xor'有特殊的反感,'pandn'和'andnps'也是可用的。 – EOF

+1

这并不可怕 - 但是,只要我期待这样的基本操作,它就是2倍。当然,这个常数可能会以登记册为代价而被吊起来。无论如何,只是检查我的假设,我没有错过这个地方。 @EOF – SODIMM

+1

鉴于'pcmpeXX'因为至少是Sandy Bridge(根据Agner Fog的微体系结构手册)而被认为是依赖打破的,所以在几乎所有情况下,无论是否需要一条或两条指令来否定向量都无关紧要。 – EOF

回答

9

对于这样的情况,看看编译器会产生什么是有益的。

E.g.以下功能:

#include <immintrin.h> 

__m256i test(const __m256i v) 
{ 
    return ~v; 
} 

GCC和铛似乎generate much the same code

test(long long __vector(4)): 
     vpcmpeqd  ymm1, ymm1, ymm1 
     vpxor ymm0, ymm0, ymm1 
     ret 
+2

感谢Paul。这是一个相当不错的表明,我认为在那里没有太多更好的东西。 – SODIMM

+1

s /指示/指示/ – SODIMM

4

如果使用Intrinsics,则可以使用这样的内联函数分别执行非操作。

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 
} 
+1

好的编译器通常会将'_mm256_set1_epi32(-1)'优化为'vpcmpeqd same,same'。我想用AVX可能不会损害编译器试图“欺骗”它发射,如果它不正常。 (使用SSE可能需要额外的MOVDQA指令,但AVX 3操作数编码解决了这个问题。) –

3

可以使用PANDN操作码为。

PANDN实现操作

DEST = NOT(DEST) AND SRC ; (SSEx) 

DEST = NOT(SRC1) AND SRC2 ; (AVXx) 

结合此操作用全1矢量有效地导致一个PNOT操作。


一些86(SSEX)汇编代码是这样的:

; XMM0 is input register 
PCMPEQB xmm1, xmm1  ; Whole xmm1 reg set to 1's 
PANDN  xmm0, xmm1  ; xmm0 = NOT(xmm0) AND xmm1 
; XMM0 contains NOT(XMM0) 

一些86(AVXx)汇编代码是这样的:

; YMM0 is input register 
VPCMPEQB ymm1, ymm1, ymm1 ; Whole ymm1 reg set to 1's 
VPANDN ymm0, ymm0, ymm1 ; ymm0 = NOT(ymm0) AND ymm1 
; YMM0 contains NOT(YMM0) 

能(的两个过程)很容易被翻译成内部函数。

2

AVX512F vpternlogd/_mm512_ternarylogic_epi32(__m512i a, __m512i b, __m512i c, int imm8)终于提供了一个方法来实现没有任何额外的常量,使用单个指令(其中有2个,每个时钟吞吐量Skylake-avx512和KNL,所以它不太好,因为PXOR/XORPS为256B和小)

vpternlogd zmm,zmm,zmm, imm8有3个输入向量和一个输出,修改目的地。如果立即执行正确的操作,您仍然可以将一个复制和不复制到一个不同的寄存器中,但是它将对输出寄存器有一个“错误”的依赖关系(vpxord dst, src, all-ones不会)。

TL:DR:DR可能仍然使用xor作为循环的一部分,除非您的注册表不足。如果稍后需要其输入,则可能需要花费额外的vmovdqa寄存器复制指令。在循环之外,vpternlogd zmm,zmm,zmm, 0xffthe compiler's best option for creating a 512b vector of all-ones in the first place,因为AVX512比较指令比较为掩码(k0-k7),所以与全1的异或可能涉及vpternlogd,或者可能是内存中的广播常量。


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

因此,如果我们想要的结果就只有SRC2依赖(这是zmm/m512/m32bcst操作数),我们应该选择在偶数位置(由src2=0选择)重复1,0,与1的位图。

vpternlogd zmm1, zmm2,zmm2, 01010101b ; 0x55 

如果你幸运的话,编译器将优化_mm512_xor_epi32(v, set1(-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); 

如果你不知道这是一个好主意,只是保持简单并使用相同的变量所有3个输入:

__m512i t2_inv = _mm512_ternarylogic_epi32(t2, t2, t2, 0b01010101);