Есть целый ряд кодовых идиомы, которые быстрее, чем «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 с помощью этого типа идиомы.
[Я построил компилятор, который вычисляет «лучшее умножение на константу» с использованием этих инструкций, выполняя небольшой исчерпывающий поиск комбинаций команд; он затем кэширует этот ответ для последующего повторного использования].
думаю в базе 10, сдвигая влево/вправо, чтобы умножить на силы 10, намного быстрее, чем выполнение реального умножения (и никто этого не делает). То же самое относится к умножению на мощность базы в любых базах. –
Чтобы узнать больше о том, что быстро в 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 часа за такт). –
Почему «Pentium Pro» играет значительную роль в этом вопросе? а) не упоминается в органе вопроса, б) они давно устарели, в) ответ относительно стабилен и полезен в современных архитектурах. Удалить из названия вопроса? –