Магда Юрий
Шрифт:
.data
al DD 312, -45, 91, -16, -377 ; сканируемый массив
len EQU $-al ; размер массива в байтах
.code
_loopd_ex proc
mov ECX, len ; размер массива в байтах -> ЕСХ
shr ECX, 2 ; преобразовать размер в двойные слова
lea ESI, a1 ; адрес первого элемента -> ESI
mov ЕАХ, -100 ; шаблон для сравнения -> ЕАХ
next:
cmp ЕАХ, [ESI] ; сравнить элемент массива
; с содержимым регистра ЕАХ
jge found ; число в массиве меньше -100,
; закончить программу
add ESI, 4 ; число больше -100, перейти
; к следующему элементу массива
loopd next ; следующая итерация
jmp not_found ; массив проверен, чисел меньше -100 нет
found:
mov ЕАХ, [ESI] ; значение элемента массива -> ЕАХ
jmp exit ; выйти из процедурь
not_found:
mov ЕАХ, 0 ; при неудачном поиске в регистр ЕАХ
; помещается О
exit:
ret
_loopd_ex endp
end
Исходный текст процедуры несложен и в дополнительных объяснениях не нуждается, нужно лишь помнить, что процедура оперирует двойными словами, и указывать следующий адрес на 4 больше предыдущего.
5.4. Оптимизация кода в процессорах Intel Pentium
До сих пор мы рассматривали различные варианты реализации условных переходов и ветвлений в программах безотносительно к тому, быстро или медленно будет работать тот или иной фрагмент кода. В большинстве случаев программист обычно не задумывается над производительностью работы приложения, учитывая то, что современные аппаратные средства обеспечивают довольно приличный уровень быстродействия и без специальных усилий со стороны разработчика программного обеспечения.
Однако в целом ряде случаев производительность программы выходит на первое место, а система команд процессоров Intel Pentium, особенно для последних моделей, позволяет ее повысить. В этом разделе мы рассмотрим некоторые вопросы, связанные с повышением эффективности программ.
Быстродействие программы в значительной степени определяется алгоритмом вычислений, а также количеством ветвлений и переходов. Кроме этого, большое влияние на производительность программы оказывают характер ветвлений и организация циклических вычислений там, где они необходимы.
В идеале высокопроизводительное приложение должно состоять из линейно выполняющегося программного кода, без ветвлений и переходов. В этом случае процессор Intel Pentium мог бы обеспечить наибольшее быстродействие программы, поскольку не понадобился бы механизм прогнозирования ветвлений, отнимающий приличную часть процессорного времени в цикле выполнения команды.
Поскольку избежать ветвлений и переходов в программах вряд ли когда-нибудь удастся, то можно, по крайней мере, уменьшить их количество или оптимизировать сами ветвления. Разработчики фирмы Intel включили в систему команд процессоров Pentium ряд новых команд, предназначенных для оптимизации переходов в программах. Лучше всего показать работу таких команд на примерах и одновременно изучить их синтаксис.
Для повышения производительности программ фирма Intel включила в новые поколения процессоров, начиная с Pentium II, ряд команд, позволяющих эффективно управлять ветвлениями программы. К таким командам относятся команды setCC, cmovCCи fcmovCC, где СС – одно из условий (е, ne, le и т. д.).
Остановимся на синтаксисе этих команд и начнем с команд setCC. Вот их формат:
setCC reg8
setCC mem8
Здесь setCC – одна из следующих команд: sete/setz, setl/setnge и т. д., a reg8/ тет8 – единственный операнд команды, представляющий собой 8-разрядный регистр, например a1, АН, BL и т. д., или байт памяти. Если заданное в команде условие выполнено, то в операнд помещается значение 1, если ложно – 0. Команды setCC анализируют соответствующие флаги, установленные предыдущими ассемблерными инструкциями.
Проиллюстрируем сказанное примером:
cmp a1, 0
sete BL
Если после выполнения команды стр обнаружено равенство нулю содержимого регистра a1, то флаг ZF будет установлен в 1. Следующая команда sete анализирует состояние этого флага и помещает в регистр BL значение 1. Если бы в a1 содержалось число, отличное от нуля, то в регистр BL было бы записано значение 0.
Перечень команд setCC приведен в табл. 5.4.
Таблица 5.4. Команды setCC
Команды setCC очень удобны при организации вычислений по условию. При этом можно избавиться от ненужных команд переходов, что дает выигрыш в быстродействии. Рассмотрим следующий пример. Пусть в массиве целых чисел требуется найти первое число, лежащее между 50 и 100. Эту задачу можно решить с помощью процедуры find num, исходный текст которой показан в листинге 5.6.
В этой процедуре просматривается массив целых чисел a1, адрес которого находится в регистре ESI. Для поиска нужного элемента используется обычный алгоритм, в котором каждый элемент массива проверяется дважды: является ли он меньшим или равным числу 100 (команды cmp dword ptr [ESI], 100 и jle next1), a также большим или равным 50 (команды cmp dword ptr [ESI], 50 и jge found). Применение нескольких команд setCC позволяет уменьшить число ветвлений программы.