Почему AVX2 и SSE2 побитовые операторы ИЛИ не быстрее, чем простой | оператор?

Я пытаюсь ускорить побитовую операцию ИЛИ для очень длинных двоичных векторов, используя 32-битные целые числа.

В этом примере мы можем предположить, что nwords — это количество слов, кратное 4 и 8. Следовательно, нет напоминания о цикле. Этот двоичный вектор может содержать многие тысячи битов.

Более того, все три битовых вектора выделяются с помощью _align_malloc() с выравниванием по 16 и 18 битам для SSE2 и AVX2 соответственно.

К моему удивлению, следующие три скалярных кода, кода SSE2 и AVX2 выполнялись с одинаковым количеством времени на моем процессоре i7. Я не испытал ожидаемого ускорения x4 и x8 для регистров SSE2 и AVX2.

Моя версия MVisual Studio 15.1.

Скалярный код:

void vectorOr_Scalar(unsigned int *ptr1, unsigned int *ptr2, unsigned int *out, int nwords)
{
    for (end = ptr1 + nwords; ptr1 < end; ptr1++, ptr2++, out++) *out = *ptr1 | *ptr2;
}

Код SSE2:

void vectorOr_SSE2(unsigned int *ptr1, unsigned int *ptr2, unsigned int *out, int nwords)
{
    for (i = 0; i < nwords; i += 4, ptr1 += 4, ptr2 += 4, out += 4)
    {
        __m128i v1 = _mm_load_si128((__m128i *)ptr1);
        __m128i v2 = _mm_load_si128((__m128i *)ptr2);
        _mm_store_si128((__m128i *)out, _mm_or_si128(v1, v2));
    }
}

Код AVX2:

void vectorOr_AVX2(unsigned int *ptr1, unsigned int *ptr2, unsigned int *out, int nwords)
{
    for (i = 0; i < nwords; i += 8, ptr1 += 8, ptr2 += 8, out += 8)
    {
        __m256i v1 = _mm256_load_si256((__m256i *)ptr1);
        __m256i v2 = _mm256_load_si256((__m256i *)ptr2);
        _mm256_store_si256((__m256i *)out, _mm256_or_si256(v1, v2));
    }
}

Возможно, это приложение не подходит для векторизации из-за ограниченного количества операций с регистрами между загрузкой и сохранением?


person Liotro78    schedule 13.11.2019    source источник
comment
Компиляторы в наши дни довольно умны, вы уверены, что они не генерируют аналогичный код для вашего скалярного случая?   -  person Mark Ransom    schedule 13.11.2019
comment
0. Убедитесь, что у вас включен AVX2 с оптимизацией (иначе компилятор будет вызывать функции замены вектора без аппаратного ускорения). 1. Не могли бы вы попробовать использовать OpenMP, просто добавьте #pragma omp parallel for simd   -  person Victor Gubin    schedule 13.11.2019
comment
Как сказал @MarkRansom, ваш компилятор, вероятно, сам понял, как векторизовать вашу функцию. Найдите инструкции vorps или vpor в горячем цикле: godbolt.org/z/MR2x8V   -  person chtz    schedule 13.11.2019
comment
Привет, ребята, да, кажется, Марк сказал. Большое спасибо!   -  person Liotro78    schedule 13.11.2019
comment
Я искал дубликат; Я уверен, что видел вопросы и ответы, где ответ заключался в том, что скалярная версия была достаточно простой для автоматического векторизации, но мне не удалось ее найти.   -  person Peter Cordes    schedule 13.11.2019
comment
Ключевое слово — авто-векторизация.   -  person phuclv    schedule 13.11.2019


Ответы (1)


Причина, по которой вы не наблюдаете разницы в производительности между циклом, который обрабатывает один unsigned за раз, и циклом SIMD, который обрабатывает 8 unsigned за раз, заключается в том, что компиляторы генерируют код SIMD для вас, а также разворачивают цикл, см. сгенерированная сборка.

person Maxim Egorushkin    schedule 13.11.2019