2015-10-05 8 views
3

Я заинтересован в определении переполняющих значений при добавлении неподписанных 8-разрядных целых чисел, и насыщать результат 0xFF:SSE2 - сравнение встроенных функций целых числа без знака

__m128i m1 = _mm_loadu_si128(/* 16 8-bit unsigned integers */); 
__m128i m2 = _mm_loadu_si128(/* 16 8-bit unsigned integers */); 

__m128i m3 = _mm_adds_epu8(m1, m2); 

Я был бы заинтересован в выполнении сравнения менее чем на эти целые числа без знака, похожие на _mm_cmplt_epi8 для знакового:

__m128i mask = _mm_cmplt_epi8 (m3, m1); 
m1 = _mm_or_si128(m3, mask); 

Если «epu8» эквивалент был доступен, mask бы 0xFF где m3[i] < m1[i] (переполнение!), 0x00 otherwise, nd мы могли бы насытить m1 с помощью «или», поэтому m1 проведет с добавлением результат, где действительно, и 0xFF, где он переполнен.

Проблема, _mm_cmplt_epi8 выполняет знаковое сравнение, так, например, если m1[i] = 0x70 и m2[i] = 0x10, то m3[i] = 0x80 и mask[i] = 0xFF, который, очевидно, не то, что я требую.

Использование VS2012.

Буду признателен за другой подход для выполнения этого. Благодаря!

+1

Конечно, '_mm_adds_epu8' уже насыщен результат. Определение того, где насыщенные результаты необходимы для расчета, который здесь не описан. –

ответ

7

Один из способов реализации сравнения для 8-разрядных векторов без знака состоит в использовании _mm_max_epu8, который возвращает максимум 8-битных элементов int без знака. Вы можете сравнить для равенства максимальное значение (без знака) двух элементов с одним из элементов-источников и затем вернуть соответствующий результат. Это переводит на 2 инструкции для >= или <= и 3 инструкции для > или <.

Пример кода:

#include <stdio.h> 
#include <emmintrin.h> // SSE2 

#define _mm_cmpge_epu8(a, b) \ 
     _mm_cmpeq_epi8(_mm_max_epu8(a, b), a) 

#define _mm_cmple_epu8(a, b) _mm_cmpge_epu8(b, a) 

#define _mm_cmpgt_epu8(a, b) \ 
     _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1)) 

#define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a) 

int main(void) 
{ 
    __m128i va = _mm_setr_epi8(0, 0, 1, 1, 1, 127, 127, 127, 128, 128, 128, 254, 254, 254, 255, 255); 
    __m128i vb = _mm_setr_epi8(0, 255, 0, 1, 255, 0, 127, 255, 0, 128, 255, 0, 254, 255, 0, 255); 

    __m128i v_ge = _mm_cmpge_epu8(va, vb); 
    __m128i v_le = _mm_cmple_epu8(va, vb); 
    __m128i v_gt = _mm_cmpgt_epu8(va, vb); 
    __m128i v_lt = _mm_cmplt_epu8(va, vb); 

    printf("va = %4vhhu\n", va); 
    printf("vb = %4vhhu\n", vb); 
    printf("v_ge = %4vhhu\n", v_ge); 
    printf("v_le = %4vhhu\n", v_le); 
    printf("v_gt = %4vhhu\n", v_gt); 
    printf("v_lt = %4vhhu\n", v_lt); 

    return 0; 
} 

компилировать и запускать:

$ gcc -Wall _mm_cmplt_epu8.c && ./a.out 
va = 0 0 1 1 1 127 127 127 128 128 128 254 254 254 255 255 
vb = 0 255 0 1 255 0 127 255 0 128 255 0 254 255 0 255 
v_ge = 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 255 
v_le = 255 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 
v_gt = 0 0 255 0 0 255 0 0 255 0 0 255 0 0 255 0 
v_lt = 0 255 0 0 255 0 0 255 0 0 255 0 0 255 0 0 
+1

Сравнивая с max для равенства, сделал трюк. Кажется очевидным сейчас :) Спасибо за очень сложный ответ! –

+0

Добро пожаловать. Я потратил много времени на поиск обходных путей для SSE и других наборов инструкций SIMD, поэтому хорошо иметь возможность перерабатывать некоторые из этих решений! –

+2

@PaulR И их нет. А ... 64-битный арифметический сдвиг вправо. Асимметричное целое число/с плавающей запятой. double <-> конверсии int64. Если бы только Intel перестала тащить ногу с материалом AVX512, у которого почти все, что я хочу ... – Mysticial

5

Другие ответы заставили меня задуматься о более простой метод, чтобы ответить на конкретный вопрос более прямо:

Чтобы просто обнаружить зажимать, насыщать и не насыщать добавления, а также сравнивать результаты.

__m128i m1 = _mm_loadu_si128(/* 16 8-bit unsigned integers */); 
__m128i m2 = _mm_loadu_si128(/* 16 8-bit unsigned integers */); 

__m128i m1m2_sat = _mm_adds_epu8(m1, m2); 
__m128i m1m2_wrap = _mm_add_epi8(m1, m2); 
__m128i non_clipped = _mm_cmpeq_epi8(m1m2_sat, m1m2_wrap); 

Так что это только две инструкции за adds, и один из них может работать параллельно с adds. Таким образом, маска non_clipped готова к выполнению одного цикла после результата сложения. (Потенциально 3 команды (дополнительный movdqa) без векторных опций без операндов AVX 3-операнда.)

Если результат добавления без насыщения 0xFF, он будет соответствовать результату насыщающего добавления и будет обнаружен как не отсекающий , Вот почему это отличается от просто проверки вывода насыщающего add для байтов 0xFF.

+1

Это более «целостный» ответ! –

1

Другой способ сравнения беззнаковых байтов: добавить 0x80 и сравнить их как подписанные.

__m128i _mm_cmplt_epu8(__m128i a, __m128i b) { 
    __m128i as = _mm_add_epi8(a, _mm_set1_epi8((char)0x80)); 
    __m128i bs = _mm_add_epi8(b, _mm_set1_epi8((char)0x80)); 
    return _mm_cmplt_epi8(as, bs); 
} 

Я не думаю, что это очень эффективно, но это работает, и это может быть полезно в некоторых случаях.Кроме того, вы можете использовать xor вместо добавления, если хотите. В некоторых случаях вы даже можете выполнить двунаправленную проверку диапазона одновременно, т. Е. Сравнить значение как с нижней, так и с верхней границей. Для этого выровняйте нижнюю границу с 0x80, аналогично тому, что делает this answer.

0

Существует реализация сравнения 8-битового целого числа без знака:

inline __m128i NotEqual8u(__m128i a, __m128i b) 
    { 
     return _mm_andnot_si128(_mm_cmpeq_epi8(a, b), _mm_set1_epi8(-1)); 
    } 

    inline __m128i Greater8u(__m128i a, __m128i b) 
    { 
     return _mm_andnot_si128(_mm_cmpeq_epi8(_mm_min_epu8(a, b), a), _mm_set1_epi8(-1)); 
    } 

    inline __m128i GreaterOrEqual8u(__m128i a, __m128i b) 
    { 
     return _mm_cmpeq_epi8(_mm_max_epu8(a, b), a); 
    } 

    inline __m128i Lesser8u(__m128i a, __m128i b) 
    { 
     return _mm_andnot_si128(_mm_cmpeq_epi8(_mm_max_epu8(a, b), a), _mm_set1_epi8(-1)); 
    } 

    inline __m128i LesserOrEqual8u(__m128i a, __m128i b) 
    { 
     return _mm_cmpeq_epi8(_mm_min_epu8(a, b), a); 
    } 
Смежные вопросы