Ответы:
Плотность кода указывает на то, сколько инструкций микропроцессора требуется для выполнения запрошенного действия, и сколько места занимает каждая инструкция. Вообще говоря, чем меньше места занимает инструкция и чем больше работы, выполняемой микропроцессором, тем плотнее код.
Я заметил, что вы отметили свой вопрос тегом 'arm'; Я могу проиллюстрировать плотность кода, используя инструкции ARM.
Допустим, вы хотите скопировать блок данных из одного места в памяти в другое. Концептуально ваш код высокого уровня будет выглядеть примерно так:
void memcpy(void *dest, void *source, int count_bytes)
{
char *s, *d;
s = source; d = dest;
while(count_bytes--) { *d++ = *s++; }
}
Теперь простой компилятор для простого микропроцессора может преобразовать это в нечто вроде следующего:
movl r0, count_bytes
movl r1, s
movl r2, d
loop: ldrb r3, [r1]
strb [r2], r3
movl r3, 1
add r1, r3
add r2, r3
sub r0, r3
cmp r0, 0
bne loop
(моя рука немного ржавая, но вы поняли)
Теперь это будет очень простой компилятор и очень простой микропроцессор, но вы можете видеть из примера, что мы смотрим 8 инструкций за итерацию цикла (7, если мы переместим '1' в другой регистр и переместим загрузку вне петли). Это не совсем плотно. Плотность кода также влияет на производительность; если ваши циклы длиннее, потому что код не плотный, вам может понадобиться больше кэша инструкций для хранения цикла. Большее количество кеша означает более дорогой процессор, но опять же сложное декодирование команд означает больше транзисторов для расшифровки запрошенной команды, так что это классическая инженерная проблема.
ARM довольно хорош в этом отношении. Каждая инструкция может быть условной, большинство инструкций может увеличивать или уменьшать значение регистров, и большинство инструкций может дополнительно обновлять флаги процессора. В ARM и с умеренно полезным компилятором тот же цикл может выглядеть примерно так:
movl r0, count_bytes
movl r1, s
movl r2, d
loop: ldrb r3, [r1++]
strb [r2++], r3
subs r0, r0, 1
bne loop
Как видите, основной цикл теперь состоит из 4 инструкций. Код более плотный, потому что каждая инструкция в основном цикле делает больше. Как правило, это означает, что вы можете делать больше с заданным объемом памяти, потому что меньше его используется для описания того, как выполнять работу.
Теперь у нативного кода ARM часто была жалоба, что он не был сверхплотным; это связано с двумя основными причинами: во-первых, 32-битная - это очень «длинная» инструкция, поэтому многие биты, по-видимому, тратятся впустую на более простые инструкции, а во-вторых, код был раздутым из-за природы ARM: каждая команда - 32 биты длинные, без исключения. Это означает, что существует большое количество 32-битных литеральных значений, которые вы не можете просто загрузить в регистр. Если я хотел загрузить «0x12345678» в r0, как мне кодировать инструкцию, которая не только содержит 0x12345678, но и описывает «загрузить литерал в r0»? Не осталось битов для кодирования фактической операции. Буквальная инструкция загрузки ARM - интересный маленький зверь, и ассемблер ARM также должен быть немного умнее, чем обычные ассемблеры, потому что он должен «ловить»
Во всяком случае, чтобы ответить на эти жалобы, ARM придумал режим Thumb. Вместо 32 битов на инструкцию длина инструкции теперь составляет 16 битов для почти всех инструкций и 32 бита для ветвей. В режиме Thumb было несколько жертв, но в целом эти жертвы было легко сделать, потому что Thumb принес вам примерно 40% улучшение плотности кода, просто уменьшив длину инструкций.
«Плотность кода» набора команд - это мера того, сколько вещей вы можете поместить в заданный объем программной памяти, или сколько байтов программной памяти вам нужно для хранения заданного объема функциональности.
Как отметил Эндрю Колсмит, даже на одном и том же MCU разные компиляторы могут получать разную плотность кода.
Вам может понравиться чтение «Насекомых компьютерного мира» Миро Самека, в котором сравниваются различные микроконтроллеры.