Я работаю над кодовой базой, которая имеет много внутреннего кода SIMD. Теперь, когда у нас есть AVX2, нам все еще нужен SIMD-код, который работает на процессорах, не поддерживающих AVX2, что будет значительно более трудоемким. Плюс эти 128-битные ограничения на пересечение полос для перемешивания AVX2 также усложняют ситуацию. По этим причинам самое время больше полагаться на автоматическую векторизацию. Главные вещи, которые меня пугают, это перспектива единственного невинного изменения, убивающего параллелизм, и перспектива отладки автоматически векторизованного кода в случае возникновения проблемы.
Я скомпилировал следующее с помощью g ++ -O1 -g -ftree-vectorize и попытался перейти с помощью GDB (кто-нибудь знает, почему -ftree-vectorize не работает с -O0?)
float a[1000], b[1000], c[1000];
int main(int argc, char **argv)
{
for (int i = 0; i < argc; ++i)
c[i] = a[i] + b[i];
return 0;
}
но не получайте никаких значимых результатов. Например, иногда значение для меня говорит <оптимизирован> в то время как в другое время он прыгает на 20.
Кажется, основная проблема заключается в том, что для отладки сложно сопоставить состояние SIMD с исходным состоянием C. Но реально ли это сделать?
Использование отладчика в авто-векторизованном коде сложно, особенно когда вы хотите проверить переменные, которые должны вести себя по-разному (например, счетчик цикла).
Вы можете использовать отладочную сборку (-O0
или же -Og
), или вы можете понять, как компилятор векторизовал код, и изучить регистры asm и registers. В зависимости от того, какую ошибку вы хотите отследить, у вас может возникнуть или не возникнуть проблема с автоматической векторизацией сборки.
Судя по комментариям, вы больше заинтересованы в проверке эффективности автоматической векторизации, а не в отладке для исправления логических ошибок в вашем коде. Глядя на ASM и тесты, вероятно, ваш лучший выбор. (даже простой rdtsc
до / после звонка или в модульном тесте, который проверяет производительность, а также правильность.)
Иногда компилятор генерирует несколько версий цикла, например, для случая, когда входные массивы перекрываются, и для случая, когда они не совпадают. Одноступенчатый (по инструкции, с stepi
, с layout asm
в gdb) может помочь, пока вы не найдете цикл, который фактически выполняет большую часть работы. Тогда вы можете сосредоточиться на том, как это векторизовано. Если вы хотите исключить проверки и альтернативные версии, restrict
указатели могут быть полезны. Есть также p = __builtin_assume_aligned(p, 16)
,
Вы также можете использовать Бесплатный анализатор кода Intel попытаться статически проанализировать, сколько циклов занимает итерация. Поместите метки IACA в верхней части тела цикла и после закрывающей части цикла, и надеюсь, что GCC поместит их в соответствующие места в векторизованном цикле, и что встроенный асм не нарушит автоматическую векторизацию.
Ответ на оптимизацию не будет полным со ссылкой на http://agner.org/optimize/, так что вот так.
Других решений пока нет …