如何测量 ARM Cortex-A53 处理器中的程序执行时间?
Posted
技术标签:
【中文标题】如何测量 ARM Cortex-A53 处理器中的程序执行时间?【英文标题】:How to measure program execution time in ARM Cortex-A53 processor? 【发布时间】:2015-07-19 20:05:18 【问题描述】:我正在使用以下方法读取 cortex-a15 中的时钟:
static void readticks(unsigned int *result)
struct timeval t;
unsigned int cc;
if (!enabled)
// program the performance-counter control-register:
asm volatile("mcr p15, 0, %0, c9, c12, 0" :: "r"(17));
//enable all counters
asm volatile("mcr p15, 0, %0, c9, c12, 1" :: "r"(0x8000000f));
//Clear overflow.
asm volatile("mcr p15, 0, %0, c9, c12, 3" :: "r"(0x8000000f));
enabled = 1;
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(cc));
gettimeofday(&t,(struct timezone *) 0);
result[0] = cc;
result[1] = t.tv_usec;
result[2] = t.tv_sec;
最终的性能分析看起来像:
before = readticks();
foo();
after = readticks();
clock_cycles = after - before.
我想在 cortex-A53、ARM64(不是 aarch32)中使用相同的逻辑。
我在关注在线门户网站后尝试过这个:
/* All counters, including PMCCNTR_EL0, are disabled/enabled */
#define QUADD_ARMV8_PMCR_E (1 << 0)
/* Reset all event counters, not including PMCCNTR_EL0, to 0
*/
#define QUADD_ARMV8_PMCR_P (1 << 1)
/* Reset PMCCNTR_EL0 to 0 */
#define QUADD_ARMV8_PMCR_C (1 << 2)
/* Clock divider: PMCCNTR_EL0 counts every clock cycle/every 64 clock cycles */
#define QUADD_ARMV8_PMCR_D (1 << 3)
/* Export of events is disabled/enabled */
#define QUADD_ARMV8_PMCR_X (1 << 4)
/* Disable cycle counter, PMCCNTR_EL0 when event counting is prohibited */
#define QUADD_ARMV8_PMCR_DP (1 << 5)
/* Long cycle count enable */
#define QUADD_ARMV8_PMCR_LC (1 << 6)
static inline unsigned int armv8_pmu_pmcr_read(void)
unsigned int val;
/* Read Performance Monitors Control Register */
asm volatile("mrs %0, pmcr_el0" : "=r" (val));
return val;
static inline void armv8_pmu_pmcr_write(unsigned int val)
asm volatile("msr pmcr_el0, %0" : :"r" (val & QUADD_ARMV8_PMCR_WR_MASK));
static void enable_all_counters(void)
unsigned int val;
/* Enable all counters */
val = armv8_pmu_pmcr_read();
val |= QUADD_ARMV8_PMCR_E | QUADD_ARMV8_PMCR_X;
armv8_pmu_pmcr_write(val);
static void reset_all_counters(void)
unsigned int val;
val = armv8_pmu_pmcr_read();
val |= QUADD_ARMV8_PMCR_P | QUADD_ARMV8_PMCR_C;
armv8_pmu_pmcr_write(val);
static void readticks(unsigned int *result)
struct timeval t;
unsigned int cc;
unsigned int val;
if (!enabled)
reset_all_counters();
enable_all_counters();
enabled = 1;
cc = armv8_pmu_pmcr_read();
gettimeofday(&t,(struct timezone *) 0);
result[0] = cc;
result[1] = t.tv_usec;
result[2] = t.tv_sec;
但是当我尝试分析时,它给出了“非法指令”作为错误。谁能帮我改一下上面的cortex-a53代码?
【问题讨论】:
您必须首先启用用户空间从内核代码对 PMU 的访问才能使其正常工作。但请注意,原始循环计数器通常不是分析工具;确定通常有一些方法可以避免您的进程在另一个 CPU 上重新调度并看到计数突然向后或向前跳跃,但是当您的代码暂停并且 CPU 正在处理中断时所有这些周期呢? 【参考方案1】:您需要为用户模式启用 PMU。这是我为它编写的内核模块(适用于 Raspberry Pi 2 中的 ARM V7):
/* Module source file 'module.c'. */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
arm_write(unsigned long val)
//Enabling both read and write - note difference between mcr and mrc
asm volatile("mrc p15, 0, %0, c9, c14, 0" :: "r"(1));
asm volatile("mcr p15, 0, %0, c9, c14, 0" :: "r"(1));
static int enabler(void)
unsigned long value = 1;
printk(KERN_INFO "Enabling PMU usermode.\n");
arm_write(value);
return 0;
static void end(void)
printk(KERN_INFO "module unloaded.\n");
module_init(enabler);
module_exit(end);
MODULE_AUTHOR("Sama");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Blahblah");
这将启用对 PMU 的用户模式访问。编译完成后,您需要启用 PMU 计数器,如下所示:
int main(int argc, char **argv)
int enable_divider =1;
int do_reset=1;
int 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. You really do not want to get all cycle count. This will increment the counter by 1 for every 64 cpu cycle.
value |= 16;
// program the performance-counter control-register with mask constructed above
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"(0x80000001));
// Select individual counter (0)
asm volatile ("MCR p15, 0, %0, c9 , c12 , 5\t\n":: "r"(0x00));
// Write event (0x11 = Cycle count)
asm volatile ("MCR p15, 0, %0, c9 , c13 , 1\t\n":: "r"(0xD));
printf("Hi");
unsigned int output;
// Read current event counter
asm volatile ("MRC p15, 0, %0, c9 , c13 , 2\t\n": "=r"(output));
printf("Event count 0: %ul\n", output);
printf("Normal Execution, No Buffer Overflow Occurred.\n");
return 0;
但是不幸的是,您得到的不仅仅是您的程序 cpu 周期,而是整个系统 cpu 周期!所以我推荐的是使用 perf。
用 C 语言将你的 asm 代码写成内联汇编代码,然后这样写:
int dummya(int z, int b)
//This is my function you need to change it for yourself
struct perf_event_attr pe;
long long count;
int fd;
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.type = PERF_TYPE_HARDWARE;
pe.size = sizeof(struct perf_event_attr);
pe.config = PERF_COUNT_HW_CPU_CYCLES;
pe.disabled = 1;
pe.exclude_kernel = 1;
pe.exclude_hv = 1;
fd = perf_event_open(&pe, 0, -1, -1, 0);
if (fd == -1)
fprintf(stderr, "Error opening leader %llx\n", pe.config);
exit(EXIT_FAILURE);
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
//From here the counter starts.
asm("Your ASM Codes");
asm("Your ASM Codes");
asm("Your ASM Codes");
asm("Your ASM Codes");
asm("Your ASM Codes");
asm("Your ASM Codes");
asm("Your ASM Codes");
asm("Your ASM Codes");
//Disabling Counter
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
read(fd, &count, sizeof(long long));
printf("%lld\n", count);
close(fd);
return 5;
请注意,您需要新的内核才能访问 Perf 驱动程序。
【讨论】:
在答案的第一部分,您展示了可以访问 PMU 的模块。我如何“安装”这个模块?以上是关于如何测量 ARM Cortex-A53 处理器中的程序执行时间?的主要内容,如果未能解决你的问题,请参考以下文章