Анимация
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

6.22.2.11. Возвраты (РММХ, РРго, Р2 и РЗ). У процессоров РММХ, РРго, Р2 и РЗ есть буфер стека возвратов (RSB - retam stack buffer), который используется для предсказывания инструкций возвратов. RSB работает как FIFO буфер. Каждый раз, когда выполняется инструкция вызова, соответствующий адрес возврата помещается в RSB. И каждый раз когда выполняется инструкция возврата, адрес возвращения извлекается из RSB и используется для предсказывания возврата. Этот механизм обеспечивает корректное предсказание этих инструкций, когда одна и та же подпрограмма вызывается из разных мест.

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

RSB может содержать до 4 элементов в РММХ, шестнадцать в РРго, Р2 и РЗ. В случае когда RSP irycT, инструкция возврата предсказывается, как и косвенный переход, то есть ожидается переход туда, куда он был совершен в последний раз.

На РММХ, когда подпрограммы вложены друг в друга более, чем на 4 уровня, все возвраты, кроме 4 самых вложенных, используют простой механизм предсказания, пока не происходит новых вызовов. Инструкция возврата, находящаяся в RSB, занимает один элемент ВТВ. Подпрограммы, вложенные более чем на 4 уровня, не являются чем-то необычным, но только внутренние уровни максимально эффективны в плане скорости, не считая возможности рекурсивных процедур.

На РРго, Р2 и РЗ, когда подпрограммы вложены глубже, чем на 16 уровней, внутренние 16 уровней используют RSB, в то время как все последующие возвраты из внешних уровней предсказьшаются неверно, поэтому рекурсивные процедуры не следует делать вложенными более чем на 16 уровней.

6.22.2.12. Статическое предсказывание (РММХ). Инструкция передачи управления, которая не встречалась ранее или которая не находится в ВТВ, всегда предсказывается как невыполняющаяся на РММХ. Не играет роли, куда совершается переход: вперед или назад.

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

Любая инструкция передачи управления, которая переходит на адрес, непосредственно следующий за ней же, не получает элемент в ВТВ:

JMP SHORT LL

Эта инструкция никогда не получит элемент в ВТВ и поэтому всегда будет предсказываться неправильно.

6.22.2.13. Статическое предсказывание (РРго, Р2 и РЗ). На РРго, Р2 и РЗ инструкция передачи управления, которая не встречалась ранее или которой нет в ВТВ, предсказывается

CALL

ЕАХ,ЕАХ

SHORT L

; добавим два байта, чтобы быть спокойными

Если нужно избежать проблем на Р1, тогда следует поместить вместо MOV две N0! Чтобы предотвратить спаривание.

Инсфукция RET особенно беззащитна перед этой проблемой, потому ее размер всег один байт длиной.

JNZ RET

NEXT

Здесь может потребоваться вставить три бай-та:

ак невыполняющаяся, если она указывает вперед, и как выполняющаяся, если она указыв азад (например, цикл). Статическое предсказываение занимает больше времени, чем ди1 1цческое предсказывание на этих процессорах. I

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

6.22.2.14. Закрытые переходы (РММХ). На РММХ существует риск, что две инстрз ЦИИ передачи управления разделят один и тот же элемент ВТВ, если они находят слишком близко друг к другу. Очевидным результатом станет то, что они всегда буд предсказываться неправильно.

Элемент ВТВ для инструкции передачи управления определяется по битам 2 -адреса последнего байта инструкции. Если две инструкции передачи управления нах дятся так близко друг от друга, что отличаются только битами О - 1 адресов, т. е. пр блема разделяемого элемента ВТВ: CALL Р JNC SHORT L

Если последний байт инструкции CALL и последний байт инструкции JNC находят внутри того же слова памяти, то результат - потери производительности. Следует изучи сгенерированный ассемблерный листинг, чтобы увидеть, разделены ли эти два адреса гран цей двойного слова или нет (фанвда двойного слова - это адрес, который делится на 4).

Есть несколько путей решить эту проблему.

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

Изменить короткий переход на ближний переход, чтобы конец инсфукции сместил! чуть вниз.

Поместить какую-либо инсфукцию между CALL и JNC. Это самый простой и еди ственный метод, если неизвестно, где находятся фаницы двойного слова, в случ; если сегмент кода не выровнен по фанице двойного слова или потому что код см щается то вверх, то вниз в результате изменений в предшествущем коде.



JNZ NEXT

MOV EAX,EAX

6.22.2.15. Последовательные вызовы или возвраты (РММХ). Возникают потери когда первая пара инструкций, следующая за меткой-целью вызова, содержит другод вызов или возврат, следующий сразу после предыдущего возврата.

Пример

FUNC1 PROC NEAR

NOP ; избегаем вызова после вызова

CALL FUNC2 CALL FUNC3

NOP ; избегаем возврата после возврата

FUNC1 ENDP

Требуется две NOP перед CALL FIINC2, потому что одна NOP будет спариваться с CALL. Одной NOP достаточно перед RET, потому что эта инструкция неспариваема. Не требуется NOP между двумя инструкциями CALL, потому что нет потерь при вызове после возврата (на Р1 потребуется здесь две NOP).

Подобные потери в последовательных вызовах возникают только, когда одни и те же подпрофаммы вызываются из более чем одного места (вероятно, что это происходит из-за обновления RSB). Последовательные возвраты всегда приводят к потерям. Иногда возникает небольшая задержка при переходе после вызова, но нет потерь при возврате после вызова, вызова после возврата, перехода, вызова или возврата после перехода или перехода после возврата.

6.22.2.16. Последовательные переходы (РРго, Р2 и РЗ). Переход, вызов или возврат не может выполниться в первый такт после предыдущего перехода, вызова или возврата. Поэтому последовательные переходы занимают по два такта на переход. В силу определенных причин цикл фебует не менее двух тактов на итерацию на этих процессорах.

6.22.2.17. Проектирование предсказуемых переход в (РММХ, РРго, Р2 и РЗ). Много-ветвенные переходы (реализующие высокоуровневые выражения switch/case) реализуются, как список адресов переходов или как дерево инсфукций переходов. Так как косвенные перс-ходы предсказываются плохо, то последний метод может быть более предпочтителен, если дерево будет представлено в виде хорошо предсказуемой последовательности и при наличии достаточного количества элементов ВТВ. В случае использования предыдущего методе; рекомендуется, чтобы адреса переходов бьши помещены в сегмент данных.

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

цикл, который выполняется 20 раз. Условный переход внизу цикла будет выпол-

гься 19 раз и на 20 раз - нет. Эта последовательность регулярна, но не будет идентифицирована механизмом распознавания последовательностей, поэтому невыполнение данной инсфукции будет всегда предсказываться неправильно. Можно сделать два вложенных цикла по 4 и 5 повторений или развернуть цикл в четыре раза и позволить ему вьшолниться 5 раз, чтобы были только распознаваемые последовательности. Этот вид сложных схем оправдывает себя только на РРго, Р2 и РЗ, где неверное предсказание стоит слишком дорого. Для циклов с большим количеством повторений нет смысла бороться с одним неправильным предсказанием.

6.22.3. Избегание переходов (все процессоры)

Есть много причин, по которым желательно снизить количество переходов, вызовов и возвратов:

неверные предсказания стоят очень дорого;

в зависимости от процессора могут возникнуть различные потери при последовательных вызовах;

инсфукции перехода могут выталкивать друг друга из буфера переходов;

возврат занимает 2 такта на Р1 и РММХ, вызовы и возвраты генерируют 4 мопа наРРго, Р2 иРЗ;

на РРго, Р2 и РЗ доставка инсфукций может быть задержена после перехода (глава 15), а вывод из обращения может быть менее эффективным для совершенных переходов, чем для других мопов (разд. 6.18).

Вызовов и возвратов можно избежать, если заменить короткие процедуры макросами. Во многих случаях можно снизить количество переходов, перефуппировав код. Например, переход на переход можно заменить переходом к конечному адресу назначения. В некоторых случаях это можно сделать даже с условными переходами, если условие дублируется или заранее известно. Переход на возврат можно заменить возвратом. Если нужно усфанить возврат на возврат, не следует манипулировать стековым указателем, потому что это будет создавать помехи механизму предсказания, основьшающимся на ISP. Вместо этого можно заменить предыдущий вызов на переход. Например, CALL PROl / RET можно заменить на JMP PROP, если PR01 заканчивается тем же RET.

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



СМР JE

CALL

CALL

[EAX+4*EDX1,ECX

EDX A

ESP, EBP EBP

Переход на С можно устранить, продублировав эпилог щпсла.

СМР [

EAX+4*EDX],ECX

CALL

CALL

ESP, EBP

Наиоолее часто выиилнжищиил 11ьр,л,д j„ww„ .....--------

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

6.22.4. Избегание условных переходов,

манипулируя флагами (все процессоры)

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

XOR EAX,EDX SUB ЕАХ,EDX

(На PI и РММХ можно использовать MOV EDX,EAX / SAR ЕВХ.ЗГ вместо CDQ) Флаг переноса особенно полезен для следующих трюков:

установка флага переноса, если значение равно нулю: СМР [VALUE],!; установка флага переноса, если значение не равно нулю: XOR ЕАХ,ЕАХ / CN EAX,[VALUE];

увеличение счетчика на 1, если установлен флаг переноса: ADC ЕАХ,0; установка бита каждый раз, когда установлен флаг переноса: RCL ЕАХ,1; генерация битовой маски, если установлен флаг переноса: SBB ЕАХ.ЕАХ; установка бита при определенном условии: SETcond AL;

установка всех бит при определенном условии: XOR ЕАХ,ЕАХ / SETNcond AL / DEi ЕАХ (в последнем примере условие должно быть сформулировано наоборот).

Пример нахождения меньшего из двух беззнаковых чисел: if (Ь < а) а = Ь.

SUB ЕВХ,ЕАХ SBB ЕСХ,ЕСХ AND ЕСХ,ЕВХ ADD ЕАХ,ЕСХ

А вот пример, как выбрать между двумя числами: if (а != 0) а = Ь; else а = с:

СМР SBB XOR AND XOR

ЕАХ, 1 ЕАХ,ЕАХ ЕСХ,ЕВХ ЕАХ,ЕСХ ЕАХ,ЕВХ

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

6.22.5. Замена условных переходов условными перемещениями (РРго, Р2 и РЗ)

У процессоров РРго, Р2 и РЗ есть инструкции условного перемещения данных, созданных специально для избежания переходов, поскольку неверное предсказание перехода стоит на этих процессорах слишком дорого. Есть инструкции условного перемещения данных и для целочисленных регистров и для регистров с плавающей запятой. В коде, который будет выполняться только на этих процессорах, можно заменить плохо предсказуемые переходы инструкциями условного перемещения там, где это возможно. Для того чтобы код выполнялся на всех процессорах, можно сделать две версии критичного кода: одну для процессоров, которые поддерживают инструкции условного перемещния, дру-



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