八位微控制器的代码优化技巧
在上述的映射文件中,我们了解到库占用了 1K 的宝贵存储器空间。深入查看映射文件,通过 Excel 进行分析后得到了如图 2 所示的结果。我们从图中移出较小的库函数部分。尽管这些函数名称比较晦涩,不过我们可以对照库参考资料逐一了解其含义。首先,ULDIV 是指无符号数的长除法 (long division),而图中第二个则是指长乘法 (long multiplication)。
.map 文件的交叉参考表明我们很幸运:上述函数只用于一个文件中。.lst 文件显示了长除法函数的两种使用情况以及长乘法函数的一种使用情况
glNandDevCapacity = CYAN_NAND_DEV_NUMPAGES_BLOCK * CYAN_NAND_UBLKS_PER_ZONE * (uint32_t)glNandNumZones;
在该特定案例中,我们知道 zone 的数量是一个二进制数,而另两个值为常量。因此,我们可用重复 8 次的左移位 (left shift) 操作替代长乘法:
{
char zoneCtr = glNandNumZones;
glNandDevCapacity = CYAN_NAND_DEV_NUMPAGES_BLOCK * CYAN_NAND_UBLKS_PER_ZONE;
while (zoneCtr)
{
glNandDevCapacity = 1;
zoneCtr >>= 1;
}
}
尽管这个例程相当大,但它仍能减少库的使用并减小代码的整体大小。
掌握比编辑器更多的信息
成熟的 8 位编译器包括代码编写良好、经过优化的库函数。不过,这些函数须考虑到通过对数据的了解可自行处理的一些不常见情况。映射文件中显示的最大库函数就是这样一个很好的例子。调用两次 ULDIV 例程,以获得输入值除以常量后得到的除数和余数:
zn = (adj_lba / CYAN_NAND_UBLKS_PER_ZONE);
glNandRelativeBlkAddr = (adj_lba % CYAN_NAND_UBLKS_PER_ZONE);
由于我们在预期值方面比编译器了解的更多,因此我们可以让编译器不使用庞大的长除法函数,而采用较小的 16 位版本来替代。
{
xdata unsigned char lastNibble = adj_lba 0xf;
adj_lba >>= 4;
zn = ((uint16_t)adj_lba / (uint8_t)CYAN_NAND_UBLKS_PER_ZONE/16));
glNandRelativeBlkAddr = ((uint16_t)adj_lba % (uint8_t) (CYAN_NAND_UBLKS_PER_ZONE/16));
glNandRelativeBlkAddr = (glNandRelativeBlkAddr 4) + lastNibble;
}
激进的的程序优化者甚至可能实现他们自己的二进制长除法例程。
评论