以下功能在 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 个周期的延迟(使用
vpmovw2m
、 vptestmw
或 vpcmpw
),但使用掩码只有另外 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/