3

[收到很多短信,因为我想尽我所能详细说明我的问题。]

我正在为 Cortex-M0 优化手写的 ARM 汇编代码。我使用的开发板是 STMicro STM32F0Discovery,它有一个 STM32F051R8 控制器。控制器以 48 MHz 运行。

不幸的是,在进行优化时,我得到了一些非常奇怪的循环计数。

例如,nop在我的代码中添加一个循环应该总共添加 2 个循环(循环 2 次)。但是,这样做会增加大约 1800 个额外的周期。现在,当我添加一个额外的nop(总共 2nop秒)时,循环计数确实增加了预期的 4 个循环。

对于下面的示例代码,我得到了类似的奇怪结果。示例代码显示,顶部摘录:c = 25 * a + 5 * b. 底部摘录是c = 5 * (5 * a + b)。因此,底部的应该更快,因为它需要 1 less mov。但是,改变这一点:

movs r4, #25
muls r3, r4, r3
add  r2, r3 

ldrb r3, [r6, #RoundStep]
movs r4, #5
muls r3, r4, r3

add  r2, r3

进入这个:

movs r4, #5
muls r3, r4, r3 

ldrb r5, [r6, #RoundStep]
add r3, r5
muls r3, r4, r3

add  r2, r3

不会将速度提高预期的 1 个周期,而是将速度降低或多或少 1000 个周期...

为了计算周期,我使用 SysTick 计数器,从最大值开始向下计数,并在溢出中断时增加溢出计数器。我为此使用的代码或多或少与ARM 网站的这段摘录相同,但针对我正在使用的 Cortex-M0 进行了重写。我的代码足够快,以至于在测量期间不会发生溢出中断。

现在,我开始认为计数器给了我错误的值,所以我还为我躺在身边的 TI Stellaris LaunchPad 编写了一些代码。这是一个以 80 MHz 运行的 Cortex-M4F。该代码测量某个引脚保持高电平的周期数。当然,M0 的时钟和 M4F 的时钟不同步运行,因此报告的周期计数略有不同,我通过对测量的周期计数 ( avg = 0.995 * avg + 0.005 * curCycles) 和重复测量 10000 次。

M4F 测量的时间与 M0 测量的时间相同,因此“不幸的是”SysTick 计数器在 M0 中工作得很好。

起初我以为这些额外的延迟是由流水线停顿引起的,但一方面 M0 似乎太简单了,另一方面我找不到 M0 流水线的任何详细信息,所以我无法验证.

所以,我的问题是:这里发生了什么?为什么添加一个nop会使我的函数需要额外的 1000 个循环/循环,但两个nops 只会将循环计数增加 2?为什么删除指令会使我的代码执行速度变慢?

4

1 回答 1

3

mul指令可以是ALU 管道的多个周期。您的c = 25 * a + 5 * bintoc = 5 * (5 * a + b)需要少一个mov。但是,流水线的加载存储阶段与 ALU 重叠。这些通常是独立的阶段,ldrb您可以通过说明免费mov获得说明。此外,根据值,可能会执行得更快;具体来说,顶部字节为零通常会导致排序器乘法循环。第一个版本的数据依赖性要少得多;指令n与n+1没有共同的寄存器。这是允许管道内衬的基本要求。muls

相比,

ldrb r5, [r6, #RoundStep]  ; 2 cycles
add r3, r5                 ; must block for r5 to load (1 cycle)

和,

ldrb r3, [r6, #RoundStep]  ; 2 cycles
movs r4, #5                ; may run in parallel with above.

因此,即使您可能将指令数加起来并拥有更少的代码,但由于流水线指令调度,可能会发现更大的替代将运行得更快。

如果您可以将其重新定位到例程的开头,则第二个版本可能会更快。ldrb

于 2013-09-23T15:39:26.073 回答