Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 [ 64 ] 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

Инструкция JBE читает и флаг переноса, и флаг нуля. Так как инструкция ГЫС изменяет флаг нуля, но не флаг переноса, то инструкции JBE приходится подождать, пока две предыдущие инструкции не будут выведены из обращения, прежде чем она сможет скомбинировать флаг переноса от инструкции СМР с флагом нуля от инструкции ШС Подобная ситуация больше похожа на ошибку, чем на преднамеренную комбинации флагов. Чтобы скорректировать эту ситуацию, достаточно изменить ГЫС ЕСХ на ADD ЕСХ, 1. Похожая ошибка, вызывающая задержку, - это SAHF / JL XX/. Инструкция JL тестирует флаг знака и флаг переполнения, но не меняет последний. Чтобы исправить это, следует изменить JL XX на JS XX.

Неожиданно (и в противоположность тому, что говорится в руководствах от Intel i частичная задержка регистра может случиться, если были изменены какие-то биты регистра флагов, а затем считаны неизмененные:

СМР ЕАХ, ЕВХ INC ЕСХ

JC XX ; задержка

но не при чтении только изменных битов:

СМР ЕАХ, ЕВХ INC ЕСХ

JE XX ; нет задержки

Частичные задержки флагов возникают, как правило, при использовании инструкций, которые считывают некоторые или все биты регистра флагов, например LAHF, PUSHF, PUSHED. Инструкции, которые вызывают задержку, если за ними идут LAHF или PUSHF(D), следующие: INC, DEC, TEST, битовые тесты, битовые сканирования, CLC, STC, CMC, CLD, STD, CLI, STI, MUL, IMUL и все виды битовых сдвигов и вращений. Следующие инструкции не вызывают задержки: AND, OR, XOR, ADD, ADC, SUB, SBB, CMP, NEG. Странно, что TEST и AND ведут себя по-разному, хотя по описанию они делают с флагами одно и то же. Можно использовать инструкции SETcc вместо LAHF или PUSHF(D) для сохранения значения флага, чтобы избежать задержки.

Примеры

INC ЕАХ / PUSHED ADD ЕАХ,1 / PUSHED

задержка нет задержки

SHR ЕАХ,1 / PUSHED ; задержка

SHR ЕАХ,1 / OR ЕАХ,ЕАХ / PUSHED ;

нет задержки

TEST ЕВХ,ЕВХ / LAHF AND ЕВХ,ЕВХ / LAHF TEST ЕВХ,ЕВХ / SETZ AL

задержка нет задержки нет задержки

CLC / SETZ AL ; задержка

CLD / SETZ AL ; нет задержки

Потери при частичной задержке флагов примерно равны 4 тактам.

6.19.3. Задержки флагов после сдвигов и вращений

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

SHR ЕАХ,1 / JZ XX ; нет задержки

SHR ЕАХ,2 / JZ XX ; задержка

SHR ЕАХ,2 / OR ЕАХ,ЕАХ / JZ XX ; нет задержки

SHR ЕАХ,5 / JC XX ; задержка

SHR ЕАХ,4 / SHR ЕАХ,1 / JC XX ; нет задержки

SHR EAX,CL / JZ XX

SHRD ЕАХ,ЕВХ,1 / JZ XX ROL ЕВХ,8 / JC XX

; задержка, даже если CL = 1

; задержка ; задержка

Потери для этого вида задержек приблизительно равны 4 тактам.

6.19.4. Частичные задержки памяти

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

MOV BYTE PTR lESI], AL MOV EBX, DWORD PTR [ESI]

; частичная задержка в памяти

Здесь случается задержка, потому что процессор должен скомбинировать байт, записанный из AL с тремя следующими байтами, которые были в памяти раньше, чтобы получить все четыре байта, необходимые для произведения записи в ЕВХ. Потери приблизительно равны 7-8 тактов.

В отличии от частичных задержек регистра, частичная задержка памяти случается, когда операнд записывается в память, а затем читается его часть, если она не начинается по тому же адресу.

MOV DWORD PTR [ESI], ЕАХ MOV BL, BYTE PTR [ESI] MOV BH, BYTE PTR [ESI+1]

нет задержки задержка

Можно избежать этой задержки, если изменить иструкцию в последней строке на Mov ВН, АН, но подобное решение невозможно в такой:

FISTP QWORD PTR [EDI] MOV EAX, DWORD PTR [EDI] MOV EDX, DWORD PTR [EDI+4;

задержка



MOV MOV MOV

BYTE ЕВХ, ЕСХ,

PTR [ESI], DWORD PTR DWORD PTR

[ESI+4092] [ESI+4096]

нет задери задержка

6.20. Цепочечные зависимости (РРго, Р*2 и РЗ)

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

MOV ЕАХ, [МЕМ1] ADD ЕАХ, [МЕМ2] ADD ЕАХ, [МЕМЗ] ADD ЕАХ, [МЕМ4] MOV [МЕМ5], ЕАХ

В этом примере инструкция ADD генерирует 2 мопа: один для чтения из памяти (порт 2) и один для сложения (порт О или 1). Моп чтения может выполняться не по порядку, в то время как моп сложения дожен ждать, пока выполниться предыдущий. Эта цепочка зависимости занимает не очень много времени, так как каждое сложение требует только один такт. Но если в коде содержатся медленные инструкции, такие, как умножение или, еще хуже, деление, тогда нужно избавляться от цепочечной зависимости. Это можно сделать, используя различые приемы.

MOV ЕАХ, [МЕМ1] MOV ЕВХ, [МЕМ2] IMUL ЕАХ, [МЕМЗ] IMUL ЕВХ, [МЕМ4] IMUL ЕАХ, ЕВХ MOV [МЕМ5], ЕАХ

; начало первой цепочки ; начало второй цепочки с другим приемником

в конце соединяем цепочки

Здесь вторая инструкция IMUL может начаться до того, как будет завершено выполнение первой. Так как инструкция IMUL вызывает задержку в 4 такта и полностью конвейеризована, вы можно использовать до 4-приемников.

Деление не конвейеризовано, и, соответственно, невозможно делать то же самое со связанными делениями, но, разумеется, можно перемножить все делители и сделать только одно деление в конце.

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

MEMl ]

; начинаем первую цепочку

MEM2]

; начинаем вторую цепочку с другим

FADD

[МЕМЗ]

FXCH

FADD

[MEM4]

FXCH

EADD

[MEM5]

FADD

; соединяем цепочки в конце

FSTP

[MEM6]

Для этого потребуется некоторое количество инструкций FXCH, однако они нересурсоемки. Инструкции FXCH обрабатываются в RAT с помощью переименования регистров, поэтому они не создают никакой назгрузки на порты выполнения. Тем не менее, FXCH генерирует один моп в RAT, ROB и в станции вывода из обращения.

Если цепочка зависимости очень длинная, может потребоваться три приемника.

цепочку цепочку цепочку

MEMl]

; начинаем первую

MEM2]

; начинаем вторую

МЕМЗ]

; начинаем третью

FADD

[MEM4]

; третья цепочка

FXCH

ST (1)

FADD

[MEM5]

; вторая цепочка

FXCH

ST(2)

FADD

[MEM6]

; первая цепочка

FXCH

ST (1)

FADD

[MEM7]

; третья цепочка

FXCH

ST(2)

FADD

[MEMS]

; вторая цепочка

FXCH

ST(1)

FADD

; соединяем первую

FADD

; результат соединяем

FSTP

[MEM9]

цепочку

Следует избегать сохранения промежуточных данных в памяти и немедленного их считывания оттуда.

MOV [TEMP], ЕАХ MOV ЕВХ, [TEMP]

Возникают потери из-за попытки чтения из памяти до того, как будет завершена предыдущая запись по тому же адресу. В вышеприведенном примере можно изменить последнюю инструкцию на MOV ЕВХ, ЕАХ или поместить какие-нибудь уместные инструкции между ними.

Интересно, что частичная задержка памяти может также случиться при записи и чтение совершенно разных адресов, если у них одинаковое set-значение в разных банках кэша.



Есть одна ситуация, когда можно избежать сохранения промежуточных данны, в памяти. Она возникает тогда, когда осуществляется перемещение данных из цело, численного регистра в регистр FPU или наоборот. Например:

MOV ЕАХ, [МЕМ1] ADD ЕАХ, [МЕМ2] MOV [TEMP], ЕАХ FILD [TEMP]

Если нечего поместить между записью в TEMP и считыванием из него, можно использовать регистр с плавающей запятой вместо ЕАХ.

FILD [МЕМ1] FIADD [МЕМ2]

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

621. Поиск узких мест (РРго, Р*2 и РЗ)

Оптимизируя код для процессоров РРго, Р2 и РЗ, важно проанализировать, где находятся узкие места. Оптимизация одного узкого места не будет иметь смысла, если есть другие более серьезные.

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

Если вы ожидается много потерь при работе с кэщем данных, нужно забыть обо всем др; гом и сконцентрироваться на том, как реструктуризовать данные, чтобы снизить потери (раз; 6.7) и избежать длинных цепочечных зависимостей при неоптимальной работе с кэщем.

Если требуется большое количество операций деления, можно попробовать nperpyi пировать инструкции так, чтобы процессору было чем заняться во время их обработки.

Цепочечные зависимости мешают изменению порядка выполнения инструкций (разд 6.20). Нужно разрывать длинные цепочечные зависимости, особенно, если они содержат медленные инструкции, такие, как умножение, деление и инструкции плавающей запятой.

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

В случае смешивания данных разной размерности (8, 16 и 32 бит), нужно следить за появлением частичных задержек. Для инструкций PUSHF или LAHF обязательно отслеживание появления частичных задержек флагов, избегайть тестирования флагов после сдвигов больших, чем на 1 (разд. 6.19).

Если ориентироваться на производительность в 3 мопа за такт, нужно следить за перебоя-(И в доставке инструкций и их раскодировке (разд. 6.14 и 6.15), особенно в коротких циклах.

Офаничение на чтение максимум из двух постоянных регисфов может снизить производительность до уровня, меньшего, чем 3 мопа за такт. Это может случиться, ecnv происходит частое считывание регисфов, по истечении 4 тактов с момента их последнего изменения. Это может, например, случиться, при использовании указатели для адресации памяти с редкой их модификацией.

Производительность в 3 мопа за такт фебует, чтобы любой порт выполнения получал „е больше 1/3 всех мопов (раздел 6.17).

Станция вывода из обращения может обрабатывать 3 мопа за такт, но может быть менее эффективной при работе с предсказанными переходами (разд. 6.18).

622. Команды передачи управления

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

Предсказания основываются на буфере переходов (ВТВ - branch target buffer), который сохраняет историю каждого перехода и делает предсказания, основываясь на предыдущих результатах выполнения каждой инсфукции. ВТВ организован как множественно-ассоциативный (set-associative) кэш, в котором новые элементы используются согласно псевдослучайному методу замещений.

Оптимизируя код важно снижать количество возможных неправильных предсказаний переходов. Это можно сделать при хорошом понимании того, как работает механизм предсказания переходов.

Механизм предсказания переходов конкретно не объясняется ни в руководствах от Intel, ни где-нибудь еще, поэтому здесь приводится детальное описание. Эта информация основывается на исследованиях Anger Fog и Karki Jitendra Bahadur.

Далее под термином "команда передачи управления" будет подразумеваться любая инструкция, которая меняет eip, включая условные и безусловные, прямые и косвенные, ближние и дальние переходы, вызовы и возвраты. Все эти инсфукции используют предсказание.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 [ 64 ] 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88