У меня есть общий код, который я пытаюсь переместить в SSE, чтобы ускорить его, так как его часто называют. Код в вопросе в основном что-то вроде этого:Неизвестное узкое место SSE
for (int i = 1; i < mysize; ++i)
{
buf[i] = myMin(buf[i], buf[i - 1] + offset);
}
где myMin ваша простая функция мин (а < б)? а: Ь (я смотрел на разборку и есть скачки здесь)
Мой SSE код (который я прошел через несколько итераций для ускорения) находится в этой форме в настоящее время:
float tmpf = *(tmp - 1);
__m128 off = _mm_set_ss(offset);
for (int l = 0; l < mysize; l += 4)
{
__m128 post = _mm_load_ps(tmp);
__m128 pre = _mm_move_ss(post, _mm_set_ss(tmpf));
pre = _mm_shuffle_ps(pre, pre, _MM_SHUFFLE(0, 3, 2, 1));
pre = _mm_add_ss(pre, off);
post = _mm_min_ss(post, pre);
// reversed
pre = _mm_shuffle_ps(post, post, _MM_SHUFFLE(2, 1, 0, 3));
post = _mm_add_ss(post, off);
pre = _mm_min_ss(pre, post);
post = _mm_shuffle_ps(pre, pre, _MM_SHUFFLE(2, 1, 0, 3));
pre = _mm_add_ss(pre, off);
post = _mm_min_ss(post, pre);
// reversed
pre = _mm_shuffle_ps(post, post, _MM_SHUFFLE(2, 1, 0, 3));
post = _mm_add_ss(post, off);
pre = _mm_min_ss(pre, post);
post = _mm_shuffle_ps(pre, pre, _MM_SHUFFLE(2, 1, 0, 3));
_mm_store_ps(tmp, post);
tmpf = tmp[3];
tmp += 4;
}
Игнорирование любых сценариев кэш-памяти, с которыми я справился, и накладные расходы для них незначительны из-за размера buf/tmp, может ли кто-нибудь объяснить, почему версия SSE медленнее на 2x? VTune отнесет его к промахам L1, но, как я вижу, он должен сделать 4 раза меньше поездок на L1 и никаких ветвей/прыжков, поэтому должен быть быстрее, но это не так. Что я тут ошибаюсь?
Благодаря
EDIT: Так что я найти что-нибудь в отдельном тесте. Я не думал, что это будет иметь значение, но, увы, это так. Так что mysize above на самом деле не такой большой (около 30-50), но их много, и все они выполняются серийно. В этом случае тройное выражение выполняется быстрее, чем SSE. Однако, если он изменен на mysize, находясь в миллионах, и есть только 30-50 итераций из них, версия SSE выполняется быстрее. Любая идея почему? Я думаю, что взаимодействие памяти будет одинаковым для обоих, в том числе упреждающего упреждающей и т.д ...
Является ли версия SSE более параллельной, чем оригинал? – user2357112
Последовательная зависимость - вот что убивает вас здесь - это делает цикл непригодным для векторизации SIMD, и в результате вы делаете большую работу в цикле SIMD. Вероятнее всего, было бы более полезно сосредоточиться на оптимизации скалярного цикла: убедитесь, что вы используете нераспределенный минимум без ненужного плавающего <-> двойных преобразований и, возможно, также разворачиваете цикл вручную (будьте осторожны с зависимостями, конечно). –
Профилировщик ошибочен. Этот цикл не является векторизуемым - если вы не попробуете что-то вроде параллельного префикса min. – Mysticial