Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Юрий Владимирович самый уважаемы человек =)экза...doc
Скачиваний:
3
Добавлен:
12.09.2019
Размер:
1.41 Mб
Скачать

70 .Низькорівнева оптимізація

Включает такие техники, как:

Оптимальный выбор (замена, объединение, разделение) инструкций

Переупорядочивание инструкций

Распределение регистров

Удаление цепочек переходов

Векторизация

Понижение силы операций

Объединение и разделение инструкций

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

Так, для конвейерной архитектуры разделение сложных инструкций на более простые может давать выигрыш в быстродействии, если несколько инструкций могут быть параллельно выполнены на конвейере. Так, например, процессор Pentium MMX быстрее выполнит набор инструкций

dec ecx

jnz метка

чем эквивалентную ему инструкцию loop метка; для ранних процессоров архитектуры x86, от 8086 до 80286, всё иначе, так как они выполняют инструкции неконвейеризированно, вследствие чего команда loop метка создаёт более экономичный и быстрый код для этих процессоров.

Так как процессоры Intel используют весьма сложный набор команд, большинство операций можно выполнить на низком уровне очень многими способами. При этом иногда оказывается, что наиболее очевидный способ — не самый быстрый или короткий. Часто простыми перестановками команд, зная механизм выполнения команд на современных процессорах, реально заставить ту же процедуру выполняться на 50 – 200% быстрее. Разумеется, переходить к этому уровню оптимизации можно только после того, как текст программы окончательно написан и максимально оптимизирован на среднем уровне.

Перечислим основные рекомендации, которым нужно следовать при оптимальном программировании для процессоров Intel Pentium, Pentium MMX, Pentium Pro и Pentium II.

Основные рекомендации

Используйте регистр ЕАХ всюду, где возможно. Команды с непосредственным операндом, с операндом — абсолютным адресом переменной и команды XCHG с регистрами занимают на один байт меньше, если другой операнд — регистр ЕАХ.

Используйте регистр DS всюду, где возможно. Префиксы переопределения сегмента увеличивают размер программы на 1 байт и время на 1 такт.

Если к переменной в памяти, адресуемой со смещением, выполняется несколько обращений — загрузите ее в регистр.

Не используйте сложные команды — ENTER, LEAVE, LOOP, строковые команды, если аналогичное действие можно выполнить небольшой последовательностью простых команд.

Не используйте команду MOVZX для чтения байта — это требует 4 тактов для выполнения. Заменой может служить такая пара команд:

xor еах,еах

mov al,source

Используйте TEST для сравнения с нулем или для других проверок равенства:

test eax,eax

jz if_zero ; переход, если ЕАХ = 0

test eax,source

jz if_zero ; переход, если ЕАХ = source

Исцользуйте команду XOR, чтобы обнулять регистр (конечно, если текущее состояние флагов больше не потребуется), эта команда официально поддерживается Intel как команда обнуления регистра:

xor еах,еах ; ЕАХ = 0

Не используйте умножение или деление на константу — его можно заменить другими командами, например:

; ЕАХ = ЕАХ * 10

shl eax,1 ; умножение на 2

lea eax,[eax+eax*4] ; умножение на 5

; ЕАХ = ЕАХ * 7

mov ebx,eax

shl еах,3 ; умножение на 8

sub eax,ebx ; и вычитание сохраненного ЕАХ

; АХ = АХ/10

mov dx,6554 ; DX = 65 536/10

mul dx ; DX = AX/10 (умножение

; выполняется быстрее деления)

; ЕАХ = ЕАХ mod 64 (остаток от деления на степень двойки)

and eax,3Fh

Используйте короткую форму команды jmp, где возможно (jmp short метка).

Как можно реже загружайте сегментные регистры.

Как можно меньше переключайте задачи — это очень медленная процедура. Часто, если надо сохранять небольшое состояние процесса, например для реализации нитей, переключение быстрее организовать программно.

Команда LEA

LEA можно использовать (кроме прямого назначения — вычисления адреса сложно адресуемой переменной) для следующих двух ситуаций:

быстрое умножение

lea еах,[еах*2] ; ЕАХ = ЕАХ * 2 (shl eax,1

; лучше)

lea еах,[еах+еах*2] ; ЕАХ = ЕАХ * 3

lea еах,[еах*4] ; ЕАХ = ЕАХ * 4 (shl eax,2

; лучше)

lea еах,[еах+еах*4] ; ЕАХ = ЕАХ * 5

lea еах,[еах+еах*8] ; ЕАХ = ЕАХ * 9

трехоперандное сложение

lea ecx,[eax+ebx] ; ЕСХ = ЕАХ * ЕВХ

Единственный недостаток LEA — увеличивается вероятность AGI с предыдущей командой (см. ниже).

Выравнивание

Восьмибайтные данные должны быть выравнены по восьмибайтным границам (то есть три младших бита адреса должны быть равны нулю).

Четырехбайтные данные должны быть выравнены по границе двойного слова (то есть два младших бита адреса должны быть равны нулю).

Двухбайтные данные должны полностью содержаться в выравненном двойном слове (то есть два младших бита адреса не должны быть равны единице).

80-битные данные должны быть выравнены по 16-байтным границам.

Когда нарушается выравнивание при доступе к данным, находящимся в кэше, теряются 3 такта на каждое невыравненное обращение на Pentium и 9 – 12 тактов — на Pentium Pro/Pentium II.

Так как линейка кэша кода составляет 32 байта, метки для переходов, особенно метки, отмечающие начало цикла, должны быть выравнены по 16-байтным границам, а массивы данных, равные или большие 32 байт, должны начинаться с адреса, кратного 32.

AGI

AGI — это ситуация, при которой регистр, используемый командой для генерации адреса как базовый или индексный, был приемником предыдущей команды. В этой ситуации процессор тратит один дополнительный такт. Последовательность команд

add edx,4

mov esi,[edx]

выполняется с AGI на любом процессоре.

Последовательность команд

add esi,4 ; U-конвейер - 1 такт (на Pentium)

pop ebx ; V-конвейер - 1 такт

inc ebx ; V-конвейер - 1 такт

mov edi,[esi] ; в U-конвейер - *AGI*, затем 1 такт

выполняется с AGI на Pentium за три такта процессора.

Кроме того, AGI может происходить неявно, например при изменении регистра ESP и обращении к стеку:

sub esp,24

push ebx ; *AGI*

или

mov esp,ebp

pop ebp ; *AGI*

но изменение ESP, производимое командами PUSH и POP, не приводит к AGI, если следующая команда тоже обращается к стеку.

Процессоры Pentium Pro и Pentium II не подвержены AGI.

Обращение к частичному регистру

Если команда обращается к 32-битному регистру, например ЕАХ, сразу после команды, выполнявшей запись в соответствующий частичный регистр (АХ, AL, АН), происходит пауза минимум в 7 тактов на Pentium Pro и Pentium II и в 1 такт — на 80486, но не на Pentium:

mov ax,8

add ecx,eax ; пауза

На Pentium Pro и Pentium II эта пауза не появляется, если сразу перед командой записи в АХ была команда XOR ЕАХ,ЕАХ или SUB ЕАХ,ЕАХ.

Префиксы

Префиксы LOCK, переопределения сегмента и изменения адреса операнда увеличивают время выполнения команды на 1 такт.