RANDOM SSE2

Логическое завершение вот этого поста. Немножко про “неоптимальность” SSE2 кода.

Можно убрать 3 shuffle, 2 and, 1 or и лишнюю маску. Вместо этого получить 1 либо 2 shuffle. Т-е для идентичного кода нужно 2 shuffle, но сохранять порядок seed-ов в MM_GSEED не обязательно – то что они перемешиваются, это совсем не плохо.

Собственно код.

[code lang="cpp"]
__m128i MM_GSEED = _mm_set_epi32 ( 7, 9, 13, 17 );
__m128i MM_MUL = _mm_set_epi32 ( 16807, 16807, 16807, 16807 );

__forceinline __m128 mm_rand_sse22()
{
__m128i seed = MM_GSEED;
__m128i low = _mm_mul_epu32 ( seed, MM_MUL );
__m128i hig = _mm_mul_epu32 (
_mm_shuffle_epi32(seed,_MM_SHUFFLE(1,3,3,1)), MM_MUL );
__m128 e_low = _mm_load_ps ( (const float *) &low );
__m128 e_hig = _mm_load_ps ( (const float *) &hig );
__m128 e_seed = _mm_shuffle_ps ( e_low, e_hig, _MM_SHUFFLE(2,0,2,0) );
MM_GSEED = _mm_load_si128 ( (const __m128i *) &e_seed );
__m128i e_res = _mm_srai_epi32 ( MM_GSEED, 8 );
__m128 f_res = _mm_load_ps ( (const float *) &e_res );
return _mm_sub_ps(_mm_or_ps(_mm_and_ps(f_res,MM_MANTISSA_MASK),
MM_EXPONENT_MASK),MM_EXPONENT_MASK);
}
[/code]

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

  • Pingback: highly professional scums » Blog Archive » RANDOM, SSE, SSE2()

  • finnan

    Очень познавательно, спасибо!)

    Объединил SEE рандомайзер с тестом из поста Ещё раз про latency.

    Вот что получилось: http://www.everfall.com/paste/id.php?ge2738pmgo1x

    MSCC v14.00, P4 3Ghz
    naive, simple rand 28312 msec
    naive, SSE rand 26188 msec
    naive, simple rand 28265 msec
    naive, SSE rand 26703 msec

    naive with SSE prefetch, simple rand 27672 msec
    naive with simple prefetch, simple rand 28360 msec
    naive with SSE prefetch, SSE rand 25968 msec
    naive with simple prefetch, SSE rand 26516 msec

    ring, simple rand 13828 msec
    ring, SSE rand 11609 msec

  • finnan

    Сорри, word press съел форматирование. Попробую еще раз (очень не хатает “Preview”, кстати).

    naive, simple rand 28312 msec
    naive, SSE rand 26188 msec
    naive, simple rand 28265 msec
    naive, SSE rand 26703 msec

    naive with SSE prefetch, simple rand 27672 msec
    naive with simple prefetch, simple rand 28360 msec
    naive with SSE prefetch, SSE rand 25968 msec
    naive with simple prefetch, SSE rand 26516 msec

    ring, simple rand 13828 msec
    ring, SSE rand 11609 msec

  • look4awhile

    разница разительная. всякое крайне наглядно
    даже более чем

  • finnan

    мне, если честно, осталось не совсем ясно, почему

    _mm_prefetch ( (const char *) np, _MM_HINT_T0 );

    принципиально лучше

    static volatile char temp = 0;
    temp = *( (const char *) np );

    которое, похоже, вообще не дает prefetch-эффекта, хотя asm-код компилятор генерирует разумный. Более того, тесты показывают, что этот “simple prefetch” наоборот еще больше тормозит исполнение.

  • IronPeter

    э… Второй фрагмент – он как бы совершенно платформо-специфичный. Где-то работает с позитивным результатом, где-то нет. Второй вариант гарантированно проходит через всю цепочку выполнения и вызывает почти гарантированное чтение когда IROB переполнится. То есть где-то через 100 тактов максимум нас ждет счастье. prefetch – он совсем не такой, он работает на более длинных промежутках времени. И не вызывает stall одним своим фактом наличия. Никогда.