如何在ARM Cortex-A8处理器中测量程序执行时间?

我正在使用一个名为i.MX515的基于ARM Cortex-A8的处理器。 有Linux Ubuntu 9.10发行版。 我正在运行一个用C编写的非常大的应用程序,我正在使用gettimeofday(); 用于衡量我的应用程序所用时间的函数。

 main() { gettimeofday(start); .... .... .... gettimeofday(end); } 

这种方法足以让我看看我的应用程序块占用了多少时间。 但是,现在,我正在尝试使用gettimeofday()计算时间的方法彻底优化我的代码,我看到连续运行之间有很多波动(在我的优化之前和之后运行),所以我不能确定实际执行时间,从而影响我的改进。

谁能告诉我应该怎么做?

如果通过访问循环计数器( ARM网站上为Cortex-M3建议的想法 ),任何人都可以向我指出一些代码,它给出了我在Cortex-A8上访问定时器寄存器时必须遵循的步骤吗?

如果这种方法不是很准确,那么请提出一些替代方案。

谢谢


跟进

跟进1:在Code Sorcery上编写了以下程序,生成了可执行文件,当我尝试在主板上运行时,我得到了 – 非法指令消息:(

 static inline unsigned int get_cyclecount (void) { unsigned int value; // Read CCNT Register asm volatile ("MRC p15, 0, %0, c9, c13, 0\t\n": "=r"(value)); return value; } static inline void init_perfcounters (int32_t do_reset, int32_t enable_divider) { // in general enable all counters (including cycle counter) int32_t value = 1; // peform reset: if (do_reset) { value |= 2; // reset all counters to zero. value |= 4; // reset cycle counter to zero. } if (enable_divider) value |= 8; // enable "by 64" divider for CCNT. value |= 16; // program the performance-counter control-register: asm volatile ("MCR p15, 0, %0, c9, c12, 0\t\n" :: "r"(value)); // enable all counters: asm volatile ("MCR p15, 0, %0, c9, c12, 1\t\n" :: "r"(0x8000000f)); // clear overflows: asm volatile ("MCR p15, 0, %0, c9, c12, 3\t\n" :: "r"(0x8000000f)); } int main() { /* enable user-mode access to the performance counter*/ asm ("MCR p15, 0, %0, C9, C14, 0\n\t" :: "r"(1)); /* disable counter overflow interrupts (just in case)*/ asm ("MCR p15, 0, %0, C9, C14, 2\n\t" :: "r"(0x8000000f)); init_perfcounters (1, 0); // measure the counting overhead: unsigned int overhead = get_cyclecount(); overhead = get_cyclecount() - overhead; unsigned int t = get_cyclecount(); // do some stuff here.. printf("\nHello World!!"); t = get_cyclecount() - t; printf ("function took exactly %d cycles (including function call) ", t - overhead); get_cyclecount(); return 0; } 

跟进2:我已经写信给飞思卡尔寻求支持,他们已经给我发回了以下回复和一个程序(我对此并不太了解)

以下是我们现在可以为您提供的帮助:我发送给您附上一个代码示例,它使用UART发送一个流,从您的代码,您似乎没有正确初始化MPU。

 (hash)include  (hash)include  (hash)define BIT13 0x02000 (hash)define R32 volatile unsigned long * (hash)define R16 volatile unsigned short * (hash)define R8 volatile unsigned char * (hash)define reg32_UART1_USR1 (*(R32)(0x73FBC094)) (hash)define reg32_UART1_UTXD (*(R32)(0x73FBC040)) (hash)define reg16_WMCR (*(R16)(0x73F98008)) (hash)define reg16_WSR (*(R16)(0x73F98002)) (hash)define AIPS_TZ1_BASE_ADDR 0x70000000 (hash)define IOMUXC_BASE_ADDR AIPS_TZ1_BASE_ADDR+0x03FA8000 typedef unsigned long U32; typedef unsigned short U16; typedef unsigned char U8; void serv_WDOG() { reg16_WSR = 0x5555; reg16_WSR = 0xAAAA; } void outbyte(char ch) { while( !(reg32_UART1_USR1 & BIT13) ); reg32_UART1_UTXD = ch ; } void _init() { } void pause(int time) { int i; for ( i=0 ; i  GPIO 2_6 pause(500000); *(R32)(0x73F88000) = 0x00000000; // 0 --> GPIO 2_6 pause(500000); } void init_port_for_led() { //GPIO 2_6 [73F8_8000] EIM_D22 (AC11) DIAG_LED_GPIO //ALT1 mode //IOMUXC_SW_MUX_CTL_PAD_EIM_D22 [+0x0074] //MUX_MODE [2:0] = 001: Select mux mode: ALT1 mux port: GPIO[6] of instance: gpio2. // IOMUXC control for GPIO2_6 *(R32)(IOMUXC_BASE_ADDR + 0x74) = 0x00000001; //Write to DIR register [DIR] *(R32)(0x73F88004) = 0x00000040; // 1 : GPIO 2_6 - output *(R32)(0x83FDA090) = 0x00003001; *(R32)(0x83FDA090) = 0x00000007; } int main () { int k = 0x12345678 ; reg16_WMCR = 0 ; // disable watchdog init_port_for_led() ; while(1) { printf("Hello word %x\n\r", k ) ; serv_WDOG() ; led() ; } return(1) ; } 

访问性能计数器并不困难,但您必须从内核模式启用它们。 默认情况下,计数器被禁用。

简而言之,您必须在内核中执行以下两行。 无论是作为可加载模块还是仅在board-init中的某处添加两条线都可以:

  /* enable user-mode access to the performance counter*/ asm ("MCR p15, 0, %0, C9, C14, 0\n\t" :: "r"(1)); /* disable counter overflow interrupts (just in case)*/ asm ("MCR p15, 0, %0, C9, C14, 2\n\t" :: "r"(0x8000000f)); 

一旦你这样做,循环计数器将开始递增每个循环。 寄存器的溢出将被忽视并且不会引起任何问题(除非它们可能弄乱您的测量)。

现在您想要从用户模式访问循环计数器:

我们从一个读取寄存器的函数开始:

 static inline unsigned int get_cyclecount (void) { unsigned int value; // Read CCNT Register asm volatile ("MRC p15, 0, %0, c9, c13, 0\t\n": "=r"(value)); return value; } 

而你很可能也希望重置并设置分隔符:

 static inline void init_perfcounters (int32_t do_reset, int32_t enable_divider) { // in general enable all counters (including cycle counter) int32_t value = 1; // peform reset: if (do_reset) { value |= 2; // reset all counters to zero. value |= 4; // reset cycle counter to zero. } if (enable_divider) value |= 8; // enable "by 64" divider for CCNT. value |= 16; // program the performance-counter control-register: asm volatile ("MCR p15, 0, %0, c9, c12, 0\t\n" :: "r"(value)); // enable all counters: asm volatile ("MCR p15, 0, %0, c9, c12, 1\t\n" :: "r"(0x8000000f)); // clear overflows: asm volatile ("MCR p15, 0, %0, c9, c12, 3\t\n" :: "r"(0x8000000f)); } 

do_reset将循环计数器设置为零。 很简单。

enable_diver将启用1/64周期分频器。 如果没有此标志设置,您将测量每个循环。 启用它后,计数器每64个周期增加一次。 如果要测量否则会导致计数器溢出的长时间,这非常有用。

如何使用它:

  // init counters: init_perfcounters (1, 0); // measure the counting overhead: unsigned int overhead = get_cyclecount(); overhead = get_cyclecount() - overhead; unsigned int t = get_cyclecount(); // do some stuff here.. call_my_function(); t = get_cyclecount() - t; printf ("function took exactly %d cycles (including function call) ", t - overhead); 

应该适用于所有Cortex-A8 CPU ..

哦 – 还有一些说明:

使用这些计数器,您将测量两次调用get_cyclecount()之间的确切时间,包括在其他进程或内核中花费的所有内容。 没有办法将测量限制在您的过程或单个线程中。

调用get_cyclecount()也不是免费的。 它将编译为单个asm指令,但从协处理器移动将停止整个ARM管道。 开销很高,可能会使您的测量偏差。 幸运的是,开销也是固定的,因此您可以测量它并从您的时间中减去它。

在我的例子中,我为每次测量做了那个。 不要在实践中这样做。 两次呼叫之间迟早会发生中断,甚至会进一步扭曲您的测量值。 我建议您在空闲系统上测量几次开销,忽略所有局外人并使用固定常量。

您需要在优化之前和之后使用性能分析工具来分析代码。

Acct是一个命令行和一个可用于监视资源的函数。 您可以更多地了解由acct生成的dat文件的使用和查看。

我将使用其他开源性能分析工具更新此post。

Gprof是另一个这样的工具。 请查看相同的文档。

现在已经过了几年,为了扩大尼尔斯的答案! – 访问这些计数器的简单方法是使用gator构建内核 。 然后报告计数器值以与Streamline一起使用, Streamline是ARM的性能分析工具。

它将在时间轴上显示每个function(为您提供系统执行情况的高级概述),显示执行所花费的时间,以及它占用的CPU百分比。 您可以将其与您设置的每个计数器的图表进行比较,以便将CPU密集型任务下载到源代码级别。

Streamline适用于所有Cortex-A系列处理器。

我曾在ARM7的工具链中工作过,它有一个指令级模拟器。 在其中运行应用程序可以为各个行和/或asm指令提供时间。 这对于给定例程的微优化非常有用。 但是,这种方法可能不适合整个应用/整个系统优化。