由 Intel 提出,经典论文: (PDF) A Top-Down method for performance analysis and counters architecture 作者在论文中提到,这个方式已经被 vTune 采纳了。

Top-Down性能分析方法(原理篇):揭秘代码运行瓶颈面对性能分析一筹莫展?不清楚该关注哪些性能指标或事件?借助Top - 掘金 这篇文章对论文进行了讲解,很细致。

最重要最重要的需要时刻记在心里的图,就是论文中的 Figure 2: The Top-Down Analysis Hierarchy

iCache miss 和 iTLB miss 都归在 Frontend Bound 上,dTLB miss 和 dCache miss 归在 Backend Bound 上。

  • iTLB miss:处理器根据 PC 寄存器中的虚拟内存地址进行内存中代码获取,但是 TLB 中没有对应虚拟内存地址翻译到物理内存地址的页表项,所以我们必须要从内存中的页表来拿,这就是 iTLB miss;
  • iCache miss:处理器通过翻译得到了 PC 寄存器中的虚拟内存地址对应的物理内存地址后,去内存中拿数据,但是 Cache 中没有,只能从内存中去拿,这就是 iCache miss。
  • dTLB miss 和 dCache miss 就是我们平时一般说的两种 miss,不需要过多解释。

为什么 Memory Bound 被归类为后端 Bound?iTLB miss/iCache miss 不还是得从内存中拿页表项/指令吗?

这是因为性能分析工具在归类时,看的不是“最终是不是访问了内存”,而是 “CPU 流水线的哪个部分被卡住了”。

另外,取指令速度过快导致频繁读取代码段打满内存带宽,几乎是不可能的。在代码段中没有办法优化访存行为,但是在数据计算的时候对内存使用方式是可以进行优化的(优化 Memory Bound 思路^)。

不同应用下不同 Bound 原因占比:

Category Client/Desktop Application Server/Database/Distributed application HPC Application
Retiring 20-50% 10-30% 30-70%
Back-End Bound 20-40% 20-60% 20-40%
Front-End Bound 5-10% 10-25% 5-10%
Bad Speculation 5-10% 5-10% 1-5%

以上数据来自:Top-down Microarchitecture Analysis Method

处理器前端和后端 / Front-end and Back-end in Microarchitecture

前端和后端的分界线应该是微指令队列。前端生成的微指令会经由这里被送到后端。

CPU 设计的前端和后端是指 CPU 的两个主要部分,分别负责指令的解析执行

所谓的前端主要是由指令拾取(Instruction Fetch)、指令解码(Instruction Decode)、分支预测(branch predict)组成。

读取指令,解码指令,预测分支,转换指令为微操作

后端负责执行 CPU 指令并更新寄存器状态。

优化 Memory Bound 思路

Memory Latency Bound 是因为单次小 size 的内存访问过多,从而导致内存带宽没有拉上去。

**1. **

Memory Bandwidth Bound 表示内存带宽被打满了,需要找到瓶颈并进行优化。

1. (优化缓存命中)使用数组结构体(SoA)来代替结构体数组(AoS):

// AoS:每个对象占用一个缓存行,但遍历 age 时只使用其中一小部分
struct Person { int id; int age; double salary; };
std::vector<Person> people;

// SoA:将 age 单独放在连续数组中,遍历 age 时缓存行全部有效
struct People { std::vector<int> id; std::vector<int> age; std::vector<double> salary; };

如果只需要访问对象的某个字段,使用 SoA 布局可大幅减少缓存污染。

2. (优化缓存命中)数组代替链表

3. (减少访存次数,从而降低访存总大小)