我已经使用Linux perf一段时间来进行应用程序分析。通常情况下,配置文件应用程序相当复杂,因此只要根据第一原则没有任何粗略差异,就可以简单地将报告的计数器值视为面值。
但是,最近,我已经描述了一些简单的64位汇编程序 - 足够的三分法,可以计算出几乎所有各种计数器的预期值,并且似乎perf stat
过度计算。
以下面的循环为例:
.loop:
nop
dec rax
nop
jne .loop
这只会循环n
次,其中n
是rax
的初始值。循环的每次迭代执行4条指令,因此您可能希望执行4 * n
条指令,加上一些用于进程启动和终止的小固定开销以及在进入循环之前设置n
的一小段代码。 / p>
这是perf stat
的(典型)n = 1,000,000,000
输出:
~/dev/perf-test$ perf stat ./perf-test-nop 1
Performance counter stats for './perf-test-nop 1':
301.795151 task-clock (msec) # 0.998 CPUs utilized
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
2 page-faults # 0.007 K/sec
1,003,144,430 cycles # 3.324 GHz
4,000,410,032 instructions # 3.99 insns per cycle
1,000,071,277 branches # 3313.742 M/sec
1,649 branch-misses # 0.00% of all branches
0.302318532 seconds time elapsed
咦。我们看到一个神秘的额外410,032指令和71,277个分支,而不是大约4,000,000,000个指令和1,000,000,000个分支。总有"额外"说明,但数量有所不同 - 例如,后续运行分别有421K,563K和464K 额外的指令。您可以通过构建我的simple github project在您的系统上自行运行。
好的,所以你可能会猜到这几十条额外的指令只是固定的应用程序设置和拆卸成本(用户区设置为very small,但可能有隐藏的东西)。让我们尝试n=10 billion
然后:
~/dev/perf-test$ perf stat ./perf-test-nop 10
Performance counter stats for './perf-test-nop 10':
2907.748482 task-clock (msec) # 1.000 CPUs utilized
3 context-switches # 0.001 K/sec
0 cpu-migrations # 0.000 K/sec
2 page-faults # 0.001 K/sec
10,012,820,060 cycles # 3.443 GHz
40,004,878,385 instructions # 4.00 insns per cycle
10,001,036,040 branches # 3439.443 M/sec
4,960 branch-misses # 0.00% of all branches
2.908176097 seconds time elapsed
现在有大约490万条额外的指令,大约比之前增加了10倍,与循环计数增加10倍成正比。
您可以尝试各种计数器 - 所有与CPU相关的计数器都会显示相似的比例增加。让我们关注指令计数以保持简单。分别使用:u
和:k
后缀来衡量用户和内核计数,显示内核中产生的计数占几乎所有额外事件:
~/dev/perf-test$ perf stat -e instructions:u,instructions:k ./perf-test-nop 1
Performance counter stats for './perf-test-nop 1':
4,000,000,092 instructions:u
388,958 instructions:k
0.301323626 seconds time elapsed
宾果。在389,050条额外指令中,其中99.98%(388,958)完全在内核中发生。
好的,但是我们离开了哪里?这是一个简单的CPU绑定循环。它不进行任何系统调用,也不访问内存(可能通过页面错误机制间接调用内核)。为什么内核代表我的应用程序执行指令?
它似乎不是由上下文切换或CPU迁移引起的,因为它们处于或接近于零,并且在任何情况下 extra 指令计数都不相关在更多这些事件发生的地方运行。
额外内核指令的数量实际上非常平滑,具有循环计数。这是一个(数十亿)循环迭代与内核指令的图表:
你可以看到这种关系几乎完全是线性的 - 事实上直到15e9迭代只有一个异常值。在那之后,似乎有两条独立的线条,暗示某种形式的量化会导致超时。在任何情况下,对于主循环中执行的每个1e9指令,都会产生大约350K的内核指令。
最后,我注意到执行的内核指令数似乎与 runtime 1 (或CPU时间)成比例,而不是执行的指令。为了对此进行测试,我使用了similar program,但其中一条nop
指令替换为idiv
,其延迟时间约为40个周期(删除了一些不感兴趣的行):
~/dev/perf-test$ perf stat ./perf-test-div 10
Performance counter stats for './perf-test-div 10':
41,768,314,396 cycles # 3.430 GHz
4,014,826,989 instructions # 0.10 insns per cycle
1,002,957,543 branches # 82.369 M/sec
12.177372636 seconds time elapsed
这里我们花了大约42e9个循环来完成1e9次迭代,我们有大约14,800,000个额外指令。相比之下,对于nop
的相同1e9循环,仅有约400,000条额外指令。如果我们将nop
循环与大约相同数量的cycles
(40e9次迭代)进行比较,我们会看到几乎完全相同的额外指令数量:
~/dev/perf-test$ perf stat ./perf-test-nop 41
Performance counter stats for './perf-test-nop 41':
41,145,332,629 cycles # 3.425
164,013,912,324 instructions # 3.99 insns per cycle
41,002,424,948 branches # 3412.968 M/sec
12.013355313 seconds time elapsed
内核中发生的这项神秘工作是什么?
1 我在这里使用的是" time"和"周期"或多或少在这里互换。在这些测试期间,CPU会平稳运行,因此模拟一些与涡轮增压相关的热效应,周期与时间成正比。
答案 0 :(得分:7)
答案很简单。 您的计数器设置为用户 和 OS 模式,并且您的测量会受到Linux时间片的定期干扰调度程序,每秒计数几次。
幸运的是,在5天前调查与@PeterCordes无关的问题时,我published a cleaned-up version of my own performance counter access software,libpfc
。
libpfc
是一个非常低级的库和Linux可加载内核模块,我只使用完整的Intel Software Developers' Manual作为参考编码自己。性能计数设施记录在SDM的第18章第3卷中。它通过将特定值写入某些x86处理器中的特定MSR(特定于模型的寄存器)来配置。
可以在我的Intel Haswell处理器上配置两种类型的计数器:
固定功能计数器。它们仅限于计算特定类型的事件。它们可以启用或禁用,但它们跟踪的内容无法更改。在2.4GHz Haswell i7-4700MQ上有3:
Unhalted Reference / 2.4e9
提供亚纳秒精度时序,Unhalted Core / Unhalted Reference
给出从Turbo Boost获得的平均加速因子。 通用计数器。这些通常可以配置为跟踪特定处理器的SDM中列出的任何事件(仅有一些限制)。对于Haswell,它们目前列在SDM的第3卷第19.4节中,我的存储库包含一个演示pfcdemo
,可以访问它们的大部分。他们被列为at pfcdemo.c:169
。在我的Haswell处理器上,当启用HyperThreading时,每个核心都有4个这样的计数器。
为了配置计数器,我承担了在LKM pfc.ko
,whose source code is included in my repository编写每个MSR的负担。
编程MSR必须非常小心地完成,否则处理器会因内核恐慌而惩罚你。出于这个原因,除了通用和固定功能计数器本身之外,我还熟悉了5种不同类型MSR的每一位。我对这些寄存器的注释是at pfckmod.c:750
,并在此处转载:
/** 186+x IA32_PERFEVTSELx - Performance Event Selection, ArchPerfMon v3
*
* /63/60 /56 /48 /40 /32 /24 /16 /08 /00
* {................................################################}
* | |||||||||| || |
* Counter Mask -----------------------------------^^^^^^^^||||||||| || |
* Invert Counter Mask ------------------------------------^|||||||| || |
* Enable Counter ------------------------------------------^||||||| || |
* AnyThread ------------------------------------------------^|||||| || |
* APIC Interrupt Enable -------------------------------------^||||| || |
* Pin Control ------------------------------------------------^|||| || |
* Edge Detect -------------------------------------------------^||| || |
* Operating System Mode ----------------------------------------^|| || |
* User Mode -----------------------------------------------------^| || |
* Unit Mask (UMASK) ----------------------------------------------^^^^^^^^| |
* Event Select -----------------------------------------------------------^^^^^^^^
*/
/** 309+x IA32_FIXED_CTRx - Fixed-Function Counter, ArchPerfMon v3
*
* /63/60 /56 /48 /40 /32 /24 /16 /08 /00
* {????????????????????????????????????????????????????????????????}
* | |
* Counter Value --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* NB: Number of FF counters determined by CPUID.0x0A.EDX[ 4: 0]
* NB: ???? FF counter bitwidth determined by CPUID.0x0A.EDX[12: 5]
*/
/** 38D IA32_FIXED_CTR_CTRL - Fixed Counter Controls, ArchPerfMon v3
*
* /63/60 /56 /48 /40 /32 /24 /16 /08 /00
* {....................................................############}
* | || |||||
* IA32_FIXED_CTR2 controls ------------------------------------------^^^^| |||||
* IA32_FIXED_CTR1 controls ----------------------------------------------^^^^||||
* ||||
* IA32_FIXED_CTR0 controls: ||||
* IA32_FIXED_CTR0 PMI --------------------------------------------------------^|||
* IA32_FIXED_CTR0 AnyThread ---------------------------------------------------^||
* IA32_FIXED_CTR0 enable (0:Disable 1:OS 2:User 3:All) -------------------------^^
*/
/** 38E IA32_PERF_GLOBAL_STATUS - Global Overflow Status, ArchPerfMon v3
*
* /63/60 /56 /48 /40 /32 /24 /16 /08 /00
* {###..........................###............................####}
* ||| ||| ||||
* CondChgd ----^|| ||| ||||
* OvfDSBuffer --^| ||| ||||
* OvfUncore -----^ ||| ||||
* IA32_FIXED_CTR2 Overflow -----------------^|| ||||
* IA32_FIXED_CTR1 Overflow ------------------^| ||||
* IA32_FIXED_CTR0 Overflow -------------------^ ||||
* IA32_PMC(N-1) Overflow ------------------------------------------------^|||
* .... -------------------------------------------------^||
* IA32_PMC1 Overflow --------------------------------------------------^|
* IA32_PMC0 Overflow ---------------------------------------------------^
*/
/** 38F IA32_PERF_GLOBAL_CTRL - Global Enable Controls, ArchPerfMon v3
*
* /63/60 /56 /48 /40 /32 /24 /16 /08 /00
* {.............................###............................####}
* ||| ||||
* IA32_FIXED_CTR2 enable ----------------------^|| ||||
* IA32_FIXED_CTR1 enable -----------------------^| ||||
* IA32_FIXED_CTR0 enable ------------------------^ ||||
* IA32_PMC(N-1) enable -----------------------------------------------------^|||
* .... ------------------------------------------------------^||
* IA32_PMC1 enable -------------------------------------------------------^|
* IA32_PMC0 enable --------------------------------------------------------^
*/
/** 390 IA32_PERF_GLOBAL_OVF_CTRL - Global Overflow Control, ArchPerfMon v3
*
* /63/60 /56 /48 /40 /32 /24 /16 /08 /00
* {###..........................###............................####}
* ||| ||| ||||
* ClrCondChgd ----^|| ||| ||||
* ClrOvfDSBuffer --^| ||| ||||
* ClrOvfUncore -----^ ||| ||||
* IA32_FIXED_CTR2 ClrOverflow -----------------^|| ||||
* IA32_FIXED_CTR1 ClrOverflow ------------------^| ||||
* IA32_FIXED_CTR0 ClrOverflow -------------------^ ||||
* IA32_PMC(N-1) ClrOverflow ------------------------------------------------^|||
* .... -------------------------------------------------^||
* IA32_PMC1 ClrOverflow --------------------------------------------------^|
* IA32_PMC0 ClrOverflow ---------------------------------------------------^
*/
/** 4C1+x IA32_A_PMCx - General-Purpose Counter, ArchPerfMon v3
*
* /63/60 /56 /48 /40 /32 /24 /16 /08 /00
* {????????????????????????????????????????????????????????????????}
* | |
* Counter Value --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* NB: Number of GP counters determined by CPUID.0x0A.EAX[15: 8]
* NB: ???? GP counter bitwidth determined by CPUID.0x0A.EAX[23:16]
*/
特别是,观察IA32_PERFEVTSELx
,位16(用户模式)和17(OS模式)和IA32_FIXED_CTR_CTRL
,位IA32_FIXED_CTRx enable
。 IA32_PERFEVTSELx
配置通用计数器x
,而从位4*x
开始的每组4位从IA32_FIXED_CTR_CTRL
中的LSB开始计算固定功能计数器x
在MSR IA32_PERFEVTSELx
中,如果在用户位置位时清除OS位,则计数器仅在用户模式下累积事件,并将排除内核模式事件。在MSR IA32_FIXED_CTRL_CTRL
中,每组4比特包含一个两比特enable
字段,如果设置为2
(0b10
)将启用用户模式中的事件计数,但不在内核模式下。
我的LKM分别对pfckmod.c:296
和pfckmod.c:330
的固定功能和通用计数器实施仅用户模式计数。
在用户空间中,用户配置计数器(流程示例,从pfcdemo.c:98
开始),然后使用宏PFCSTART()
和PFCEND()
将要定时的代码夹在中间。这些是非常特定的代码序列,但是它们具有成本,因此产生偏差结果,其超过来自定时器的事件的数量。因此,您还必须拨打pfcRemoveBias()
,当它们包围0条指令时会PFCSTART()/PFCEND()
,并从累计数中消除偏差。
我把你的代码放到pfcdemo.c:130
中,现在就读了
/************** Hot section **************/
PFCSTART(CNT);
asm volatile(
".intel_syntax noprefix\n\t"
"mov rax, 1000000000\n\t"
".loop:\n\t"
"nop\n\t"
"dec rax\n\t"
"nop\n\t"
"jne .loop\n\t"
".att_syntax noprefix\n\t"
: /* No outputs we care about */
: /* No inputs we care about */
: "rax", "memory", "cc"
);
PFCEND (CNT);
/************ End Hot section ************/
。我得到了以下内容:
Instructions Issued : 4000000086
Unhalted core cycles : 1001668898
Unhalted reference cycles : 735432000
uops_issued.any : 4000261487
uops_issued.any<1 : 2445188
uops_issued.any>=1 : 1000095148
uops_issued.any>=2 : 1000070454
Instructions Issued : 4000000084
Unhalted core cycles : 1002792358
Unhalted reference cycles : 741096720
uops_issued.any>=3 : 1000057533
uops_issued.any>=4 : 1000044117
uops_issued.any>=5 : 0
uops_issued.any>=6 : 0
Instructions Issued : 4000000082
Unhalted core cycles : 1011149969
Unhalted reference cycles : 750048048
uops_executed_port.port_0 : 374577796
uops_executed_port.port_1 : 227762669
uops_executed_port.port_2 : 1077
uops_executed_port.port_3 : 2271
Instructions Issued : 4000000088
Unhalted core cycles : 1006474726
Unhalted reference cycles : 749845800
uops_executed_port.port_4 : 3436
uops_executed_port.port_5 : 438401716
uops_executed_port.port_6 : 1000083071
uops_executed_port.port_7 : 1255
Instructions Issued : 4000000082
Unhalted core cycles : 1009164617
Unhalted reference cycles : 756860736
resource_stalls.any : 1365
resource_stalls.rs : 0
resource_stalls.sb : 0
resource_stalls.rob : 0
Instructions Issued : 4000000083
Unhalted core cycles : 1007578976
Unhalted reference cycles : 755945832
uops_retired.all : 4000097703
uops_retired.all<1 : 8131817
uops_retired.all>=1 : 1000053694
uops_retired.all>=2 : 1000023800
Instructions Issued : 4000000088
Unhalted core cycles : 1015267723
Unhalted reference cycles : 756582984
uops_retired.all>=3 : 1000021575
uops_retired.all>=4 : 1000011412
uops_retired.all>=5 : 1452
uops_retired.all>=6 : 0
Instructions Issued : 4000000086
Unhalted core cycles : 1013085918
Unhalted reference cycles : 758116368
inst_retired.any_p : 4000000086
inst_retired.any_p<1 : 13696825
inst_retired.any_p>=1 : 1000002589
inst_retired.any_p>=2 : 1000000132
Instructions Issued : 4000000083
Unhalted core cycles : 1004281248
Unhalted reference cycles : 745031496
inst_retired.any_p>=3 : 999997926
inst_retired.any_p>=4 : 999997925
inst_retired.any_p>=5 : 0
inst_retired.any_p>=6 : 0
Instructions Issued : 4000000086
Unhalted core cycles : 1018752394
Unhalted reference cycles : 764101152
idq_uops_not_delivered.core : 71912269
idq_uops_not_delivered.core<1 : 1001512943
idq_uops_not_delivered.core>=1 : 17989688
idq_uops_not_delivered.core>=2 : 17982564
Instructions Issued : 4000000081
Unhalted core cycles : 1007166725
Unhalted reference cycles : 755495952
idq_uops_not_delivered.core>=3 : 6848823
idq_uops_not_delivered.core>=4 : 6844506
rs_events.empty : 0
idq.empty : 6940084
Instructions Issued : 4000000088
Unhalted core cycles : 1012633828
Unhalted reference cycles : 758772576
idq.mite_uops : 87578573
idq.dsb_uops : 56640
idq.ms_dsb_uops : 0
idq.ms_mite_uops : 168161
Instructions Issued : 4000000088
Unhalted core cycles : 1013799250
Unhalted reference cycles : 758772144
idq.mite_all_uops : 101773478
idq.mite_all_uops<1 : 988984583
idq.mite_all_uops>=1 : 25470706
idq.mite_all_uops>=2 : 25443691
Instructions Issued : 4000000087
Unhalted core cycles : 1009164246
Unhalted reference cycles : 758774400
idq.mite_all_uops>=3 : 16246335
idq.mite_all_uops>=4 : 16239687
move_elimination.int_not_eliminated : 0
move_elimination.simd_not_eliminated : 0
Instructions Issued : 4000000089
Unhalted core cycles : 1018530294
Unhalted reference cycles : 763961712
lsd.uops : 3863703268
lsd.uops<1 : 53262230
lsd.uops>=1 : 965925817
lsd.uops>=2 : 965925817
Instructions Issued : 4000000082
Unhalted core cycles : 1012124380
Unhalted reference cycles : 759399384
lsd.uops>=3 : 978583021
lsd.uops>=4 : 978583021
ild_stall.lcp : 0
ild_stall.iq_full : 863
Instructions Issued : 4000000087
Unhalted core cycles : 1008976349
Unhalted reference cycles : 758008488
br_inst_exec.all_branches : 1000009401
br_inst_exec.0x81 : 1000009400
br_inst_exec.0x82 : 0
icache.misses : 168
Instructions Issued : 4000000084
Unhalted core cycles : 1010302763
Unhalted reference cycles : 758333856
br_misp_exec.all_branches : 2
br_misp_exec.0x81 : 1
br_misp_exec.0x82 : 0
fp_assist.any : 0
Instructions Issued : 4000000082
Unhalted core cycles : 1008514841
Unhalted reference cycles : 757761792
cpu_clk_unhalted.core_clk : 1008510494
cpu_clk_unhalted.ref_xclk : 31573233
baclears.any : 0
idq.ms_uops : 164093
不再有开销了!您可以从固定功能计数器(例如最后一组打印输出)中看到IPC为4000000082 / 1008514841
,大约4个IPC,757761792 / 2.4e9
,代码占用0.31573408秒,以及来自{ {1}} = 1.330912763941521,核心是Turbo Boosting到2.4GHz或3.2GHz的133%。