TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.36K subscribers
Post #1084 3.58K
​​Loop unrolling. Compiler
#новичкам

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

Будем обсуждать все на примере 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
  • ❤ 26
  • 👍 11
  • 🔥 4
  • 😁 3
  • 🤣 2
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →