【问题标题】:Is there an equivalent instruction to rdtsc in ARM?ARM中是否有与rdtsc等效的指令?
【发布时间】:2017-03-20 03:30:09
【问题描述】:

对于我的项目我必须使用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处理器上工作?

【问题讨论】:

  • rdtsc 在多核处理器上可能会出现问题。见msdn.microsoft.com/en-us/library/ee417693(VS.85).aspx
  • 单个指令将根据缓存等具有可变时序。最好在它/它们上循环数千次,并使用 perf_events() 通用功能使其在所有支持的 CPU 上工作。

标签: c++ c assembly arm inline-assembly


【解决方案1】:

您应该读取协处理器p15(不是实际的协处理器,只是CPU 功能的入口点)的PMCCNTR 寄存器以获得循环计数。请注意,只有在以下情况下,它才可用于非特权应用:

  1. 允许非特权的PMCCNTR 读取:

    PMUSERENR 寄存器的位 0 必须设置为 1 (official docs)

  2. PMCCNTR 实际上是在计算周期数:

    PMCNTENSET 寄存器的第 31 位必须设置为 1 (official docs)

这是 a real-world example 的完成方式。

【讨论】:

  • @Curious 请注意,上面的答案对 ARMv6 及更高版本有效。较旧的架构版本可能有自己的方法来获取这些数据(特定于特定芯片 - 所以信息可以在芯片的数据表中找到),而一些基于 ARM 的芯片根本不提供此类数据。
  • 我的ARM CPU是ARM7A,确认通过使用编译器Macro__ARM_ARCH_7A__,但是,当我尝试使用指令asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));,编译器给出错误信息:Error “no such instruction” asm volatile("mrc p15, 0, % eax, c9, c13, 0" : "=r"(pmccntr));
  • 我的构建环境= PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=4.3 TARGET_PRODUCT=full_manta TARGET_BUILD_VARIANT=eng TARGET_BUILD_TYPE=release TARGET_BUILD_APPS= TARGET_ARCH=arm TARGET_ARCH_VARIANT=armv7-a-neon TARGET_CPU_CPU_CPU a15 HOST_ARCH=x86 HOST_OS=linux HOST_OS_EXTRA=Linux-3.16.0-70-generic-x86_64-with-Ubuntu-14.04-trusty HOST_BUILD_TYPE=release BUILD_ID=JWR66V OUT_DIR=out
  • @hidefromkgb:这是我使用的代码,但它给出了上述错误。 {uint32_t pmccntr;asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));t0=static_cast(pmccntr) * 64;} //C++ 语句测量其执行时间 {uint32_t pmccntr;asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));t1=static_cast(pmccntr) * 64;}时间 = t1-t0;
  • @Curious 你很可能使用了错误的 binutils,因为as 肯定是在尝试组装 X86 而不是 ARM7A。而且,顺便说一句,* 64 等价于&lt;&lt; 6,结果不必提升为uint64_t 或乘以直到计算出 (T1 – T0)。由于差异通常小于 2²⁶,因此将其乘以 64 不需要升级为 64 位类型。
【解决方案2】:

对于 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/了解更多详情。

【讨论】:

  • 您认为将 Linux 内核中的 GPL 2.0 代码重新许可为 CC BY-SA 并将其发布在 StackOverflow 上是否合适?
  • @JeffHammond 谢谢你的指点。我添加了 GPL 2.0 许可证。
猜你喜欢
  • 2016-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-21
  • 2013-11-10
  • 2012-04-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多