1. AMU簡(jiǎn)介
AMU是ActivityMonitor Unit的縮寫,在Arm v8.4架構(gòu)中引入。從Arm文檔描述來(lái)看,AMU與PMU(PerformanceMonitor Unit)有類似的性能監(jiān)控功能,但其設(shè)計(jì)初衷是為了系統(tǒng)管理,而PMU的用途是用戶態(tài)程序或者調(diào)試功能。AMU可以為系統(tǒng)性能、功耗管理提供持續(xù)不斷的監(jiān)控并獲取非常有用的信息。
在介紹AMU之前,先介紹下PMU幾個(gè)基本概念,對(duì)了解AMU會(huì)有所幫助。PMU用于跟蹤、統(tǒng)計(jì)系統(tǒng)內(nèi)部的一些底層硬件事件,這些事件反映了程序在CPU上執(zhí)行的行為,可以幫助我們對(duì)程序進(jìn)行分析和調(diào)優(yōu):
Event,即事件,例如CPU相關(guān)的事件包括執(zhí)行指令數(shù),時(shí)鐘周期等,cache相關(guān)的事件包括各級(jí)cache訪問(wèn)、refill計(jì)數(shù)等,以及與TLB有關(guān)的事件等。每個(gè)event都有一個(gè)特定的eventid。
Counter,事件計(jì)數(shù)器,數(shù)量固定,以Cortex-A53為例,一共有1+6個(gè)Counter,CycleCounter只用于記錄CPU Cycle數(shù),另外6個(gè)Counter是可配置的Counter,根據(jù)配置選擇的Event進(jìn)行計(jì)數(shù)。當(dāng)計(jì)數(shù)器發(fā)生溢出時(shí),計(jì)數(shù)器會(huì)產(chǎn)生overflow中斷。
AMUv1架構(gòu)定義(architecturally defined)4個(gè)固定的EventCounter,分別是:
既然是架構(gòu)規(guī)定要實(shí)現(xiàn)的,那么一定是被經(jīng)常用到的Event。未來(lái)可能會(huì)增加固定Counter的數(shù)量,AMU最多可支持16個(gè)固定Counter。
除了固定Counter,還支持最多16個(gè)輔助的Counter,這些Counter可以做成固定的也可以是可配置的,取決于具體實(shí)現(xiàn)。所有AMUEvent Counter都是64位的,在溢出時(shí)沒有提示或者中斷。
2. AMU寄存器
AMU寄存器如下圖所列,
AMU寄存器通過(guò)systemregister接口MRS和MSR指令進(jìn)行訪問(wèn)。
訪問(wèn)控制
可通過(guò)配置控制是否允許更低EL級(jí)別訪問(wèn)AMU寄存器。AMUSERENR_EL0.ENbit控制EL0訪問(wèn)AMU寄存器,可在EL1,EL2,EL3中配置。CPTR_EL2.TAMbit控制來(lái)自EL0和EL1的訪問(wèn),CPTR_EL3.TAMbit控制來(lái)自EL0,EL1和EL2的訪問(wèn)。其他的配置,只允許在最高的EL級(jí)別中進(jìn)行。
Counter寄存器
Counter寄存器都有以下特點(diǎn):
都是64位寄存器,并且overflow時(shí)不產(chǎn)生狀態(tài)提示或者中斷;
CPU復(fù)位后Counter寄存器值恢復(fù)為0;
Event Type寄存器
EventType寄存器配置了各Counter對(duì)哪個(gè)event進(jìn)行計(jì)數(shù),這點(diǎn)與PMU的PMEVTYPER
AMCNTENSET0_EL0/AMCNTENCLR0_EL0可對(duì)各Counterenable/disable進(jìn)行控制,其他寄存器含義可參考ARM文檔,這里不一一介紹。
3. 代碼實(shí)現(xiàn)
在Kernel中通過(guò)CONFIG_ARM64_AMU_EXTN宏進(jìn)行AMU代碼控制,Kernel最早支持AMU的功能,在這個(gè)patch中:
arm64: add support for the AMU extension v1
在arch/arm64/include/asm/sysreg.h中定義了AMU各寄存器的地址
/* Definitions for system register interface to AMU for ARMv8.4 onwards */ #define SYS_AM_EL0(crm, op2) sys_reg(3, 3, 13, (crm), (op2)) #define SYS_AMCR_EL0 SYS_AM_EL0(2, 0) #define SYS_AMCFGR_EL0 SYS_AM_EL0(2, 1) #define SYS_AMCGCR_EL0 SYS_AM_EL0(2, 2) #define SYS_AMUSERENR_EL0 SYS_AM_EL0(2, 3) #define SYS_AMCNTENCLR0_EL0 SYS_AM_EL0(2, 4) #define SYS_AMCNTENSET0_EL0 SYS_AM_EL0(2, 5) #define SYS_AMCNTENCLR1_EL0 SYS_AM_EL0(3, 0) #define SYS_AMCNTENSET1_EL0 SYS_AM_EL0(3, 1) #define SYS_AMEVCNTR0_EL0(n) SYS_AM_EL0(4 + ((n) >> 3), (n) & 7) #define SYS_AMEVTYPER0_EL0(n) SYS_AM_EL0(6 + ((n) >> 3), (n) & 7) #define SYS_AMEVCNTR1_EL0(n) SYS_AM_EL0(12 + ((n) >> 3), (n) & 7) #define SYS_AMEVTYPER1_EL0(n) SYS_AM_EL0(14 + ((n) >> 3), (n) & 7) /* AMU v1: Fixed (architecturally defined) activity monitors */ #define SYS_AMEVCNTR0_CORE_EL0 SYS_AMEVCNTR0_EL0(0) #define SYS_AMEVCNTR0_CONST_EL0 SYS_AMEVCNTR0_EL0(1) #define SYS_AMEVCNTR0_INST_RET_EL0 SYS_AMEVCNTR0_EL0(2) #define SYS_AMEVCNTR0_MEM_STALL SYS_AMEVCNTR0_EL0(3)
在arm-trusted-firmware中,也有AMU寄存器讀寫相關(guān)代碼,還包括上下電時(shí)AMU備份恢復(fù)的操作,代碼主要在lib/extensions/amu/aarch64/和lib/cpus/aarch64/cpuamu_helpers.S。
4. AMU的應(yīng)用
在內(nèi)核中有這么一個(gè)patch,目的是為了讓調(diào)度LoadTracking的FIE計(jì)算更準(zhǔn)確,
arm64: use activity monitors for frequency invariance
在此之前,Kernel中的freq scale計(jì)算都是通過(guò)cpufreq模塊記錄的當(dāng)前頻率和最大頻率來(lái)計(jì)算的,但是實(shí)際頻率可能在Kernel之外的地方發(fā)生變化,與Kernel cpufreq模塊記錄的當(dāng)前頻率不同,或者支持ACPI的cpufreq driver是不知道當(dāng)前頻率的。因此AMU就為獲取CPU平均頻率提供了一個(gè)方法。通過(guò)CPU_CYCLES和CNT_CYCLES的Counter計(jì)數(shù)來(lái)計(jì)算CPU頻率,具體代碼如下。
arch/arm64/kernel/topology.c
/* Initialize counter reference per-cpu variables for the current CPU */ void init_cpu_freq_invariance_counters(void) { this_cpu_write(arch_core_cycles_prev, read_sysreg_s(SYS_AMEVCNTR0_CORE_EL0)); this_cpu_write(arch_const_cycles_prev, read_sysreg_s(SYS_AMEVCNTR0_CONST_EL0)); } void topology_scale_freq_tick(void) { u64 prev_core_cnt, prev_const_cnt; u64 core_cnt, const_cnt, scale; int cpu = smp_processor_id(); if (!amu_freq_invariant()) return; if (!cpumask_test_cpu(cpu, amu_fie_cpus)) return; const_cnt = read_sysreg_s(SYS_AMEVCNTR0_CONST_EL0); core_cnt = read_sysreg_s(SYS_AMEVCNTR0_CORE_EL0); prev_const_cnt = this_cpu_read(arch_const_cycles_prev); prev_core_cnt = this_cpu_read(arch_core_cycles_prev); if (unlikely(core_cnt <= prev_core_cnt || const_cnt <= prev_const_cnt)) goto store_and_exit; /* * /core arch_max_freq_scale * scale = ------- * -------------------- * /const SCHED_CAPACITY_SCALE * * See validate_cpu_freq_invariance_counters() for details on * arch_max_freq_scale and the use of SCHED_CAPACITY_SHIFT。 */ scale = core_cnt - prev_core_cnt; scale *= this_cpu_read(arch_max_freq_scale); scale = div64_u64(scale >> SCHED_CAPACITY_SHIFT, const_cnt - prev_const_cnt); scale = min_t(unsigned long, scale, SCHED_CAPACITY_SCALE); this_cpu_write(freq_scale, (unsigned long)scale); store_and_exit: this_cpu_write(arch_core_cycles_prev, core_cnt); this_cpu_write(arch_const_cycles_prev, const_cnt); }
5. AMU與PMU的區(qū)別
前文提到AMU與PMU的設(shè)計(jì)目的不同。除此之外,還有哪些差異點(diǎn)呢?
AMU與PMU具有類似的特性,設(shè)計(jì)的目的有所不同,AMU是為了系統(tǒng)功耗性能管理,PMU是為了用戶態(tài)程序或者調(diào)試目的。從硬件角度都是對(duì)事件進(jìn)行計(jì)數(shù),我們同樣也可以使用PMU的統(tǒng)計(jì)結(jié)果進(jìn)行功耗和性能管理,為什么還要再引入AMU呢?
可能的原因是,PMU有多個(gè)使用場(chǎng)景,如果在性能功耗管理方案上使用了PMU,那么在性能調(diào)試時(shí)也要獲取PMU信息就可能產(chǎn)生沖突,例如使用強(qiáng)大的perf工具抓取PMU信息,尤其在Counter數(shù)量有限的情況下,沖突概率更大。引入AMU是為了給固定的資源專門用來(lái)做系統(tǒng)控制方案,從而釋放PMU資源用作其他目的。
PMU和AMU的軟件使用方式上,也有很大的不同。AMU通過(guò)system register interface就可以訪問(wèn),而PMU需要通過(guò)內(nèi)核的perf接口,軟件開銷遠(yuǎn)超過(guò)AMU。
下圖列出了兩者功能上的一些差異,
審核編輯:劉清
-
寄存器
+關(guān)注
關(guān)注
31文章
5363瀏覽量
121157 -
cpu
+關(guān)注
關(guān)注
68文章
10902瀏覽量
213000 -
計(jì)數(shù)器
+關(guān)注
關(guān)注
32文章
2261瀏覽量
94981 -
Cortex-A53
+關(guān)注
關(guān)注
0文章
33瀏覽量
21399
原文標(biāo)題:Perf monitor:從PMU到AMU
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論