从指令流水线视角解析:数据依赖指令为何出现两种阻塞情况?
数据依赖指令的阻塞问题:Cortex-A76上两段FMLA代码的执行差异
核心结论
存在数据依赖的相邻指令并非一定会相互阻塞,是否阻塞取决于依赖类型、处理器流水线特性以及指令间的间隔/可用的无关指令。结合你给出的两段代码和Cortex-A76的特性,具体原因如下:
第一段代码:无阻塞的原因
先看第一段代码的依赖关系:
.loop1: fmla v2.4s, v0.4s, v1.4s // 写v2 → 后续v4依赖v2 fmla v3.4s, v0.4s, v1.4s // 写v3 → 后续v5依赖v3 subs x0, x0, #1 fmla v4.4s, v0.4s, v2.4s // 读v2(依赖第一条FMLA) fmla v5.4s, v0.4s, v3.4s // 读v3(依赖第二条FMLA) fmla v6.4s, v0.4s, v4.4s // 读v4(依赖第四条FMLA) fmla v7.4s, v0.4s, v5.4s // 读v5(依赖第五条FMLA) fmla v8.4s, v0.4s, v6.4s // 读v6(依赖第六条FMLA) fmla v9.4s, v0.4s, v7.4s // 读v7(依赖第七条FMLA) bne .loop1
依赖间隔足够,延迟被隐藏
Cortex-A76中fmla指令的延迟为4,指指令发射后需4个周期生成可用结果。但第一段代码里,每个写寄存器的fmla和读它的fmla之间,间隔了至少2条无关指令(比如写v2的指令1到读v2的指令4,中间有指令2、3)。同时处理器支持乱序执行和超标量发射,可以在等待v2结果的间隙,先执行其他不依赖v2的指令,完全填满流水线,不会出现停顿。独立依赖链并行执行
代码包含两条完全独立的依赖链:v2→v4→v6→v8和v3→v5→v7→v9,链间无数据依赖,Cortex-A76的FP单元可同时调度两条链的指令并行执行,进一步避免阻塞。
第二段代码:出现阻塞的原因
修改后的代码引入了循环携带的依赖:
.loop1: fmla v2.4s, v0.4s, v8.4s // 读v8(依赖上一次循环的最后一条FMLA) fmla v3.4s, v0.4s, v9.4s // 读v9(依赖上一次循环的倒数第二条FMLA) subs x0, x0, #1 fmla v4.4s, v0.4s, v2.4s fmla v5.4s, v0.4s, v3.4s fmla v6.4s, v0.4s, v4.4s fmla v7.4s, v0.4s, v5.4s fmla v8.4s, v0.4s, v6.4s // 写v8(供下一次循环的第一条指令使用) fmla v9.4s, v0.4s, v7.4s // 写v9(供下一次循环的第二条指令使用) bne .loop1
这里的关键是:当前循环的前两条fmla指令需要读取v8和v9,而这两个寄存器的值是上一次循环的最后两条指令才生成的,形成了跨迭代的依赖链:上一次循环的v8写指令 → 当前循环的v2读指令。
由于fmla延迟为4,当前循环的第一条指令必须等待上一次循环的最后一条fmla完成(4个周期)才能执行,导致每个循环迭代都要等待4个周期的停顿,无法利用乱序执行或并行调度隐藏延迟。最终执行时间t2变成t1的4倍,完全符合延迟带来的阻塞效果。
总结
- 数据依赖指令是否阻塞,核心看依赖是否会导致流水线无法填充:若依赖间有足够无关指令,处理器可通过乱序执行隐藏延迟,就不会阻塞;
- 循环携带的依赖是最易导致严重阻塞的场景,它会让迭代之间必须串行等待,无法利用并行或乱序执行的优势。
内容的提问来源于stack exchange,提问作者zhiyujiang
相关产品推荐
相关产品推荐

