Анимация
JavaScript
|
Главная Библионтека 442 Ассемблер в задачах защиты инфор,! Сначала проанализируем раскодировку инструкций (разд. 6.14); одна из инструк генерирует два мопа (MOV [EDI],EAX). Эта инструкция должна попасть в декодер Есть три раскодировываемые группы в цикле, поэтому его можно раскодировать за 3 Теперь посмотрим на доставку инструкций (разд. 6.15): если границы между дадут первым трем инструкциям раскодироваться вместе, тогда будет три раскодирову ваемые фуппы в последнем БДИ, поэтому в следующем повторении БДИ начнется с первой инструкции, там где это нужно, и задержка возникнет только в первом повторе НИИ. Худщим вариантом будет 16-байтная граница и граница БДИ в одной из следующр трех инструкций. Это сгенерирует задержку в один такт и приведет к тому, что в еле-дующем повторении первый БДИ будет выровнен на 16 и проблема будет повторяться каждую итерацию. В результате время доставки будет равно 4 тактам на итерацию (а не 3). Есть два пути предотвратить подобный вариант развития событий: первый метод заключается в том, чтобы контролировать расположение 16-байтных границ. Другой проще: так как во всем цикле только 15 байт кода, можно избежать 16-байтных границ, выровняв цикл на 16, как показано выще. Тогда весь цикл будет умещаться в один БДИ, поэтому никакого дальнейщего анализа доставки инструкций не потребуется. Третья проблема - это задержки чтения регистров (разд. 6.16). В этом цикле не читается ни один регистр, если, по крайней мере, несколько тактов назад в него не была произведена запись. Четвертая проблема - это выполнение инструкций (разд. 6.17). Подсчитывая мопы, предназначающиеся разным портам, имеем: порт О или 1: 4 мопа порт 1 : 1 мои порт 2:1 МОП порт 3: 1 МОП порт 4: 1 мои. Предположив, что мопы, которые могут пойти в порт О или 1, распределяются оптимальным образом, время выполнения будет равно 2.5 такта на итерацию. И последний анализ, который необходимо провести, - это вывод из обращения (разд. 6.18). Так как количество мопов в цикле не кратно 3, слоты вывода из обращения не будут использоваться оптимальным образом, когда переход будет выводиться из обращения через первый слот. Время, необходимое для вывода из обращения, равно количеству мопов, деленное на 3 и округленное в сторону ближайщего целого числа. Это дает 3 такта для вывода из обращения. В заключение, цикл, представленный вьше, может выполняться за 3 такта на итерацию, если код цикла выровнен на 16. Предполагается, что условный переход предсказывается каждый раз, кроме выхода из цикла. Использование одного и того же регистра для счетчика и индекса, и последнее значение счетчика равно нулю (РРго, Р2 и РЗ). Глава 6, Оптимизация для процессоров семейства Pentium MOV MOV MOV LEA LEA NEG ECX, ESI, EDI, ESI, EDI, ECX [N] [A] [B] [ESI+4*ECX] [EDI+4*ECX] ; указывает на конец массива ; указывает на конец массива ; -N ALIGN 16 - 1 : MOV NEG MOV INC JNZ L2: SHORT L2 EAX, [ESI+4*ECX] EAX [EDI+4*ECX], EAX len=3, p2rESIrECXwEAX len=2, pOlrwEAXwF len=3, p4rEAX, p3rEDIrECX len=l, pOlrwECXwF ; len=2, plrF Количество мопов было снижено до 6. Базовые указатели указывают на конец масси ВОВ, поэтому индекс можно увеличивать от отрицательных значений до нуля. 6.25.2.1.1. Раскодировка. В этом цикле две раскодировываемые группы, поэтому рас кодировка пройдет в два такта. 6.25.2.1.2. Доставка инструкций. Цикл всегда занимает, по крайней мере, на один так больше, чем количество 16-байтных блоков. Так как в нем только 11 байт кода, их мож но уместить в один БДИ. Выровняв цикл на 16, можно быть уверенным, что уместите в один 16-байтный блок, поэтому возможно осущесвить доставку за 2 такта. 6.25.2.1.3. Задержки чтения регистров. Регисфы ESI и EDI читаются, но не модифиц» руются внутри цикла. Поэтому эти считывания будут осуществляться из постоянных регг стров, но не в одном триплете. Регистры ЕАХ, ЕСХ и флаги модифицируются внутри цикл и считываются до того, как они записываются обратно, поэтому чтения постоянных реп стров не будет, т. е. можно сделать заключение, что задержек чтения регистров нет. 6.25.2.1.4. Выполнение. Порты О или 1: 2 мои порт 1: 1 моп порт 2: 1 мои порт 3: МОП порт 4: 1 МОП Время выполнения: 1.5 такта. 6.25.2.1.5. Вывод из обращения. 6 мопов = 2 такта. Заключение: этот цикл занимает только два такта на итерацию. Если используются абсолютные адреса вместо ESI и EDI, тогда цикл будет занимат 3 такта, потому что он не сможет уместиться в один 16-байтный блок. 6.25.2.2. Развертывание цикла (РРго, Р2 и РЗ). Следующий пример развертывает ра( сматриваемый цикл в два раза, что означает выполнение двух операций за раз, и меш шее в два раза количество проходов. Пример
В этом примере пункта 6.25.2.1 инструкции управления циклом (т. е. изменение значений указателей и счетчика, а также переход назад) занимал 4 мопа, а реальная работа занимала 4 мопа. При разворачивании цикла в два раза, анализируя доставку инструкций в этом цикле, видно, что новый БДИ начинается с инструкции ADD ESI, 8, следовательно, она пойдет в декодер DO. Поэтому цикл раскодировывается за 5 тактов, а не за 4, как хотелось бы. Можно решить эту проблему, заменив предыдущую инструкциюМОУ [EDI+4],EAX на более длинную. ; создаем инструкцию с большим смещением ; изменяем смещение на 4 MOV [EDI+99991,ЕАХ ORG $-4 DD 4 Это заставит новый БДИ начаться с длинной инструкции MOV [EDI+4], поэтому время раскодировки сейчас близко к 4 тактам. Оставшаяся свободная часть конвейера может обрабатывать 3 мопа за такт, поэтому ожидаемое время выполнения равно 4 тактам или 2 тактам на операцию. Тестирование этого решения показало, что в реальности оно занимает немного больше ( 4.5 такта за итерацию). Вероятно, это связано с неоптимальной перегруппировкой мопов. Возможно, ROB не смог найти оптимального порядка выполнения. Проблемы подобного рода непредсказуемы, и только тестирование может выявить их. Можно помочь ROB сделать часть перегруппировки. ALIGN L2:
L3 : Цикл теперь выполняется за 4 такта на итерацию. Также бьша решена проблема с БДИ. Это стоило дополнительного регистра, потому здесь не используются преимущества переименования регистров. 6.25.2.3. Развертывание более чем в 2 раза. Развертка циклов рекомендуется, когда инструкции по управлению циклами занимают значительную часть общего времени выполнения. В примере пункта 6.25.2.2 они занимают только 2 мопа, поэтому выгода будет невелика, но тем не менее рассмотрим, как его развернуть, просто ради упражнения. Настоящая работа равна 4 мопам, на управление циклом уходит 2 мопа. Развернув его, получаем 2*4-1-3 = 10 мопов. Время вывода из обращения будет равно 10/3, огругля-ем в сторону ближайшего целого, получается 4 такта. Эти вьиисления показывают, что разворачивание ничего не дает. L2: ALIGN
количество, обработать которое нужно на на указываем указываем ; -4*N ; тестируем конец конец массива массива N на нечетность N нечетно. Делаем нечетный раз Тестируем N/2 на нечетность ; N/2 нечетно. Делаем два ; дополнительных раза 446 Ассемблер в задачах защиты uнфopмaцuj L3 :
L4 : БДИ распределяются так, как нужно. Время раскодировки равно 6 тактам. Задержки чтения регистров являются здесь проблемой, так как ЕСХ выводится из обращения в конце цикла, а нужно читать ESI, EDI и ЕСХ. Инструкции были перегруппированы так, чтобы избежать чтения ESI в конце цикла во избежание задержки чтения регистра. Другими словами, причина перегруппировки инструкций и использования дополнительного регистра отличается от пункта 6.25.2.2. Здесь 12 мопов и цикл выполняется за 6 тактов на итерацию или 1.5 такта на операцию. 1У1ожете рассмотреть возможность использования более чем двух операций за одно повторение, чтобы снизить количество действий, необходимых для организации цикла. Но так как в большинстве случаев время, требуемое для выполнения таких действий, можно снизить только на один такт, то разворачивание цикла в четыре раза, а не в два, сохранит только 1/4 такта на операцию, что вряд ли стоит усилий, которые будут на это потрачены. Только в случае если N очень велико, стоит думать о разворачивании цикла в четыре раза. Недостатки слишком большого разворачивания цикла следующие: Ш необходимо посчитать N MODULO R, где R - это коэффициент разворачивания, и сделать N MODULO R операций до или после основного цикла, чтобы выполнить недостающее количество операций. На это уйдет дополнительный код и плохо предсказуемые операции условного перехода. И, конечно, тело цикла станет больше; ш участок кода обычно выполняется в первый раз гораздо дольше, и чем больше код. тем больше будут связанные с этим потери, особенно если N невелико; значительное увеличение кода делает работу с кэшем менее эффективной. Использование коэффициента развертьшания, не являющегося степенью 2, делает вычисление N MODULO R довольно трудным и, как правило, не рекомендуется, е" только N не кратно R. Пример пункта 6.25.1.13 показывает, как разворачивать в 3 раза. Г..•..?.".У...V?.!." процессоров семейства Pentium 6.25.2.3. Обработка нескольких 8-ми шш 16-ти битных операндов одновременно в , битнь,х регионах (РРго, Р2 или РЗ). Иногда возможно обрабатывать четыре байт. ГссивТбГтов регистре. Следующий пример добавляет 2 ко всем элемент Пример MOV ESI, [А] MOV ЕСХ, [N] JECXZ L2 ALIGN 16 LI : db 7 dup (90h) mov eax, [esi] mov ebx, eax and eax, 7f7f7f7fh xor ebx, eax add eax, 02020202h xor ebx, eax mov [esi], ebx add esi, 4 sub ecx, 4 ja li L2 : ; адрес массива байтов количество элементов в массиве байто ; 7 NOPoB выравнивания ; читаем четыре байта ; копируем в ЕВХ ; получаем 7 нижних бит каждого бай получаем наивысший бит каждого байта ; добавляем значение ко всем байтам ; снова комбинируем биты ; сохраняем результат ; увеличиваем значение указателя ; понижаем значение счетчика ; цикл Следует обратить внимание, что перед прибавлением 2 сохраняется старший ( каждого байта, чтобы не испортить его значение (если результат сложения для конкр ного байта превысит 256. Этот цикл в идеальном случае занимает 4 такта на итерацию, но в реальном -может занять немного больше из-за цепочки зависимости и трудностей с перегруппир кой. На Р2 и РЗ можно это делать еще более эффективно, используя регистры ММХ. Следующий пример находит длину строки, заканчивающейся нулем. Он гора; быстрее, чем REPNE SCASB. Пример strlen PROC NEAR
получаем указатель на строку указатель+3 используется в kohi читаем первые 4 байта повышаем значение указателя вычитаем 1 из каждого байта инвертируем все байты и эти два тестируем все биты 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 |