我正在尝试为ARM架构计算一些生成的程序集。在此特定情况下,目标是aarch64-unknown-linux-gnu
。我真的想要倒数到个人周期,花几个小时来获得最短的时间并消除差异。
我无法直接访问ARM硬件,因此我尝试在QEMU下运行我的代码。
对于x86 / x86_64,我使用rdtsc
和rdtscp
指令返回周期数。
对于aarch64,我以为我可以使用
let clocks: u64;
asm!("mrs $0, pmccntr_el0" : "=r" (clocks) ::: "volatile");
但是当我跑步时
qemu-aarch64 -L /usr/aarch64-linux-gnu myprogram
我正在
qemu: uncaught target signal 4 (Illegal instruction) - core dumped
我认为可能需要在pmcr_el0
寄存器中设置一些位,但是甚至可以使用
let pmcr: u32;
asm!("mrs $0, pmcr_el0" : "=r" (pmcr) ::: "volatile");
给出了相同的Illegal instruction
错误。
这让我觉得好像这些是需要为我启用的特权指令 - 但是我找不到如何使用QEMU执行此操作的文档。
那么有没有办法访问QEMU中的性能硬件?有没有办法以其他方式计算周期?我真的希望它与x86代码尽可能匹配。
答案 0 :(得分:6)
似乎你忘了在pmuserenr寄存器中启用一些位。
另外,要使用Performance Monitors Extension,请遵循ARMv8 architecture reference manual的D6章。
请注意,QEMU不适合进行代码分析和优化。
QEMU的第一个目标是仿真速度(> 40 MIPS),它为OS开发提供了一些可靠的架构配置文件。 然后QEMU不需要支持准确的ARMv8性能监视器功能,当前的实现是相当抽象和最小的:除了周期计数器PMCCNTR的不准确模型之外什么也没有,并且根本没有性能监视器事件基础结构。
您最好使用普通物理计数器来生成时间间隔:
mrs x0, cntpct_el0
要理解为什么QEMU上的循环计算是无用的,请注意,QEMU是一个功能模型,它基于一些假设:
1)所有指令逐个顺序执行,每个指令消耗相同的时间段:
1 guest instruction counter tick = 1 emulated nano second << icount_time_shift
icount_time_shift由&#34; -icount&#34;指定命令行选项,默认为3。然后1个模拟的客户指令是8个模拟纳秒。
指令计数器和纳秒之间的这种严格转换是QEMU动态客户代码转换机制的关键概念,它允许确定性地生成转换块(TB):外围模型,这是纳秒驱动,绑定到TB执行,这是指令计数器驱动的。
例如,您将10个访客指令作为TB执行,然后将外设时钟提前到80 ns。外围设备也可以告诉TB执行循环,预计800 ns内没有任何访客事件,并且可以将下一个100条指令作为一个TB执行。
2)仿真纳秒级是一个基本的时钟单位,在qemu提供时间量,所有其他客人计数器按一定的整数因子缩放:
例如,当前QEMU实现的ARM物理系统计数器(CNTPCT)硬编码频率为62 MHz。然后scale_factor = 10^9 / (62 *10^6) = 16, (division is integer)
即。 QEMU每16个模拟纳秒的增量进行CNTPCT单次增量。基于该规模的ARMv8通用定时器QEMU实现。
此外,QEMU将PMCR实现为具有某种整数范围的计数器。
在QEMU,您可以手动计算来宾程序中的指令,将其复数为常量,并且我认为它将等于您的来宾代码尝试在QEMU上运行时计算的值。
对于在HW上运行的实际代码而言,结果将毫无意义:您需要使用专有的性能模拟器之一来实现具有缓存模型和管道的目标微架构,或者直接在HW上进行测试。