2017-04-15 52 views
2

我最近使用了一个板(LPCXpresso 5411x)做一些计算,我们试图减少周期,只要我们能够节省我们的特定需求的运行时间,所以我需要对cortex-m4指令的成本周期进行一些研究。我发现很多奇怪的东西(不能用我在互联网上找到的东西解释)用手臂皮质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-> CYCC NT

"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> 

这两个ldrs正在从DWT-> CYCCNT读取,另外,为什么这会花费10个周期,我估计是2(来自ldr)+ 4 = 6

顺便说一句,板没有任何缓存,并且我将代码存储在sramx中,并且堆栈在sram2中。

我错过了什么,它有什么办法可以弄清楚每个周期如何消耗?此外,我也对cortex-m4的数据依赖性感到困惑。

+0

如果没有任何缓存,您可能不得不为循环获取指令提取额外的费用。另请注意,您计算的周期时间只包含读取DWT-> CYCCNT的两条LDR指令之一,而不是两条指令。推测它们在执行两条指令时(例如在开始时)在相同的相对点处读取循环计数。要包含这两者,循环次数必须在第一个LDR指令开始时和第二个LDR指令结束时。 –

+0

我同意你的意见,但你的意思是每个指令fetche的周期额外成本 –

+0

每个指令获取,但我不知道如何Cortex-M4指令提取。例如,它可能会在每个提取中获取32位字,因此并非每条指令都会支付它。 –

回答

1

采取变化,我没有那个芯片,但有其他人。在这种情况下使用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 

因此,没有第二MOVW需要闪光灯为0x11的时钟,并根据对准在RAM为0x10和0×11之间。当thumb2指令在字边界上对齐时,它比未对齐时需要更长的时钟。

使用Thumb指令为0x2102

00000000 20001016 00000010 
00000002 20001018 00000010 
00000004 2000101A 00000010 
00000006 2000101C 00000010 

使用的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以及其他芯片)你必须控制闪存上的等待状态,有时闪存速度是内存速度的一半,相同的代码,相同的机器代码,运行速度更快即使在低速情况下也可以在内存上运行,并且随着您增加时钟和增加闪存上的等待状态数量而变得更糟,以保持其速度。

特别是ST家族有一些营销术语,用于预取缓存,它们放在你不能禁用的东西。您可以在测试刚代码之前做DSB/ISB和例如见等待状态为单次的影响,但如果做一个测试循环

test_loop: sub r3,#1 
bne test_loop 

并运行它的时候有很多的几个时钟在开始时会反映出来,但很小,就像使用缓存一样,但如果处理器让您看到这些缓存,您仍应该看到对缓存的获取线效果。

某些芯片具有可以启用或禁用的闪存预取功能,尤其是循环可能会损害性能,而不是帮助您将事情正确对齐,以便预取程序在循环结束时读取良好。 ARM的IP在内核边缘(AXI,AMBA,AHB,APB,无论什么)的边界总线上停止,一般情况下,你可能有一个L2缓存的ARM IP(不是这些微控制器中的一个),而你可能会购买一些arm ip来帮助你的总线,但是最终该芯片具有芯片特定的东西,该芯片与芯片供应商和芯片供应商没有任何关系,特别是闪存和sram接口。如上所示,首先没有理由期望使用流水线处理器获得可预测的结果,并且真正容易用两个指令循环显示,由于单独对齐,相同的机器代码在性能上可能差别很大,而且还您直接或间接控制的因素,闪光灯等待状态,时钟与闪光灯的相对速度。如果我们设备上的N和N + 1等待状态之间的边界为24Mhz,那么在N + 1个等待状态下,N个等待状态下的24Mhz比24Mhz快得多。在N + 1等待状态下,28Mhz(N + 1等待状态)比24Mhz快,但最终cpu时钟可能会克服等待状态,你可以发现CPU速度超过24Mhz n + 1等待状态,时钟定时性能,而不是计数CPU时钟,如果受到闪存等待状态的影响,CPU时钟应该始终受到闪存等待状态的影响。

srams通常不会有等待状态,运行速度与CPU一样快,但可能有例外情况。毫无疑问,性能有限制,许多供应商都有关于外设时钟的规则,尽管该部分达到了48位,但仍然不能超过32mhz,所以访问外设的基准测试将采用不同数量的cpu时钟在不同的CPU /系统速度设置。

您也可以在处理器中配置选项,基本上编译时间选项。 cortex-m4不会做广告,但cortex-m0 +可以配置为16或32位指令取指宽度。我没有那个源代码的可见性,所以它可能是必须是编译时间的东西,或者如果你选择你可以设置一个控制寄存器并使其可以运行时配置,或者可能有逻辑说明如果pll设置是这样的强迫一种方式,其他方式,等等。所以,即使你有两个来自不同厂商的芯片具有相同的rev和型号的cpu核心,这并不意味着它们会表现相同。更不用说芯片供应商拥有源代码并且可以进行修改。

因此,试图预测系统中流水线处理器的循环计数,这种情况不会发生。您将有时间添加一个额外的nop,并且它会变得更快,您添加一个的时间会减少,并且会变得更慢,正如您期望的那样,以及它不会更改的时间。如果一个nop可以做到这一点,那么任何其他的指令也可以。

更不用说与管道本身搞混了,这些cortex-ms真的很短管道,所以我们被告知如此迫使一系列带有很多依赖关系的指令与类似的顺序没有不会有太大的影响。

以相同的机器码进行测试在不同供应商的几个cortex-m4上运行它(甚至是cortex-m3s和cortex-m7s),使用不同设置的闪存和RAM,并且不应该有惊喜在cpu ticks中的执行时间会有所不同。

+0

如果您正在使用内联汇编以获得某些手工汇编性能,那是另一个会影响性能的未知数,或者会因编译而异(在此处添加一行,在其他位置删除一行)该程序)。 –

+0

非常感谢,我也注意到指令对齐会影响性能。我会阅读你以后认真说过的话。 –

+0

您正处于理解的正确轨道。或者根本不了解情况。你应该能够像你一样做实验,并看到相同的机器代码可以有不同的表现。为什么我们可以推测它并且非常接近,但是没有获得逻辑和模拟的确实知道。 –