Невыравниваемая нагрузка против неустановленного хранилища
Короткий вопрос: если у меня есть функция, которая берет два вектора. Один вход, другой вывод (нет псевдонимов). Я могу выровнять только один из них, какой из них выбрать?
Более длинная версия такова, что рассмотрим функцию,
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
}
Если ваши расчеты имеют неизбежные задержки данных, вы также должны рассмотреть возможность расчета двух пакетов с чередованием (это использует в два раза больше регистров).