Анимация
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.27.6. Использование целочисленных инструкций для осуществления операций с плавающей запятой (все процессоры)

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

FLD QWORD PTR [ESI] / FSTP QWORD PTR [EDI]

МОЖНО заменить на:

MOV EAX, [ESI] / MOV EBX, [ESI + 4] / MOV [EDI],EAX / MOV [EDI+4],EBX

6.27.6.1. Тестирование, не равно ли значение с плавающей запятой нулю. Значение с плавающей запятой, равное нулю, обычно представляется, как 32 или 64 нулевых бита, но здесь есть один подводный камень: бит знака может быть равен нулю! Минус ноль считается правильным числом с плавающей запятой, и процессор может сгенерировать ноль с уставноленным битом знака, если, например, отрицательное число было умножено на ноль. Чтобы узнать, не равно ли число с плавающей запятой нулю, следует тестировать знаковый бит:

FLD DWORD PTR [ЕВХ] / FTST / FNSTSW АХ / AND АН,40Н / JNZ IsZerc

Вместо этого, можно использовать целочисленные инструкции:

MOV ЕАХ, [ЕВХ] / ADD ЕАХ,ЕАХ / JZ IsZero Если число с плавающей запятой имеет двойную точность (QWORD), тогда нужно протестировать только биты 32-62. Если они равны нулю, тогда нижняя половина будет также равна нулю, если это правильноеное число с плавающей запятой.

6.27.6.2. Тестирование на неотрицательное значение. Число с плавающей запятой отрицательно, если установлен бит знака и, по крайней мере, один произвольный бит:

MOV ЕАХ, [NumberToTest] / СМР ЕАХ,8 О О О О О О ОН / JA IsNegative

6.27.6.3. Манипулирование битом знака. Можно изменить знак числа с плавающей запятой, просто инвертировав бит знака:

XOR BYTE PTR [а] + (TYPE а) - 1, 80Н

Похожим образом можно получить асбсолютное значение числа с плавающей запятой, просто сбросив бит знака в 0.

; скопировать бит знака ; убрать бит знака

6.27.6.5. Сравнение чисел с плавающей запятой. Числа с плавающей запятой хранз g особом формате, который позволяет использовать целочисленные инструкции сравнения чисел с плавающей запятой, не считая, бита знака. В случае если два cpai раемые числа с плавающей запятой являются корректными и положительными, мо: просто сравнить их как два целых:

FLD [а] / FCOMP [Ь] / FNSTSW АХ / AND АН,1 / JNZ ASmallerThanI

Можно заменить на:

MOV ЕАХ, [а] / MOV ЕВХ,[Ь] / СМР ЕАХ,ЕВХ / JB ASma11егThanB " Этот метод работает только, если у обоих чисел одинаковая точность, и.ни у одв

из них не установлен бит знака.

В случае отрицательных чисел их можно сконвертировать определенным обра

и сделать знаковое сравнение:

MOV ЕАХ, [а]

MOV ЕВХ, [Ь]

MOV ЕСХ, ЕАХ

MOV EDX, ЕВХ

SAR ЕСХ, 31

AND ЕАХ, 7FFFFFFFH

SAR EDX, 31

AND ЕВХ, 7FFFFFFFH

XOR ЕАХ, ЕСХ ; преобразуем, если установлен бит знг

XOR ЕВХ, EDX

SUB ЕАХ, ЕСХ

SUB ЕВХ, EDX

СМР ЕАХ, ЕВХ

JL ASmallerThanB ; знаковое сравнение Этот метод работает для всех правильных чисел с плавающей запятой, включая -0.

6.27.7. Использование инструкции с плавающей

запятой, чтобы осуществлять целочисленные операции (Р1 и РММХ)

6.27.7.1. Целочисленное умножение (Р1 и РММХ). Умножение чисел с плаваю! запятой выполняется быстрее, чем целочисленное умножение, на Р1 и РММХ. Но l Конверсии целых чисел в числа с плавающей запятой и конвертирование резулы Обратно в целое число очень высока, поэтому умножение с плавающей запятой ш смысл только тогда, когда количество требуемых преобразований мало по сравнет



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

На РММХ инструкш1и умножения ММХ быстрее, чем целочисленное умножение и могут конвейеризоваться, поэтому одним из лучших решений на РММХ может быть использование этих инструкций, если достаточно 16-ти битной точности.

Целочисленное умножение выполняется быстрее, чем умножение с плавающей запятой на РРго, Р2 и РЗ.

6.27.7.2. Целочисленное деление (Р1 и РММХ). Деление с плавающей запятой не быстрее, чем целочисленное деление, но возможно параллельное выполнение целочисленных операций (включая целочисленное деление, но не целочисленное умножение), в то время как работает FPU - занимается выполнением деления.

6.27.7.3. Конвертирование двоичных чисел в десятичные (все процессоры). Использование инструкции FBSTP - простой и удобный способ конвертировать двоичные числа в десятичные, хотя не самый быстрый.

6.27.8. Пересылка блоков данных (все процессоры)

Есть несколько способов пересьшки блоков данных. Наиболее общий метод - это REP MOVSD, но при определенных условиях другие методы оказываются быстрее.

На Р1 и РММХ быстрее переместить 8 байтов за раз, если место назначения не находится в кэше следующим образом:

ТОР :

ТОР;

FILD

QWORD PTR

[ESI]

FILD

QWORD PTR

[ESI +

FXCH

FISTP

QWORD PTR

[EDI]

FISTP

QWORD PTR

[EDI +

ESI, 16

EDI; 16

Источник и место назначения должны быть выравнены на 8. Дополнительное время, используемое медленными инструкциями FILD и FISTP компенсируется уменьшенным в два раза числом операций записи. Но этот метод имеет преимущество только на Р и РММХ и только тогда, когда место назначения не находится в кэше первого уровня-Невозможно использовать FLD и FSTP (без I), потому что ненормализованные числа обрабатываются медленно и не гарантируется, что они останутся неизмененными.

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

MOVQ

MOVQ

MMO,[ESI] [EDI],MMO ESI, 8 EDI, 8 ECX TOP

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

На процессорах РРго, Р2 и РЗ инструкция REP MOVSD особенно быстра, есл! соблюдены следующие условия:

источник и назначение должны быть выравнены на 8;

направление пересылки "вперед" (очищен флаг направления, инструкция CLD); счетчик (ЕСХ) должен быть больше или равен 64; разность между EDI и ESI должна быть больше или равна 32.

На Р2 выгоднее использовать регистры ММХ, если вышеприведенные условия н( соблюдены или место назначения находится в кэше первого уровня. Цикл можно развер нугь в два раза, а источник и назначение должны быть выравнены на 8.

На РЗ самый быстрый путь пересылки данных - использовать инструкцию MOVAPS если вышеприведенные условия не соблюдены или если место назначения не находите: в кэше первого или второго уровня.

ТОР:

MOVAPS

MOVAPS

EDI, ESI XMMO, [ESi; [ESI+EDI], ESI, 16 ECX TOP

XMMO

в отличии от FLD, MOVAPS может обрабатывать любую поледовательность бито без всяких проблем, но источник и назначение должны быть выравнены на 16.

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

На РЗ также есть опция прямой записи в RAM-память без вовлечения в эту операцш оша, используя инструкцию MOVNTQ или MOVNTPS. Это может быть полезным длятс , чтобы место назначения не попало в кэш. MOVNTPS чуть-чуть быстрее, чем MOVNTC



Ассемблер в задачах защиты информации

6.27.9. Самомодифицирующийся код (все процессоры)

Потери при выполнении кода сразу после того, как тот был изменен, занимают примерно 19 тактов на Р1, 31 на РММХ и 150-300 на РРго, Р2 и РЗ. Процессоры 80486 и более ранние требуют переход между модифицирующим и модифицируемым кодом, чтобы очистить кэш кода.

Чтобы получить разрешение на модифицирование кода в защищенной операционной системе, требуется вызвать специальные системные функции: в 16-битной Windows это ChangeSelector, в 32-битной Windows - VirtualProtect и FlushlnstructionCache (или поместить код в сегмент данных).

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

6.27.10. Определение типа процессора (все процессоры)

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

; задаем инструкцию CPUID, если она не известна ассемблеру; CPUID MACRO

DB OFH, 0A2H

ENDM

Прототип С++:

extern "С" long int DetectProcessor (void); возвращаемое значение:

bits 8-11 = семья (5 для PI и РММХ, 6 для РРго, Р2 и РЗ) bit О = поддерживаются инструкции FPU

bit 15 = поддерживаются условные переходы и инструкция FCOMI bit 23 = поддерживаются инструкции ММХ bit 25 = поддерживаются инструкции ХММ DetectProcessor PROC NEAR PUBLIC DetectProcessor PUSH EBX PUSH ESI

ГС?.1;..°."У..*::."..Т.!..9..Т семейства Pentium 477

PUSH

PUSH

; определяем, поддержив

PUSHED

EBX, EAX

EAX, 1 SHL 21

PUSH

POPFD

PUSHED

EAX.

EAX, EBX

EAX, 1 SHL 21

SHORT DPEND

EAX, EAX

CPUID

TEST

EAX, EAX

SHORT DPEND

EAX, 1

CPUID

EAX, OOOOOOFOOH

EDX, OFFFFFOFFH

EAX, EDX

проверяем, можно ли изменять бит CPUID

; инструкция CPUID не поддерживается

; получаем количество функций CPUID

; функция 1 CPUID не поддерживается

получаем семью и особенности процессора

; семья

; флаги особенностей

; комбинируем биты

DetectProcessor ENDP

Следует обратить внимание, что некоторые операционные системы не позволяют использовать инструкции ХММ. Информация о том, как узнать, поддерживает ли операционная система инструкции ХММ, можно найти в интеловской инструкции АР-900: "Identifying support for Streaming SIMD Extensions in the Processor and Operating System". Больше информации о идентификации процессора можно найти в инструкции АР-485: "Intel Processor Identification and the CPUID Instruction".

Если ассемблер не поддерживает инструкции ММХ, ХММ, условной пересылки данных, можно использовать специальные макросы

(например, www.agner.org/assem/macros.zip).



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