Цикл с нулевыми накладными расходами - Zero-overhead looping

Цикл с нулевыми накладными расходами это особенность некоторых процессор наборы инструкций чье железо может повторить тело петля автоматически, вместо того, чтобы требовать программных инструкций, которые циклы (и, следовательно, время) сделать это.[1][2] Петли с нулевыми издержками распространены в цифровые сигнальные процессоры и немного CISC наборы инструкций.

Задний план

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

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

Например, следующий код C можно скомпилировать и оптимизировать в следующий код сборки x86:

Код CСборка
беззнаковый int массив[100];беззнаковый int я;для (я = 0; я < 100; я++) {    массив[я] = я;}
; Установите количество итераций цикла.; Обратите внимание, что компилятор полностью изменил цикл; так что он считает в обратном порядке от 99 до 0,; а не от 0 до 99.mov eax, 99.МЕТКА:; array [i] = imov DWORD PTR массив[0+eax*4], eax; Декремент ясуб eax, 1; Проверьте i> = 0. Если это правда, повторите цикл.jnb .МЕТКА

Реализация

Процессоры с циклическим управлением с нулевыми издержками имеют машинные инструкции и регистры для автоматического повторения одной или нескольких инструкций. В зависимости от доступных инструкций, они могут подходить только для циклов с управлением по счетчику («циклы for»), в которых количество итераций может быть вычислено заранее, или только для циклов, управляемых условием («циклы while»), таких как операции на строки с завершающим нулем.

Примеры

ПОС

в Набор инструкций PIC, то ПОВТОРЕНИЕ и ДЕЛАТЬ инструкции реализуют циклы с нулевыми накладными расходами.[1] ПОВТОРЕНИЕ повторяет только одну инструкцию, а ДЕЛАТЬ повторяет указанное количество следующих инструкций.

Blackfin

Blackfin предлагает два контура с нулевыми накладными расходами.[3] Циклы могут быть вложенными; если оба аппаратных цикла сконфигурированы с одним и тем же адресом «конца цикла», цикл 1 будет вести себя как внутренний цикл и повторяться, а цикл 0 будет вести себя как внешний цикл и повторяться, только если цикл 1 не будет повторяться.

Циклы управляются с помощью LTx и LBx регистры (Икс либо от 0 до 1), чтобы установить начало и конец цикла, то есть первую и последнюю инструкции, которые должны быть выполнены, что может быть одинаковым для цикла только с одной инструкцией - и LCx для количества петель. Цикл повторяется, если LCx отличен от нуля в конце цикла, и в этом случае LCx уменьшается.

Регистры цикла можно установить вручную, но обычно это занимает 6 байтов для загрузки регистров и 8–16 байтов для установки значений для загрузки. Чаще всего используется инструкция по настройке цикла (представленная в сборке как ПЕТЛЯ с псевдоинструкцией LOOP_BEGIN и LOOP_END, или в одной строке как LSETUP), который при необходимости инициализирует LCx и устанавливает LTx и LBx до желаемых значений. Для этого требуется всего 4–6 байтов, но можно установить только LTx и LBx в ограниченном диапазоне относительно того, где находится инструкция по настройке цикла.

P0 = массив + 396;R0 = 100;LC0 = R0;ПЕТЛЯ my_loop LC0;   // устанавливает LT0 и LB0LOOP_BEGIN my_loop;   // псевдо-инструкция; генерирует метку, используемую для вычисления LT0// LC0 нельзя записать напрямую в память,// поэтому мы должны использовать временный регистр.R0 += -1;   // одинаково быстро и мало будет R0 = LC0[P0--] = R0;LOOP_END my_loop;   // псевдо-инструкция; генерирует метку, используемую для вычисления LB0

x86

В язык ассемблера x86 REP префиксы реализуют циклы с нулевыми издержками для нескольких инструкций (а именно MOVS / STOS / CMPS / LODS / SCAS).[4] В зависимости от префикса и инструкции инструкция будет повторяться несколько раз с (E) CX удерживая счетчик повторов, или пока не будет найдено совпадение (или несовпадение) с AL / AX / EAX или с DS: [(E) SI]. Это может быть использовано для реализации некоторых типов поиска и операций с строки с завершающим нулем.

использованная литература

  1. ^ а б "Zero Overhead Loops". Получено 2020-08-18.
  2. ^ «Понимание расширенных функций процессора способствует эффективному программированию» (PDF). Аналоговые устройства. Получено 2020-08-18.
  3. ^ «Справочник по программированию процессоров Blackfin, версия 2.2» (PDF). Аналоговые устройства. Февраль 2013. Получено 2020-08-18.
  4. ^ «REP / REPE / REPZ / REPNE / REPNZ: Префикс операции повтора строки (справочник по набору команд x86)». c9x.me. Получено 2020-08-18.