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

__m512i _mm512_sign_epi16 (__m512i a, __m512i b)

它会很快上市还是有替代品?

最佳答案

如果您不需要归零部分 ,则只需要 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 -> mask 在 Skylake-X 上有 3 个周期的延迟(使用 vpmovw2mvptestmwvpcmpw ),但使用掩码只有另外 1 个周期的延迟。所以从输入到输出的延迟是:
  • 来自 signs 的 4 个周期 -> SKX
  • 上的结果
  • 来自 target 的 1 个循环 -> SKX 上的结果(只是从零开始的屏蔽 vpsubw。)


  • 还应用为零条件 :您可能能够对您对向量执行的下一个操作进行零掩码或合并掩码,因此本应为零的元素未被使用。

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

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

    __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 折叠为 add/or/xor 的合并掩码,或乘法/and 的零掩码。但可能是一个好主意自己做。

    关于simd - AVX512 中是否有类似 _mm512_sign_epi16 (__m512i a, __m512i b) 的函数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55742533/

    10-15 06:06