为什么最好避免执行指令分支?

| 从性能方面来看,我经常读到,在汇编指令级别上分支是不好的。但是我还没有真正明白为什么会这样。所以为什么?     
已邀请:
大多数现代处理器都会预取指令,甚至在代码流到达该指令之前以推测方式执行它们。拥有分支意味着突然有两条不同的指令可能是下一条指令。至少可以通过三种方式与预取进行交互: 分支后的说明未预先提取。指令流水线变空,处理器必须等待,因为在最后一刻提取了下一条指令,从而导致性能下降。 处理器可以猜测将采用哪个分支(分支预测)并预取并执行适当的指令。如果它猜测错误的分支,则将不得不放弃已完成的工作,并等待提取正确的指令。 处理器可以获取并执行两个分支,然后丢弃未使用的分支的结果。 与没有分支的等效代码相比,分支可能会或可能不会对性能产生重大影响,具体取决于处理器和特定代码。如果执行代码的处理器使用分支预测(大多数情况下这样做),并且大多数猜测对于特定的一段代码正确,则可能不会对性能产生重大影响。另一方面,如果大多数人猜测不正确,则可能会大大降低速度。 对于一段特定的代码,很难预测是否删除分支会大大加快代码的速度。当进行微优化时,最好衡量两种方法的性能,而不是猜测。     
这很糟糕,因为它会干扰指令的预取。现代处理器可以开始加载下一个命令的字节,同时仍在处理第一个命令,以便更快地运行。当发生分支时,必须丢弃已预取的“下一个命令”,这会浪费时间。在紧密循环之类的内部,那些错过的预取可能会加起来。     
因为如果可能的话,处理器不知道应该为执行预取什么指令。如果分支的运行方式超出了预期,则必须刷新指令管道,因为这些已加载的指令现在出错了,这使它慢了几个周期。     
除了预取问题之外,如果您正在跳跃,也不会做其他工作...     
考虑一下汽车装配线,您会听到一天有X辆汽车下线的消息。这并不意味着原材料从生产线的开头开始,而X号则在一天内完成了整个生产过程。谁知道这可能不会,但是每辆车可能要花几天的时间才能结束,这就是装配线的重点。试想一下,如果由于某种原因,您进行了制造变更,并且您基本上必须冲洗生产线中的所有汽车,然后报废或报废它们的零件,以便在其他时间将其放置在另一辆汽车上。填充该装配线并返回到每天X辆汽车需要一段时间。 处理器中的指令流水线工作原理完全相同,流水线中有数百个步骤,但是概念是相同的,以确保您需要每个时钟周期执行速率(每天X辆汽车)一条或多条指令使管道保持畅通。因此,您进行预取会消耗一个内存周期,该周期通常很慢,但是多层缓存会有所帮助。解码需要花费另一个时钟才能执行,尤其是在x86之类的CISC上需要花费很多时钟。在大多数处理器上执行分支时,如果考虑一般的简化流水线,则必须丢弃执行和预取中的指令,基本上是流水线的2/3。然后,您必须等待这些时钟以进行提取,然后进行解码,然后才能恢复平稳执行。最重要的是,从定义上讲,并不是下一条指令,某些时间的百分比比高速缓存行还多,并且某些百分比的时间意味着从内存或更高层的高速缓存获取时钟甚至更多。周期要比线性执行要好。另一个常见的解决方案是,某些处理器声明无论执行一条分支指令之后的指令是什么,有时总是执行该分支指令之后的两条指令。这样,您在冲洗管道时就执行了,好的编译器会安排指令,以便在每个分支之后都出现其他nop。懒惰的方法是在每个分支之后只打一两下,造成另一个性能损失,但是对于该平台,大多数人都会使用它。第三种方法是ARM在有条件执行的情况下所做的事情。简短的说,前向分支并不少见,而不是说有条件的分支,而是将要尝试分支的几条指令标记为有条件的情况下执行,它们进入解码并以nops和管道的形式执行。不断前进。 ARM依靠传统的刷新和重新填充来实现更长或向后的分支。 较旧的x86(8088/86)手册以及其他处理器的其他同样较旧的处理器手册以及微控制器手册(新旧)仍将发布每个指令执行的时钟周期。对于分支指令,如果发生分支,它将说增加x个时钟数。您的现代x86甚至是打算运行Windows或linux或其他(慢速和慢速)操作系统的ARM和其他处理器都不会打扰,他们通常只是说它每个时钟运行一条指令,或者谈论兆赫兹或类似情况的尖峰,而不一定每个指令都有一个时钟表。您只假设一个,并记住那就像每天一辆汽车,它的最后执行时钟而不是其他时钟到达那里。尤其是微控制器的人,每条指令不处理一个时钟,与普通的桌面应用程序相比,它们必须更加了解执行时间。查看其中一些规范,这些芯片是Mi​​crochip PIC(不是mips的PIC32),msp430,绝对是8051,尽管这些规范是由许多不同公司制造或由许多公司制造的,它们的时序规范差异很大。 最重要的是,对于桌面应用程序,甚至是操作系统上的内核驱动程序,编译器的效率都不高,并且操作系统会增加更多的开销,您几乎不会注意到节省了时钟。切换到微控制器并放入太多分支,您的代码将慢2至3倍。即使使用编译器而不是汇编器。使用编译器(而不是用汇编器编写)授予的权限也可能会使您的代码变慢2至3倍,因此您必须在开发,维护和可移植性与性能之间取得平衡。     

要回复问题请先登录注册