SSE оптимизированная эмуляция 64-битных целых

Для хобби-проекта, над которым я работаю, мне нужно эмулировать определенные 64-разрядные целочисленные операции на процессоре x86, и это должно быть быстрым.

В настоящее время я делаю это с помощью инструкций MMX, но с этим действительно сложно работать, потому что мне приходится все время сбрасывать состояние регистра fp (и потому что большинство инструкций MMX имеют дело со знаковыми целыми числами, и мне нужно поведение без знака).

Поэтому мне интересно, могут ли гуру SSE/ оптимизации здесь, в SO, предложить лучшую реализацию с использованием SSE.

Мне нужны следующие операции (вполне конкретные):

uint64_t X, Y;

X = 0;
X = 1;
X << 1;
X != Y;
X + 1;
X & 0x1 // get lsb
X | 0x1 // set lsb
X > Y;

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

За исключением, конечно, на x86, uint64_t эмулируется с помощью двух 32-битных скаляров, что является медленным (и, в моем случае, просто не работает, потому что мне нужно, чтобы загрузки / хранилища были атомарными, чего не будет при загрузке / хранении двух отдельных регистров),

Следовательно, мне нужно SIMD решение. Некоторые из этих операций тривиальны, уже поддерживаются SSE2. Другие (!= а также <) требуется немного больше работы.

Предложения? SSE и SSE2 в порядке. Чтобы разрешить SSE3, потребовалось бы какое-то убеждение, и о SSE4, вероятно, не может быть и речи (CPU, поддерживающий SSE4, в любом случае, вероятно, будет работать 64-битным, и поэтому мне не нужны эти обходные пути)

1 ответ

Решение

SSE2 имеет прямую поддержку для некоторых 64-битных целочисленных операций:

Установите оба элемента на 0:

__m128i z = _mm_setzero_si128();

Установите оба элемента на 1:

__m128i z = _mm_set_epi32(0,1,0,1);

Вертикально складывать / вычитать каждое 64-битное целое число:

__m128i z = _mm_add_epi64(x,y)
__m128i z = _mm_sub_epi64(x,y)

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_integer_arithmetic.htm

Сдвиг влево:

__m128i z = _mm_slli_epi64(x,i)   // i must be an immediate

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_int_shift.htm

Битовые операторы:

__m128i z = _mm_and_si128(x,y)
__m128i z = _mm_or_si128(x,y)

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_integer_logical.htm

SSE не имеет приращений, поэтому вам придется использовать константу с 1,


Сравнения сложнее, поскольку 64-битная поддержка отсутствует.

Вот один для равенства:

__m128i t = _mm_cmpeq_epi32(a,b);
__m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177));

Это установит для каждого 64-битного элемента значение 0xffffffffffff если они равны Если вы хотите это как 0 или же 1 в intВы можете вытащить его, используя _mm_cvtsi32_si128() и добавить 1,

И меньше, чем: (не полностью проверено)

a = _mm_xor_si128(a,_mm_set1_epi32(0x80000000));
b = _mm_xor_si128(b,_mm_set1_epi32(0x80000000));
__m128i t = _mm_cmplt_epi32(a,b);
__m128i u = _mm_cmpgt_epi32(a,b);
__m128i z = _mm_or_si128(t,_mm_shuffle_epi32(t,177));
z = _mm_andnot_si128(_mm_shuffle_epi32(u,245),z);

Это установит для каждого 64-битного элемента значение 0xffffffffffff если соответствующий элемент в a меньше чем b,


Вот версии "равно" и "меньше чем", которые возвращают логическое значение. Они возвращают результат сравнения для нижнего 64-разрядного целого числа.

inline bool equals(__m128i a,__m128i b){
    __m128i t = _mm_cmpeq_epi32(a,b);
    __m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177));
    return _mm_cvtsi128_si32(z) & 1;
}
inline bool lessthan(__m128i a,__m128i b){
    a = _mm_xor_si128(a,_mm_set1_epi32(0x80000000));
    b = _mm_xor_si128(b,_mm_set1_epi32(0x80000000));
    __m128i t = _mm_cmplt_epi32(a,b);
    __m128i u = _mm_cmpgt_epi32(a,b);
    __m128i z = _mm_or_si128(t,_mm_shuffle_epi32(t,177));
    z = _mm_andnot_si128(_mm_shuffle_epi32(u,245),z);
    return _mm_cvtsi128_si32(z) & 1;
}
Другие вопросы по тегам