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