Answers:
代码密度宽松地指执行一个请求的动作需要多少微处理器指令,以及每条指令占用多少空间。一般来说,一条指令占用的空间越少,微处理器可以完成的每条指令的工作量就越大,其代码越密集。
我注意到您已经用'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
(我的ARM有点生锈,但是您知道了)
现在这将是一个非常简单的编译器和一个非常简单的微处理器,但是从示例中您可以看到,每次循环迭代我们查看8条指令(如果将“ 1”移动到另一个寄存器并移动加载,则为7条指令)在循环之外)。这根本不是很密集。代码密度也会影响性能。如果由于代码不密集而导致循环较长,则可能需要更多指令高速缓存来保存循环。更多的缓存意味着更昂贵的处理器,但是复杂的指令解码又意味着更多的晶体管可以解密请求的指令,因此这是一个经典的工程问题。
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模式。现在,几乎所有指令的指令长度为16位,分支的指令长度为32位,而不是每条指令32位。Thumb模式有一些牺牲,但是总的来说,这些牺牲很容易实现,因为Thumb仅仅通过减少指令长度就使代码密度提高了40%。
指令集的“代码密度”是对可以在给定数量的程序存储器中放入多少东西,或者在存储给定数量的功能时需要多少字节程序存储器的度量。
正如Andrew Kohlsmith指出的那样,即使在同一MCU上,不同的编译器也会获得不同的代码密度。
您可能喜欢阅读 Miro Samek撰写的“计算机世界的昆虫”,该书比较了各种MCU。