原理篇

指令级并行原理

名词

Clock Cycle Time

主频的概念大家接触的比较多,而 CPU 的 Clock Cycle Time(时钟周期时间),等于主频的倒数,意思是 CPU 能够识别的最小时间单位,比如说 4G 主频的 CPU 的 Clock Cycle Time 就是 0.25 ns,作为对比,我们墙上挂钟的Cycle Time 是 1s

例如,运行一条加法指令一般需要一个时钟周期时间

CPI

有的指令需要更多的时钟周期时间,所以引出了 CPI (Cycles Per Instruction)指令平均时钟周期数

IPC

IPC(Instruction Per Clock Cycle) 即 CPI 的倒数,表示每个时钟周期能够运行的指令数

CPU 执行时间

程序的 CPU 执行时间,即我们前面提到的 user + system 时间,可以用下面的公式来表示

  1. 程序 CPU 执行时间 = 指令数 * CPI * Clock Cycle Time

鱼罐头的故事

加工一条鱼需要 50 分钟,只能一条鱼、一条鱼顺序加工,可以将每个鱼罐头的加工流程细分为 5 个步骤:

  • 去鳞清洗 10分钟
  • 蒸煮沥水 10分钟
  • 加注汤料 10分钟
  • 杀菌出锅 10分钟
  • 真空封罐 10分钟

原理篇 - 图1

即使只有一个工人,最理想的情况是:他能够在 10 分钟内同时做好这 5 件事,因为对第一条鱼的真空装罐,不会影响对第二条鱼的杀菌出锅…

指令重排序优化

事实上,现代处理器会设计为一个时钟周期完成一条执行时间最长的 CPU 指令。为什么这么做呢?可以想到指令还可以再划分成一个个更小的阶段,例如,每条指令都可以分为: 取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回 这 5 个阶段。

原理篇 - 图2

术语参考:

instruction fetch (IF)

instruction decode (ID)

execute (EX)

memory access (MEM)

register write back (WB)

在不改变程序结果的前提下,这些指令的各个阶段可以通过重排序组合来实现指令级并行,这一技术在 80’s 中叶到 90’s 中叶占据了计算架构的重要地位。

提示:

分阶段,分工是提升效率的关键!指令重排的前提是,重排指令不能影响结果,例如:

  1. // 可以重排的例子
  2. int a = 10; // 指令1
  3. int b = 20; // 指令2
  4. System.out.println( a + b );
  5. // 不能重排的例子
  6. int a = 10; // 指令1
  7. int b = a - 5; // 指令2

支持流水线的处理器

现代 CPU 支持多级指令流水线,例如支持同时执行 取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回 的处理器,就可以称之为五级指令流水线。这时 CPU 可以在一个时钟周期内,同时运行五条指令的不同阶段(相当于一条执行时间最长的复杂指令),IPC = 1,本质上,流水线技术并不能缩短单条指令的执行时间,但它变相地提高了指令地吞吐率。

原理篇 - 图3

SuperScalar理器

大多数处理器包含多个执行单元,并不是所有计算功能都集中在一起,可以再细分为整数运算单元、浮点数运算单元等,这样可以把多条指令也可以做到并行获取、译码等,CPU 可以在一个时钟周期内,执行多于一条指令,IPC> 1

原理篇 - 图4

CPU缓存结构原理

CPU缓存结构

原理篇 - 图5

速度比较

原理篇 - 图6

查看 cpu 缓存行

  1. root@yihang01 ~ cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
  2. 64

cpu 拿到的内存地址格式是这样的

  1. [高位组标记][低位索引][偏移量]

原理篇 - 图7

CPU缓存读

读取数据流程如下

  • 根据低位,计算在缓存中的索引
  • 判断是否有效
    • 0 去内存读取新数据更新缓存行
    • 1 再对比高位组标记是否一致
      • 一致,根据偏移量返回缓存数据
      • 不一致,去内存读取新数据更新缓存行

CPU缓存一致性

MESI 协议

  1. E、S、M 状态的缓存行都可以满足 CPU 的读请求
  2. E 状态的缓存行,有写请求,会将状态改为 M,这时并不触发向主存的写
  3. E 状态的缓存行,必须监听该缓存行的读操作,如果有,要变为 S 状态

原理篇 - 图8

  1. M 状态的缓存行,必须监听该缓存行的读操作,如果有,先将其它缓存(S 状态)中该缓存行变成 I 状态(即6的流程),写入主存,自己变为 S 状态
  2. S 状态的缓存行,有写请求,走 4. 的流程
  3. S 状态的缓存行,必须监听该缓存行的失效操作,如果有,自己变为 I 状态
  4. I 状态的缓存行,有读请求,必须从主存读取
    原理篇 - 图9

    详细见

并发编程_原理.pdf