AVX512中是否有像_mm512_sign_epi16(__ m512i a,__ m512i b)的功能

问题描述 投票:4回答:2

以下功能似乎在AVX512上不可用:

__m512i _mm512_sign_epi16 (__m512i a, __m512i b)

它会很快提供还是有替代品?

simd intrinsics instruction-set avx2 avx512
2个回答
5
投票

如果您不需要归零部分,则只需要2条指令(以及归零寄存器):

您可以将符号位_mm512_movepi16_mask()转换为掩码(QXxswpoi的AVX512版本),并从零开始进行合并掩码减法,以根据另一个符号取消向量。

pmovmskb

vector - > mask在Skylake-X上有3个循环延迟(使用#ifdef __AVX512BW__ // does *not* do anything special for signs[i] == 0, just negative / non-negative __m512i conditional_negate(__m512i target, __m512i signs) { __mmask32 negmask = _mm512_movepi16_mask(signs); // vpsubw target{k1}, 0, target __m512i neg = _mm512_mask_sub_epi16(target, negmask, _mm512_setzero_si512(), target); return neg; } #endif vpmovw2mvptestmw),但使用掩码只有另外1个循环延迟。因此,从输入到输出的延迟是:

  • 来自vpcmpw的4个周期 - > SKX上的结果
  • 来自signs的1个循环 - > SKX上的结果(只是来自零的蒙面target。)

要同时应用is-zero条件:您可以使用零掩码或合并掩码对向量执行的下一个操作,因此应该为零的元素未使用。

您需要额外的比较来创建另一个掩码,但您可能不需要浪费第二条额外的指令来立即应用它。

如果你真的想用这种方式构建一个独立的vpsubw,我们可以做最后的零屏蔽,但这是4个内在函数,编译为4个指令,并且吞吐量可能比@ wim的min / max / multiply更差。但是这具有良好的关键路径延迟,在SKX上总共约5个周期(如果你可以将最终的屏蔽折叠成其他的话,则为4个周期)。关键路径是sign-> mask,然后是mask sub。 sign-> nonzeromask可以与其中任何一个并行运行。

vpsignw

可能编译器可以将这个零屏蔽__m512i mm512_psignw(__m512i target, __m512i signs) { __mmask32 negmask = _mm512_movepi16_mask(signs); // vpsubw target{negmask}, 0, target merge masking to only modify elements that need negating __m512i neg = _mm512_mask_sub_epi16(target, negmask, _mm512_setzero_si512(), target); __mmask32 nonzeromask = _mm512_test_epi16_mask(signs,signs); // per-element non-zero? return _mm512_maskz_mov_epi16(nonzeromask, neg); // zero elements where signs was zero } instrinsic折叠为vmovdqu16 / add / or的合并屏蔽,或者对multiply / xor进行零屏蔽。但自己这样做可能是一个好主意。


4
投票

可能的解决方案是:

and

此解决方案应具有合理的吞吐量,但由于整数乘法,延迟可能不是最佳的。一个很好的选择是__m512i mm512_sign_epi16(__m512i a, __m512i b){ /* Emulate _mm512_sign_epi16() with instructions */ /* that exist in the AVX-512 instruction set */ b = _mm512_min_epi16(b, _mm512_set1_epi16(1)); /* clamp b between -1 and 1 */ b = _mm512_max_epi16(b, _mm512_set1_epi16(-1)); /* now b = -1, 0 or 1 */ a = _mm512_mullo_epi16(a, b); /* apply the sign of b to a */ return a; } ,它具有更好的延迟。但实际上,高吞吐量通常比低延迟更有意义。

无论如何,不​​同替代方案的实际性能(这里的解决方案,Peter Cordes的回答以及Peter Cordes' solution中的分裂思想)取决于周围的代码和执行指令的CPU类型。您必须对备选方案进行基准测试,以确定在您的特定情况下哪一个最快。

© www.soinside.com 2019 - 2024. All rights reserved.