Более эффективно касаться меньшего количества регистров в сборке ARM? - PullRequest
0 голосов
/ 06 февраля 2019

Я только начал изучать ассемблер с помощью Raspbian, и у меня возник вопрос: насколько эффективно экономить место на регистре в ассемблере?Например, если я хотел бы сделать быстрое добавление, есть ли значимая разница в

mov r1, #5
mov r2, #3
add r1, r1, r2

и

mov r1, #5
mov r2, #3
add r3, r1, r2     @ destination in a new register that wasn't previously used

(кроме хранения в разных регистрах)?

Ответы [ 3 ]

0 голосов
/ 06 февраля 2019

Использование того же регистра для вывода, что и для ввода, не имеет присущего недостатка для ARM 1 .Однако я не думаю, что в этом есть и какое-то внутреннее преимущество. В общем случае все может стать более интересным, когда мы говорим о записи регистров, которых инструкция уже не должна ждать (т. Е. Не вводит).

Используйте столько регистров, сколько необходимо для сохранения инструкций.( Имейте в виду соглашение о вызовах, хотя : если вы используете больше, чем r0..r3, вам придется сохранять / восстанавливать дополнительные, которые вы используете, если вы хотите вызывать свою функцию из C).В частности, обычно оптимизировать для наименьшего динамического количества команд;выполнение дополнительной настройки / очистки для сохранения инструкций внутри циклов обычно того стоит.

И не только для сохранения инструкций: программная конвейеризация для скрытия задержки загрузкипотенциально ценный на конвейерных процессорах исполнения по порядку.например, если вы зацикливаетесь на массиве, загрузите в регистр значение, которое вам понадобится через 2 итерации, и не трогайте его до тех пор.(Разверните цикл).Процессор по порядку может только запускать инструкции по порядку, но они могут потенциально завершаться не по порядку.например, загрузка, которая отсутствует в кэше, не останавливает процессор, пока вы не попытаетесь прочитать его, когда он не готов.Я думаю, вы можете предположить, что высокопроизводительные процессоры с порядком работы, такие как современные ARM, будут иметь все необходимые табло для отслеживания того, какие регистры ожидают готовности ALU или результата загрузки.

Без фактического перехода на полное программное обеспечение.При конвейерной обработке вы можете иногда получить аналогичные результаты, выполняя блок нагрузок, затем вещи, а затем блок магазинов.например, memcpy, оптимизированный для больших копий, может загрузить 12 регистров в своем основном развернутом цикле, а затем сохранить эти 12 регистров.Таким образом, расстояние между загрузкой и хранилищем одного и того же регистра все еще достаточно велико, чтобы скрыть задержку загрузки кэша L1 как минимум.


Текущие (?) Платы Raspberry Pi ( RPi 3+ ) использовать ARM Cortex-A53 , суперскалярную микроархитектуру порядка 2 в ширину.

Любое ядро ​​ARM (например, Cortex-A57), которое превосходитдля выполнения заказа будет использоваться переименование регистра , чтобы исключить опасность WAW (запись после записи) и WAR.(https://en.wikipedia.org/wiki/Hazard_(computer_architecture)#Data_hazards).

На обычном ядре, таком как A53, WAR, безусловно, не проблема: более поздняя инструкция не может записать регистр до того, как более ранняя инструкция сможет прочитать оттуда свой операнд..

Но опасность WAW может ограничить способность ЦП запускать две инструкции одновременно . Это будет актуально только при написании регистра, который вы еще не прочитали. add r1, r1, r2приходится ждать, пока r1 будет готов, прежде чем он сможет начать выполнение, потому что это ввод.

Например, если бы у вас был этот код, мы могли бы на самом деле увидеть отрицательную производительностьЭффект от записи одного и того же выходного регистра в 2-х инструкциях, которые могут выполняться в одном и том же цикле. Я не знаю, как Cortex-A53 или любой другой ARM по порядку справляется с этим, но другой процессор с двумя выпусками по порядку (Intel P5 Pentiumс 1993 г.) не объединяет инструкции, записывающие в один и тот же регистр ( Руководство xarch uarch Agner Fog для x86 ). 2-й должен ждать цикл перед запуском (но может быть pair с инструкцией после этого).

@ possible WAW hazard
adds  r3, r1, r2      @ set flags, we don't care about the r3 output
add   r3, r1, #5      @ now actually calculate an integer result we want

Если бы вы использовали другой фиктивный регистр вывода, они оба могли бы начаться в одном и том же тактовом цикле. (Или если бы выиспользуйте cmn r1, r2 (сравнение с отрицанием), вы могли бы установить флаги с r1 - (-r2) без записи вывода вообще, что в соответствии с руководством такое же, как установка флагов с r1 + r2.) Новозможно, есть какой-то случай, который вы можете придумать, который нельзя заменить инструкцией cmp, cmn, tst (ANDS) или teq (EORS).

Я ожидаю, что ARM не по порядку может переименовывать один и тот же регистр несколько раз за один и тот же цикл (процессоры OoO x86 могут это сделать), чтобы полностью избежать опасностей WAW.


Мне не известно о какой-либо микроархитектурной выгоде, когда некоторые регистры остаются "холодными".

На ЦП с переименованием регистров, обычно это делается с помощью физического файла регистров и даже безНедавно измененный архитектурный регистр (например, r3) будет нуждаться в записи PRF для хранения значения любой инструкции, в которой он был последний раз написан, независимо от того, как давно это было.Таким образом, написание регистра всегда выделяет новый физический регистр и (в конце концов) освобождает физический регистр, содержащий старое значение.Независимо от того, было ли старое значение также только что записано, или оно долгое время имело это значение.

В семействе Intel P6 действительно использовался «файл регистров выхода на пенсию», в котором состояние выхода на пенсию хранится отдельно от «живых» значенийв нерабочем состоянииНо он сохранил эти значения в реальном регистре прямо в ROB с порождающим их uop (вместо ссылки на запись PRF), поэтому он не мог исчерпать физические регистры для переименования до того, как серверная часть была заполнена.См. http://blog.stuffedcow.net/2013/05/measuring-rob-capacity/ дополнительные интересные эксперименты с процессором x86, измеряющие пределы ROB и PRF для размера окна не по порядку для других процессоров x86, которые используют PRF.

Фактически из-за ограниченного чтенияпорты в файле регистров выхода на пенсию семейства P6 (PPro через Nehalem) могут фактически зависать при чтении слишком большого количества регистров, которые не были записаны в последнее время, в одной группе проблем.(См. Руководство по микроархам Agner Fog, зарегистрируйте прочитанные киоски.) Но я не думаю, что это типичная проблема для других уарчей, как и для любых вышедших из строя ядер ARM.Установите константы / инварианты циклов в регистрах вне циклов и свободно используйте их внутри.


Сноска 1 : это, как правило, верно для всех архитектур, но есть исключения.Единственный известный мне случай - довольно особый случай: на последних процессорах Intel x86 (в 64-битном режиме) mov eax, eax (задержка 1 цикла) медленнее mov ecx, eax (задержка 0 циклов) для усечения 64-битного регистрадо 32 бит, потому что mov-исключение работает только между разными регистрами.( Может ли MOV x86 действительно быть "бесплатным"? Почему я вообще не могу воспроизвести это? )

0 голосов
/ 06 февраля 2019

для arm эффективность в основном зависит от соглашения о вызовах, вне нормального конвейерного материала (добавляет xx, r1, r2, которые нужно остановить, чтобы завершить mov r2, xx).

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

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

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

0 голосов
/ 06 февраля 2019

Риск быть сбитым кем-то, кто знает гораздо больше о теоретических аспектах, использование большего количества регистров может быть более быстрым - это одна из причин, почему на архитектуре есть необходимость включить больше регистров (сравните T32 / A32 /A64 для диапазона адресуемых регистров ядра по мере увеличения стоимости реализации архитектуры.

На уровне архитектуры все регистры ядра эквивалентны (при условии, что код операции может их адресовать) - т.е. некоторые инструкции могут разрешать только доступк нижним 8 регистрам.

На микроархитектурном уровне было бы очень необычно отдавать предпочтение некоторым регистрам.Одним из примеров преференциального режима на архитектурном уровне, ARMv7-M и связанных с ним, является поведение push / pop с исключением.Компилятор может довольно легко воспользоваться преимуществами этой оптимизации (избегая вставки некоторого промежуточного кода).

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

В вашем примере ваш первый фрагмент кода явно указывает ЦПУ, что первое значение r1 никогда не будет использовано в будущем.Во втором фрагменте кода вы оставили r1 == 5 вид блокировки на оставшееся время - нет способа заглянуть в будущее и предсказать, будете ли вы когда-либо использовать это снова.

Итак:

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

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

...