Если вам не нужна часть обнуления , вам нужно только 2 инструкции (и регистр обнуления):
Вы можете _mm512_movepi16_mask()
подписать биты в маске (версия AVX512 pmovmskb
) и сделать вычитание слияния из нуля, чтобы свести к нулю вектор на основе признаков другого.
#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 имеет 3 задержки цикла на Skylake-X (с vpmovw2m
, vptestmw
или vpcmpw
), но использование маски имеет только еще одну задержку 1 цикла. Таким образом, задержка от входов до выходов:
- 4 цикла из
signs
-> результат по SKX
- 1 цикл из
target
-> результат на SKX (только замаскированный vpsubw
с нуля.)
Чтобы также применить условие is-zero : вы можете быть в состоянии обнулить или слить маску при следующей операции, которую вы выполняете с вектором, поэтому элементы, которые должны были быть равны нулю, не используются.
Вам нужно дополнительное сравнение для создания другой маски, но вам, вероятно, не нужно нужно тратить 2-ю дополнительную инструкцию, чтобы применить ее сразу.
Если вы действительно хотите построить автономный vpsignw
таким образом, мы можем выполнить это окончательное маскирование нуля, но это 4 встроенные функции, которые компилируются в 4 инструкции, и, вероятно, хуже для пропускной способности, чем @ wim's min / max / размножаться. Но это имеет хорошую задержку критического пути, всего около 5 циклов на SKX (или 4, если вы можете сложить окончательную маскировку во что-то еще). Критический путь это знаки-> маска, затем маскируемый саб. Знаки-> ненулевая маска могут работать параллельно с любым из них.
__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
. Но, вероятно, хорошая идея сделать это самостоятельно.