4 Terminology and metrics in performance analysis

//术语和性能分析指标
对于初学者来说,查看由分析工具(如Linux perf和Intel VTune Profiler)生成的性能剖面可能非常困难。这些配置文件具有许多复杂的术语和指标。本章是关于性能分析中使用的基本术语和指标的简介。
4.1 Retired vs. Executed Instruction
解释下Retired vs. Executed Instruction:
在性能分析中,“Retired”和“Executed Instruction”是两个不同的指标。
“Retired Instructions”是指完成指令操作并更新寄存器状态的指令数。这意味着它们已经被确认为已经执行。因此,这是计算机实际上执行的指令数量。这通常是衡量程序执行时间和CPU效率的最直接的指标。
“Executed Instructions”是指计算机已经尝试执行的指令数,包括已经执行和未执行的指令。这些指令可能会因为一些错误而没有成功地执行,例如缓存未命中或分支预测失败。因此,该指标通常被用来衡量指令流水线的效率和质量。
现代处理器通常执行比程序流程要求的指令更多。这是因为其中一些指令会被作为预测执行(如第3.3.3节所讨论的)。对于通常的指令,CPU在结果可用后提交并退役所有先前的指令。但是,对于执行的预测指令,CPU会保留它们的结果而不会立即提交它们的结果。当预测正确时,CPU将解除阻塞并像正常情况一样继续进行这些指令。但是,当推测错误时,CPU将放弃由预测指令所做的所有更改,并不退役它们。因此,CPU处理的指令可以被执行但不一定被退役。考虑到这一点,我们通常可以期望执行的指令数高于退役的指令数。有一个固定的性能计数器(PMC)可以收集退役指令的数量。可以通过在Linux perf中运行以下命令轻松获得它:

$ perf stat -e instructions ./a.exe
2173414 instructions # 0.80 insn per cycle
# or just simply do:
$ perf stat ./a.exe

4.2 CPU Utilization
CPU利用率是指在某个时间段内,CPU忙碌的时间所占的百分比。从技术上讲,当CPU不运行内核空闲线程时,它被认为是被利用的。
CPU利用率的计算公式如下:
CPU Utilization = CPU_CLK_UNHALTED.REF_TSC / TSC
其中,CPU_CLK_UNHALTED.REF_TSC PMC计数器记录了核心处于非挂起状态时参考周期的数量。TSC是时间戳计数器(在第2.5节中讨论),它始终在不断地递增。
如果CPU利用率较低,通常意味着应用程序的性能较差,因为CPU的某些时间被浪费了。然而,高CPU利用率也并非总是好的。这表明系统正在做一些工作,但并不确切地说明它在做什么:即使CPU在等待内存访问而停顿不前,它仍可能被高度利用。在多线程环境中,线程也可以在等待资源继续之际自旋,因此有“Effective CPU utilization”来过滤自旋时间(参见第11.2节)。Linux perf会自动计算系统上所有CPU的CPU利用率:

$ perf stat -- a.exe
0.634874 task-clock (msec) #0.773 CPUs utilized

如何通过simpleperf得到CPU利用率:
可以通过以下步骤使用simpleperf来获取CPU利用率:
1. 首先,使用以下命令启动simpleperf:

```
sudo simpleperf record -a -e cpu-clock sleep 10
```

该命令将使用cpu-clock事件在所有CPU上对系统执行采样,并记录10秒钟的数据。
2. 然后,使用以下命令生成报告:

```
sudo simpleperf report --show-utilization -i perf.data
```

该命令将生成一个带有CPU利用率的报告,展示了在记录期间各个进程和线程的CPU利用率。如果想要了解更多关于simpleperf报告的信息,可以尝试使用 `--full-callgraph` 和 `-g` 参数来生成更详细的报告。
注意,在使用simpleperf时需要root权限。
4.3 CPI & IPC
这是两个非常重要的指标,分别代表:
• CPI(Cycles Per Instruction):平均每条指令需要多少个周期才能退役。CPI越小,表明CPU性能越好,因为它可以在更少的周期内完成更多的操作。
• IPC(Instructions Per Cycle):平均每个周期可以退役多少条指令。IPC越高,表明CPU性能越好,因为CPU可以在一个周期内执行更多的操作。
CPI和IPC之间有着对立的关系,这是因为如果CPI很高,那么IPC就会相应地降低。因此,通过监控这两个指标,可以了解CPU的效率和性能,并找到可能存在的性能瓶颈。同时,CPI和IPC也可以用于比较不同CPU架构或不同优化级别的代码的性能。
IPC(Instructions Per Cycle)和CPI(Cycles Per Instruction)可以通过以下公式计算:
IPC = INST_RETIRED.ANY / CPU_CLK_UNHALTED.THREAD
其中,INST_RETIRED.ANY PMC计数器记录了退役指令的数量,CPU_CLK_UNHALTED.THREAD计数器记录了CPU核心处于非挂起状态时的周期数。
CPI = 1 / IPC
CPI是IPC的倒数,因为CPI表示平均每条指令需要的周期数,而IPC表示平均每个周期可以退役多少条指令。因此,CPI和IPC是CPU性能的两个重要度量值,可以用于比较不同架构或不同优化级别的代码的性能,并找到可能存在的性能瓶颈。
基于这些指标,可以进行许多类型的分析。它对于评估硬件和软件的效率都非常有用。硬件工程师使用这个指标来比较不同供应商的CPU和不同的CPU代。软件工程师在优化他们的应用程序时会关注IPC和CPI。普遍来说,我们希望CPI尽可能低,IPC尽可能高。Linux性能分析工具(perf)用户可以通过运行以下命令来了解他们的工作负载的IPC:

$ perf stat -e cycles,instructions -- a.exe
2369632 cycles
1725916 instructions # 0,73 insn per cycle
# or just simply do:
$ perf stat ./a.exe

4.4 UOPs (micro-ops)
解释下UOPs (micro-ops):
UOPs(微操作,micro-ops)是CPU执行指令时的一个内部阶段,也称为指令解码阶段。在这个阶段,CPU将指令分解成多个小的微操作,然后将它们发送到执行单元中执行。每个微操作都只是一个非常小的基本操作,例如将两个寄存器相加或者将一个字节加载到内存中。
通过将指令分解成多个微操作,CPU可以更好地进行流水线处理,从而提高指令的执行效率和吞吐量。因此,现代CPU都使用微操作作为其内部运行方式。
x86体系结构的微处理器将复杂的类CISC指令转换为简单的类RISC微操作(缩写为µops或uops)。这样做的主要优势在于,µops可以乱序执行[Fog,2012年,第2.1章]。例如,一个简单的加法指令,如ADD EAX,EBX只会生成一个µop,而更复杂的指令,如ADD EAX,[MEM1]可能会生成两个µops:一个用于从内存中读取到一个临时(未命名)寄存器中,另一个用于将临时寄存器中的内容与EAX相加。指令ADD [MEM1],EAX可能会生成三个µops:一个用于从内存中读取,一个用于加法,一个用于将结果写回内存中。不同CPU代之间指令和它们被分解为微操作的关系可能会有所不同。
如何理解µops可以乱序执行?
乱序执行是指CPU可以在不改变程序执行结果的前提下重新排列指令的执行顺序。这种技术允许CPU在等待某些指令执行的过程中,通过执行其他指令来保持其同时应用并发性。而µops是可以独立执行的最小指令单元,它们之间没有数据依赖关系或顺序限制。因此,CPU可以将µops以任意顺序投入执行单元,从而达到执行指令的最快速度。
通过乱序执行µops,CPU可以更有效地利用其内部硬件资源。例如,如果一个微操作需要访问内存,CPU可以在等待该微操作完成的同时执行另一个不需要等待内存的微操作,从而在指令执行期间实现高效利用CPU资源的目的。
总之,µops可以乱序执行,可以提高CPU的执行效率和并发性能,从而使CPU在相同时间内执行更多的指令。
与将复杂的CISC指令分解为类RISC微操作(uops)相反,后者也可以进行融合。现代英特尔CPU中有两种类型的融合:
- 微融合 - 将来自同一机器指令的µops融合在一起。微融合只能应用于两种类型的组合:内存写入操作和读-修改操作。例如:

# Read the memory location [ESI] and add it to EAX
# Two uops are fused into one at the decoding step.
add eax, [esi]

- 宏融合 - 将不同机器指令的µops融合在一起。对于某些情况下,解码器可以将算术或逻辑指令与随后的条件跳转指令融合成一个单独的计算和分支µop。例如:

# Two uops from DEC and JNZ instructions are fused into one
.loop:
dec rdi
jnz .loop

uops是什么的缩写:
"uops"是micro-operations的缩写,意为微操作。
Microfusion和Macrofusion都可以在CPU的所有阶段,从解码到退役中节省带宽。融合操作共享重新排序缓冲区(ROB)中的单个条目,当一个融合uop仅使用一个条目时,ROB的容量将增加。这个单一的ROB条目代表了两个需要由两个不同的执行单元完成的操作。融合ROB条目被分派到两个不同的执行端口,但再次作为单个单元退役。
Linux perf用户可以通过运行以下命令来收集其工作负载中发出、执行和退役的µops数量:

$ perf stat -e uops_issued.any,uops_executed.thread,uops_retired.all -- a.exe
2856278 uops_issued.any
2720241 uops_executed.thread
2557884 uops_retired.all

对于最近的x86微架构,可以在uops.info网站上找到指令的延迟、吞吐量、端口使用和µops的数量。该网站收集了许多现代CPU架构中处理器的微操作统计数据,并提供了一个方便的查询工具,使用户可以查找单个指令的性能特征。这些数据包括每个指令所需的µops数、它们各自的延迟和吞吐量、以及每个端口的使用率等信息。这些信息对于编写高效的代码和优化性能非常有用。
https://uops.info/            //Intel Core CPU
uops_issued.any uops_executed.thread uops_retired.all的区别:
"uops_issued.any"、"uops_executed.thread"和"uops_retired.all"是Linux perf事件中针对µops计数的不同指标。
- "uops_issued.any"表示发出的µops数量,包括控制流和数据流µops,但不考虑它们是否实际执行。
- "uops_executed.thread"表示线程执行的µops数量,只包括实际执行的µops,跳过了因分支预测失败或其他原因被丢弃的µops。
- "uops_retired.all"表示最终退役的µops数量。
这些指标都描述了µops在CPU中的不同阶段的行为,并提供有关程序或工作负载对CPU的利用率和效率的信息。通常情况下,"uops_retired.all"是最实用的指标,因为它反映了真正完成的µops的数量,可以用来衡量CPU处理工作负载的能力。
"uops_retired.all"比"uops_executed.thread"数值要低,怎么解释:
如果 "uops_retired.all" 的数值比 "uops_executed.thread" 低,这通常意味着执行单元正在陷入一些等待状态,从而导致某些指令没有真正执行。这可能有多种原因,如下所述:
1. 异常和其他中断:当CPU执行到一个异常指令(如缺页或除以零等)时,它就会停止当前线程的执行,并进入异常处理程序。在此期间,CPU可能不会发出任何新的 µops,因此这些µops不会被计入 "uops_retired.all" 中。
2. 数据相关性和控制相关性:某些指令依赖于先前指令的结果,或者需要等待跳转目标的计算结果。如果指令的依赖项尚未准备好,或者分支预测失败,则该指令将无法执行,并且不会被计入 "uops_retired.all"。
3. 指令缓存未命中:如果CPU无法在指令缓存中找到要执行的指令,则需要从内存中读取指令。这可能需要一定的时间,而在此期间,CPU不能发出新的 µops。这些µops也不会被计入 "uops_retired.all"。
总之,如果 "uops_retired.all" 的数值比 "uops_executed.thread" 低,那么这可能说明CPU正在等待某些事件的完成,并且尚未发出该线程计划中的所有 µops。
再解释下"uops_retired.all":
“uops_retired.all”是一种Linux perf事件,用于针对指定的CPU核心统计退役的微操作(µops)数量。微操作是处理器中的操作单元所理解的最低级别操作,通常指一条指令被解码后生成的多个µops。
在CPU执行指令时,它首先将指令分解成微操作,然后按照顺序将它们派发到相应的执行单元上执行。随着微操作的执行完成,它们将被标记为“退役”,表示它们已经完成了它们预期的功能,并从CPU中删除。因此,“uops_retired.all”表示在一个特定时间段内从CPU中完全退出的µops的总数。
与其他类似的计数器(如“uops_executed.thread”)不同,“uops_retired.all”并不关心微操作是否实际上被执行。例如,如果发生分支预测失败或数据缓存未命中等意外事件,某些µops可能会被丢弃而不被执行,但仍然会被计入“uops_retired.all”。因此,这个事件通常用来衡量一个工作负载在CPU上运行所需的总 µops 数量。
需要注意的是,尽管“uops_retired.all”可以用来衡量CPU处理工作负载的能力,但它并不提供有关µops执行效率和吞吐量的详细信息。对于想要更深入了解CPU性能的用户,还需要结合其他类似的微操作计数器,如“uops_issued.any”和“uops_executed.thread”,进行分析。
4.5 Pipeline Slot    //流水线阶段
Pipeline Slot的翻译可以是“流水线槽位”,也可以是“流水线阶段”。在CPU中,指令执行过程被划分为多个阶段或步骤,并通过流水线的方式执行,从而提高了CPU的执行效率。每个流水线阶段通常由一个或多个物理单元组成,这些单元一起协同工作来完成指令的执行。
在流水线中,每个指令需要被分解为多个操作,并逐步执行每个操作。每个操作需要在相应的硬件物理单元(如整数单元、浮点数单元等)上执行,而这些单元通常被划分为不同的流水线槽位或阶段。例如,在Intel CPU中,整型单元通常有三个流水线阶段,分别是取指令(IF)、解码指令(ID)和执行指令(EX)。
因此,实现高效的流水线处理通常需要优化每个流水线阶段的效率,并确保每个阶段都能够充分利用硬件资源。对于一些特别密集的计算或者具有高度数据可并行性的工作负载,还可能需要通过添加更多的流水线阶段来进一步提高CPU的执行效率。
一个流水线槽位表示处理一个µop所需的硬件资源。图17展示了一个CPU的执行流水线,每个周期可以处理四个µops。几乎所有现代的x86 CPU都使用4个µops(4-wide)作为其流水线宽度。在图中连续的6个周期中,仅利用了可用流水线槽位的一半。从微体系结构的角度来看,执行这样的代码的效率只有50%。
流水线槽位是Top-Down微体系结构分析中的核心指标之一(参见第6.1节)。例如,前端瓶颈和后端瓶颈指标均表达为由于各种原因导致未利用流水线槽位的百分比。

Figure 17: Pipeline diagram of a 4-wide CPU.
4.6 Core vs. Reference Cycles    //核心时钟周期与参考时钟周期
Core vs. Reference Cycles的翻译可以是“核心时钟周期与参考时钟周期”。
CPU中有许多不同类型的时钟周期,其中包括核心时钟周期和参考时钟周期。核心时钟周期是指这个特定CPU核心中执行的实际指令周期的数量,而参考时钟周期是指这个CPU的参考时钟信号发生的周期数。
核心时钟周期通常用于衡量CPU执行任务所需的时间,而参考时钟周期通常用于测量CPU的整体性能和稳定性。在大多数情况下,参考时钟周期要比核心时钟周期更长,因为它包括了一些额外的处理时间,如操作系统调度、缓存和内存访问等。
在性能评估和微体系结构分析中,核心时钟周期和参考时钟周期通常被广泛使用以提供对CPU的性能和效率的深入理解。例如,在性能基准测试中,可以使用核心时钟周期来衡量CPU的处理速度和执行能力,而使用参考时钟周期来衡量CPU整体性能的稳定性和可靠性。
CPU参考时钟周期是指CPU与其他系统组件(如系统总线和内存)进行通信所需的时钟周期。与此不同,CPU核心时钟周期是指CPU内核处理每条指令所需的时钟周期。
simpleperf上如何查看核心时钟周期和参考时钟周期?
在simpleperf上查看核心时钟周期和参考时钟周期,可以使用以下两个命令:
1. 查看核心时钟周期:

```
$ simpleperf stat -e cpu-clock <command>
```

这个命令将会运行给定的 `<command>` 命令,并在其结束时输出指令执行期间核心时钟周期的数量。执行该命令后,simpleperf 将显示指令执行时的 CPU 频率、指令周期数以及其他计数器。在计数器列表中搜索 “cpu-clock” 项,即可获取核心时钟周期的计数值。
2. 查看参考时钟周期:

```
$ simpleperf stat -e ref-cycles <command>
```

这个命令将会运行给定的 `<command>` 命令,并在其结束时输出参考时钟周期的数量。执行该命令后,simpleperf 将显示指令执行过程中参考时钟周期的数量及其他相关计数器。在计数器列表中搜索 “ref-cycles” 项,即可获取参考时钟周期的计数值。
需要注意的是,不是所有的CPU支持 core clock 计数器或 reference clock 计数器。如果你尝试运行上述命令但没有看到相应计数器的值,则说明你的CPU可能不支持这些计数器。
计算IPC的时钟周期是参考时钟周期还是核心时钟周期?
计算IPC(Instructions Per Cycle,每个时钟周期的指令数)时,我们通常使用核心时钟周期而不是参考时钟周期。这是因为核心时钟周期只统计实际执行指令所用的时间,而不包括等待资源或上下文切换等操作所需的时间。因此,使用核心时钟周期作为分母可以更好地反映出CPU的运行性能。
计算IPC的公式为:
IPC = Instructions executed / Core clock cycles elapased
其中,分母使用的是核心时钟周期,代表CPU实际执行指令所用的时间。分子为指令执行数量,代表CPU在这段时间内实际执行的指令数目。
需要注意的是,IPC只是一个相对的指标,具体数值会受到很多因素的影响,如指令类型、数据依赖性、缓存/内存访问等。因此,IPC并不总是越高越好,需要根据具体情况进行评估。
线程上下文切换影响的是CPU参考时钟周期还是CPU核心时钟周期?
线程上下文切换通常会影响CPU参考时钟周期而不是CPU核心时钟周期。
线程上下文切换是指CPU在执行一个线程时,暂停该线程的执行并切换到另一个线程。当CPU执行一个线程时,它需要保存该线程的寄存器状态、程序计数器和其他有关信息。在切换到另一个线程时,CPU需要加载新的寄存器状态和程序计数器以继续执行。这个过程就是线程上下文切换。
由于线程上下文切换需要CPU在两个线程之间进行切换并保存和恢复寄存器状态等信息,因此会产生额外的CPU开销,从而影响参考时钟周期。但是,这并不会直接影响CPU内核处理指令的速度,也就是不会影响CPU核心时钟周期。
需要注意的是,在一些特殊情况下,如线程数过多、线程设计不合理等情况下,线程上下文切换可能会成为系统的瓶颈,导致CPU利用率降低、系统响应时间变慢等问题。因此,在进行系统设计和优化时,需要合理规划线程的数量和使用方式,以减少线程上下文切换的影响。
多线程同步影响的是CPU参考时钟周期还是CPU核心时钟周期?
多线程同步通常会影响CPU参考时钟周期而不是CPU核心时钟周期。
多线程同步是指在多线程编程中,为了避免多个线程同时对同一资源进行访问而引发的竞态条件问题,需要采取同步控制措施。常用的同步控制手段包括互斥锁、信号量、条件变量等。
在多线程同步过程中,由于需要对共享资源进行访问控制,因此需要进行加锁解锁等操作,这会增加线程的执行时间和CPU的参考时钟周期。但是,它并不会直接影响CPU内核处理指令的速度,也就是不会影响CPU核心时钟周期。
需要注意的是,在多线程编程中,过多的同步控制可能会降低系统的性能。因此,在进行系统设计和优化时,需要合理规划线程的数量和使用方式,以减少多线程同步所带来的开销。
缓存和内存访问影响的是CPU参考时钟周期还是CPU核心时钟周期?
缓存和内存访问的影响是在CPU参考时钟周期上的,而不是在CPU核心时钟周期上的。
CPU参考时钟周期指的是CPU与主板时钟之间的交互,这个时钟周期考虑了一切与CPU有关的延迟,包括内部数据总线延迟、前端总线延迟、二级缓存延迟、内存引擎延迟等等。根据CPU的架构不同,这个参考时钟周期的值也会有所不同。
而CPU核心时钟周期是指CPU内部核心运行的时钟周期,每个核心都有自己的时钟周期。计算机系统中各种资源的工作效率都以CPU核心时钟周期为基准,因此IPC的计算公式也是基于CPU核心时钟周期的。
缓存和内存访问对CPU参考时钟周期的影响很大。由于内存带宽有限,每次内存访问都需要等待一定的时间才能获得数据,这个等待的时间称为内存延迟。CPU会在等待内存的同时继续执行其他指令,但是如果等待时间过长,就会导致CPU的效率降低,从而影响整个系统的性能。为了降低内存延迟,CPU会采用一系列技术,如缓存、预取等。缓存是一个高速的临时存储器,用于存放最常使用的数据和指令,以减少CPU等待内存的时间,提高数据访问效率。内存和缓存之间的数据传输通常需要消耗一定的参考时钟周期,所以缓存和内存访问都会影响CPU的参考时钟周期。
因此,在进行系统设计和优化时,需要合理配置和优化缓存和内存访问,以提高系统的整体性能。
线程上下文切换影响的是CPU的参考时钟周期,而IPC的计算公式是CPU的核心时钟周期,那么为什么线程上下文切换与IPC有关?
确实,IPC的计算公式为每个CPU核心执行的指令数除以核心时钟周期,而线程上下文切换涉及到CPU参考时钟周期的影响。
事实上,线程上下文切换和IPC之间的关系比较复杂,不是简单的一对一的关系。在计算IPC时,需要考虑CPU的各种硬件资源的利用率,包括指令流水线、缓存效率、内存带宽等等。而线程上下文切换会占用CPU的时间和资源,降低CPU的硬件资源的利用率,进而降低IPC。
//即不是通过影响CPU核心时钟周期来影响IPC的,是通过影响CPU硬件利用率而影响到每个时钟周期内能够完成的指令数量,进而影响IPC的
具体来说,线程上下文切换的开销通常是在软件层面产生的,涉及线程之间的切换、临界区保护等操作。在进行线程上下文切换时,CPU需要保存和恢复线程状态,这会占用一定的参考时钟周期。这些开销会导致CPU在处理每个线程时的效率降低,从而降低IPC。
当然,线程上下文切换只是影响IPC的众多因素之一,还有很多其他因素也会对IPC产生重要的影响,比如指令流水线的效率、缓存的命中率、内存带宽等等。在进行系统设计和优化时,需要综合考虑多个因素,并寻找最佳的性能平衡点,以提高系统的IPC和性能。
多线程同步影响的是CPU的参考时钟周期,而IPC的计算公式是CPU的核心时钟周期,那么为什么多线程同步与IPC有关?
多线程同步对CPU参考时钟周期的影响是通过降低指令执行的效率来实现的。在多线程同步的情况下,线程之间需要协调和等待,这样会导致每个线程在执行指令时需要暂停或等待某些资源的释放,进而增加指令的延迟和执行时间。这就意味着,在多线程同步的情况下,每个核心在单位时间内可以执行的指令数(IPC)会减少。
虽然IPC的计算公式中使用的是CPU的核心时钟周期,但是实际上IPC是衡量CPU性能的指标之一,它受到CPU的总体性能、架构设计、指令并行度等因素的影响。而多线程同步会降低每个核心的实际效率,从而导致IPC的下降。因此,多线程同步与IPC是有关联的。
当多个线程同时运行时,它们需要共享不同的资源(如内存、I/O等),这些资源的访问需要进行同步。如果同步操作效率低下,将导致线程之间的竞争变得更加激烈,进而影响整个系统的性能。因此,优化多线程同步的效率和机制可以有效提高系统的整体性能,从而提高IPC。
多线程同步暂停时也会占用CPU吧?
是的,多线程同步暂停时也会占用CPU资源。当某个线程需要进行同步操作时,它需要通过锁、信号量或者其他同步机制来协调线程之间的动作。这种同步机制在实现上通常会涉及到一些等待操作,比如线程挂起、加锁等,这些过程都需要占用CPU资源。
当一个线程被挂起等待同步操作完成时,它会进入睡眠状态,此时不再占用CPU时间片。但是,一旦同步操作完成,线程就会被唤醒并重新获得CPU时间片,从而开始继续执行。在这个过程中,线程需要等待一定的时间,这就导致了它在这段时间内无法执行任何指令,虽然此时线程不再占用CPU时间片,但是CPU还是需要对其进行调度,使其进入等待队列和唤醒队列中。
这种等待和唤醒操作的时间会影响整个系统的性能,会降低线程的效率,进而影响IPC。因此,在设计和实现多线程应用程序时,需要使用合适的同步机制和算法,避免不必要的等待和唤醒操作,从而提高整个系统的性能和吞吐量。
缓存和内存访问影响的是CPU的参考时钟周期,而IPC的计算公式是CPU的核心时钟周期,那么为什么缓存和内存访问与IPC有关?
对于IPC(Instructions Per Cycle),它的计算公式是指令数除以时钟周期数,其中时钟周期数是指CPU核心时钟周期。而缓存和内存访问虽然不会直接影响CPU核心时钟周期,但它们确实会影响每个时钟周期内CPU所执行指令数量的多少,从而间接地影响IPC的值。
由于缓存和内存访问的速度和效率不同,每个时钟周期内能够完成的指令数量也会不同。如果CPU缓存命中率高、缓存访问速度快,或者能够高效处理内存访问,那么每个时钟周期内能够完成的指令数量就会相应地增加,从而提高IPC的值;反之,如果缓存失效率高或内存访问速度慢,每个时钟周期内能够完成的指令数量就会降低,从而降低IPC的值。
因此,虽然缓存和内存访问影响的是CPU的参考时钟周期,但它们确实会影响每个时钟周期内能够完成的指令数量,间接地影响CPU的性能和IPC的值。
翻译:
大多数CPU使用一个时钟信号来控制它们的顺序操作。时钟信号由外部发生器产生,每秒提供一定数量的脉冲。时钟脉冲的频率确定了CPU执行指令的速率。因此,时钟信号越快,CPU每秒执行的指令就越多。
时钟信号是计算机系统中非常关键的一个组成部分,它对于保证计算机系统的稳定性和正确性起着至关重要的作用。时钟信号可以看作是CPU和其他系统组件之间通信的基础,在时钟信号的作用下,CPU能够准确地分配资源,按照正确的顺序执行操作,并在正确的时间点完成各种任务。
除了影响CPU的处理速度以外,时钟信号还可以影响CPU的电源消耗、散热等方面。因为随着时钟信号频率的提高,CPU需要更多的电力和更好的散热系统才能保持正常工作。因此,在设计CPU时需要权衡处理速度、功耗和散热等多个因素,以提供最佳的性能和可靠性。
公式 Frequency = Clockticks / Time ,其中的频率指的是CPU运行的频率,通常是指CPU核心的时钟频率。根据该公式,我们可以通过计算单位时间内CPU核心运行的总时钟周期数来得到CPU的实际工作频率。
然而,现代的大多数CPU,包括Intel和AMD的CPU,都采用了动态频率调整技术,也就是所谓的Turbo Boost和Turbo Core等技术。这些技术允许CPU动态地增加和减少其工作频率,以在功耗节约和性能之间进行权衡。这意味着CPU的工作频率不再是固定的,因此上述公式无法准确计算CPU的实际工作频率。
对于这种情况,计算器样例给出了一个实验,使用了Skylake i7-6000处理器作为测试对象。该处理器的基础频率为3.4 GHz,但在实验中发现,当CPU核心处于负载高峰时,处理器会自动提高其工作频率,达到更高的性能水平。因此,在使用动态频率调整技术的CPU中,实际工作频率将随着负载的变化而不断变化,这也是为什么现代CPU不再有固定工作频率的原因。

$ perf stat -e cycles,ref-cycles ./a.exe
43340884632 cycles     # 3.97 GHz
37028245322 ref-cycles # 3.39 GHz
10,899462364 seconds time elapsed

//-e cycles应该是包含动态频率调整技术的cycles
//-e ref-cycles应该是设置这个cpu freq后的cycles
//所以说,实际的准确的cycles应该是这个-e cycles???
ref-cycles是一种事件计数器,它记录了CPU参考时钟周期数,也就是排除了动态频率调整等因素导致的额外时钟周期数,只记录了CPU执行指令所用的“主动”时钟周期数。在Skylake i7-6000处理器中,它的基础频率为3.4 GHz,即每个外部时钟脉冲对应的内部周期数为34。因此,通过乘以34,可以得到内部时钟周期数。
cycles也是一种事件计数器,它记录了真实的CPU时钟周期数,包括了动态频率调整等因素导致的额外时钟周期数。使用cycles来统计程序的性能表现可以更加准确,同时也可以计算出Turbo Boost的利用率。
在进行代码测试的时候,使用ref-cycles可以避免时钟频率上下波动但实际代码运行次数相同的情况下因时钟变化造成的误差,更好地判断哪个版本的代码更快。而使用cycles可以更加全面地了解程序的实际执行效率和性能瓶颈,包括动态频率调整等因素对性能的影响。
4.7 Cache miss    //缓存缺失
正如第3.5节所讨论的,任何在特定级别缓存中丢失的内存请求都必须由更高级别的缓存或DRAM提供服务。这意味着这种内存访问的延迟会显著增加。典型的内存子系统组件延迟如表3所示。

Table 3: Typical latency of a memory subsystem.
当内存请求在最后一级缓存(LLC)中丢失并到达主存储器(DRAM)时,性能会大大下降。Intel® Memory Latency Checker(MLC)是一种用于测量内存延迟和带宽以及它们随着系统负载的增加而如何变化的工具。 MLC可用于为测试系统建立基线,进行性能分析。
缓存未命中可能发生在指令和数据两种情况下。根据Top-Down微架构分析(见第6.1节),指令(I-cache)缓存未命中被称为前端停顿(Front-End stall),而数据缓存(D-cache)未命中被称为后端停顿(Back-End stall)。当指令获取期间出现I-cache未命中时,这被归因于前端问题。因此,当所请求的数据在D-cache中找不到时,这将被归类为后端问题。
Linux perf用户可以通过运行以下命令收集L1高速缓存未命中数:

$ perf stat -e mem_load_retired.fb_hit,mem_load_retired.l1_miss,mem_load_retired.l1_hit,mem_inst_retired.all_loads -- a.exe
29580 mem_load_retired.fb_hit
19036 mem_load_retired.l1_miss
497204 mem_load_retired.l1_hit
546230 mem_inst_retired.all_loads

以上是L1数据缓存的所有加载项的细分。我们可以看到,只有3.5%(19036/546230)的加载项在L1缓存中未命中。我们可以进一步细分L1数据缺失,并通过运行以下命令分析L2缓存行为:

$ perf stat -e mem_load_retired.l1_miss,mem_load_retired.l2_hit,mem_load_retired.l2_miss -- a.exe
19521 mem_load_retired.l1_miss
12360 mem_load_retired.l2_hit
7188 mem_load_retired.l2_miss

从这个例子中,我们可以看到37%(7188/19521)在L1 D-cache未命中的加载项也在L2缓存中未命中。同样地,L3缓存的细分也可以进行。
4.8 Mispredicted branch    //错误的分支预测
现代CPU尝试预测分支指令(跳转或不跳转)的结果。例如,当处理器看到以下代码时:

dec eax
jz .zero
# eax is not 0
...
zero:
# eax is 0

指令jz是一个分支指令,为了提高性能,现代CPU架构尝试预测这种分支的结果。这也称为“推测执行”。处理器会猜测,例如,该分支不会被执行,并将执行对应于eax不为0的情况的代码。但是,如果猜测错误,这就被称为“分支预测失败”,CPU需要撤消最近所做的所有推测性工作。这通常涉及10到20个时钟周期的惩罚。
Linux perf用户可以通过运行以下命令来检查分支预测失败的数量:

$ perf stat -e branches,branch-misses -- a.exe
358209 branches
14026 branch-misses #3,92% of all branches

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值