Сравните два значения __m128i для общего порядка

Мне нужен способ сравнить значения типа __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: Подобные вопросы были заданы для проверки равенства, но не для заказа:

Простым решением (с помощью gcc/clang) было бы преобразовать в __int128 и сравнить с <: godbolt.org/z/Kh0Yd5. Не обязательно самый эффективный (но это также зависит от того, обычно ли ваши входные данные поступают из регистров или памяти)

chtz 28.05.2019 15:42
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
1
880
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ну вот.

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;
}

Порядок не идеален, потому что 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).

Я пробовал это, но он генерирует ужасную сборку, не так ли? Или этого следует ожидать?

Passer By 28.05.2019 19:52

Вы можете использовать -Gv с MSVC, поэтому он использует __vectorcall, поэтому ваш исходный код по-прежнему компилируется с помощью GCC. (И не будет переопределять соглашение о вызовах clang x86-64 System V.)

Peter Cordes 28.05.2019 23:07

Можем ли мы сделать что-нибудь полезное с помощью pcmpgtq, а затем перетасовать + замаскировать младший элемент старшим элементом? Это, вероятно, дороже; pmovmskb или movmskpd дешевые, в худшем случае 2 мкп на бульдозерном семействе. (Высокая задержка в семействе Bulldozer для xmm->int одинаково вредна в любом случае, и оба могут происходить параллельно.)

Peter Cordes 28.05.2019 23:22

Другие вопросы по теме