Arduino 上数学运算的计时速度 - 异常

Timing speed of math operations on Arduino - anomaly

本文关键字:速度 异常 运算 Arduino      更新时间:2023-10-16

我在Arduino Uno上对整数除法进行了一些研究,并试图确定解决除法的最快方法。

在阅读中,我遇到了这篇文章。

我抓住并修改了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 没有

谁能告诉我为什么执行手术所需的时间会有这么大的差异?

如果编译器知道除法中的分母是什么,它也许能够用乘法(乘以分母的倒数(和移位来计算除法。由于除法非常慢,即使在具有除法指令的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