就我个人而言,条件执行的指令似乎是一个很好的想法。
然而,当我阅读更多关于ARM(和类似ARM)指令集(Thumb2,Unicore,AArch64)的内容时,我发现它们都缺少条件执行所需的位。
为什么这些指令集都缺少条件执行呢?
是在当时条件执行是一个错误,还是后来的变化使得它成为了一种昂贵的指令位浪费?
就我个人而言,条件执行的指令似乎是一个很好的想法。
然而,当我阅读更多关于ARM(和类似ARM)指令集(Thumb2,Unicore,AArch64)的内容时,我发现它们都缺少条件执行所需的位。
为什么这些指令集都缺少条件执行呢?
是在当时条件执行是一个错误,还是后来的变化使得它成为了一种昂贵的指令位浪费?
一般说法是现代系统具有更好的分支预测器和编译器,因此它们在指令编码空间上的成本不被证明是合理的。
这段文字来自于 ARMv8 指令集概述。
A64指令集不包括谓词或条件执行的概念。{基准测试表明,现代分支预测器足够有效,使得指令的谓词执行不提供足够的好处来证明它在操作码空间的显著使用以及在高级实现中的实现成本是合理的。}
接着文章继续说道:
提供了一个非常小的“条件数据处理”指令集。这些指令被无条件地执行,但使用条件标志作为指令的额外输入。这个指令集已经被证明在条件分支预测表现差或者效率低下的情况下是有益的。
另一篇名为 在ARM处理器上为寄存器交换条件执行 的论文则声称:
......条件执行会占用宝贵的指令空间,因为每个32位ARM指令都会在4位条件代码选择器中编码条件。此外,在现代嵌入式应用程序中,只有很小一部分指令实际上被条件化,并且条件执行可能甚至不会提高现代嵌入式处理器的性能。
说ARMv8没有条件执行有点误导人。问题在于理解为什么不想执行某些指令。也许在早期的ARM时代,实际上不执行指令很重要(因为功耗或其他原因),但今天这个特性的意义在于它允许您避免进行小而愚蠢的跳转的分支,例如像a=(b>0?1:2)这样的代码。这种情况比您想象的更常见——从概念上讲,它类似于MAX/MIN或ABS(尽管对于某些CPU可能有特定任务的指令)。
在ARMv8中,虽然没有一般的条件执行指令,但有一些指令可以执行我所描述的具体任务,即允许您避免在短而愚蠢的跳转上进行分支;CSEL是最明显的例子,尽管还有其他情况(例如条件设置条件)来处理其他常见模式(在那种情况下,C短路表达式求值的模式)。
在我看来,ARM在这里所做的就是最有意义的。他们提取了现代CPU上仍然有价值的条件执行功能(避免许多分支),同时改变了实现细节以匹配现代CPU的微体系结构。
其中一个原因是由于指令编码。
在 thumb 中,你不能将四个更多的位数压缩进紧凑的 16 位空间中,因为甚至没有足够的空间来存储寄存器操作数的高 3 位,它们必须被缩减为仅有 8 个寄存器的子集。请注意,在 thumb2 中,你有一个单独的 IT(E) 指令用于 选择下一条 4 条指令的条件。但你不能在同一条指令中存储条件,因为上述原因。
对于 AArch64,与32位ARM相比,寄存器数量翻倍了,但是你没有剩余的位用于新的3个高位寄存器。如果您想使用旧的编码,则必须从窄的12位立即数或4位条件中“借用”。与其他RISC架构(如MIPS)相比,12位已经太小了,减少数量只会使情况更糟,因此删除条件是更好的选择。由于分支预测变得越来越先进,这不会成为太大的问题。它还使得实现乱序执行更容易,因为现在有一件少事要重命名和关心。在编译时甚至在存在分析信息的情况下,确定这样的路径非常困难,预测执行最有效的情况是路径平衡或最长路径最频繁执行,但是确定这样的路径非常困难。
...
在ARM架构中,原始的32位指令集提供了一种称为条件执行的功能,允许大多数指令由13个基于前一条指令设置的四个条件代码之一的谓词进行预测。 ARM的Thumb指令集(1994年)放弃了条件执行,以减小指令的大小,使其适合16位,但是它的后继者Thumb-2(2003年)通过使用一种特殊指令来解决了这个问题,该指令除了为以下四条指令提供谓词外没有其他作用。在ARMv8-A(2011年)中引入的64位指令集用条件选择指令替换了条件执行。
在Joseph A. Fisher、Paolo Faraboschi和Cliff Young的《嵌入式计算:一种基于VLIW的架构、编译器和工具》一书中,第172页写道:...
减少条件性。较少的指令可以设置条件标志。只有条件分支和少量数据处理指令读取条件标志。不提供条件或谓词执行,并且没有T32的IT指令的等效物(参见§3.2)。
...
3.2 条件指令
A64指令集不包括谓词或条件执行的概念。基准测试表明,现代分支预测器的工作足够好,以至于指令的谓词执行不提供足够的好处来证明其在高级实现中的实现成本。提供了一组非常小的“条件数据处理”指令。这些指令是无条件执行的,但使用条件标志作为指令的额外输入。已经证明,在条件分支预测不佳或效率低下的情况下,这个集合是有益的。
更多信息在“4.3条件代码”中提供,但它并没有说明如何做出决策。
RISC-V ISA的设计师(一个无关的最近设计的ISA)在第23页解释了设计处理器所需考虑的一些因素(http://riscv.org/spec/riscv-spec-v2.0.pdf)。
条件分支被设计为在两个寄存器之间包括算术比较操作(如PA-RISC、Xtensa和MIPS R6中所做的那样),而不是使用条件码(x86、ARM、SPARC、PowerPC)或仅将一个寄存器与零进行比较(Alpha、MIPS),或仅对两个寄存器进行相等性比较(MIPS)。这种设计的动机在于观察到,组合比较和分支指令适合于常规流水线,避免了额外的条件码状态或使用临时寄存器,并减少了静态代码大小和动态指令获取流量。在64位ARM上移除预测指令,释放了每个指令编码中的四个位,这使得可以向每个寄存器字段添加一位,从而将寄存器数量翻倍。
在我的看法中,在服务器处理器固定到Fabric时省略elison能力是一个错误,但需要进行权衡。拥有它并不是一个错误(如果实现良好),它很昂贵,但不是浪费(比特很聪明,会自己管理)。条件语句是一个更简单/更好的选择。
就像任何CPU扩展或添加GPU一样;如果您能熟练使用工具,则可以轻松应对,否则请尽量轻装上阵。
维基百科引用:“根据不同基准测试,TSX可以在特定工作负载中提供约40%更快的应用程序执行速度,并每秒钟可进行4-5倍的数据库事务(TPS)。”。
它在当前编程风格中非常重要(在某些情况下成本高昂),或者更悲观地说,是一种在合成基准测试中取得更高分数的手段。
有一天,编程会像乐高一样容易,你可以让“它”自己组装并执行你的命令;但在那之前,处理器必须支持程序员(和编译器编写者)的懒惰——因此大部分能够在GPU上运行的程序仍然很少(但我们正在努力)。因此,需要删除那些被认为不需要或者没有以成本效益和竞争力的方式实现的(伟大)功能。因此,目前TSX规则占主导地位;但ARM CPU也需要其芯片中的线程变得更加复杂。.
就像MIPS中的延迟槽一样,当时在ARM中的条件执行也是一个技巧,PC提前两个指令同样如此。那么它们对未来有多大影响?ARM的分支预测器真的会产生很大的差异吗?或者真正的答案是他们需要更多的位于32位指令字中,就像Thumb一样,最容易去除的是条件位。
做一些性能测试来看看分支预测器到底有多好或多坏并不太困难,我曾在ARM11上尝试过无条件分支,尽管这已经是一个老的架构,但仍然广泛使用。很难使分支预测显示出任何改进,并且在任何情况下都无法与条件执行竞争。我还没有在Cortex-A系列的任何设备上重复这些实验。