(十九)冒险和预测,解决危险就能抓住机会

      前面我们讲到,流水线技术能够显著提高CPU的吞吐率,只不过我们需要解决结构冒险、数据冒险、控制冒险三个方面的问题,这些问题和CPU的运行机制密切相关。

一、结构冒险:

      结构冒险问题本质是一个硬件资源抢占的问题,也就是电路问题。同一个时钟周期内,同时运行两条计算机指令的不同阶段的指令时,如果会恰好用到相同的电路,就产生了电路竞争的问题。下图是一个典型的内存数据访问的冲突问题:

由图可见,第一条指令和第四条在同一个时钟周期内前者执行访问内存操作,后者执行了取指令操作,由于内存只有一个地址译码器作为地址输入,因此没办法同时执行指令一和指令是的访问内存操作。

类似这种“资源冲突”问题的解决方案其实只有一种,就是增加资源,我们可以将内存分为两部分,一部分称为“指令内存”,另一部分称为“数据内存”,各自拥有译码器。这种将内存分为两部分的方式称为哈佛架构,但是它并不是冯·诺依曼体系的结构的方案,将内存分为指令内存和数据内存的方式固然能够解决一部分资源冲突的问题,但它使得我们不能够动态分配内存,丧失了灵活性。现代计算机体系在告诉缓存的设计上借鉴了这种架构,将高速缓存分为了指令缓存和数据缓存,如下图:

由于CPU是从高速缓存读取数据而不是直接读取内存,因此,这种方式也就解决了数据访问和指令访问同时的资源冲突问题。

二、数据冒险:三种不同的依赖关系

      和上述结构冒险问题不同的是,数据冒险的问题在于程序逻辑,当多个指令的执行相互之间有数据上的依赖关系,问题就产生了,主要是三种依赖关系:先写后读、先读后写、写后再写。

  • 先写后读,程序中对一个变量对先写入再读取操作对应着不同的指令
  • 先读后写,后续的指令对变量进行写入,需要读取前置指令赋值的变量的值
  • 写后再写,变量的赋值先后顺序需要保障,否则最终得到错误的值

三、解决数据冒险问题

由于在执行指令之前事先知道要访问的寄存器和内存地址,因此可以判定其是否会触发冒险,如会触发则通过插入空指令NOP,什么都不做,相当于让CPU停顿一个时钟周期这样的方式来避免发生冒险。

四、总结

结构冒险,主要是由于并行指令使用系统的硬件电路所引发,通过增加硬件资源的方式可解决;

数据冒险,主要是由于不同指令执行所要操作的数据有交叉,为了指令执行的准确性,需要保障一定的执行顺序,通过插入NOP指令解决。