数据冒险

(2) 数据冒险

        流水线使原先有先后顺序的指令同时处理,当出现某些指令的组合时,可能会导致指令使用了错误的数据。

看下面这个例子:

寄存器访问的数据冒险

 

        add R1,R2,R3将寄存器R2和R3的和赋给R1,add R4,R1,R5将寄存器R1和R5的和赋给R4,很明显,R1在第1条指令中被更新,然后在第2条指令中使用,可是R1必须在第5个cycle之后才被更新,而指令2再第4个cycle就要访问R1,这时的R1还不是正确的值,执行结果出错。

        一种解决方法是在这2条语句之间增加2个cycle的等待:

通过增加等待来消除数据冒险

 

        延时2个cycle,cycle 5将第1条指令的结果写回到寄存器R1中后,第2个add指令的EX单元就可以正常执行了。

这种方法很大一个缺点就是中间增加了2个cycle的等待,影响了执行效率。实际的处理器使用直通(Forwarding)来解决这个问题。

        当硬件检测到当前指令的源操作数正好在EX/MEM流水线寄存器中时,就直接将EX/MEM寄存器的值传递给ALU的输入,而不是从寄存器堆中读数据,如图:

 

使用Forwarding,解决数据冒险

 

        不只寄存器访问有这样的问题,Memory访问也有同样的问题。看下面这个例子:

 

Memory访问的数据冒险

 

        load R1,(R2)将存储器中的值装载到R1中来,存储器的地址在R2中存储,store R1,(R3) 将R1中的值放到存储器中去。在cycle 4,load指令的MEM阶段将存储器中的值装载到MEM/WB寄存器中,在cycle 5,load指令的WB阶段将MEM/WB寄存器的值装载到寄存器R1中,而在cycle 5,store指令的MEM要将寄存器R1的值放到存储器中去,R1的值还没有被load指令更新。这个数据冒险的解决方法可以延时1个周期,也可以采用同样的Forwarding方法,将MEM/WB寄存器直通到MEM输入端。

  

使用Forwarding,解决数据冒险

        

        也不是每种数据冒险都能解决,同时又不引起stall,例如下面这个例子:

仍然需要stall的直通示例

 

        sub指令需要R1,但是R1最早也要在cycle 4才到MEM/WB寄存器中,即使将MEM/WB寄存器直通到EX的输入端,也需要延时一个cycle才能使用。

0
<think>我们正在讨论数据冒险的分类。根据引用[1]、[2]、[3]和[5],数据冒险(Data Hazard)主要分为三种类型:RAW、WAR和WAW。这些冒险都是由于指令之间的数据依赖关系在流水线中未能得到正确处理而导致的。 1. **RAW(Read After Write)冒险**:也称为写后读冒险或真数据相关(True Data Dependency)。这是最常见的数据冒险类型,发生在一条指令需要读取一个由前一条指令写入的数据,但该数据尚未被写入(即前一条指令的写操作还未完成)的情况下。例如: ``` I1: ADD R1, R2, R3 ; R1 = R2 + R3 I2: SUB R4, R1, R5 ; R4 = R1 - R5 (I2需要读取R1,而R1由I1写入) ``` 如果I1还没有将结果写入R1,I2就尝试读取R1,就会发生RAW冒险。 2. **WAR(Write After Read)冒险**:也称为读后写冒险或反相关(Anti-dependency)。这种冒险发生在一条指令需要写入一个数据,而前一条指令需要读取同一个数据,但写入操作在读取操作之前完成(即后面的指令先写,导致前面的指令读到了错误的值)。这种情况在按序流水线中较少出现,但在乱序执行中可能出现。例如: ``` I1: ADD R1, R2, R3 ; R1 = R2 + R3 I2: SUB R2, R4, R5 ; R2 = R4 - R5 (I2写入R2,而I1需要读取R2) ``` 注意:在按序流水线中,指令按程序顺序执行,所以I1一定在I2之前读取R2(此时I2还未写入R2),因此不会发生WAR冒险。但是,如果处理器支持乱序执行,I2可能先于I1执行(只要I2的操作数就绪),那么I2会先写入R2,导致I1读取到的是I2写入后的值(而不是原来的值),从而出错。 3. **WAW(Write After Write)冒险**:也称为写后写冒险或输出相关(Output Dependency)。这种冒险发生在两条指令都要写入同一个目标,但写入顺序错误(即后面的指令先写,然后前面的指令再写,覆盖了后面的结果)。同样,在按序流水线中,由于指令按序完成,不会出现WAW冒险。但在乱序执行中,如果两条指令有相同的目标寄存器,且后面的指令先执行完成,就会导致最终结果错误。例如: ``` I1: ADD R1, R2, R3 ; R1 = R2 + R3 I2: SUB R1, R4, R5 ; R1 = R4 - R5 ``` 如果I2先于I1完成,那么R1的值将是I1执行后的结果(覆盖了I2的结果),而正确的顺序应该是I2的结果覆盖I1(因为程序顺序是I1先,I2后,所以最后R1应该是I2的结果)。 需要注意的是,引用[5]提到读后读(RAR)不是冒险,因为多条指令读取同一个位置不会产生冲突。 总结: - RAW(写后读):真数据相关,必须保证写操作在读操作之前完成。 - WAR(读后写):反相关,需要保证读操作在写操作之前完成(避免后面的写操作覆盖了前面指令还需要读取的值)。 - WAW(写后写):输出相关,需要保证写操作按照程序顺序完成。 在流水线处理器中,解决这些冒险的方法包括: - 对于RAW:使用转发(旁路)技术,将结果直接从执行阶段传递到需要它的指令,避免等待写回;或者插入气泡(停顿)来等待数据可用。 - 对于WAR和WAW:在按序流水线中,由于指令按程序顺序执行和写回,通常不会发生(所以主要出现在乱序执行处理器中)。乱序处理器可以通过寄存器重命名技术来解决,即为相同的逻辑寄存器分配不同的物理寄存器,从而消除名称相关。 引用[2]特别指出,转发技术并不能解决所有的数据冒险,例如Load-Use冒险(即一条加载指令后紧跟着使用该加载结果的指令)可能需要插入停顿。 [^1] [^2] [^3] [^5]</think>### 数据冒险的类型及分类 在计算机体系结构的流水线设计中,数据冒险(Data Hazard)主要分为以下三种类型,它们都源于指令间的数据依赖关系在流水线中未能正确同步: --- #### 1. **RAW(Read After Write)冒险** **定义**:后一条指令需要读取前一条指令尚未写入完成的数据。 **本质**:真数据相关(True Data Dependency) **示例**: ```asm ADD R1, R2, R3 ; 写入 R1 SUB R4, R1, R5 ; 读取 R1(需等待 ADD 完成) ``` **影响**:若 SUB 在 ADD 写回 R1 前执行,会读取错误值。 **解决方案**: - 转发(Forwarding):将 ADD 的执行结果直接传递给 SUB[^2] - 插入气泡(Stalling):强制 SUB 等待一个周期[^1] --- #### 2. **WAR(Write After Read)冒险** **定义**:后一条指令写入数据时,前一条指令尚未完成读取。 **本质**:反相关(Anti-Dependency) **示例**: ```asm LD R1, [R2] ; 读取 R2 ADD R2, R3, R4 ; 写入 R2(需等待 LD 完成读取) ``` **影响**:若 ADD 在 LD 读取 R2 前写入 R2,LD 会读到新值而非原值。 **典型场景**:寄存器重命名或乱序执行时易发生[^3][^5]。 **解决方案**: - 寄存器重命名:为 R2 分配临时物理寄存器 - 按序提交指令 --- #### 3. **WAW(Write After Write)冒险** **定义**:两条指令写入同一位置时,后一条指令先于前一条完成写入。 **本质**:输出相关(Output Dependency) **示例**: ```asm MUL R1, R2, R3 ; 写入 R1(多周期操作) ADD R1, R4, R5 ; 写入 R1(若先完成会覆盖 MUL 结果) ``` **影响**:最终结果取决于写入顺序,而非程序逻辑顺序[^5]。 **典型场景**:长延迟指令(如乘除)与短指令竞争同一目标寄存器。 **解决方案**: - 乱序处理器:通过重排序缓冲区(ROB)确保按序提交 - 插入停顿:强制后续指令等待 --- ### 关键区别总结 | 类型 | 依赖关系 | 本质原因 | 常见解决方案 | |------|----------------|------------------------|----------------------| | RAW | 读依赖于写 | 真数据相关 | 转发、气泡插入[^2] | | WAR | 写依赖于读 | 寄存器/内存名称冲突 | 寄存器重命名[^3] | | WAW | 写依赖于写 | 输出顺序冲突 | 按序提交、停顿[^5] | > ⚠️ **注意**:读后读(RAR)不是冒险,因为多指令读取同一数据不会冲突[^5]。 --- ### 示例代码分析 ```python # WAR 冒险示例(Python 模拟) R2 = 10 def hazard_demo(): # 指令1:读取 R2 data = R2 # LD R1, [R2] # 指令2:写入 R2 R2 = data + 5 # ADD R2, R1, 5 (若提前执行会破坏指令1的读取) return R2 ``` 若 `ADD` 在 `LD` 完成前修改 R2,`LD` 将读取到错误值。 [^1][^2][^3][^5]
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值