Сравните два значения __m128i для общего заказа - PullRequest
7 голосов
/ 28 мая 2019

Мне нужен способ сравнить значения типа __m128i в C ++ для общего порядка между любыми значениями типа __m128i. Тип заказа не имеет значения, поскольку он устанавливает общий порядок между всеми значениями типа __m128i. Следовательно, сравнение может быть меньше, чем между 128-битными целыми числами или чем-то еще, если только это обеспечивает полный порядок.

Я попытался использовать оператор <, но он не вернул bool, но вместо этого, кажется, сравнивает векторные компоненты __m128i (т.е. SIMD):

#include <emmintrin.h>

inline bool isLessThan(__m128i a, __m128i b) noexcept {
     // error: cannot convert '__vector(2) long int' to 'bool' in return
     return a < b;
}

Другой возможностью будет использование memcmp / strcmp или аналогичного, но это, скорее всего, не будет оптимальным. Ориентируясь на современные процессоры Intel x86-64 по крайней мере с SSE4.2 и AVX2, есть ли какие-либо встроенные функции / инструкции, которые я мог бы использовать для таких сравнений? Как это сделать?

PS: аналогичные вопросы задавались для проверки равенства, но не для заказа:

1 Ответ

5 голосов
/ 28 мая 2019

Вот, пожалуйста.

inline bool isLessThan( __m128i a, __m128i b )
{
    /* Compare 8-bit lanes for ( a < b ), store the bits in the low 16 bits of the
       scalar value: */
    const int less = _mm_movemask_epi8( _mm_cmplt_epi8( a, b ) );

    /* Compare 8-bit lanes for ( a > b ), store the bits in the low 16 bits of the
       scalar value: */
    const int greater = _mm_movemask_epi8( _mm_cmpgt_epi8( a, b ) );

    /* It's counter-intuitive, but this scalar comparison does the right thing.
       Essentially, integer comparison searches for the most significant bit that
       differs... */
    return less > greater;
}

Порядок меньше идеального, поскольку coz pcmpgtb обрабатывает эти байты как целые числа со знаком, но вы сказали, что это не важно для вашего варианта использования.


Обновление: вот немного более медленная версия с uint128_t порядком сортировки.

// True if a < b, for unsigned 128 bit integers
inline bool cmplt_u128( __m128i a, __m128i b )
{
    // Flip the sign bits in both arguments.
    // Transforms 0 into -128 = minimum for signed bytes,
    // 0xFF into +127 = maximum for signed bytes
    const __m128i signBits = _mm_set1_epi8( (char)0x80 );
    a = _mm_xor_si128( a, signBits );
    b = _mm_xor_si128( b, signBits );

    // Now the signed byte comparisons will give the correct order
    const int less = _mm_movemask_epi8( _mm_cmplt_epi8( a, b ) );
    const int greater = _mm_movemask_epi8( _mm_cmpgt_epi8( a, b ) );
    return less > greater;
}

Мы строим сравнения без знака из числа со знаком, сдвигая диапазон входных данных без знака к знаку (переключив старший бит = вычесть 128).

...