Есть ли способ вычесть упакованные двойные слова без знака, насыщенные, на x86, используя MMX/SSE?

Я смотрел на MMX/SSE, и мне интересно. Существуют инструкции для упакованного насыщенного вычитания беззнаковых байтов и слов, но не двойных слов.

Есть ли способ сделать то, что я хочу, а если нет, то почему его нет?


person z0rberg's    schedule 10.06.2019    source источник
comment
Вы можете использовать сравнение и маску. Что касается того, почему он не существует как единая инструкция, остается только гадать.   -  person Jester    schedule 10.06.2019
comment
Я не понимаю. Как бы я это сделал?   -  person z0rberg's    schedule 10.06.2019
comment
Ваши исходные значения подписаны или не подписаны? Это довольно просто, если входные данные беззнаковые, немного сложнее, если они подписаны.   -  person Paul R    schedule 10.06.2019
comment
Посмотрите, как LLVM автоматически векторизует Rust u32.saturating_sub(): подписанный с помощью PXOR, затем PCMPGTD со знаком-сравнением, затем AND/ANDN/OR для применения насыщения к результату PSUBD. Я не уверен, что это оптимально; ему нужно просто PAND, потому что единственный случай насыщения для беззнакового вычитания - это насыщение до 0.   -  person Peter Cordes    schedule 10.06.2019
comment
Вы можете использовать subus(a, b) == max(a, b) - b - E: ну, это хорошо с SSE4.1, означает ли MMX/SSE буквально только MMX и SSE?   -  person harold    schedule 10.06.2019
comment
@harold: о да, это очень хорошо с SSE4.1 для pmaxud.   -  person Peter Cordes    schedule 10.06.2019
comment
вау, они взрывают мой бюджет. я задавал свой вопрос, потому что хотел вообще избежать cmp.PaulR без знака, как указано в вопросе. Думаю, SSE4 подойдет? Я должен проверить. Питер, @harold, спасибо за ваши предложения, я посмотрю на их производительность.   -  person z0rberg's    schedule 10.06.2019
comment
Не знаю, какой из ваших комментариев будет лучшим ответом...   -  person z0rberg's    schedule 10.06.2019
comment
@ z0rberg Лучшее решение зависит от некоторого контекста, например, заботитесь ли вы о пропускной способности, задержке, переносимости и т. д.   -  person chtz    schedule 11.06.2019
comment
Только пропускная способность @chtz. у меня еще не было времени, чтобы сесть с предложениями.   -  person z0rberg's    schedule 11.06.2019


Ответы (1)


Если у вас есть доступный 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

person chtz    schedule 17.06.2019