Генерация высококачественного кода для программ, написанных на СИ
Шрифт:
Манифестные константы часто могут скрывать существование недостижимого кода, особенно если такой код определяется внутри включаемого файла-заголовка.
"Удаление лишних присваиваний" включает нахождение промежутка жизни переменной и удаление присваиваний этой переменной, если эти присваивания не могут изменить логику программы. Этот метод освобождает ограниченные ресурсы, такие как пространство стека или машинные регистры. В следующей последовательности команд:
первый
Цель "распределения переменных по регистрам" состоит в попытке обеспечить оптимальное назначение регистров путем сохранения часто используемых переменных в регистрах так долго, как это возможно, для того, чтобы исключить более медленный доступ к памяти. Количество регистров, доступных для использования, зависит от архитектуры процессора. Семейство микропроцессоров Intel 80x86 резервирует много регистров для специального использования и имеет несколько универсальных регистров. В помощь распределению переменных по регистрам язык Си предоставляет спецификатор класса регистровой памяти, который дает возможность программисту указывать, какие переменные должны располагаться в регистрах.
При назначении переменных регистрам компилятор принимает во внимание не только какие переменные нужно выделить, но также и регистры, которым они назначаются. Выбор переменных зависит от частоты их использования, промежутков жизни текущих регистровых переменных (которые определяются при анализе потоков данных) и количества доступных регистров. В зависимости от степени выполняемой компилятором оптимизации промежуток жизни переменной может определяться внутри единственного оператора, внутри базового блока или перекрывать несколько базовых блоков. Переменная сохраняется в регистре только если она будет снова использоваться. Если на переменную в дальнейшем не будет ссылок, то она сохраняется в оперативной памяти, освобождая регистр для другой переменной.
Поскольку оптимизирующему компилятору известен промежуток жизни переменной, он не будет намеренно генерировать "лишние операции сохранения и загрузки" (регистров). Лишние операции сохранения удаляются посредством удаления излишних присваиваний; лишние операции загрузки опускаются с помощью усовершенствованного распределения переменных по регистрам. Имея текст:
компилятор без возможностей оптимизации может сгенерировать следующий код:
тогда как оптимизирующий компилятор может использовать механизм размещения переменных в регистрах для удаления лишней четвертой инструкции (mov AX,a).
Время,
– -------------------------------------------------------------¬
¦РИСУНОК 2: Простой цикл ¦
+-------------------------------------------------------------+
¦Исходный текст на Си BORLAND METAWARE ¦
¦ Turbo C 1.5 High C 1.4 ¦
¦(x) - врем. циклы (125) (87) ¦
+-------------------------------------------------------------+
¦k5 = 10000; mov j5,0 mov j5,0 ¦
¦j5 = 0; mov k5,10000 mov k5,10000 ¦
¦do { @10: L00e3: ¦
¦ k5 = k5 - 1; mov AX,k5 dec k5 ¦
¦ j5 = j5 + 1; dec AX inc j5 ¦
¦ i5 = (k5 * 3) / mov k5,AX mov AX,j5 ¦
¦ (j5 * constant5); mov AX,j5 mov SI,AX ¦
¦} while (k5 > 0); inc AX sal SI,2 ¦
¦ mov j5,AX add SI,AX ¦
¦ mov AX,k5 mov AX,k5 ¦
¦ imul AX,AX,3 mov DX,AX ¦
¦ push AX add DX,DX ¦
¦ mov AX,j5 add DX,AX ¦
¦ imul AX,AX,5 xchg AX,DX ¦
¦ mov BX,AX cwd ¦
¦ pop AX idiv SI ¦
¦ cwd mov I5,AX ¦
¦ idiv BX cmp k5,0 ¦
¦ mov i5,AX jnle L00e3 ¦
¦ cmp k5,0 ¦
¦ jg @10 ¦
+-------------------------------------------------------------+
¦ MICROSOFT WATCOM ¦
¦ C 5.0 C 6.0 ¦
¦ (46) (91) ¦
+-------------------------------------------------------------+
¦ mov j5,10000 mov j5,0 ¦
¦ mov k5,0 mov DI,10000 ¦
¦ mov CX,30000 L4 dec DI ¦
¦ sub SI,SI imul AX,DI,3 ¦
¦ $0265: inc j5 ¦
¦ sub CX,3 imul BX,j5,5 ¦
¦ add SI,5 cwd ¦
¦ mov AX,CX idiv BX ¦
¦ cwd mov i5,AX ¦
¦ idiv SI test DI,DI ¦
¦ mov DI,AX jg L4 ¦
¦ or CX,CX ¦
¦ jg $0265 ¦
¦ mov i5,DI ¦
+-------------------------------------------------------------+
¦ Компилятор Microsoft C 5.0 выполнил снижение мощности на ¦
¦ константном выражении и разместил в регистрах все ¦
¦ переменные внутри простого цикла, включая вычисляемое ¦
¦ значение i5. Высокая степень проведенного анализа цикла ¦
¦ демонстрируется тем, что заключительные состояния k5 и j5 ¦
¦ были определены заранее компилятором, а не позже, во ¦
¦ время выполнения. ¦
L--------------------------------------------------------------
"Вынесение инвариантного (неизменяющегося) кода" - один из путей ускорения циклов, заключающийся в вынесении выражений за пределы цикла, если значения, ими вычисляемые, являются неизменными во время выполнения цикла. Если инвариантный код выносится из следующего цикла: