ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



 

Часть 47

Приложение G. Оптимизация кода
----------------------------------------------------------------
Процессор i486 совместим по коду и по данным с процессорами
386(TM) DX и SX. Были добавлены всего лишь три команды уровня
прикладного программирования, которые полезны в специальных
ситуациях. Любая существующая прикладная программа для
процессора 8086/8088, 80286 и 386 может быть выполнена на
процессоре i486 немедленно, без всякой дополнительной
модификации и перекомпиляции. Любой компилятор, который
генерирует код для процессоров семейства 386, будет также
генерировать код, который будет выполняться на процессоре i486
без какой-либо модификации.
Тем не менее, сушествует несколько способов оптимизации кода,
которые сделают выполнение прикладных программ на процессоре
i486 более быстрым, при помощи всего лишь минимальных изменений
или вообще безо всяких изменений по отношению к их реализации
для процессоров 386 DX или SX, за исключением, быть может,
различий в размере кода. Эти способы следует применять по
отношению к выбору последовательности команд и командам
управления для того, чтобы получить преимущества встроенного
модуля режима конвейерной обработки процессора i486 и большой
кэш-памяти, расположенной на одной микросхеме.
G1. Режимы адресации
----------------------------------------------------------------
Подобно процессору 386, процессору i486 необходим дополнительный
такт для генерации исполнительных адресов, когда используется
индексный регистр. Более того, если используется одна индексная
компонента (т.е. одновременно не используются базовый регистр и
индексный регистр), и нет необходимости в масштабировании, более
быстрым является использование регистра в качестве базового, а
не в качестве индексного. Например :
mov   eax, [esi]      ; использование esi в качестве базы
mov   eax, [esi*]     ; использование esi в качестве индекса, 1
                      ; такт дополнительно
Если используются и база, и индекс, или используется
масштабирование индекса, более быстрым способом адресации
является использование комбинированного метода, даже если это и
займет один дополнительный тактовый цикл процессора при
выполнении.
Когда регистр используется в качестве базовой компоненты, тогда
используется дополнительный тактовый цикл, если этот регистр
являлся приемником в команде, непосредственно выполнявшейся
перед данной командой (предполагается, что все операции уже
находятса в предварительно созданной очереди). Поэтому для
получения наибольшего быстродействия две команды должны
разделяться по крайней мере еще одной командой. Например:
add   esi, eax    ; esi - регистр-приемник
mov   eax, [esi]  ; esi - базовый регистр, 1 такт дополнительно
Существуют также и другие неявные или косвенные методы
использования приемника и базового регистра, в первую очередь
регистра указателя стека ESP. Регистр ESP является неявной базой
всех команд типа PUSH/POP/RET и он же является неявным
приемником в командах CALL/ENTER/LEAVE/RET/PUSH/POP. Более того,
команда LEAVE, следующаа непосредственно за командой RET, будет
использовать один дополнительный цикл. Но если команды LEAVE и
RET реорганизованы таким образом, что одну от другой отделяет
какая-то другая команда, не требуется никаких дополнительных
тактов. (Смотри также другие рекомендации, касающиеся команды
LEAVE).
Нет никакой необходимости разделять последовательные команды
PUSH/POP. Процессор i486 допускает такую последовательность без
использования дополнительного такта.
Все такие преобразования последовательности команд не влияют на
выполнение программы на 386 процессоре.
Процессор i486 также требует дополнительного такта для
выполнения команды, которая имеет одновременно и операнд -
непосредственное значение, и операнд, заданный как смещение в
памяти. Например :
mov   указатель на двойное слово foo, 1234h
;   одновременно  и  непосредственное  значение,  и  операнд -
;   смещение в памяти.
mov   указатель на двойное слово baz, 1234h
mov   [ebp-200], 1234h
Когда требуется использовать константы, более эффективно
использовать непосредственные значения констант вместо
предварительной загрузки констант в регистры. Но если одна и та
же константа используется больше, чем один раз, то быстрее будет
загрузить значение этой константы в регистр и затем много раз
использовать этот регистр. Эта оптимизация не влияет на
выполнение программы на 386 процессоре. Следующая
последовательность действий выполняется быстрее, чем приведенная
выше, если все команды находятся в предварительно созданной
очереди, и, поскольку команды короче, их в действительности
легче организовать в очередь :
mov   eax, 1234h
mov указатель на двойное слово foo, eax
mov указатель на двойное слово baz, eax
mov   [ebp-200], eax
G.2 Модуль предварительной выборки
----------------------------------------------------------------
Модуль предварительной выборки процессора i486 получает доступ к
расположенной на микросхеме кэш-памяти для того, чтобы заполнить
предварительную очередь команд в тот момент, когда кэш-память не
задействована для работы и имеется достаточно места в очереди
для размещения другой строки кэша (16 байт). Если
предварительная очередь команд пуста, может потребоваться три
дополнительных такта для запуска новой команды. Предварительная
очередь команд занимает 32 байта (2 строки в кэш-памяти).
Так как доступ к данным всегда имеет высший приоритет по
сравнению с запросами на предварительную выборку, поддержание
кэш-памяти в состоянии занятости при помощи доступа к данным
может заблокировать модуль предварительной выборки.
Более того, важно организовать команды таким образом, чтобы не
использовалась постоянно шина памяти путем последовательности
команд обращения к памяти. Команды должны быть реорганизованы
таким образом, чтобы имелась команда, не обращающаяся к памяти
(например, команда работы регистр/регистр), по крайней мере за
два такта до того, как предварительно сформированная очередь
команд станет исчерпаной. Например :
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДї
і          Команда                і       Длина                і
ГДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ
і   mov  память, 1234567h         і    10  байт                і
і   mov  память, 1234567h         і    10  байт                і
і   mov  память, 1234567h         і    10  байт                і
і   mov  память, 1234567h         і    10  байт                і
і   add  регистр, регистр         і     2  байта               і
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Если предварительно сформированная очередь команд полна на
момент начала работы, тогда после выполнения третьей команды MOV
появляется достаточно пространства для другой строки кэша в
очереди, но, так как шина памяти продолжает постоянно
использоваться, нет необходимого времени для того, чтобы
передать строку из кэша в предварительно формируемую очередь
команд. Если перед или после третьей команды MOV не будет
вставлена команда, не обращающаяся к памяти, очередь будет
исчерпана четвертой командой MOV. В этом случае команды должны
быть реорганизованы таким образом, чтобы команда ADD была
расположена до или после третьей команды MOV, для того, чтобы
разрешить кэш-памяти переслать очередную командную строку в
модуль предварительной выборки.
Ни одна из подобных реорганизаций команд не будет оказывать
влияния на скорость выполнения программы на процессоре 386 DX.
G.3 Выравнивание кэша и кода.
----------------------------------------------------------------
При работе на процессоре 386 приемник любой из команд JUMP/CALL/
RET должен быть выровнен по адресу, кратному 4, это помогает
модулю предварительной выборки команд в формировании
предварительной очереди команд со всей допустимой быстротой, так
как выборка выполняется по 4 байта за одно обращение при
выровненных границах. Так как на процессоре i486 имеется
кэш-память на микросхеме, любая предварительная выборка команд
будет выбирать 16 байт для заполнения строки кэша. По этой
причине наилучшее быстродействие может быть достигнуто путем
выравнивания операндов-приемников в командах JUMP/CALL/RET по
адресам, кратным 16.
Однако выравнивание по адресам, кратным 16, является причиной
увеличения размеров кода, и поэтому важен компромисс между
скоростью выполнения и размерами кода.
Более того, рекомендуется, чтобы только входные адреса функций
(т.е., приемники команд CALL) были выровнены по адресам, кратным
16; в то время как все метки (т.е. приемники в командах JUMP)
продолжали бы оставаться выровненными по адресам, кратным 4.
На процессоре i486 требуется дополнительно до пяти тактов для
начала выполнения команды, если она разбивается более чем на две
16-байтные строки кэша. Например, если команда CALL
заканчивается на адресе 0x0000000Е и следом за ней идет команда
умножения байтов, тогда по возвращении из выполнения команды
CALL процессор должен использовать дополнительно пять тактов для
того, чтобы заполнить предварительно формируемую очередь команд,
если только целевая команда уже не находится в кэше. Даже если
целевая команда уже находится в кэше, тем не менее потребуется
дополнительные 2 цикла для того, чтобы переслать ее в модуль
предварительной выборки команд.
Поэтому, если компилятору известно выравнивание приемника, тогда
будет быстрее включить команды заполнения так, чтобы команда
умножения байтов начиналась с выровненного адреса. Это может
быть достигнуто либо путем перестановки последовательности
команд, либо включением команды NOP.
Такое выравнивание команд будет также влиять на скорость
выполнения на процессоре 386.
G.4. Команда NOP
----------------------------------------------------------------
Иногда программам необходим заполнитель между командами для
того, чтобы выравнивать их. На процессорах 386 и i486 таковым
заполнителем является однобайтовая команда NOP, которая в
действительности обменивает содержимое регистра ЕАХ с содержимым
регистра ЕАХ же.
Команды других длин могут быть выполнены за один цикл. Таблица
ниже приводит некоторые из них.
1-байная   inc  регистр               ; модифицирует регистр и
                                      ; флаги
2-байтная  mov  регистр, регистр      ; в действительности NOP
3-байтная  lea  регистр, 0[регистр]   ; в действительности NOP,
                                      ; с использованием
                                      ; 8-разрядного смещения
5-байтная  mov  eax, 0                ; модифицирует регистр eax
5-байтная  add  eax, 0                ; модифицирует флаги
6-байтная  lea  reg, 0[eax]           ; в действительности NOP,
                                      ; с использованием
                                      ; 32-разрядного смещения
Вдобавок, многие команды процессоров 386/i486 имеют особые формы
и длины, использующие непосредственные данные различной длины
или смещения в памяти различной размерности. Также некоторые
команды имеют укороченные формы, если операндом-приемником
является регистр EAX/AX/AL.
Не все команды с различными формами будут выполняться за одно и
тоже время. Примером, в котором различные формы будут
выполняться за различное время, являются команды PUSH/POP/REG.
Если они закодированы в однобайтовую форму, они будут
выполняться за один цикл, но если они закодированы в 2-байтную
форму, они будут выполняться за 4 цикла.
Команды заполнения NOP будут также выполняться быстрее, чем
команда XCHG на процессорах семейства 386. Использование
различных форм одной и той же команды не влияет на
быстродействие при выполнение на процессоре 386.
G.5. Команды работы с целыми числами
----------------------------------------------------------------
Процессор i486 может выполнять большинство часто используемых
команд (таких, как загрузка или запоминание регистра,
АЛУ-операции с регистром, и т.д.) за один такт. Однако, в
отличие от процессора 386, некоторые из операций с памятью
занимают теперь больше циклов, чем соответствующие операции с
регистрами. Например, команда PUSH MEM :
ЪДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДД
і     Команда        і Такты ЦПУ 386(ТМ) DX і Такты ЦПУ i486(TM) 
ГДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДД
і mov регистр,память і           4          і          1         
і push регистр       і           2          і          1         
і push память        і           5          і          4         
АДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДД
Tаким образом, для процессора i486, загрузка значения из памяти
в регистр и затем занесение этого регистра в стек приведет к
чистому сохранению 2 тактов; но для процессора 386 DX таже самая
последовательность команд приведет к чистой потере одного такта.
Однако для того, чтобы загрузить значение в регистр на
процессоре i486, необходимо найти пустой регистр; если действие
по загрузке значения уничтожит значение регистра, которое могло
быть использовано позднее, то сохранение значения может привести
к отрицательному результату по причине потери повторно
используемого значения регистра.
Другим примером служит команда LEAVE :
ЪДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДї
і      Команда     і Такты ЦПУ 386(ТМ) DX і Такты ЦПУ i486(TM) і
ГДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДґ
і mov esp, ebp     і           2          і          1         і
і pop ebp          і           4          і 1+1 (штраф за esp) і
і leave            і           4          і          5         і
АДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДЩ
Опять же, для процессора i486, выполнение последовательности
команд MOV/POP приводит к чистому сохранению 2 тактов по
сравнению с командой LEAVE; в то время, как выполняясь на
процессоре 386 DX, команда LEAVE одновременно и быстрее, и
короче. Однако, так как первая команда MOV использует регистр
ESP в качестве приемника и команда POP также неявно использует
регистр ESP в качестве базы (как уже упоминалось выше), эта
последовательность действий приводит к получению одного
штрафного такта, если только две команды не разделены другой
командой. Если возможно таким образом переупорядочить команды,
чтобы команды MOV/POP были бы разделены полезной командой, тогда
чистый выигрыш в тактах по сравнению с командой LEAVE составил
бы 3 такта на процессоре i486.
Так как процессор i486 может работать с операндами в регистрах
быстрее, чем с операндами в памяти (как и почти большинство
других архитектур), важно иметь хорошее размещение регистров и
оптимизацию трекинга значений в любом из компиляторов. С другой
стороны, нет сохранения при загрузке каждого значения перед его
использованием, как это сделано в RISC архитектуре. Процессор
i486 может выполнять команды АЛУ типа регистр,память также
быстро, как и последовательности команд
загрузить/операция/запомнить. Например, для присваивания :
память1 = память1 + память2 - можно использовать следующие
последовательности команд, с варьированием общего количества
тактов для процессоров 386 DX и SX, но одним и тем же
количеством тактов для процессора i486 :
ЪДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДї
і      Команда     і Такты ЦПУ 386(ТМ) DX і Такты ЦПУ i486(TM) і
ГДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДґ
і mov eax, память1 і           4          і          1         і
і mov ebx, память2 і           4          і          1         і
і add eax, ebx     і           2          і          1         і
і mov память1, eax і           2          і          1         і
і                  і                      і                    і
і mov eax, память1 і           4          і          1         і
і add eax, память2 і           6          і          2         і
і mov память1, eax і           2          і          1         і
і                  і                      і                    і
і mov eax, память1 і           4          і          1         і
і add память2, eax і           7          і          3         і
АДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДЩ
Команда MOVZX является другим примером, в котором процессор i486
может работать быстрее с использованием простых команд, если
приемником является регистр, который в свою очередь может быть
адресуем побайтно. Например, загрузка значения байта :
ЪДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДД
і      Команда       і Такты ЦПУ 386(ТМ) DX і Такты ЦПУ i486(TM) 
ГДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДД
і movzx eax, память1 і           6          і 3+1 (префикс 0Fh)  
і xor eax, eax       і           2          і          1         
і movb al, память1   і           4          і          1         
АДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДД
Таким образом, для процессора i486, сначала очистка регистра и
затем загрузка значения регистра может дать в качестве
результата чистую экономию двух тактов (в зависимости от того,
где такт декодирования префикса может быть перекрыт предыдущей
командой, смотри Раздел G.8, посвященный Префиксным кодам
операций), в то время как нет разницы в выполнении команд на
процессоре 386 DX.
G.6. Коды условия
----------------------------------------------------------------
В некоторых языках программирования высокого уровня иногда
необходимо преобразовать результат булевского условия (а именно,
равенства, больше-чем или меньше-чем и так далее) в значение
истина или ложь (т.е. 0/1). Процессоры 386 и i486 обычно
поддерживают результаты сравнения в регистре флагов, поэтому для
того, чтобы преобразовать результат сравнения в значение истина/
ложь, необходимо преобразовать значения флагов в целые значения.
Процессоры 386 и i486 имеют множество команд SETcc, которые
выполняют такое преобразование, однако команды SETcc занимают 3
или 4 такта при выполнении на процессоре i486, в зависимости от
того, когда условие проверяется на истину или ложность.
Специально при сравнении значений без знака на больше чем или
меньше чем, имеется необязательная для использования
последовательность действий. Например, если "x" и "y" являются
значениями без знака, и "x" загружен в регистр eax и "y"
загружен в регистр ecx, тогда код для сравнения "(x




Яндекс цитирования