Переписываем циклы
Написать более грамотный код можно и в циклах. Стандартная запись с известным числом итераций имеет вид:
uint32_t i = 0;
for (i = 0; i < 127; i++) {}
Очевидно, что для реализации самого цикла на языке ассемблера потребуются следующие три инструкции:
- увеличение
iна 1 (инструкцияADD); - сравнение
iс пороговым значением (127, инструкцияCMP); - и инструкция продолжения цикла, если
i < 127(BCC.N).
Компилятор выведет примерно следующее:
B.N 0x80001f4
ADDS R0, R0, #1
CMP R0, #127
BCC.N 0x80001f2
Однако особенность ARM позволяет реализовать цикл, используя всего две инструкции:
- уменьшение
iна1, которая в то же время будет являться условием цикла; - инструкция продолжения цикла, если
i != 0.
То есть переписать цикл стоит следующим образом:
for (i = 127; i != 0; i--) {}
В таком случае транслятор выдаст следующий ассемблерный код:
B.N 0x8000222
SUBS R1, R1, #1
NE.N 0x8000220
Таким образом можно сэкономить дополнительные 127 операций.
Если число итераций заранее не известно, то поступать стоит следующим образом:
uint32_t n = 127;
for (; n != 0; n--) {
// code here
}
или прибегнуть к конструкции do—while.
n = 127;
do {
// code here
} while (--n != 0);
Если использовать for, проверка n производится перед выполнением тела цикла, а значит, записав цикл через do—while, можно исключить одну лишнюю операцию.
Все эти тонкости зачастую решаются компилятором. Также он способен совершать размотку цикла. Данный метод мы рассматривать не будем.
