大多数通用处理器确实会在分支预测错误时刷新管道。条件分支对性能的负面影响除了对分支预测的广泛研究(以及与其他技术一样)。 (Mark Smotherman 关于热切执行的页面 http://people.cs.clemson.edu/~mark/eager.html提供了一些细节和参考。我会将 Hyesoon Kim 等人的“Wish Branches: Combining Conditional Branching and Predicate for Adaptive Predicated Execution”(2005 年)添加为重要论文。)
IBM 的 POWER7 似乎是第一个实现比预取替代路径(即急切取)更复杂的功能的主流处理器,并且它只处理单指令情况。 (POWER7 使用分支预测置信度估计来选择是进行谓词还是使用预测。)
急切执行具有明显的资源使用爆炸性问题。即使基于分支预测置信度、推测深度和资源可用性(前端可用的信息)的选择性渴望,沿着单一路径进行更深入的推测也很容易变得更有效。发现多条路径的连接点并避免过多的冗余计算也会增加复杂性。 (理想情况下,独立于控制的操作只会执行一次,并且会优化连接和数据流,但这种优化会增加复杂性。)
对于深度流水线的有序处理器,预测短的前向分支不被采用并且仅在实际采用分支时才在流水线中向后刷新到所采用的分支的目标指令似乎很有吸引力。如果管道中一次只允许一个这样的分支(其他分支使用预测),则向每条指令添加一位可以控制是将其转换为 nop 还是执行。 (如果只处理单个指令分支的情况,那么在管道中允许多个分支可能不会特别复杂。)
这类似于“如果采取则取消”分支延迟时隙。 MIPS 有“可能分支”指令,如果not已采取,并且这些在修订版 2.62 中被标记为已过时。虽然这样做的一些理由大概是为了将实现与接口分开以及恢复指令编码空间的愿望,但这一决定也暗示该概念存在一些问题。
如果对所有短前向分支都执行此操作,则当分支被正确预测为采用时,它将丢弃指令。 (请注意,如果所采用的分支总是在取指重定向中遇到延迟,则这种惩罚可能会更小,这在深度流水线处理器中的多周期指令缓存访问中更可能发生。在这种情况下,就好像没有分支一样进行取指可以具有与正确预测的采用分支相同的性能。但是,有人可能会争辩说,处理器特殊情况如此短的采用分支可以最大限度地减少此类获取气泡。)
作为一个例子,考虑一个标量管道(每个周期的非分支指令等于 1.0),在第八阶段结束时进行分支解析,并且在正确预测的采用分支上没有获取重定向惩罚,处理单指令分支。假设此类短前向分支(指令的 2%,占用 30% 的时间)的分支预测器准确度为 75%(不受方向影响),其他分支(指令的 18%)的准确度为 93%。对于错误预测为采用的短分支,将节省 8 个周期(此类分支的 17.5%;指令的 0.35%),如果错误预测为未采用,则将节省 7 个周期(7.2%;0.144%),而正确时将丢失 1 个周期预测为实际情况(22.5%;0.45%)。每条指令总共将节省 0.03358 个周期。如果没有这种优化,每条指令的周期将为 1.2758。
(虽然上面的数字只是举例,但除了用于非分支指令的 1.0 IPC 之外,它们可能与现实相距不远。提供小型循环缓存将减少误预测损失(并在短循环中节省功耗),因为指令缓存访问可能是八个周期中的三个。添加缓存未命中的影响将进一步减少此分支优化的百分比改进。避免预测“强烈采用”短分支的开销might是值得的。)
为了降低设计、功耗和面积成本,设计倾向于使用较窄和较浅的管道,并且更喜欢简单性。由于指令集可能支持许多短分支情况的无分支代码,因此优化这方面的动力进一步降低。
对于无序实现,必须预测潜在的分支指令,因为处理器希望能够执行以后的非相关指令。预测引入了额外的数据依赖性,必须检查该数据依赖性以进行调度。指令调度器通常为每条指令仅提供两个比较器并拆分条件移动(仅具有三个数据流操作数的简单指令:旧值、替代值和条件;谓词寄存器寄存器加法将具有四个操作数。(有其他方法可以解决这个问题,但这个答案已经很长了。)
当分支条件不可用时,无序实现也不会停止。这是控制依赖性和数据依赖性之间的权衡。通过准确的分支预测,控制依赖性非常便宜,但数据依赖性可以阻止等待数据操作数的前进进度。 (当然,通过布尔数据依赖性,值预测变得更有吸引力。在某些情况下使用谓词预测可能是可取的,并且比使用动态成本和置信度估计的简单预测具有优势。)
(也许很能说明问题的是,ARM 选择放弃 64 位 AArch64 中的大量预测。虽然其中很大一部分用于指令编码,但预测对高性能实现的好处可能相对较低。)
编译器问题
无分支与分支代码的性能取决于分支的可预测性和其他因素(如果采取的话,包括重定向获取的任何惩罚),但编译器很难确定分支的可预测性。即使概况数据通常也仅提供分支频率,这可能给出可预测性的悲观观点,因为这没有考虑使用局部或全局历史的分支预测器。编译器也不能完全了解数据可用性的时间和其他动态方面。如果条件晚于用于计算的操作数可用,则用数据依赖(预测)替换控制依赖(分支预测)可能会降低性能。无分支代码还可能引入更多实时值,从而可能增加寄存器溢出和填充开销。
更复杂的是,大多数仅提供条件移动或选择指令的指令集不提供条件存储。虽然可以通过使用条件移动来选择安全的、被忽略的存储位置来解决这个问题,但这似乎是一个没有吸引力的复杂化。此外,条件移动指令通常比简单的算术指令更昂贵;加法和条件移动可能需要三个周期,而正确预测的分支和加法将需要零(如果加法分支)或一个周期。
更复杂的是分支预测器通常会忽略谓词操作。如果后来保留的分支与删除的分支的条件相关,则该后来的分支的分支错误预测率可能会增加。 (谓词预测可用于保留此类删除分支的预测器效果。)
随着对向量化的日益重视,无分支代码的使用变得更加重要,因为基于分支的代码限制了在整个向量上使用操作的能力。