3

我正在使用 8 位处理器并在 C 编译器中编写代码,现在 140 多行代码只占用 1200 个字节,而这一行占用了 200 多个字节的 ROM 空间。eeprom_read()是一个函数,这个1000和100和10的乘法应该有问题。

romAddr = eeprom_read(146)*1000 + eeprom_read(147)*100 +
          eeprom_read(148)*10 + eeprom_read(149);

处理器为 8 位,数据类型romAddrint. 有没有办法以更优化的方式编写这一行?

4

4 回答 4

1

你关心的是空间,而不是时间,对吧?你有四个函数调用,每个函数都传递一个整数参数,然后是乘以一个常数,然后是加法。就像第一个猜测一样,这可能是

  1. 将整数常量加载到寄存器中(6 个字节)
  2. 推送寄存器(2字节,
  3. 调用eeprom_read(6 个字节)
  4. 调整堆栈(4 字节)
  5. 将整数乘数加载到寄存器中(6 个字节)
  6. 推送两个寄存器(4 个字节),
  7. 调用乘法例程(6 个字节)
  8. 调整堆栈(4 字节)
  9. 将临时总和加载到寄存器中(6 个字节)
  10. 将乘法的结果(2 个字节)添加到该寄存器
  11. 存储回临时总和(6 个字节)。

让我们看看,每次调用 6+2+6+4+6+4+6+4+6+2+6= 大约 52 个字节eeprom_read。最后一次调用会更短,因为它不进行乘法运算。

我会尝试eeprom_read不使用参数调用,146而是使用(unsigned char)146,而不是乘以,1000而是乘以(unsigned short)1000。这样,您也许可以让编译器使用更短的指令,并且可能使用乘法指令而不是乘法函数调用。此外,调用eeprom_read可能会被宏化为直接内存获取,从而保存参数的推送、函数的调用和堆栈调整。

另一个技巧可能是将四个产品中的每一个存储在一个局部变量中,并在最后将它们加在一起。这可以生成更少的代码。所有这些可能性也会使其更快,更小,尽管您可能不需要关心这一点。

节省空间的另一种可能性是使用循环,如下所示:

static unsigned short powerOf10[] = {1000, 100, 10, 1};
unsigned short i;
romAddr = 0;
for (i = 146; i < 150; i++){
  romAddr += powerOf10[i-146] * eeprom_read(i);
}

这应该通过只调用和乘法一次以及循环指令而不是四个副本来节省空间。

无论如何,请使用编译器生成的汇编语言。

于 2013-05-27T15:19:08.273 回答
1

使用最多空间的东西可能是乘法的使用。如果您的处理器缺少执行乘法的指令,编译器将被迫使用软件逐步执行,这可能需要相当多的代码。

很难说,因为您没有指定任何有关您的目标处理器(或您正在使用的编译器)的信息。

一种方法可能是以某种方式尝试减少内联,因此可以重用乘以 10 的代码(在所有四个术语中都使用)。

要知道是否是这种情况,必须检查机器代码。顺便说一句,使用十进制常量来计算地址真的很奇怪。

于 2013-05-27T14:05:55.363 回答
1

它非常非常依赖于编译器,但我建议您至少以这种方式简化乘法:

romAddr = ((eeprom_read(146)*10 + eeprom_read(147))*10 +
          eeprom_read(148))*10 + eeprom_read(149);

你可以把它放在一个循环中:

uint8_t i = 146;
romAddr = eeprom_read(i);
for (i = 147; i < 150; i++)
    romAddr = romAddr * 10 + eeprom_read(i);

希望编译器应该认识到将 16 位值乘以 10 与分别实现乘以 1000 和 100 相比要简单得多。

不过,依靠编译器来有效地处理循环,我并不完全满意。

也许:

uint8_t hi, lo;
hi = (uint8_t)eeprom_read(146) * (uint8_t)10 + (uint8_t)eeprom_read(147);
lo = (uint8_t)eeprom_read(148) * (uint8_t)10 + (uint8_t)eeprom_read(149);
romAddr = hi * (uint8_t)100 + lo;

所有这些都未经测试。

于 2013-05-30T18:34:26.003 回答
1

有时,乘法可以编译成一系列加法,是的。您可以通过使用左移运算符来优化它。

A*1000 = A*512 + A*256 + A*128 + A*64 + A*32 + A*8

或者同样的事情:

A<<9 + A<<8 + A<<7 + A<<6 + A<<5 + A<<3

这仍然比单个“乘法”指令长得多,但您的处理器显然没有它,所以这可能是下一个最好的事情。

于 2013-05-27T14:17:59.780 回答