我正在对Arduino Uno上的整数除法进行一些研究,并试图确定解决除法的最快方法。
在阅读中,我遇到了this个帖子。
我抓取并修改了尼克·加蒙(Nick Gammon)在回复#4中发布的草图,最终目的是编译我需要执行的计算及其时序的列表。
这是我修改后的草图:
#define DATATYPE byte
#define OPERATION i / j
volatile DATATYPE x=0;
volatile DATATYPE y=0;
//volatile DATATYPE j=7;
unsigned long time;
unsigned long loop_time;
void setup ()
{
Serial.begin(115200);
Serial.println ();
time = micros();
for (DATATYPE i = 1; i < 101; i++) {
//for (DATATYPE j = 1; j < 101; j++) {
for (DATATYPE j = 56; j < 57; j++) {
y++;
} // end of for j
} // end of for i
loop_time = micros() - time;
Serial.print("Loop baseline Completion time: ");
Serial.print(loop_time);
Serial.print(" ");
time = micros();
for (DATATYPE i = 1; i < 101; i++) {
//for (DATATYPE j = 1; j < 101; j++) {
for (DATATYPE j = 56; j < 57; j++) {
y++;
x = OPERATION;
} // end of for j
} // end of for i
time = micros() - time;
Serial.print("Completion time: ");
Serial.print(time);
Serial.print(" Operation time: ");
time = time - loop_time;
Serial.println(time);
} // end of setup
void loop() {}
此输出为:
Loop baseline Completion time: 52 Completion time: 644 Operation time: 592
对于每个使用字节的除法运算,这实际上给出了5usec的时间。
但是,从更改两个循环:
for (DATATYPE j = 56; j < 57; j++) {
到
for (DATATYPE j = 57; j < 58; j++) {
给出以下输出:
Loop baseline Completion time: 52 Completion time: 108 Operation time: 56
我为内循环(7、56、58、3)等尝试了几种不同的值
3给出类似的结果,但7、11和13则不显示
有人可以告诉我为什么执行操作的时间会有如此大的差异吗?
答案 0 :(得分:1)
如果编译器知道除法的分母是什么,则它可能能够通过乘以(乘分母的倒数)和一个移位来计算除法。由于除法运算的速度确实很慢,即使在具有除法指令的CPU上,这种技术(通常称为倒数乘法)也可以大获成功。
不幸的是,某些分母比其他分母更容易优化,尤其是在乘法限制为16位的情况下。因此,您可能会看到具有不同常数除数的不同行为。
我认为编译器没有看到可用于该循环的更好的优化,即将一个临时值设置为0并将其用作除法结果。当i
达到56(或视情况而定为57)时,临时变量会增加。我认为这将从根本上减少执行时间。
好奇心是我最好的,所以我安装了当前的Ubuntu avr-gcc软件包v5.4.0,并尝试了一些编译。我的程序比原始程序简单得多:
uint8_t dodiv(uint8_t d) {
return d / DIVISOR;
}
然后我用如下命令编译它:
avr-gcc -S -Wall -O3 -o - -mmcu=atmega32 -DDIVISOR=57 avrdiv.c
据我所知,-mmcu=atmega32
对于Arduino Uno是正确的。
以下是一些结果,仅限于dodiv
函数的正文。我认为这并不能真正解释OP的测试结果在56到57之间的差异,但我可能会遗漏一些东西。这肯定说明了为什么3和7可能有非常不同的时间安排。
/* DIVISOR 3 */ /* DIVISOR 7 */ /* DIVISOR 56 */ /* DIVISOR 57 */
dodiv: dodiv: dodiv: dodiv:
ldi r25,lo8(-85) ldi r25,lo8(37) lsr r24 ldi r25,lo8(9)
mul r24,r25 mul r24,r25 lsr r24 mul r24,r25
mov r24,r1 mov r25,r1 lsr r24 mov r24,r1
clr __zero_reg__ clr __zero_reg__ ldi r25,lo8(37) clr __zero_reg__
lsr r24 sub r24,r25 mul r24,r25 lsr r24
ret lsr r24 mov r24,r1 ret
add r24,r25 clr __zero_reg__
lsr r24 ret
lsr r24
ret