#новичкам
В прошлых постах я часто писал, что компилятор сам умеет сносно разворачивать циклы. Но это конечно оптимизация и надо знать, как ее включать. Можно конечно и прагмами, но это уже специальные подсказки в коде. Но как заставить компилятор разворачивать циклы, не трогая сам код?
Будем обсуждать все на примере gcc.
Ну и для начала: базово без оптимизаций компилятор просто генерирует код слово в слово.
int sum(std::span<int> ints) {
int sum = 0;
for (auto value : ints)
sum += value;
return sum;
}В ассемблере, сгенеренном на -O0, здесь будут вызовы итераторов, честные проверки и тд
Хоть какие-то циклы компилятор начинает разворачивать на -O2. И далеко не во всех случаях. Например для сниппета выше он очень хорошо упростит код цикла, но все еще развертки не будет.
Но если спан будет фиксированного размера(да, такой есть), кратного 2-м или 4-м:
int sum(std::span<int, 20> ints) {
int sum = 0;
for (auto value : ints)
sum += value;
return sum;
}То компилятор развернет цикл c фактором 4 и дополнительно оптимизирует 4 сложения с использованием sse векторных инструкций и xmm регистров:
sum(std::span<int, 20ul>):
lea rax, [rdi+80]
pxor xmm0, xmm0
.L2:
movdqu xmm2, XMMWORD PTR [rdi]
add rdi, 16
paddd xmm0, xmm2
cmp rax, rdi
jne .L2
movdqa xmm1, xmm0
psrldq xmm1, 8
paddd xmm0, xmm1
movdqa xmm1, xmm0
psrldq xmm1, 4
paddd xmm0, xmm1
movd eax, xmm0
ret
Однако, поменяв размер 20 на 19, то векторизация сломается.
Итого: на О2 gcc разворачивает циклы с фиксированным, кратным 2-м, числом итераций.
Тепер -O3. Здесь уже могут разворачиваться циклы с неизвестным числом итераций. Обязательно добавляется обработка хвоста и, при возможности, цикл векторизуется. Если число итераций известно, то цикл может быть полностью развернут. Вот примерчики на годболте. На этом уровне компилятор руководствуется сложными эвристиками, которые позволяют ему оценивать, будет ли в конкретном случае буст от развертки или нет.
Ну это базовые флаги оптимизации. Есть и специальные, конкретно под развертку.
-funroll-loops - позволяет разворачивать все циклы, количество итераций которых может быть определено во время компиляции. Если цикл маленький, он развернется полностью и цикла вообще не останется. Цитата из документации gcc: This option makes code larger, and may or may not make it run faster.-funroll-all-loops - разворачиваем все, что может быть развернуто, нас ничего не остановит. Цитата из доки: This usually makes programs run more slowly.В общем-то ничего нового: не нужно тупо делать вещи, которые в теории что-то делают быстрее. Профилируйте, возможно стоит просто положиться на компилятор и будет вам счастье.
Don't follow the rules blindly. Stay cool.
#compiler #optimization #performance