【发布时间】:2023-04-07 13:34:01
【问题描述】:
我最近用一块板子 (LPCXpresso 5411x) 来做一些计算,我们尽量减少周期,以节省运行时间以满足我们的特定需求,所以我需要做一些研究 cortex-m4 指令如何花费周期。而且我发现了很多奇怪的东西(无法用我从互联网上找到的东西来解释)
我使用 DWT->CYCCNT 来计算我要测试的函数消耗的周期数。
int start_cycle, end_cycle;
__asm volatile (
"LDR %[s1], [%[a]], #0\n\t"
:[s1] "=&r"(start_cycle): [a] "r"(&(DWT->CYCCNT)):);
AddrSumTest();
__asm volatile (
"LDR %[s1], [%[a]], #0\n\t"
:[s1] "=&r"(end_cycle): [a] "r"(&(DWT->CYCCNT)):);
printf("inside the func() cycles: %d\n",end_cycle - start_cycle);
这是我的函数的定义方式:
__attribute__( ( always_inline )) static inline void AddrSumTest(){
uint32_t x, y, i, q;
__asm volatile (
"nop\n\t"
:[x] "=r" (x), [y] "=r" (y), [i] "=r" (i), [q] "=r" (q):);
}
}
- 根据Arm Infocenter,指令MOV应该花费一个周期,但我发现
以下指令需要 8 个周期(不是 3 个,因为从 DWT->CYCCNT 读取需要额外的周期)
"nop\n\t"
"MOV %[x], #2\n\t"
"nop\n\t"
添加一条 MOV 指令后,接下来的周期需要 10 个周期(为什么不是 9 个周期)
"nop\n\t"
"MOV %[x], #2\n\t"
"MOV %[y], #3\n\t"
"nop\n\t"
后一种情况的汇编代码是
4000578: f853 4b00 ldr.w r4, [r3], #0
400057c: bf00 nop
400057e: f04f 0502 mov.w r5, #2
4000582: f04f 0603 mov.w r6, #3
4000586: bf00 nop
4000588: f853 1b00 ldr.w r1, [r3], #0
400058c: 4805 ldr r0, [pc, #20] ;(40005a4<test_AddrSum+0x30>)
400058e: 1b09 subs r1, r1, r4
4000590: f000 f80e bl 40005b0 <__printf_veneer>
这两个 ldr 正在从 DWT->CYCCNT 读取,此外,这也很奇怪为什么这会花费 10 个周期,而我估计是 2(from ldr) + 4 = 6
顺便说一下,板子没有缓存,我把代码存储在sramx中,堆栈在sram2中。
我错过了什么吗?有什么方法可以弄清楚每个周期是如何消耗的?此外,我也对 cortex-m4 的数据依赖性感到困惑。
【问题讨论】:
-
如果没有任何缓存,您可能必须为指令提取支付额外的周期成本。另请注意,您计算的周期时间将仅包括读取
DWT->CYCCNT的两条 LDR 指令之一,而不是两者。据推测,他们在两条指令的执行中(例如在开始时)的相同相对点读取循环计数。要包含两者,循环计数必须在第一条 LDR 指令的开头和第二条 LDR 指令的结尾。 -
我同意你的观点,但你的意思是说每条指令提取都会产生额外的周期成本
-
每次取指令,但不知道Cortex-M4是怎么取指令的。例如,它可能会在每次提取时提取 32 位字,因此并非每条指令都会支付。