какой метод выполняется быстрее на языке ассемблера: добавление по переменным или добавление по непосредственному значению? Зачем? - PullRequest
4 голосов
/ 07 января 2012

Например:

; Method 1
.data
val1 DWORD 10000h
.code
add eax,val1

v.s:

; Method 2
.code
add eax,10000h

Какой метод будет выполняться быстрее после компиляции (сборки)? Я думаю, что метод 2 будет генерировать более быстрый код, потому что ЦПУ не нужно будет считывать значение из основной памяти перед добавлением в регистр eax. Я не очень ясно в своем ответе, кто-нибудь может помочь?

Ответы [ 4 ]

5 голосов
/ 07 января 2012

По всей вероятности, это будет зависеть от ситуации, и разница может даже не быть заметной.

Такие факторы, как исполнение не по порядку , скорее всего,скрыть любую присущую «медлительность» любой из версий, если только на самом деле нет узкого места.

Тем не менее, если бы нам пришлось выбирать, что быстрее, то вы правы, что второй случай, скорее всего, будет быстрее.

Если мы посмотрим на таблицы Агнера Фога для всех текущих процессоров x86:

Core 2:

add/sub    r, r/i    Latency = 1        , 1/Throughput = 0.33
add/sub    r, m      Latency = unknown  , 1/Throughput = 1

Нехалем:

add/sub    r, r/i    Latency = 1        , 1/Throughput = 0.33
add/sub    r, m      Latency = unknown  , 1/Throughput = 1

Песчаный мост:

add/sub    r, r/i    Latency = 1        , 1/Throughput = 0.33
add/sub    r, m      Latency = unknown  , 1/Throughput = 0.5

K10:

add/sub    r, r/i    Latency = 1        , 1/Throughput = 0.33
add/sub    r, m      Latency = unknown  , 1/Throughput = 0.5

Во всех случаях версия с операндом памяти имеет меньшую пропускную способность.Задержка неизвестна во всех случаях, но почти наверняка будет более 1 цикла.Так что это хуже во всех отношениях.

Версии операндов памяти используют все те же порты выполнения, что и непосредственная версия +, для этого также требуется порт чтения памяти.Это может только ухудшить ситуацию.Фактически, именно поэтому пропускная способность ниже с операндом памяти - порты памяти могут выдерживать только 1 или 2 чтения / цикл, тогда как сумматор может поддерживать полный 3 / цикл.

Кроме того, это предполагает, что данныенаходится в кеше L1.Если это не так, то версия операнда памяти будет НАМНОГО медленнее.


Сделав еще один шаг вперед , мы можем проверить размерзакодированные инструкции:

add eax,val1    ->   03 05 14 00 00 00
add eax,10000h  ->   05 00 00 01 00

Кодировка для первого может немного отличаться в зависимости от адреса val1.Примеры, которые я показал здесь, взяты из моего конкретного тестового примера.

Таким образом, версия доступа к памяти требует дополнительного байта для кодирования - что означает немного больший размер кода - и, возможно, больше i-cache пропускает приextreme.


Итак, в заключение, если существует разница в производительности между версиями, вполне вероятно, что немедленная версия будет быстрее, потому что:

  1. Она имеет меньшую задержку.
  2. У него более высокая пропускная способность.
  3. У него более короткая кодировка.
  4. Ему не требуется доступ к кэшу данных, что потенциально может привести к потере кеша.
3 голосов
/ 07 января 2012

10000h будет считываться из памяти независимо от того, что - либо из ее местоположения в памяти данных, либо из его местоположения в памяти команд. Для меньших постоянных значений CPU предоставляют специальные инструкции, которые не требуют дополнительного пространства для добавляемого значения, но это зависит от конкретной архитектуры. Немедленное добавление, вероятно, будет быстрее из-за кэширования: ко времени декодирования инструкции константа будет в кеше, и сложение будет очень быстрым.

Небольшое замечание, не относящееся к теме: в вашем примере показан случай, когда оптимизирующий компилятор C генерирует более быстрый код, чем рукописная сборка: вместо добавления 10000h оптимизатор может увеличивать верхнее половинное слово на единицу и оставлять нижнее слово как есть.

3 голосов
/ 07 января 2012

Добавление немедленного (вашего магического шестнадцатеричного значения) действительно быстрее (на архитектурах, о которых я знаю, по крайней мере).

Я думаю, вопрос в том, сколько.Теперь я считаю, что это зависит от того, кэшируется ли val1 или нет.

Если он НЕ кешируется, он ОЧЕНЬ медленный, поскольку доступ к памяти происходит намного медленнее, чем доступ к кешу (независимо от уровня, кеш l1 действительно самый быстрый).

Если он действительно кешируется,результаты, по моему скромному мнению, довольно близки друг к другу.

0 голосов
/ 07 января 2012

Я некоторое время не делал сборку, но я считаю, что этот код не эквивалентен.

В методе 1 вы добавляете адрес val1 в eax, в методе 2 добавляете постоянное значение 10000hto eax ... Чтобы добавить содержимое переменной, вам нужно будет сделать

add eax,[val1]

, и это будет медленнее, потому что это вызовет чтение из памяти.И этот код может даже не быть законным.Разве вы не должны делать что-то вроде:

mov ecx, val1
add eax, [ecx]

Как я уже сказал, моя сборка Intel довольно ржавая:)

...