Я смотрел на MMX/SSE, и мне интересно. Существуют инструкции для упакованного насыщенного вычитания беззнаковых байтов и слов, но не двойных слов.
Есть ли способ сделать то, что я хочу, а если нет, то почему его нет?
Я смотрел на MMX/SSE, и мне интересно. Существуют инструкции для упакованного насыщенного вычитания беззнаковых байтов и слов, но не двойных слов.
Есть ли способ сделать то, что я хочу, а если нет, то почему его нет?
Если у вас есть доступный SSE4.1, я не думаю, что вы можете стать лучше, чем использовать подход pmaxud
+psubd
, предложенный @harold. Конечно, с AVX2 вы также можете использовать соответствующие 256-битные варианты.
__m128i subs_epu32_sse4(__m128i a, __m128i b){
__m128i mx = _mm_max_epu32(a,b);
return _mm_sub_epi32(mx, b);
}
Без SSE4.1 вам нужно каким-то образом сравнить оба аргумента. К сожалению, нет сравнения epu32
(не до AVX512), но вы можете смоделировать его, сначала добавив 0x80000000
(что в данном случае эквивалентно xor-ing) к обоим аргументам:
__m128i cmpgt_epu32(__m128i a, __m128i b) {
const __m128i highest = _mm_set1_epi32(0x80000000);
return _mm_cmpgt_epi32(_mm_xor_si128(a,highest),_mm_xor_si128(b,highest));
}
__m128i subs_epu32(__m128i a, __m128i b){
__m128i not_saturated = cmpgt_epu32(a,b);
return _mm_and_si128(not_saturated, _mm_sub_epi32(a,b));
}
В некоторых случаях может быть лучше заменить сравнение некоторым изменением битов старшего бита и трансляцией этого каждому биту с использованием сдвига (это заменяет pcmpgtd
и три битовые логические операции (и необходимо загрузить 0x80000000
хотя бы один раз) с помощью psrad
и пяти битовых логических операций):
__m128i subs_epu32_(__m128i a, __m128i b) {
__m128i r = _mm_sub_epi32(a,b);
__m128i c = (~a & b) | (r & ~(a^b)); // works with gcc/clang. Replace by corresponding intrinsics, if necessary (note that `andnot` is a single instruction)
return _mm_srai_epi32(c,31) & r;
}
Godbolt-Link, включая adds_epu32
вариантов: https://godbolt.org/z/n4qaW1 Странно, clang требует больше копий регистров, чем gcc для вариантов без SSE4.1. С другой стороны, clang находит оптимизацию pmaxud
для варианта cmpgt_epu32
при компиляции с SSE4.1: https://godbolt.org/z/3o5KCm
u32.saturating_sub()
: подписанный с помощью PXOR, затем PCMPGTD со знаком-сравнением, затем AND/ANDN/OR для применения насыщения к результату PSUBD. Я не уверен, что это оптимально; ему нужно просто PAND, потому что единственный случай насыщения для беззнакового вычитания - это насыщение до 0. - person Peter Cordes   schedule 10.06.2019subus(a, b) == max(a, b) - b
- E: ну, это хорошо с SSE4.1, означает ли MMX/SSE буквально только MMX и SSE? - person harold   schedule 10.06.2019pmaxud
. - person Peter Cordes   schedule 10.06.2019