趣谈Java高并发(一)并发的硬件相关(一):指令流水

一、什么是指令流水

1、首先了解一下什么是并发,什么是并行。

  1. 所谓的并行,同时包含同时性和并发性。同时性说的是在同一个时刻有一个以上的时间发生,而并发性是指有一个以上的任务在同一时间段发生。也就是说并行 = 并发 + 同一时刻发生。
  2. 并行性有四个级别:作业/程序级、任务/进程级、指令之间级、指令之内级。前两级使用算法进行调度实现,也就是程序、线程调度,要实现这两个级别的并行至少需要两个CPU内核,一个内核的时候是并发。后面两个级别需要硬件进行支持,其中指令之间并行也是需要多个内核的,但是指令之内级的并行只需要一个内核。指令流水就是后两个级别的并行,且主要是指令内的并行。

2、什么是流水线

有这么一段话:

大家好,我是来自富土康3号流水线的工人,张全蛋。…

在印象中,流水线什么的都是制造业才有的东西,《国富论》提出:劳动分工是提高效率的关键。流水线就是劳动分工的产物,流水线根据产品生产的不同阶段进行生产分工,每个生产段负责某个具体环节,从而提高生产效率。

将流水线的思想用到指令执行上,就是指令流水。

指令流水的原理很简单,利用指令完成需要多个阶段的特点,将一条指令的多个阶段进行流水线式的执行,以提高CPU各个组件的利用率。

先来一个不正经的比方,假如我们一群人要生产一个洋娃娃,但是甲只会安装头,乙只会安装身体,丙只会给洋娃娃穿衣服。那我们生产洋娃娃的时候,为了追求最大的产量肯定是这个过程:

  1. 甲安好头后给乙安装身体,开始下一个洋娃娃的头部安装。
  2. 乙接过甲只有头的洋娃娃后给她安装上身体,然后把她交给丙,继续安装甲递过来的下一个只有头的洋娃娃
  3. 丙接过乙没有衣服的洋娃娃后给她穿上衣服,然后把她放在产品柜里面,继续给乙递过来的没有衣服的洋娃娃穿衣服
  4. 如此循环往复…

再来一个例子,现在我来模拟一段程序,就是饭店炒菜上菜的场景。因为CPU不是一个单独的组件,而是一群组件,所以我们用一个后厨团队来模拟CPU,用前台下的菜单来模拟一条条指令。在这个厨师团队中,肯定是有一个老大(CU)的,就是厨师长,他负责协调团队。还有一个专门跑腿拿食材的甲,一个专门处理食材的乙,一个专门抄菜的丙,一个专门装盘送菜的丁,现在开始场景模拟:

  1. 前台:一号桌宫保鸡丁一份、土豆肉丝一份、麻辣鱼一份,拔丝日本豆腐一份…
  2. 老大:甲,去拿一份宫保鸡丁的材料。
  3. …:甲跑去拿宫保鸡丁的材料,乙丙丁这个时候是闲着的。
  4. 老大:乙把这宫保鸡丁的材料处理了,甲去拿土豆肉丝的材料。
  5. …:甲跑去拿土豆肉丝的材料,乙处理宫保鸡丁的材料,丙丁这个时候是闲着的。
  6. 老大:丙把宫保鸡丁炒了,乙把土豆肉丝的材料处理了,甲去拿麻辣鱼的材料。
  7. …:甲去拿麻辣鱼的材料,乙处理土豆肉丝的材料,丙炒着宫保鸡丁,丁还闲着的。
  8. 老大:丁把宫保鸡丁装盘送出去,丙把土豆肉丝炒了,乙把麻辣鱼的材料处理了,甲去拿拔丝日本豆腐的材料来。
  9. …:甲去拿拔丝日本豆腐的材料,乙在处理麻辣鱼的材料,丙在炒土豆肉丝,丁把宫保鸡丁装盘送给了客人。
  10. 如此循环往复,直到饭店打烊…

最后来一个正经的解释,假设每一条计算机指令的执行都需要经历如下六个步骤:

  1. 取址:FI;
  2. 指令译码:DI;
  3. 计算操作数地址:CO;
  4. 取操作数:FO;
  5. 执行指令:EI;
  6. 写操作数:WO;

那么指令流水线就相当于下图这样的一个流程:
指令流水图示

经过上面三个例子,应该可以感受什么是指令流水了,接下来讨论为什么要这样子做?

二、为什么要指令流水?

仔细看看我刚刚写的三个例子,里面都有两个特点:

  1. 所有的组件或者说人员,在除开第一个指令开始的时候,其他任何时候所有的人员都是在运行忙碌的,就饭店那个例子就是到最后发现没有人是闲着的了。
  2. 所有的任务都被划分为时间相同的步骤,每一个步骤都能够同时完成。

就第一个特点,可以看出来:指令流水线相较于一个指令完成后再开始下一个指令的情况,就理论执行效率上来说,一条指令能划分为几个流水步骤,处理速率就能够达到几倍!!这是非常的的效率提升,所以指令流水是非常有必要的。

三、指令流水的限制条件

指令流水线可以提高效率,但是现实并没有理论那么美好,有几个方面的原因造成不可能出现理论上的效率:

  1. 结构相关:重叠执行的指令对同一资源竞争而产生冲突,例如访存冲突(一条指令取数据,一条指令取地址,但是地址与数据在同一个存储器,该存储器只有一个访问接口,这个时候就需要暂停后面一个指令的执行以解决冲突)
  2. 数据相关:重叠执行的指令有可能会改变对操作数的写访顺序,从而导致数据相关冲突。例如后一条指令需要前一条指令的执行结果,但是当后一条指令需要读取前一条指令的结果时,前一条指令还没有将结果写回去。这个时候需要暂停后面的指令。常见的数据相关有写后读相关(下一条指令读到旧值)、读后写(上一条指令读到新值)、写后写(下一条指令的修改值被覆盖)。
  3. 控制相关:转移指令更改PC值,导致后面的指令流水全部失效,直接断流。
  4. 指令流:程序指令是我们编码的,不了解底层原理,所写的代码指令无法构成指令流水,而且一条指令多个步骤的执行时间不一致,会卡流。

四、优化指令流水

指令流水的效率提升简直让人垂涎欲滴,既然提出这个理想状况,那么就要克服困难去接近那个目标,前面三个限制条件只有硬件支持,最后面一个就看起来可以搞点文章,这就是指令重排。

指令重排:

在不影响as-if-serial语义的情况下,对程序指令进行乱序执行。

就是这个万恶的优化带来了一系列问题,但是比起带来的性能提升,也就忍了。

下一篇博文我将分享指令重排的见解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章