Assembly 手臂皮质m4周期计数有点奇怪
我最近使用了一块板(LPCXpresso 5411x)来进行一些计算,我们试图尽可能减少周期,以节省运行时间满足特定需求,因此我需要对cortex-m4指令如何循环进行一些研究。我发现了很多奇怪的事情(这是我在互联网上发现的无法解释的) 我使用了DWT->CYCCNT来计算我想要测试的函数所消耗的周期Assembly 手臂皮质m4周期计数有点奇怪,assembly,arm,cortex-m,lpc,Assembly,Arm,Cortex M,Lpc,我最近使用了一块板(LPCXpresso 5411x)来进行一些计算,我们试图尽可能减少周期,以节省运行时间满足特定需求,因此我需要对cortex-m4指令如何循环进行一些研究。我发现了很多奇怪的事情(这是我在互联网上发现的无法解释的) 我使用了DWT->CYCCNT来计算我想要测试的函数所消耗的周期 int start_cycle, end_cycle; __asm volatile ( "LDR %[s1], [%[a]], #0\n\t" :[s1] "=&r"(star
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):);
}
}
- 根据,指令MOV应该花费一个周期,但我发现
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>
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)
400058e:1b09接头r1、r1、r4
4000590:f000 f80e bl 40005b0
这两个ldr是从DWT->CYCCNT读取的,此外,奇怪的是为什么这将花费10个周期,我估计是2(从ldr)+4=6
顺便说一下,该板没有任何缓存,我在sramx中存储代码,堆栈在sram2中
我是否错过了一些东西,并且以任何方式我都可以找出每个周期是如何消耗的?此外,我还对cortex-m4的数据依赖性感到困惑 换一种,我没有那种芯片,但有其他芯片。在这种情况下,使用ti cortex-m4。st部件在闪存前面有这个缓存,我认为您无法关闭它,并且(按照设计)会影响性能
00000082 <test>:
82: f3bf 8f4f dsb sy
86: f3bf 8f6f isb sy
8a: 6802 ldr r2, [r0, #0]
8c: 46c0 nop ; (mov r8, r8)
8e: 46c0 nop ; (mov r8, r8)
90: 46c0 nop ; (mov r8, r8)
92: 46c0 nop ; (mov r8, r8)
94: 46c0 nop ; (mov r8, r8)
96: 46c0 nop ; (mov r8, r8)
98: f240 0102 movw r1, #2
9c: f240 0103 movw r1, #3
a0: 46c0 nop ; (mov r8, r8)
a2: 46c0 nop ; (mov r8, r8)
a4: 46c0 nop ; (mov r8, r8)
a6: 46c0 nop ; (mov r8, r8)
a8: 46c0 nop ; (mov r8, r8)
aa: 46c0 nop ; (mov r8, r8)
ac: 46c0 nop ; (mov r8, r8)
ae: 6803 ldr r3, [r0, #0]
b0: 1ad0 subs r0, r2, r3
b2: 4770 bx lr
使用thumb2扩展名0xf240、0x0102
00000000 20001016 00000010
00000002 20001018 00000011
00000004 2000101A 00000010
00000006 2000101C 00000011
使用thumb2扩展名0xf240、0x0102、0xf240、0x0103
00000000 20001016 00000012
00000002 20001018 00000013
00000004 2000101A 00000012
00000006 2000101C 00000013
这并不奇怪,可能与抓取有关。这些微控制器比全尺寸的手臂简单得多。完整大小的缓存每次提取8条指令,这取决于提取行中的内容可能会影响性能,尤其是循环和分支在提取行中的位置(无论缓存是打开还是关闭)。分支也有分支预测器,您可以打开和关闭,并且可以在设计上有所不同
这个特殊的芯片说,在40Mhz以上,它可以实现一个预取,即取一个字,这意味着在它下面取一个半字(总线很可能是一个字宽,所以读取同一地址两次,以获取那里的两条指令…为什么?)
其他芯片(cortex ms以及其他芯片)您必须控制闪存上的等待状态,有时闪存的速度只有ram的一半,并且相同的代码、相同的机器代码在ram上运行得更快,即使在低速下,也会随着时钟的增加和闪存上等待状态的增加而变得更糟,以保持其速度可控
特别是ST系列,它有一些营销术语,用来指预取缓存(prefetch cache),它们放在里面的东西是无法禁用的。您可以在测试代码之前执行dsb/isb,例如查看等待状态对单个过程的影响,但如果执行测试循环
test_loop: sub r3,#1
bne test_loop
运行它很多次,开始时的几个时钟被反射,但是很小,就像使用缓存一样,但是如果处理器允许您看到这些,您仍然应该看到缓存的获取线效果
有些芯片有一个可以启用或禁用的闪存预取功能,特别是在循环中,如果您将事情调整得恰到好处,使预取器的读数远远超过循环的末尾,那么闪存预取功能可能会损害性能,而不是帮助性能
ARM ip停在核心边缘的ARM总线上(AXI、AMBA、AHB、APB等),一般来说,您可能有用于二级缓存的ARM ip(不在这些微控制器中),您可以购买一些ARM ip来帮助您使用总线,但最终芯片中有特定于芯片的东西,哪个arm与芯片供应商无关,也不一致,尤其是闪存和sram接口
首先,没有理由期望流水线处理器会产生可预测的结果,如上图所示,而且很容易通过两个指令循环显示,同一机器代码的性能可能会有很大的差异,这不仅是因为对齐本身,还因为您可以直接或间接地控制闪存等待状态,时钟与闪光灯的相对速度。如果我们的设备上N和N+1等待状态之间的边界是24Mhz,那么N等待状态下的24Mhz比N+1等待状态下的24Mhz快得多。28Mhz(N+1等待状态)在N+1等待状态下比24Mhz快,但最终cpu时钟可能会克服等待状态,您可以找到一个cpu速度优于24Mhz N+1等待状态,就整体墙上时钟计时性能而言,而不是cpu时钟计数而言,如果受到闪存等待状态的影响,则计数的cpu时钟应始终受到闪存等待状态的影响
SRAM往往没有等待状态,运行速度与CPU一样快,但可能有例外。毫无疑问,外设是有限制的,很多厂商都有关于外设时钟的规定,这个不能超过32mhz,即使部分达到48,诸如此类,所以访问外设的基准测试将在不同的cpu/系统速度设置下采用不同数量的cpu时钟
处理器中还有可配置选项,基本上是编译时选项。cortex-m4不宣传这一点,但cortex-m0+可以配置为16或32位指令提取宽度。我对源代码没有可见性,所以它可能是编译时的代码,或者如果您选择的话,您可以设置一个控制寄存器并让它运行
00000000 20001016 00000010
00000002 20001018 00000011
00000004 2000101A 00000010
00000006 2000101C 00000011
00000000 20001016 00000012
00000002 20001018 00000013
00000004 2000101A 00000012
00000006 2000101C 00000013
test_loop: sub r3,#1
bne test_loop