2016-12-03 6 views
3

Почему вы хотите использовать:MUL/DIV инструкции против MOV и SHL/SHR (Pentium Pro)

MOV EAX, 22 
SHL EAX, 2 

... при умножении на 4 против просто используя MUL инструкции?
Я понимаю, что это также можно сделать с помощью SHR вместо DIV.

В чем преимущества этого?
Также вы можете сделать это с нечетными числами или это могут быть только четные числа?

+1

думаю в базе 10, сдвигая влево/вправо, чтобы умножить на силы 10, намного быстрее, чем выполнение реального умножения (и никто этого не делает). То же самое относится к умножению на мощность базы в любых базах. –

+1

Чтобы узнать больше о том, что быстро в asm, см. [X86 tag wiki] (http://stackoverflow.com/tags/x86/info), особенно [Agner Путеводители тумана] (http://agner.org/optimize). См. Также [этот ответ, который я написал] (http://stackoverflow.com/questions/40354978/why-is-this-c-code-faster-than-my-hand-written-assembly-for-testing-the-collat/40355466 # 40355466) о том, насколько быстро сдвиг и LEA сравниваются с DIV. Современные процессоры Intel имеют чрезвычайно высокопроизводительное многопользовательское оборудование (например, задержка в 3 цикла, по одному на 1 с пропускную способность «imul r64, r64'), но немедленные смены еще быстрее (1 с, 2 часа за такт). –

+1

Почему «Pentium Pro» играет значительную роль в этом вопросе? а) не упоминается в органе вопроса, б) они давно устарели, в) ответ относительно стабилен и полезен в современных архитектурах. Удалить из названия вопроса? –

ответ

3

Использование инструкции SHL/SHR, как правило, намного быстрее, чем MUL/DIV.

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

Например: следующий код умножает на 5 без использования MUL инструкции:

mov num, 5 
mov eax, num 
mov ebx, num 
shl eax, 2 ; MULs by 4 
add eax, ebx ; ADD the x1 to make = 5 
+0

Brain fart .. это полностью делает с тех пор. Я пытался усложнить использование 'SHL' с помощью float. – LearningProcess

+3

Сколько циклов сдвиг зависит от модели cpu, но он не был 1 такт на бит в течение длительного времени (если он когда-либо был). Он не спрашивал об умножении на 5, и вы использовали 'ADD' там: P – Jester

+1

Шут вы можете ответить? Я хотел бы посмотреть, что вы скажете. Нечего грубить парням в комментариях. – LearningProcess

5

Есть целый ряд кодовых идиомы, которые быстрее, чем «MUL константы».

Современные процессоры x86 выполняют MUL в несколько часов, минимум. Таким образом, любая последовательность кода, которая вычисляет продукт в 1-2 часах, будет превосходить MUL. Вы можете использовать быстрые инструкции (ADD, SHL, LEA, NEG) и тот факт, что процессор может выполнять некоторые из этих инструкций параллельно за один такт, чтобы заменить MUL. Возможно, это означает, что вы можете выполнить 4 из этих инструкций во многих комбинациях за 2 такта, если избегаете некоторых зависимостей данных.

Инструкция LEA особенно интересна тем, что она может умножаться на некоторые небольшие константы (1,2,3,4,5,8,9), а также перемещать продукт в другой регистр, что является одним из простых способов разбить зависимостей данных. Это позволяет вам вычислить субпродукт, не разрушая исходный операнд.

Некоторые примеры:

Умножить EAX на 5, перемещать продукт ESI:

LEA ESI, [EAX+4*EAX] ; this takes 1 clock 

Multiply EAX на 18:

LEA EAX, [EAX + 8*EAX] 
    SHL EAX, 1 

Multiply EAX на 7, результат перейти к EBX:

LEA EBX, [8*EAX] 
    SUB EBX, EAX 

Multiply EAX на 28:

LEA EBX, [8*EAX] 
    LEA ECX, [EAX+4*EAX] ; this and previous should be executed in parallel 
    LEA EAX, [EBX+4*ECX] 

Умножаем на 1020:

LEA ECX, [4*EAX] 
    SHL EAX, 10   ; this and previous instruction should be executed in parallel 
    SUB EAX, ECX 

Умножить на 35

LEA ECX, [EAX+8*EAX] 
    NEG EAX    ; = -EAX 
    LEA EAX, [EAX+ECX*4] 

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

Замечательно, как много многозначных констант могут быть созданы таким образом. Вы могли бы подумать, что это полезно только для очень маленьких констант, но, как вы можете видеть из примера 1020 выше, вы можете получить некоторые удивительно средние размеры. Это оказывается очень удобно при индексировании в массивы-структуры, потому что вам нужно умножить индекс на размер структуры. Часто при индексировании массива, подобного этому, вы хотите вычислить адрес элемента и получить значение; в этом случае вы можете объединить заключительную инструкцию LEA в инструкцию MOV, которую вы не можете сделать с реальным MUL. Это купит вам дополнительный такт (ы), в который нужно выполнить MUL с помощью этого типа идиомы.

[Я построил компилятор, который вычисляет «лучшее умножение на константу» с использованием этих инструкций, выполняя небольшой исчерпывающий поиск комбинаций команд; он затем кэширует этот ответ для последующего повторного использования].

+2

'imul r, r/m, imm32' довольно хорошо, как mov-and-multiply. На современных процессорах Intel он имеет только 3 задержки цикла (даже для 64-разрядного размера операнда) и одну пропускную способность каждого такта. Много множимых констант можно сделать в 2 циклах, хотя, как вы хорошо демонстрируете примеры параллелизма на уровне инструкций. gcc и clang делают то же самое. (clang-3.6 и старше обычно предпочитают IMUL, если он не может выполнять работу только с одним LEA, но современный clang способствует латентности над инструкцией/uop, подсчитывает способ gcc). –

 Смежные вопросы

  • Нет связанных вопросов^_^