ARMv8的精确测量时钟周期




















ARMv8的精确测量时钟周期













































一、TSC (Time Stamp Counter)

TSC是Time Stamp Counter(时间戳计数器)的缩写,它是 Inter X86 架构上的一个计数器,它记录自启动以来处理器消耗的时钟周期数。

在每个时钟到来时,该计数器自动加一。

因为 TSC 随着处理器周期速率的变化而变化,所以它提供了非常高的精确度。它经常被用来分析和检测代码。

TSC 的值可以通过在X86指令rdtsc指令来读取。

在X86上的内联汇编实现如下:

uint64_t Rdtsc(void) 
{ 
	uint32_t lo, hi;

	__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));

	return (uint64_t)hi << 32 | lo;
}

二、ARM Generic Timer

通用计时器(ARM Generic Timer)为 ARM 内核提供了一个标准化的计时器框架。通用计时器包括一个系统计数器(a System Counter)和一组单核计时器(a set of per-core timers),如下图所示:

在这里插入图片描述

系统计数器是一种常开设备,提供固定频率的递增计数(其计数频率和CPU主频无关)。系统计数器的计数值通过广播到系统中的每一个内核中,从而使内核对时间的流逝有一个共同的看法。其频率通常等于CPU外部时钟频率

每个核心都有一组计时器(Each core has a set of timers)。这些计时器是作为比较器存在的,用来与系统计数器所广播值进行比较。软件可以配置计时器用来在将来的设定点产生中断或事件。软件还可以使用系统计数器值来添加时间戳,因为系统计数为所有内核提供了一个公共参考点。

注意

通用计时器仅测量时间的流逝。它不报告时间或日期。通常,SoC也会包含一个实时时钟(RTC)来表示时间和日期。


三、精确计时理论公式

设系统计数器的值为 N N N,其固定频率为 f f f,则精确流逝的时间 t t t(单位:秒)是:

t = N f t=\frac{N}{f} t=fN

设CPU主频为 F F F,则CPU精确流逝的时钟周期 C C C是:

C = t F C=tF C=tF

综合一下可得:

C = N F f C=N\frac{F}{f} C=NfF

又由于 f f f的值通常等于外频,那么 F f \frac{F}{f} fF就是倍频了,如果设倍频为 B B B的话,那么该式可转化为:

C = N B C=NB C=NB

那么这下牛逼了!


注意

这里测量出来的时钟周期其实和真实的CPU时钟周期之间还是存在着误差的,由于系统计数器每跳1下,CPU实际上跳了倍频的数目,所以误差在0到倍频之间。即:

C = N B 0 + B 或 C ∈ [ N B , ( N + 1 ) B ) C=NB_{0}^{+B} \qquad\text{或}\qquad C\in[NB,(N+1)B) C=NB0+BC[NB,(N+1)B)


四、实现


1. 读取系统计数器固定频率


方法一:通过读取cntfrq_el0寄存器的值得到

long long cntfrq(void)
{
	long long val;

	__asm__ __volatile__("mrs %0, cntfrq_el0" : "=r" (val));
	
	return val;
}

方法二:通过外部时钟频率(external clock)得到

系统计数器的固定频率其实采用的是外部时钟频率,而 x86 上的 TSC 采用的是主频,所以ARM上系统计数器的值和x86 上的 TSC还是有所区别。

By the way,外部时钟频率指得就是外频。所谓外部频率,指的就是系统总线频率,即 CPU 与外部(主板芯片组)交换数据、指令的工作时钟频率。

目前主流 CPU 的外频大多为 66MHz 与 100MHz 。鲲鹏920得外频为 100MHz。我们可以通过在 Linux 系统得 Bash 中输入 dmidecode |grep MHz 命令得到,该命令须在 root 权限下才能使用。


2. 读取系统计数器值


long long cntvct(void)
{
	long long val;

	__asm__ __volatile__("mrs %0, cntvct_el0" : "=r" (val));

	return val;
}

3. 计算CPU时钟周期


方法一:原则上计算


#define CPUFRQ (2600000000) //2.6 GHz

long long cycle(void)
{
	return cntvct()*CPUFRQ/cntfrq();
}

方法二:利用倍频计算


#define CPUMUL (26) //26倍频

long long cycle(void)
{
	return cntvct()*CPUMUL;
}

4. 完整的测时钟周期实现


#define SYS_CPUFRQ (2600000000) //主频

#define SYS_BUSFRQ (100000000) //外频

#define SYS_FRQMULPER (26) //倍频

#define Cycle_Start(_cs)                long long _cs; __asm__ __volatile__("mrs %0, cntvct_el0" : "=r" (_cs))

#define Cycle_End(_ce)                  long long _ce; __asm__ __volatile__("mrs %0, cntvct_el0" : "=r" (_ce))

#define Cycle_Elapse(_cu, _cs, _ce)     long long _cu = ((_ce)-(_cs))*SYS_FRQMULPER

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值