AVX512中是否有类似_mm512_sign_epi16(__m512i a,__m512i b)的功能

时间:2019-04-18 08:56:54

标签: simd intrinsics instruction-set avx2 avx512

AVX512似乎不提供以下功能:

__m512i _mm512_sign_epi16 (__m512i a, __m512i b)

它将很快可用还是有替代选择?

2 个答案:

答案 0 :(得分:5)

如果不需要调零部分,则只需要2条指令(和调零寄存器)即可:

您可以_mm512_movepi16_mask()将符号位放入掩码(pmovmskb的AVX512版本)中,并进行从零开始的合并掩码减法,以基于另一个的符号取反向量。

#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

vector->掩码在Skylake-X(具有vpmovw2mvptestmwvpcmpw上具有3个周期的延迟,但是使用该掩码仅具有1个周期的延迟。因此,从输入到输出的延迟为:

    signs-> SKX结果中
  • 4个周期
  • target开始1个周期->在SKX上得到结果(只是从零开始屏蔽的vpsubw。)

也要应用等零条件:您可以对向量执行的下一个操作零屏蔽或合并屏蔽,因此未使用的元素应为零

您需要进行额外的比较才能创建另一个蒙版,但是您可能不需要浪费第二条额外的说明来立即应用它。

如果您真的想以这种方式构建一个独立的vpsignw,我们可以做最后的零掩码,但这是4个内在函数,可编译为4条指令,并且吞吐量可能比@wim的min差。 / max /相乘。但这具有良好的关键路径延迟,在SKX上总共大约有5个周期(如果可以将最终的掩模折叠成其他东西,则为4个周期)。关键路径是迹象->遮罩,然后是遮罩子。符号-> nonzeromask可以与任何一个并行运行。

__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
}

可能的是,编译器可以将vmovdqu16的零掩码{in1}折叠为add / or / xor的合并掩码,或将乘法\ {{ 1}}。但是,自己动手做可能是个好主意。

答案 1 :(得分:4)

可能的解决方案是:

__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' solution是一个很好的选择,它具有更好的延迟。 但是实际上,高吞吐量通常比低延迟更受关注。

无论如何,不​​同替代方案(此处的解决方案,彼得·科德斯的答案以及chtz' comment中的拆分思路)的实际性能取决于周围的代码和执行指令的CPU类型。您必须对其他选择进行基准测试,以查看在您的特定情况下哪种选择最快。