变量应存储在寄存器中多长时间?

时间:2016-09-27 01:53:45

标签: assembly optimization x86

寄存器是在Assembly Coding中存储和处理数据的最强大的地方,但是它与主存储器相比空间有限。因此,我认为确定何时将数据移动到寄存器以及何时移出数据对于优化汇编代码非常重要,尤其是在需要大量寄存器使用时。

所以,在将数据移入主存储器以供以后使用(在寄存器中处理之后)之前,数据应存储多长时间?或者,当我没有更多的寄存器来处理新数据时,我是否只是将它们放入内存中? (我个人认为这不合适:P)

考虑以下代码(第一个代码):

MOV EBX,SomeAddressForLaterUse
;...-imagine 37 lines of assembly code here
MOV ESI,SomeAddress
MOV EDI,EBX
MOV ECX,SIZE_IN_BYTES
REP MOVSB

现在考虑其他代码(第二代码);

MOV EBX,SomeAddressForLaterUse
PUSH EBX
;...-imagine 37 lines of assembly code here
MOV ESI,SomeAddress
POP EDI
MOV ECX,SIZE_IN_BYTES
REP MOVSB

上面,我认为很明显第二个代码有利于在那些37行汇编代码中保存另外一个寄存器(除非这37行汇编代码不使用很多寄存器),但有时它非常很难在这两种方法之间做出选择,例如,如果是10行代码而不是37行呢?

总而言之,在确定移动数据远离寄存器时是否存在某种规则?

2 个答案:

答案 0 :(得分:4)

你完全倒退了。什么东西溢出到内存的规则真的就像#34;当你用完寄存器"。

复杂的部分是决定哪个注册重用

一旦你做出决定,你就可以确定代码中的两个关键点 - 最后一次使用该寄存器用于其旧目的,以及第一次用于新目的。溢出可能发生在它们之间的任何一点,在这里你要考虑处理器特性,正在执行的其他指令需要内存访问,依赖链在计算最终值时需要多长时间 - 并最终到来以最佳时间执行溢出,以避免管道停止等待内存控制器。

添加调用约定的注意事项,例如哪些寄存器必须由被调用的函数保留,哪些寄存器被认为是临时空间,并且在许多情况下,叶函数根本不需要对内存进行任何溢出。

在某些体系结构中,处理器特性变化很大,以至于在订购指令流时,优化所需的信息是不可用的。 (x86就是一个很好的例子)。在这种情况下,CPU本身可能有一个无序执行引擎,其中有大量逻辑专用于重新排序指令(或者它们被分成的微操作),其方式对于特定的方式是最佳的。微架构。此执行时优化还可以考虑分支预测统计信息,即只有在收集了分析跟踪时才能进行提前编译。

答案 1 :(得分:3)

你真正想做的是在寄存器中计算/放置值,理想情况下永远不要将它们移动到内存("溢出"),这是远离"远离"因此访问速度很慢。

许多编译器处理这种方式的方法是首先假设每个计算值都在代码中某个点的寄存器中。这需要无限(好的,无限制的)数量的寄存器,这显然比机器更多,因此它无法真正起作用。是的,对于真正重要的程序,您可能在某个时间点在寄存器中有10,000个值。 (甚至更多!)

但是,如果你可以决定哪些无限的那些是最不重要的,你可以强迫它们溢出/留在内存中,现在你有更少的寄存器包含值。如果你有足够的溢出,你就会发现剩下的东西会适合真实的寄存器。

这是通过所谓的graph-coloring algorithm来实现的。我们的想法是构建一个图形,其节点在代码中的某个点处是值寄存器,以及连接值必须同时存在的节点的弧形;这些被称为"干扰弧"。

着色算法的作用是用颜色装饰节点(好吧,寄存器号)。想象一下你的机器有8个寄存器,你想在某个寄存器中有变量X;图中的一个节点代表了这一点。

这是一个两个阶段的过程,以此为目的。由于具有比寄存器少的邻居,第一阶段选择可以明显着色的节点,并且解决了节点具有比寄存器更多的邻居的情况。第二阶段分配颜色/寄存器。

  • 阶段1:重复直到图表为空:选择邻居少于实际寄存器的节点;从图表中删除它(及其弧线)并推入堆栈。如果没有剩下的选择,选择一个邻居太多的节点[通常选择邻居数量最多的节点是好的],从图中删除,推入堆栈[这样的节点可能将第2阶段的寄存器溢出到记忆中。

  • 阶段2:重复直到堆栈为空:从堆栈中弹出一个节点,然后将其及其弧线添加回图形。如果可能的话,为该节点分配一个颜色/寄存器,它不是它的邻居的任何颜色/寄存器;这是值将使用的实际寄存器。 (此步骤有时会成功为可能必须溢出的节点着色)如果无法选择这样的颜色/寄存器,则无法将此节点分配给寄存器,而不会将其中任何一个或其邻居溢出;我们通过将一个任意寄存器分配给该节点以用于其计算来解决这个问题,并在某个位置保存其计算值,同时计算其相邻节点。

Viola,您已经为每个值分配了一个寄存器编号,其实际上只会泄漏少数寄存器。 This document gives more detailed explanation and basic algorithm

不,您不想手动执行此操作,但很容易构建一个执行此操作的过程。

This paper discusses how to revise this scheme to handle messy ("irregular") architectures like the x86 where the registers are not all equivalent