LEA или ADD?
Когда я создаю почерк, я обычно выбираю форму
lea eax, [eax+4]
По форме ..
add eax, 4
Я слышал, что lea – это «0-часовая» инструкция (например, NOP), а «add» – нет. Однако, когда я смотрю на сборщик, выпущенный сборкой, я часто вижу последнюю форму, используемую вместо первой. Я достаточно умен, чтобы доверять компилятору, так может ли кто-нибудь пролить свет, на котором лучше? Какой из них быстрее? Почему компилятор выбирает последнюю форму над первой?
- Какие целые операции с дополнением 2 можно использовать без обнуления высоких бит в входах, если требуется только низкая часть результата?
- Цикл с вызовом функции быстрее, чем пустой цикл
- Assembly x86 Date to Number - разбиение строки на более мелкие разделы
- Что такое инструкции IN & OUT для x86?
- Сколько байтов вводит push-команду в стек, если я не укажу размер операнда?
- Ошибка Weird MSC 8.0: «Значение ESP не было должным образом сохранено в вызове функции ...»
- Как читать и писать регистры x86 флаги напрямую?
- Какова цель инструкции LEA?
- Почему x86 маленький endian?
- Атомные операции, std :: atomic и упорядочение записи
- Как разобрать 16-разрядный код загрузочного сектора x86 в GDB с помощью «x / i $ pc»? Он рассматривается как 32-битный
- Как планируется x86 uops?
- Прямой просмотр программы
Одно существенное различие между LEA
и ADD
на процессорах x86 – это исполнительный блок, который фактически выполняет инструкцию. Современные процессоры x86 являются суперскалярными и имеют несколько исполнительных блоков, которые работают параллельно, причем конвейер подает их несколько как круглые (барные стойки). Дело в том, что LEA
обрабатывается (одним из) единицей (адресами), касающейся адресации (что происходит на ранней стадии в конвейере), тогда как ADD
переходит к ALU (арифметическим / логическим единицам) и в конце трубопровод. Это означает, что суперскалярный процессор x86 может одновременно выполнять LEA
и арифметическую / логическую инструкцию.
Тот факт, что LEA
проходит логику генерации адресов вместо арифметических единиц, также является причиной, по которой ее называли «нулевые часы»; это не требует времени для выполнения, потому что генерация адресов уже произошла к моменту его выполнения /.
Это не бесплатно , поскольку генерация адресов является шагом в конвейере выполнения, но на него не наложен накладные расходы. И он не занимает слот в трубопроводах ALU.
Изменить: Чтобы уточнить, LEA
не является бесплатным . Даже на процессорах, которые не реализуют его через арифметический блок, требуется время для выполнения из-за команд декодирования / отправки / выхода на пенсию и / или других этапов конвейера, которые проходят все инструкции. Время, затраченное на выполнение LEA
просто происходит на другом этапе конвейера для процессоров, которые реализуют его посредством генерации адресов.
Я достаточно умен, чтобы доверять компилятору, так может ли кто-нибудь пролить свет, на котором лучше?
Да, немного. Во-первых, я беру это из следующего сообщения: https://groups.google.com/group/bsdnt-devel/msg/23a48bb18571b9a6
В этом сообщении разработчик оптимизирует некоторые сборки, которые я написал очень плохо, чтобы работать безумно быстро в процессорах Intel Core 2. В качестве фона для этого проекта, это библиотека bsd bignum, в которую я и несколько других разработчиков были вовлечены.
В этом случае все, что оптимизируется, это добавление двух массивов, которые выглядят так: uint64_t* x, uint64_t* y
. Каждая «конечность» или член массива представляет собой часть бигума; основной процесс состоит в том, чтобы перебирать его, начиная с наименее значимой конечности, добавлять пару вверх и продолжать вверх, каждый раз перенося перенос (любое переполнение). adc
делает это для вас на процессоре (невозможно получить доступ к флагом переноса из CI, не думайте).
В этом fragmentе кода используется комбинация lea something, [something+1]
и jrcxz
, которые, по-видимому, более эффективны, чем jnz
/ add something, size
пары, которую мы ранее могли использовать. Однако я не уверен, что это было обнаружено в результате простого тестирования разных инструкций. Вы должны спросить.
Однако в более позднем сообщении он измеряется на чипе AMD и не работает так хорошо.
Мне также дано понять, что разные операции выполняются по-разному на разных процессорах. Я знаю, например, что проект GMP обнаруживает процессоры, использующие cpuid
и проходит в разных сборочных процедурах на основе разных архитектур, например core2
, nehalem
.
Вопрос, который вы задаете себе, заключается в том, что ваш компилятор производит оптимизированный вывод для вашей архитектуры процессора? Известно, что компилятор Intel, как известно, делает это, поэтому, возможно, стоит измерить производительность и посмотреть, какой результат он производит.
LEA не быстрее, чем команда ADD, скорость выполнения одинакова.
Но LEA иногда предлагает больше, чем ADD . Если нам нужно простое и быстрое добавление / умножение в сочетании со вторым регистром, то LEA может ускорить выполнение программы. С другой стороны LEA не влияет на флаг CPU, поэтому нет возможности обнаружения переполнения.
Вы можете выполнить инструкцию 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