Существует большое число методов оптимизации
циклов с самыми экзотическими названиями: «разгрузка
циклов», «вывод инвариантов за циклы», «устранение индуктивных переменных», «сращивание циклов», «разматывание циклов» и т. д. В действительности все эти методы можно объединить в два эмпирических правила.
1. Никогда не следует делать в цикле ничего такого,
что можно сделать вне его.
2. Где это можно, следует избавиться от передач
управления внутри циклов.
Первое правило следует из истины, по которой 90%
времени исполнения программы приходится на 10%
ее кода. Эти 10% чаще всего оказываются циклами того или иного рода. Таким образом, первое, что необходимо сделать для ускорения выполнения программы,
— это определить в ней «горячие точки» и проверить
все циклы в них в качестве потенциальных объектов
оптимизации. Цикл далеко не всегда представляет собой изящную конструкцию, которая завершается командами LOOP, LOOPZ или LOOPNZ; часто это просто
серия команд, выполнение которых повторяется в зависимости от величины некоторой управляющей переменной или флажка.
Циклы можно разделить на два вида: к первому относятся циклы со временем исполнения, которое определяется некоторыми внешними механизмами синхронизации, ко второму — те, в которых участвует только
процессор. Примером первого вида цикла служит та-
кой, в котором набор символов передается на параллельный порт. Скорость выполнения программы никогда не будет выше темпа приема байтов параллельным портом, а быстродействие данного порта на два порядка ниже, чем время выполнения любой
приемлемой кодовой последовательности управления
портом. Оптимизация подобных циклов с внешней
синхронизацией не часто применяется. Циклы второй
категории — свободные от взаимодействия с «внешним миром».
Для полной оптимизации циклов нужен методический подход к проблеме. Сначала следует тщательно
проверить все циклы для отыскания операций, которые абсолютно не связаны с переменной цикла,
и разгрузить цикл от этих вычислений. Следует проанализировать оставшиеся коды и по возможности
упростить их, используя просмотр управляющих таблиц, которые ориентированы на определенную модель
процессора команды, отказ от универсальности и любые другие известные приемы, позволяющие уменьшить кодовые последовательности и убрать «дорогостоящие» команды.
Если в некоторых вычислениях применяется текущее значение переменной цикла, следует вывернуть
ситуацию наизнанку, определяя нужные величины из
начального и конечного значений переменной цикла,
т. е. без перебора.
После оптимизации содержимого цикла, насколько это возможно, необходимо посмотреть, можно ли
где-то убрать управляющие циклы операций перехода или вызова подпрограмм.