为什么有条件执行的指令在后来的ARM指令集中不存在?

32

就我个人而言,条件执行的指令似乎是一个很好的想法。

然而,当我阅读更多关于ARM(和类似ARM)指令集(Thumb2,Unicore,AArch64)的内容时,我发现它们都缺少条件执行所需的位。

为什么这些指令集都缺少条件执行呢?

是在当时条件执行是一个错误,还是后来的变化使得它成为了一种昂贵的指令位浪费?

7个回答

37

一般说法是现代系统具有更好的分支预测器和编译器,因此它们在指令编码空间上的成本不被证明是合理的。

这段文字来自于 ARMv8 指令集概述

A64指令集不包括谓词或条件执行的概念。{基准测试表明,现代分支预测器足够有效,使得指令的谓词执行不提供足够的好处来证明它在操作码空间的显著使用以及在高级实现中的实现成本是合理的。}

接着文章继续说道:

提供了一个非常小的“条件数据处理”指令集。这些指令被无条件地执行,但使用条件标志作为指令的额外输入。这个指令集已经被证明在条件分支预测表现差或者效率低下的情况下是有益的。

另一篇名为 在ARM处理器上为寄存器交换条件执行 的论文则声称:

......条件执行会占用宝贵的指令空间,因为每个32位ARM指令都会在4位条件代码选择器中编码条件。此外,在现代嵌入式应用程序中,只有很小一部分指令实际上被条件化,并且条件执行可能甚至不会提高现代嵌入式处理器的性能。


9
此外,预测执行与乱序执行不太兼容:它可能需要 四个 数据流源操作数(谓词、目标寄存器的当前值[如果谓词为假]和两个源寄存器的值),必须检查其可用性。AArch64的预测指令只需要三个源(更可能得到乱序执行机制的支持[例如支持FMA],并且更容易分裂成2源微操作[就像Alpha 21264为CMOV所做的那样])。 - user2467198
我甚至找不到一个条件分支来注册或条件返回,也没有条件加载。 - Ant6n
@Ant6n 条件加载特别棘手,因为你不能轻易地将它们分成多个µops。 - fuz

13

说ARMv8没有条件执行有点误导人。问题在于理解为什么不想执行某些指令。也许在早期的ARM时代,实际上不执行指令很重要(因为功耗或其他原因),但今天这个特性的意义在于它允许您避免进行小而愚蠢的跳转的分支,例如像a=(b>0?1:2)这样的代码。这种情况比您想象的更常见——从概念上讲,它类似于MAX/MIN或ABS(尽管对于某些CPU可能有特定任务的指令)。

在ARMv8中,虽然没有一般的条件执行指令,但有一些指令可以执行我所描述的具体任务,即允许您避免在短而愚蠢的跳转上进行分支;CSEL是最明显的例子,尽管还有其他情况(例如条件设置条件)来处理其他常见模式(在那种情况下,C短路表达式求值的模式)。

在我看来,ARM在这里所做的就是最有意义的。他们提取了现代CPU上仍然有价值的条件执行功能(避免许多分支),同时改变了实现细节以匹配现代CPU的微体系结构。


10

其中一个原因是由于指令编码。

thumb 中,你不能将四个更多的位数压缩进紧凑的 16 位空间中,因为甚至没有足够的空间来存储寄存器操作数的高 3 位,它们必须被缩减为仅有 8 个寄存器的子集。请注意,在 thumb2 中,你有一个单独的 IT(E) 指令用于 选择下一条 4 条指令的条件。但你不能在同一条指令中存储条件,因为上述原因。

对于 AArch64,与32位ARM相比,寄存器数量翻倍了,但是你没有剩余的位用于新的3个高位寄存器。如果您想使用旧的编码,则必须从窄的12位立即数或4位条件中“借用”。与其他RISC架构(如MIPS)相比,12位已经太小了,减少数量只会使情况更糟,因此删除条件是更好的选择。由于分支预测变得越来越先进,这不会成为太大的问题。它还使得实现乱序执行更容易,因为现在有一件少事要重命名和关心。

3
AArch64包括一系列优秀的谓词指令,例如条件递增和选择,比x86的CMOV更强大。它确实是为了高效无分支代码而设计的,在适当的情况下。但它们只是ALU指令,不是谓词存储或谓词加载,不能让你从可能无效的指针中进行无分支的条件加载或存储。 - Peter Cordes
是的,这比每个指令都进行条件执行要好。 - phuclv

6
条件执行在许多辅助或位操作例程的实现中是一个不错的选择,例如排序、列表或树操作、数字转字符串转换、sqrt或长除法。我们可以添加UART驱动程序和在路由器中提取位字段。这些具有高分支到非分支比率和相当高的不可预测性。
然而,一旦您超出最低级别的服务(或通过使用更高级别的语言增加抽象级别),代码看起来完全不同:条件分支内的代码块更多地由数据移动和调用子例程组成。在这里,这些额外的4位的好处迅速消失。这不仅是个人发展,还是文化:编程从非结构化(Basic、Fortran、Assembler)向结构化发展。不同的编程范式也在不同的指令集架构中得到更好的支持。
技术上的妥协可能是将五位“cond.S”字段压缩为最常用的四位或三位组合之一。
  • 一篇关于基于配置文件的模式选择的论文,提供了流行的SA-110 thumb/ARM编译例程的功率、周期时间、代码大小和指令计数基准。一些例程在ARM模式下表现更好,而其他例程在Thumb模式下表现更好。这取决于算法和最终的代码/编译器。

2
“这不仅是个人发展,也是文化。” - 什么? - OJFord
@OJFord:我认为这意味着你的个人开发流程与流行的流程不同。面向对象编程往往没有内联,会跳来跳去。使用IPA可以在新语言中实现相当大的优化。我认为这是一个公正的观点,来自于母语不是英语的人。 - artless noise

3
在旧的ARM v4上,条件指令只有在执行概率很高或者概率约为50%并且这些指令连续出现2到4个时才能节省时间。如果它们没有被执行,那么在获取它们之后会浪费周期,而使用分支来跳过它们的开销则较大。如果它们被执行,分支指令将被获取但不会被执行。一个小麻烦是,在调试时,对条件指令设置断点总是导致在该指令上中断,无论条件如何(除非有一些我公司没有的真正聪明的调试器)。

0
"为什么有条件执行的指令没有出现..." "在当时,条件执行是一个错误吗,还是后续的更改使其成为指令位的昂贵浪费?" 维基百科关于“预测-缺点”的文章提供了一些信息: 缺点
预测执行的主要缺点在于增加了编码空间。在典型的实现中,每个指令都为谓词保留了一个位域,指定该指令在何种条件下应该生效。当可用内存受限时,例如在嵌入式设备上,这种空间成本可能是禁止性的。然而,一些体系结构(如Thumb-2)能够避免这个问题(见下文)。其他劣势如下:
  • 预测执行通过向关键路径添加逻辑级别来复杂化硬件,并可能降低时钟速度。
  • 预测块包括所有操作的周期,因此较短的路径可能需要更长时间并受到惩罚。

在编译时甚至在存在分析信息的情况下,确定这样的路径非常困难,预测执行最有效的情况是路径平衡或最长路径最频繁执行,但是确定这样的路径非常困难。

...

在ARM架构中,原始的32位指令集提供了一种称为条件执行的功能,允许大多数指令由13个基于前一条指令设置的四个条件代码之一的谓词进行预测。 ARM的Thumb指令集(1994年)放弃了条件执行,以减小指令的大小,使其适合16位,但是它的后继者Thumb-2(2003年)通过使用一种特殊指令来解决了这个问题,该指令除了为以下四条指令提供谓词外没有其他作用。在ARMv8-A(2011年)中引入的64位指令集用条件选择指令替换了条件执行。

在Joseph A. Fisher、Paolo Faraboschi和Cliff Young的《嵌入式计算:一种基于VLIW的架构、编译器和工具》一书中,第172页写道:
"...全预测会使硬件、ISA和编译器变得更加复杂。与推测有所不同,推测倾向于更深的流水线和更快的时钟;而预测则为关键路径添加了许多逻辑并潜在地降低了时钟速度。所有指令中都要使用宝贵的编码位来操作谓词操作数,绕过具有谓词操作数的操作会极大地增加转发逻辑的复杂性。对于非循环或“控制导向”代码,预测的优点一直是学术和商业辩论的话题,而关于预测是否值得支持全预测所需的大量硬件成本,目前还无定论。
全预测和部分预测之间的争论甚至更微妙。 全预测更具表现力,并允许编译器预测包含任何组合操作的块。 部分预测需要积极的推测,并嵌入了一些固有的限制(例如,无法预测包含调用操作的块)。 就实现复杂性而言,如先前所述,全预测对指令编码和微体系结构有更高的要求,而具有选择操作的部分预测适用于大多数微体系结构和数据路径,并且对复杂性、面积或速度没有影响。
在嵌入式领域中,很难证明一个大量谓词寄存器组的代码大小惩罚是合理的。全预测意味着采用“提前付款”的理念,即不管使用频率如何,都需要支付谓词机制的成本。例如,添加6个谓词位以处理64个谓词操作已经将IPF编码推到了每个操作42个位 - 这种方法对于嵌入式处理器来说成本过高。..."
成本、TDP和专利,甚至是开发竞争产品所需的技术水平都起着作用。在这种情况下,通过更新编码技术实现了成本效益,原本认为需要的东西实际上并没有被使用,或者至少没有有效地使用(对于其实施成本而言)。
正如另一个答案中所解释的那样,ARM手册很少涉及原因,比RISC手册还要少,以下是ARM在“ARMv8指令集概述”第8页中的说法: 3 A64概述
A64指令集在AArch32或ARMv7中提供了类似于A32和T32指令集的功能。然而,就像将32位指令添加到T32指令集一样,合理化了一些ARM ISA行为,A64指令集包括进一步的合理化。新指令集的亮点如下:
  • ...

  • 减少条件性。较少的指令可以设置条件标志。只有条件分支和少量数据处理指令读取条件标志。不提供条件或谓词执行,并且没有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)。这种设计的动机在于观察到,组合比较和分支指令适合于常规流水线,避免了额外的条件码状态或使用临时寄存器,并减少了静态代码大小和动态指令获取流量。
无论是条件移动还是谓词指令都会增加乱序微架构的复杂性,由于需要将目标体系结构寄存器的原始值复制到重命名的目标物理寄存器中,如果谓词为false,则会添加一个隐式的第三个源操作数。此外,静态编译时决定使用谓词而不是分支可能会导致在编译器训练集中未包含的输入上性能降低,特别是考虑到不可预测的分支很少出现,并且随着分支预测技术的改进而变得越来越少。
我们注意到,存在各种微架构技术,可以动态地将不可预测的短向前分支转换为内部谓词代码,以避免在分支错误预测时刷新流水线的成本[6、10、9],并已经在商用处理器中实现[17]。
最简单的技术只是通过仅刷新分支阴影中的指令而不是整个获取流水线,或者使用宽指令获取或空闲指令获取插槽从两侧获取指令来减少从错误预测的短向前分支中恢复的惩罚。对于乱序核心的更复杂的技术,在分支阴影中的指令上添加内部谓词,由分支指令编写内部谓词值,允许分支和后续指令与其他代码一起被推测执行和乱序执行[17]。
[6] Timothy H. Heil和James E. Smith。选择性双路径执行。技术报告,威斯康星大学-麦迪逊,1996年11月。
[9] Hyesoon Kim、Onur Mutlu、Jared Stark和Yale N. Patt。愿望分支:结合条件分支和谓词以进行自适应谓词执行。在第38届IEEE/ACM国际微体系结构研讨会论文集MICRO 38中,第43-54页,2005年。
[10] A. Klauser、T. Austin、D. Grunwald和B. Calder。非谓词指令集体系结构的动态吊床谓词。在1998年国际并行架构和编译技术会议论文集PACT '98中,华盛顿特区,美国,1998年。
[17] Balaram Sinharoy、R. Kalla、W. J. Starke、H. Q. Le、R. Cargnoni、J. A. Van Norstrand、B. J. Ronchetti、J. Stuecheli、J. Leenstra、G. L. Guthrie、D. Q. Nguyen、B. Blaner、C. F. Marino、E. Retter和P. Williams。IBM POWER7多核服务器处理器。IBM研究与发展杂志,55(3

在64位ARM上移除预测指令,释放了每个指令编码中的四个位,这使得可以向每个寄存器字段添加一位,从而将寄存器数量翻倍。

在我的看法中,在服务器处理器固定到Fabric时省略elison能力是一个错误,但需要进行权衡。拥有它并不是一个错误(如果实现良好),它很昂贵,但不是浪费(比特很聪明,会自己管理)。条件语句是一个更简单/更好的选择。

就像任何CPU扩展或添加GPU一样;如果您能熟练使用工具,则可以轻松应对,否则请尽量轻装上阵。

维基百科引用:“根据不同基准测试,TSX可以在特定工作负载中提供约40%更快的应用程序执行速度,并每秒钟可进行4-5倍的数据库事务(TPS)。”。

它在当前编程风格中非常重要(在某些情况下成本高昂),或者更悲观地说,是一种在合成基准测试中取得更高分数的手段。

有一天,编程会像乐高一样容易,你可以让“它”自己组装并执行你的命令;但在那之前,处理器必须支持程序员(和编译器编写者)的懒惰——因此大部分能够在GPU上运行的程序仍然很少(但我们正在努力)。因此,需要删除那些被认为不需要或者没有以成本效益和竞争力的方式实现的(伟大)功能。因此,目前TSX规则占主导地位;但ARM CPU也需要其芯片中的线程变得更加复杂。
URL参考:
AMD: https://en.wikipedia.org/wiki/Advanced_Synchronization_Facility Intel: https://en.wikipedia.org/wiki/Transactional_Synchronization_Extensions

.


-1

就像MIPS中的延迟槽一样,当时在ARM中的条件执行也是一个技巧,PC提前两个指令同样如此。那么它们对未来有多大影响?ARM的分支预测器真的会产生很大的差异吗?或者真正的答案是他们需要更多的位于32位指令字中,就像Thumb一样,最容易去除的是条件位。

做一些性能测试来看看分支预测器到底有多好或多坏并不太困难,我曾在ARM11上尝试过无条件分支,尽管这已经是一个老的架构,但仍然广泛使用。很难使分支预测显示出任何改进,并且在任何情况下都无法与条件执行竞争。我还没有在Cortex-A系列的任何设备上重复这些实验。


但是有一种情况,你可以通过某种方式来改进代码或数据,以提高分支预测的效果。你可以设计一个更好的算法/代码。你可以帮助编译器。你可以对数据进行排序,使得预测不会受到随机性的影响。所以,相比于一个在ISA核心中的设计选择,所有这些都是可以理解设计者的。 - auselen
我在汇编语言中进行了这项工作,没有涉及编译器,我给处理器提供了每一个机会。基本上是一堆nop指令和被测试指令,首先改变循环中的对齐方式和项目数量,以找到条件分支末尾和循环顶部入口的最佳位置(根据它们所在位置的性能差异),然后调整被测试指令在循环中的位置。之后使用条件执行重复类似的测试。 - old_timer
正如Aki可能在说的那样,你不必查看太多编译后的代码就会意识到编译器通常不能利用条件执行,仍需要进行分支处理。每个指令4位十分昂贵,而我们可能并未使用它们。每个指令3位可以立即将三操作数指令的寄存器数量加倍,我宁愿拥有两倍的寄存器数量,也不要条件执行。 - old_timer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接