Введение

В информатике, разделение цикла (или распределение цикла) — это оптимизация компилятора, при которой цикл разбивается на несколько циклов с одинаковым диапазоном индексов, при этом каждый из них выполняет только часть тела исходного цикла. Цель состоит в том, чтобы разбить большое тело цикла на более мелкие, чтобы улучшить использование локальности ссылок. Эта оптимизация наиболее эффективна на многоядерных процессорах, которые могут разделить задачу на несколько подзадач для каждого ядра. И наоборот, слияние циклов (или объединение циклов) — это оптимизация компилятора и преобразование цикла, которое заменяет несколько циклов одним. Другие преимущества слияния циклов заключаются в том, что оно позволяет избежать накладных расходов, связанных со структурами управления циклом, а также в том, что оно позволяет процессору параллелизировать тело цикла, используя параллелизм на уровне инструкций. Это возможно, когда между телами двух циклов отсутствуют зависимости по данным (это резко контрастирует с другим основным преимуществом слияния циклов, описанным выше, которое проявляется только при наличии зависимостей по данным, требующих промежуточного выделения памяти для хранения результатов). Если слияние циклов позволяет устранить избыточные выделения памяти, прирост производительности может быть значительным. Однако, по состоянию на clang 12.0.0 и gcc 11.1, это слияние циклов и удаление избыточных выделений памяти не происходит даже на самом высоком уровне оптимизации. Некоторые языки, специально предназначенные для численных вычислений, такие как Julia, могут иметь встроенную концепцию слияния циклов на высоком уровне, когда компилятор обнаруживает смежные поэлементные операции и объединяет их в один цикл. В настоящее время, для достижения того же синтаксиса в языках общего назначения, таких как C++, функции sin и оператор+ вынуждены пессимистично выделять массивы для хранения своих результатов, поскольку они не знают, в каком контексте они будут вызваны. В C++ этой проблемы можно избежать, используя другой синтаксис, который не полагается на компилятор для удаления ненужных временных выделений памяти (например, используя функции и перегрузки для операций на месте, такие как оператор+= или std::transform).