Вопрос

У меня основной вопрос по сборке.

Зачем нам выполнять арифметические операции только с регистрами, если они могут работать и с памятью?

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

Фрагмент 1

.data
    var dd 00000400h

.code

    Start:
        add var,0000000Bh
        mov eax,var
        ;breakpoint: var = 00000B04
    End Start


Фрагмент 2

.code

    Start:
        mov eax,00000400h
        add eax,0000000bh
        ;breakpoint: eax = 0000040B
    End Start



Судя по тому, что я вижу, большинство текстов и руководств выполняют арифметические операции в основном с регистрами.Просто с регистрами работать быстрее?

Редактировать:Это было быстро :)

Было дано несколько отличных ответов;лучший ответ выбирался на основе первого хорошего ответа.

Это было полезно?

Решение

Регистры работают намного быстрее, а операции, которые вы можете выполнять непосредственно с памятью, гораздо более ограничены.

Другие советы

Если вы посмотрите на компьютерную архитектуру, вы обнаружите ряд уровней памяти.Те, что расположены близко к ЦП, являются быстрыми, дорогими (на бит) и, следовательно, маленькими, а на другом конце у вас есть большие, медленные и дешевые устройства памяти.В современном компьютере это обычно что-то вроде:

 CPU registers (slightly complicated, but in the order of 1KB per a core - there
                are different types of registers. You might have 16 64 bit
                general purpose registers plus a bunch of registers for special
                purposes)
 L1 cache (64KB per core)
 L2 cache (256KB per core)
 L3 cache (8MB)
 Main memory (8GB)
 HDD (1TB)
 The internet (big)

Со временем добавлялось все больше и больше уровней кэша — я помню время, когда в процессорах не было встроенного кэша, а я еще даже не стар!В наши дни жесткие диски имеют встроенный кэш, а Интернет кэшируется в любом количестве мест:в памяти, на жестком диске и, возможно, на кеширующих прокси-серверах.

Наблюдается резкое (часто на несколько порядков) снижение пропускная способность и увеличение задержка на каждом шагу от процессора.Например, жесткий диск может читаться со скоростью 100 МБ/с с задержкой 5 мс (эти цифры могут быть не совсем верными), а ваша основная память может читаться со скоростью 6,4 ГБ/с с задержкой 9 нс (шесть порядков величина!).Задержка — очень важный фактор, поскольку вы не хотите заставлять процессор ждать дольше, чем необходимо (это особенно верно для архитектур с глубокими конвейерами, но это обсуждение в другой раз).

Идея состоит в том, что вы часто будете повторно использовать одни и те же данные снова и снова, поэтому имеет смысл поместить их в небольшой быстрый кеш для последующих операций.Это называется временная местность.Еще одним важным принципом локальности является пространственная локальность, в котором говорится, что ячейки памяти, расположенные рядом друг с другом, скорее всего, будут считаны примерно в одно и то же время.Именно по этой причине чтение из ОЗУ приведет к чтению и помещению в кэш процессора гораздо большего блока ОЗУ.Если бы не эти принципы локальности, то любое место в памяти имело бы равновероятную вероятность быть прочитанным в любой момент времени, поэтому не было бы возможности предсказать, к чему будет осуществляться доступ в следующий раз, и все уровни кэша в мире не улучшит скорость.С таким же успехом вы могли бы просто использовать жесткий диск, но я уверен, что вы знаете, что значит, когда компьютер резко останавливается при подкачке страниц (что по сути использует жесткий диск в качестве расширения оперативной памяти).Концептуально возможно не иметь никакой памяти, кроме жесткого диска (а многие небольшие устройства имеют одну память), но это было бы очень медленно по сравнению с тем, с чем мы знакомы.

Еще одним преимуществом наличия регистров (и лишь небольшого их количества) является то, что они позволяют вам иметь более короткие инструкции.Если у вас есть инструкции, содержащие два (или более) 64-битных адреса, у вас будут длинные инструкции!

Доступ к регистрам осуществляется способ быстрее оперативной памяти, поскольку вам не нужен доступ к «медленной» шине памяти!

x86, как и любой другой «обычный» процессор, для которого вы можете изучить ассемблер, является "зарегистрировать машину".Есть и другие способы спроектировать что-то, что можно запрограммировать (например.машина Тьюринга, которая движется по логической «ленте» в памяти), но регистровые машины оказались, по сути, единственным способом достижения высокой производительности.

Поскольку x86 был разработан для использования регистров, вы не можете полностью избежать их, даже если вы этого хотите и не заботитесь о производительности.

Современные процессоры x86 могут читать/записывать за такт гораздо больше регистров, чем ячеек памяти.

Например, Intel Skylake может выполнять две загрузки и одно сохранение из/в свой 8-канальный ассоциативный кэш L1D емкостью 32 КБ за цикл (в лучшем случае), но может считывание более 10 регистров за такт и запись 3 или 4 (плюс EFLAGS).

Создание кэша L1D с таким количеством портов чтения/записи, файл регистрации было бы непомерно дорого (с точки зрения количества/площади транзисторов и энергопотребления), особенно если вы хотели сохранить его таким большим, какой он есть.Вероятно, просто физически невозможно создать что-то, что могло бы использовать память так, как x86 использует регистры с такой же производительностью.

Кроме того, запись регистра и последующее его повторное чтение имеют практически нулевую задержку, поскольку ЦП обнаруживает это и пересылает результат непосредственно с выхода одного исполнительного устройства на вход другого, минуя этап обратной записи.(Видеть https://en.wikipedia.org/wiki/Classic_RISC_pipeline#Solution_A._Bypassing).

Эти соединения для пересылки результатов между исполнительными модулями называются «обходной сетью» или «сетью пересылки», и процессору гораздо проще сделать это для конструкции регистра, чем если бы все должно было идти в память и возвращаться обратно.ЦП должен проверять только номер регистра длиной от 3 до 5 бит вместо 32-битного или 64-битного адреса, чтобы обнаружить случаи, когда выходные данные одной инструкции необходимы сразу в качестве входных данных для другой операции.(И эти номера регистров жестко закодированы в машинном коде, поэтому они доступны сразу.)

Как уже отмечали другие, 3 или 4 бита для адресации регистра делают формат машинного кода намного более компактным, чем если бы каждая инструкция имела абсолютные адреса.


Смотрите также https://en.wikipedia.org/wiki/Memory_hierarchy:вы можете думать о регистрах как о небольшом быстром исправленный размер пространство памяти, отдельное от основной памяти, где поддерживается только прямая абсолютная адресация.(Вы не можете «индексировать» регистр:задано целое число N в одном регистре вы не сможете получить содержимое Nй регистр с одним инсн.)

Регистры также являются частными для одного ядра ЦП, поэтому выполнение вне очереди может делать с ними все, что захочет.Что касается памяти, ему приходится беспокоиться о том, в каком порядке данные становятся видимыми для других ядер ЦП.

Наличие фиксированного количества регистров — это часть того, что позволяет процессорам делать переименование реестра за внеочередное исполнение.Наличие номера регистра, доступного сразу же при декодировании инструкции, также упрощает задачу:никогда не происходит чтения или записи в еще неизвестный регистр.

Видеть Почему на Haswell mulss занимает всего 3 цикла, в отличие от таблиц инструкций Агнера? для объяснения переименования регистров и конкретного примера (более поздние изменения к вопросу/более поздние части моего ответа, показывающие ускорение развертывания с помощью нескольких аккумуляторов, чтобы скрыть задержку FMA, даже если он повторно использует один и тот же архитектурный регистр).


Буфер хранилища с перенаправлением хранилища по сути дает вам «переименование памяти».Сохранение/перезагрузка в ячейку памяти не зависит от предыдущих сохранений и загрузки в эту ячейку из этого ядра.

Повторные вызовы функций с соглашением о вызовах stack-args и/или возврат значения по ссылке — это случаи, когда одни и те же байты памяти стека могут использоваться повторно несколько раз.

Секундное сохранение/перезагрузка может выполняться, даже если первое сохранение все еще ожидает своих входных данных.(Я тестировал это на Skylake, но ИДК, если я когда-либо публиковал результаты где-либо в ответе.)

Мы используем регистры, потому что они быстрые.Обычно они работают на скорости процессора.
Регистры и кэш ЦП выполнены с использованием разных технологий/фабрик и
они дорогие.С другой стороны, оперативная память дешевая и в 100 раз медленнее.

Вообще говоря, регистровая арифметика намного быстрее и предпочтительнее.Однако в некоторых случаях полезна арифметика с использованием прямой памяти.Если все, что вам нужно сделать, это увеличить число в памяти (и ничего больше, по крайней мере, для нескольких миллионов инструкций), то одна арифметическая инструкция с прямой памятью обычно выполняется немного быстрее, чем загрузка/добавление/сохранение.

Кроме того, если вы выполняете сложные операции с массивами, вам обычно нужно много регистров, чтобы отслеживать, где вы находитесь и где заканчиваются ваши массивы.В старых архитектурах регистры могли исчерпаться очень быстро, поэтому возможность сложения двух битов памяти без отключения какого-либо из текущих регистров была действительно полезной.

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

Да, также вы обычно можете легко перемещать/извлекать регистры для вызова процедур, обработки прерываний и т. д.

Просто набор инструкций не позволит проделать такие сложные операции:

add [0x40001234],[0x40002234]

Вам придется пройти через регистры.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top