LEA или ADD?
Когда я создаю почерк, я обычно выбираю форму
lea eax, [eax+4]
По форме..
add eax, 4
Я слышал, что lea - это "0-часовая" инструкция (например, NOP), а "add" - нет. Однако, когда я смотрю на сборщик, выпущенный сборкой, я часто вижу последнюю форму, используемую вместо первой. Я достаточно умен, чтобы доверять компилятору, так может ли кто-нибудь пролить свет, на котором лучше? Какой из них быстрее? Почему компилятор выбирает последнюю форму над первым?
Ответы
Ответ 1
Одно существенное различие между LEA
и ADD
на процессорах x86 - это исполнительный блок, который фактически выполняет инструкцию. Современные процессоры x86 являются суперскалярными и имеют несколько исполнительных блоков, которые работают параллельно, причем конвейер подает их несколько как круглые (барные стойки). Вещь LEA
обрабатывается (одним из) единицей (адресами), обращающейся с адресацией (которая происходит на ранней стадии в конвейере), а ADD
переходит в ALU (арифметический/логический блок), и в конце трубопровода. Это означает, что суперскалярный процессор x86 может одновременно выполнять LEA
и арифметическую/логическую инструкцию.
Тот факт, что LEA
проходит логику генерации адресов вместо арифметических единиц, также является причиной, по которой ее называли "нулевые часы"; это не требует времени, потому что генерация адресов уже произошла к тому времени, когда он будет/выполнен.
Это не бесплатно, так как генерация адресов - это шаг в конвейере исполнения, но он не получил накладных расходов. И он не занимает слот в трубопроводах ALU.
Изменить: Чтобы уточнить, LEA
не является бесплатным. Даже на процессорах, которые не реализуют его через арифметический блок, требуется время для выполнения из-за команд декодирования/отправки/выхода на пенсию и/или других этапов конвейера, которые проходят все инструкции. Время, затраченное на выполнение LEA
, просто происходит на другом этапе конвейера для процессоров, которые реализуют его с помощью генерации адреса.
Ответ 2
Я достаточно умен, чтобы доверять компилятору, так может ли кто-нибудь пролить свет, на котором лучше?
Да, немного. Во-первых, я беру это из следующего сообщения: https://groups.google.com/group/bsdnt-devel/msg/23a48bb18571b9a6
В этом сообщении разработчик оптимизирует некоторые сборки, которые я написал очень плохо, чтобы работать безумно быстро в процессорах Intel Core 2. В качестве фона для этого проекта это библиотека bsd bignum, в которую я и несколько других разработчиков были вовлечены.
В этом случае все, что оптимизировано, это добавление двух массивов, которые выглядят так: uint64_t* x, uint64_t* y
. Каждая "конечность" или член массива представляет собой часть бигума; основной процесс состоит в том, чтобы перебирать его, начиная с наименее значимой конечности, добавлять пару вверх и продолжать вверх, каждый раз перенося перенос (любое переполнение). adc
делает это для вас на процессоре (невозможно получить доступ к флагом переноса с C, я не думаю).
В этом фрагменте кода используется комбинация lea something, [something+1]
и jrcxz
, которые, по-видимому, более эффективны, чем пара jnz
/add something, size
, которую мы ранее могли использовать. Однако я не уверен, что это было обнаружено в результате простого тестирования разных инструкций. Вы должны спросить.
Однако в более позднем сообщении он измеряется на чипе AMD и не работает так хорошо.
Мне также дано понять, что разные операции выполняются по-разному на разных процессорах. Я знаю, например, что проект GMP обнаруживает процессоры, использующие cpuid
и проходящие в разных сборочных процедурах на основе разных архитектур, например. core2
, nehalem
.
Вопрос, который вы зададите себе, заключается в том, что ваш компилятор производит оптимизированный вывод для вашей архитектуры процессора? Известно, что компилятор Intel, как известно, делает это, поэтому может стоить оценить производительность и посмотреть, какой результат он производит.
Ответ 3
LEA не быстрее, чем команда ADD, скорость выполнения одинакова.
Но LEA иногда предлагает больше, чем ADD.
Если нам нужно простое и быстрое добавление/умножение в сочетании со вторым регистром, то LEA может ускорить выполнение программы.
С другой стороны LEA не влияет на флаг CPU, поэтому нет возможности обнаружения переполнения.
Ответ 4
Вы можете выполнить инструкцию lea в том же такте, что и операция добавления, но если вы используете lea и добавляете вместе, вы можете выполнить добавление трех операндов всего за один цикл! Если вы будете использовать две операции добавления, которые могут выполняться только в 2 тактовых циклах:
mov eax, [esp+4] ; get a from stack
mov edx, [esp+8] ; get b from stack
mov ecx, [esp+12] ; get c from stack
lea eax, [eax+edx] ; add a and b in the adress decoding/fetch stage of the pipeline
add eax, ecx ; Add c + eax in the execution stage of the pipeline
ret 12