Невыравниваемая нагрузка против неустановленного хранилища

Короткий вопрос: если у меня есть функция, которая берет два вектора. Один вход, другой вывод (нет псевдонимов). Я могу выровнять только один из них, какой из них выбрать?

Более длинная версия такова, что рассмотрим функцию,

void func(size_t n, void *in, void *out)
{
    __m256i *in256 = reinterpret_cast<__m256i *>(in);
    __m256i *out256 = reinterpret_cast<__m256i *>(out);
    while (n >= 32) {
         __m256i data = _mm256_loadu_si256(in256++);
         // process data
         _mm256_storeu_si256(out256++, data);
         n -= 32;
    }
    // process the remaining n % 32 bytes;
}

Если in и out совпадают с 32 байтами, тогда нет штрафа за использование vmovdqu вместо vmovdqa. Наихудший сценарий заключается в том, что оба они не выравниваются, а каждый из четырех загрузок/хранилищ пересекает границу линии кэша.

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

Ответ 1

Рискуя заявить очевидное здесь: нет "правильного ответа", кроме "вам нужно сравнить как фактический код, так и фактические данные". Какой бы вариант ни был быстрее, он сильно зависит от используемого вами процессора, количества вычислений, которые вы делаете на каждом пакете и многих других.

Как отмечено в комментариях, вы также должны попробовать невременные магазины. То, что иногда также может помочь, заключается в загрузке ввода следующего пакета данных внутри текущего цикла, то есть:

__m256i next =  _mm256_loadu_si256(in256++);
for(...){
    __m256i data = next; // usually 0 cost
    next = _mm256_loadu_si256(in256++);
    // do computations and store data
}

Если ваши расчеты имеют неизбежные задержки данных, вы также должны рассмотреть возможность расчета двух пакетов с чередованием (это использует в два раза больше регистров).