_mm_stream_si128 на 2000% медленнее, чем _mm_store_si128

Я писал код C для создания генератора случайных чисел и использовал следующий код:

// header
typedef struct {
    uint64_t values[2];
} fy5z_state_t;
fy5z_state_t fy5z_seed(uint64_t seed_value);

uint64_t fy5z_generate(fy5z_state_t* state);
// source
fy5z_state_t fy5z_seed(uint64_t seed_value)
{
    fy5z_state_t state;
    state.values[0] = (seed_value & 0xFFFFFFFF);
    state.values[1] = (seed_value & (0xFFFFFFFF << 31)) + seed_value;
    return state;
}

uint64_t fy5z_generate(fy5z_state_t* state)
{
    __m128i got_data = _mm_load_si128((__m128i const*)(state->values));
    __m128i shuffled = _mm_shuffle_epi32(got_data, 0x8d);
    __m128i final_add = _mm_add_epi8 (shuffled, got_data);
    _mm_store_si128((__m128i*)(state->values), final_add);
    return state->values[0];
}

Кроме того, для определения времени производительности используется следующий код:

#ifndef TRIAL_COUNT
#define TRIAL_COUNT 1024 * 1024 * 10
#endif

static void print_time_us(const char* name, void(*fn)(void))
{
    struct timespec start, end;
    clock_gettime(CLOCK_MONOTONIC_RAW, &start);
    fn();
    clock_gettime(CLOCK_MONOTONIC_RAW, &end);
    uint64_t delta_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000;
    printf("Running: '%s' took %llu u/s\n", name, delta_us);
}

static void test_fy5z(void) {
    fy5z_state_t fseed = fy5z_seed(0xfab5381);
    unsigned long total = 0; 
    for (int i = 0; i < TRIAL_COUNT; ++i)
    {
        total += fy5z_generate(&fseed);
    }
}

Что-то, что я обнаружил в функции генератора, если используется _mm_store_si128, я получаю: Running: 'fy5z' took 113328 u/s, но если я поменяю его местами с _mm_stream_si128, я получу Running: 'fy5z' took 1956792 u/s.

Это на MacOS с четырехъядерным процессором Intel Core i7 с тактовой частотой 2,7 ГГц.

Почему store намного быстрее, чем stream в этом случае использования?


person Josh Weinstein    schedule 19.03.2021    source источник
comment
Это для _mm_storeu_si128, этот вопрос для _mm_store_si128. Это для выровненной версии   -  person Josh Weinstein    schedule 19.03.2021
comment
Потому что _mm_stream_si128 не использует кеш?   -  person user253751    schedule 19.03.2021
comment
_mm_store_si128 (movdqa [mem], xmm) имеет точно такую ​​же скорость, как movdqu, когда указатель оказывается выровненным (на Nehalem и более поздних версиях, то есть с самого первого поколения i7 в 2009 году), что также является требованием для movntdq. Это возражение на Почему `_mm_stream_si128` намного медленнее, чем `_mm_storeu_si128` на Skylake-Xeon при записи частей из 2 строк кэша? Но меньшее влияние на Haswell не имеет смысла. И да, это похоже, за исключением того, что вы на самом деле перезагружаете хранилище NT, чтобы вернуть его, так что это еще хуже.   -  person Peter Cordes    schedule 19.03.2021
comment
Какой процессор у у вас есть? И не говорите просто i7, это ничего нам не говорит. например У меня i7-6700k, Skylake. (Обратите внимание, что четырехъядерный процессор сильно отличается от Xeon с точки зрения задержки без ядра, особенно для Skylake, но в ответе Би упоминаются некоторые общие вещи о том, что хранилища NT с частичной строкой являются анти-шаблоном, и вы не храните 64 всего байтов. Хуже того, вы перезагружаете его из того же потока, который мог бы выиграть от кэширования / переадресации хранилища.)   -  person Peter Cordes    schedule 19.03.2021