Введение

Техника преобразования циклов. Развертывание цикла, также известное как раскрутка цикла, — это техника преобразования циклов, направленная на оптимизацию скорости выполнения программы за счет увеличения размера исполняемого кода, что является подходом, известным как компромисс между пространством и временем. Преобразование может выполняться вручную программистом или оптимизирующим компилятором. На современных процессорах развертывание цикла часто оказывается неэффективным, поскольку увеличение размера кода может приводить к большему количеству промахов кэша; см. устройство Даффа. Цель раскрутки цикла — повысить скорость программы за счет уменьшения или устранения инструкций, управляющих циклом, таких как адресная арифметика и проверки "конца цикла" на каждой итерации, снижения штрафов за переходы и скрытия задержек, включая задержку при чтении данных из памяти. Для устранения этих вычислительных накладных расходов циклы могут быть переписаны в виде повторяющейся последовательности схожих независимых операторов. Развертывание циклов также используется в некоторых методах формальной верификации, в частности, в ограниченной модели проверки.

Преимущества

Накладные расходы в "тесных" циклах часто состоят из инструкций для инкремента указателя или индекса к следующему элементу массива (арифметика указателей), а также проверок завершения цикла. Если оптимизирующий компилятор или ассемблер способен предварительно вычислить смещения для каждой индивидуально адресуемой переменной массива, эти смещения могут быть непосредственно встроены в машинный код, что исключает необходимость дополнительных арифметических операций во время выполнения. Существенный выигрыш достигается, если уменьшение количества выполняемых инструкций компенсирует возможное снижение производительности из-за увеличения размера программы. Минимизируется штраф за переход. Если операторы в цикле независимы друг от друга (то есть, операторы, расположенные в начале цикла, не влияют на последующие), они потенциально могут выполняться параллельно. Реализация возможна динамически, если число элементов массива неизвестно на этапе компиляции (как, например, в устройстве Даффа). Оптимизирующие компиляторы могут выполнять развертывание цикла автоматически или по запросу.

Недостатки

Увеличение размера программного кода, что может быть нежелательно, особенно для встраиваемых приложений, также может привести к увеличению промахов кэша инструкций, что может негативно сказаться на производительности. Если это не выполняется прозрачно оптимизирующим компилятором, код может стать менее читаемым. Если код внутри тела цикла содержит вызовы функций, может оказаться невозможным совместить развертывание цикла с подстановкой функций, поскольку увеличение размера кода может быть чрезмерным. Таким образом, между этими двумя оптимизациями может существовать компромисс. Также возможно увеличение использования регистров в одной итерации для хранения временных переменных, что может снизить производительность, хотя многое зависит от возможных оптимизаций. За исключением очень небольшого и простого кода, развернутые циклы, содержащие ветвления, работают даже медленнее, чем рекурсия.

Статика/ручная развертка петли

Ручное (или статическое) разворачивание цикла предполагает анализ цикла программистом и преобразование его итераций в последовательность инструкций, что позволяет снизить накладные расходы, связанные с циклом. Это отличается от динамического разворачивания, выполняемого компилятором.

Динамическое развертывание

Поскольку преимущества разворачивания цикла часто зависят от размера массива, который нередко неизвестен до времени выполнения, JIT-компиляторы (например) могут определить, вызывать ли "стандартную" последовательность цикла или вместо этого генерировать (относительно короткую) последовательность отдельных инструкций для каждого элемента. Эта гибкость – одно из преимуществ техник компиляции "на лету" по сравнению со статической или ручной оптимизацией в контексте разворачивания цикла. В этой ситуации выигрыш часто ощутим при относительно небольших значениях n, требуя незначительного (или вообще отсутствующего) увеличения общего размера программы (которое может быть включено лишь однажды, как часть стандартной библиотеки). Программисты на языке ассемблера (включая разработчиков оптимизирующих компиляторов) также могут воспользоваться техникой динамического разворачивания цикла, используя метод, аналогичный применяемому для эффективных таблиц переходов. Здесь преимущество наиболее заметно, когда максимальный сдвиг любого адресуемого поля в конкретном массиве меньше максимального сдвига, который можно указать в машинной инструкции (превышение которого будет отмечено ассемблером).