Низкоуровневая оптимизация (original) (raw)

Быстрые умножения и деления. Умножения на степени двойки (2, 4, 8, 16) заменяются логическими сдвигами (соответственно на 1, 2, 3, 4 разрядов влево), инструкцией LEA или последовательными сложениями:

x *= 8;
shl eax, 3 ; Сдвинуть на 3 разряда влево — так делают LCC32, D, MinGW,
; MSVC++ (оптимизация по размеру), FreePascal и BCC
lea ecx, DWORD PTR [eax*8] ; MSVC++ (оптимизация по скорости)
add eax, eax ; eax = x * 2 — Intel C++ Compiler
add eax, eax ; eax = x * 4
add eax, eax ; eax = x * 8
Последние два варианта на современных процессорах работают быстрее, так как инструкции LEA и ADD могут выполняться параллельно с другими инструкциями на разных конвейерах процессора. Некоторые компиляторы умеют представлять умножение на 7 как умножение на 8, а затем вычитание сомножителя. Точно так же умножение x на пять представляется как x*4 + x, которое можно выполнить одной быстрой инструкцией LEA:

x *= 7;
; компиляторы MinGW, MSVC++
lea ecx, DWORD PTR [eax*8] ; Умножить eax на восемь
sub ecx, eax ; Вычесть eax
; компилятор Borland C++
mov edx, eax
shl eax,3 ; Умножить eax на 8, вычесть edx. Слегка медленнее, чем LEA,
sub eax,edx ; так как SHL может выполняться только на U-конвейере
; компиляторы LCC32, Free Pascal и D используют гораздо более медленный вариант:
imul eax, 07
x *= 5;
; компиляторы MinGW, MSVC++, LCC, Borland C++
lea eax, DWORD PTR [eax*4 + eax] ; Быстрый способ умножить eax на 5
; компиляторы D и Free Pascal
imul eax, 05 ; Обычное умножение работает намного медленнее

Деление на константу можно заменить умножением на обратную константу, которое выполняется слегка быстрее. Эту особенность учитывают компиляторы MinGW, MSVC++, LCC:

unsigned int x; // Для чисел со знаком требуется чуть
x /= 3; // более сложный ассемблерный трюк
mov eax, aaaaaaabH ;Записываем в eax число (2^33)/3
mul [ebp-4] ;Часть произведения x * (2^33/3), начиная с 32-го разряда,
;будет записана в edx. В результате edx = (x / 3) * 2
shr edx, 1 ;edx = edx / 2 = x / 3Из-за погрешности при переводе в двоичную систему счисления приходится сохранять один дополнительный разряд. Так, если бы мы умножили 3 на (2^32)/3, в EAX получили бы FF FF FF FF, а в EDX — ноль. Так как мы отбрасываем EAX, в результате деления на три получился бы ноль, а не единица. Поэтому приходится умножать на (2^33)/3, а затем делить на 2.

Музыка: Special Prank - Baby [ПЕРЕЗАГРУЗКА]