对于我的项目我必须使用内联汇编指令例如rdtsc来计算一些C/C++指令的执行时间。
以下代码似乎适用于 Intel,但不适用于 ARM 处理器:
{unsigned a, d;asm volatile("rdtsc" : "=a" (a), "=d" (d)); t0 = ((unsigned long)a) | (((unsigned long)d) << 32);}
//The C++ statement to measure its execution time
{unsigned a, d;asm volatile("rdtsc" : "=a" (a), "=d" (d)); t1 = ((unsigned long)a) | (((unsigned long)d) << 32);}
time = t1-t0;
我的问题是:
如何编写类似于上面的内联汇编代码(计算指令的执行时间)以在ARM处理器上工作?
对于 Arm64,系统寄存器
CNTVCT_EL0
可用于检索计数器
用户空间。
// SPDX-License-Identifier: GPL-2.0
u64 rdtsc(void)
{
u64 val;
/*
* According to ARM DDI 0487F.c, from Armv8.0 to Armv8.5 inclusive, the
* system counter is at least 56 bits wide; from Armv8.6, the counter
* must be 64 bits wide. So the system counter could be less than 64
* bits wide and it is attributed with the flag 'cap_user_time_short'
* is true.
*/
asm volatile("mrs %0, cntvct_el0" : "=r" (val));
return val;
}
请参阅此补丁https://lore.kernel.org/patchwork/patch/1305380/了解更多详细信息。
我在这里找到了一个非 GPL,但关于读取 ARM64 上系统实时计数器的 BSD 源代码:https://github.com/cloudius-systems/osv/blob/master/arch/aarch64/arm-clock.cc
更好的是,那里的代码不仅像
rdtsc()
在 Intel/AMD 上那样提供某种滴答声,它甚至还报告这些滴答声的频率。是的,它在多核上的所有核心上都是同步的。因此它对很多事情都很有用,包括基准测试或跟踪线程池中的线程等。当然,它不会具有系统时钟的长期稳定性,系统时钟通过 同步到外部时间源ntp
。
引用的代码中定义的
class arm_clock
对于许多目的来说可能有点过分了。例如,它还展示了如何设置硬件计时器,这是普通用户模式进程可能没有权限执行的操作。以下是读取 TSC 和频率的最重要部分的摘录。它可以在 Intel、AMD 和 ARM 上与最新的 GCC 良好地编译。当然,频率读数仅在 ARM 上提供:
#ifdef __ARM_ARCH_ISA_A64
// Adapted from: https://github.com/cloudius-systems/osv/blob/master/arch/aarch64/arm-clock.cc
uint64_t rdtsc() {
//Please note we read CNTVCT cpu system register which provides
//the accross-system consistent value of the virtual system counter.
uint64_t cntvct;
asm volatile ("mrs %0, cntvct_el0; " : "=r"(cntvct) :: "memory");
return cntvct;
}
uint64_t rdtsc_barrier() {
uint64_t cntvct;
asm volatile ("isb; mrs %0, cntvct_el0; isb; " : "=r"(cntvct) :: "memory");
return cntvct;
}
uint32_t rdtsc_freq() {
uint32_t freq_hz;
asm volatile ("mrs %0, cntfrq_el0; isb; " : "=r"(freq_hz) :: "memory");
return freq_hz;
}
#else
#include <x86intrin.h>
uint64_t rdtsc(){ return __rdtsc(); }
#endif
我测试的执行时间在
rdtsc()
为7 ns,rdtsc_barrier()
为30 ns,与Intel和AMD非常相似。