它是更有效的接触较少的寄存器在ARM汇编?

问题描述 投票:1回答:3

我刚刚通过Raspbian开始学习大会,并有一个简单的问题:如何有效地在大会保存寄存器空间?例如,如果我想要做一个快速另外,有一种有意义的差异

mov r1, #5
mov r2, #3
add r1, r1, r2

mov r1, #5
mov r2, #3
add r3, r1, r2     @ destination in a new register that wasn't previously used

(除了在不同的寄存器存储)?

assembly arm cpu-architecture micro-optimization
3个回答
4
投票

使用相同的寄存器作为输入的输出对ARM1没有固有的缺点。我不认为有任何先天优势或者,虽然。当我们谈论写寄存器,该指令尚未必须等待(即不输入)东西可以得到在一般情况下,更有趣。

如您需要保存指令使用尽可能多的寄存器。 (请注意调用约定的,虽然:如果使用非r0..r3你必须拯救更多/恢复使用额外的,如果你想从C调用函数)。具体而言,通常优化最低的动态指令计数;做了一些额外的设置/清理,以节省循环中的指令通常是值得的。

而且不只是为了节省说明:software pipelining隐藏加载延迟是流水线上的顺序执行的CPU具有潜在价值。例如如果你遍历数组,装载值,你需要从现在开始2次迭代到寄存器中,而不要触摸它,直到然后。 (展开循环)。一个有序CPU只能按顺序启动指令,但他们有可能完成的顺序进行。例如在高速缓存未命中的负载,直到你试着去阅读它时,它没有准备好不会停止的CPU。我想你可以假设高性能有序的现代化ARM的处理器将拥有的任何记分板是必要的跟踪哪些寄存器等待ALU或负载的结果做好准备。

如果没有实际发生完整的软件流水,有时你可以通过做负载,然后东西,然后商店的块的块得到类似的结果。例如对于大副本优化的memcpy的可能装入12个寄存器在其主展开循环,然后存储这些12个寄存器。所以,同一个寄存器的加载和存储之间的距离仍足够大,至少隐藏L1缓存加载等待时间。


电流(?)树莓裨板(RPi 3+)使用ARM Cortex-A53核,2-宽超标量有序微架构。

这确实乱序执行将使用register renaming使WAW任何ARM核心(像的Cortex-A57)(写后读)和WAR危害一个非问题。 (https://en.wikipedia.org/wiki/Hazard_(computer_architecture)#Data_hazards)。

在一个有序核心像A53,WAR绝对是一个不是问题的问题:有没有办法,以后的指令可以写寄存器之前较早的指令有机会从那里读取其操作数。

但WAW危害可能会限制CPU的同时运行两条指令的能力。写这篇文章您尚未读取寄存器时,只会是相关的。 add r1, r1, r2必须等待r1做好准备之前就可以开始执行,因为它是一个输入端。

举例来说,如果你有这样的代码,我们可能会看到从写在可能在同一个周期运行2个指令相同的输出寄存器性能产生负面影响。我不知道的Cortex-A53或任何其他按顺序ARM如何处理这一点,但另一双问题顺序CPU(英特尔奔腾P5 1993年)并不对,写同一个寄存器(Agner Fog's x86 uarch guide)的说明。第二届一个具有启动前等待一个周期(但可以使用后,该指令可能对)。

@ possible WAW hazard
adds  r3, r1, r2      @ set flags, we don't care about the r3 output
add   r3, r1, #5      @ now actually calculate an integer result we want

如果你使用了不同的虚拟输出寄存器,这些都可以在同一时钟周期启动。 (或者,如果你会使用cmn r1, r2(比较否定的),你可能会设置为从r1 - (-r2)标志,而完全不写一个输出,这according to the manual是一样的,从r1 + r2设置标志。),不过也许有些是你能想出一些情况下,可能不与cmpcmntst(ANDS),或teq(EORS)指令来代替。

我期望乱序的ARM可以重命名在同一周期相同的注册多次(000 x86处理器可以做到这一点)完全避免WAW危害。


我不知道有任何微架构的好处留下一些寄存器“冷”。

在带有寄存器重命名的CPU,通常是与物理寄存器文件来完成,甚至一个不最近修改的架构寄存器(如r3)将需要一个PRF条目持有任何指令的价值最后写的,不管多久以前是如此。所以写寄存器总是分配一个新的物理寄存器,以及(最终)释放了物理寄存器,用于保存旧值。不管旧的价值也只是写的,或者如果它有很长一段时间的价值。

英特尔P6系列没有使用从乱序后端“活”值分别持有退休状态的“退休寄存器文件”。但它一直住那些寄存器值就在ROB与产生它们的UOP(而不是一个PRF条目的引用),所以它不能运行了物理寄存器重命名前,后端得满满的。见http://blog.stuffedcow.net/2013/05/measuring-rob-capacity/更多一些有趣的x86 CPU的实验测量ROB主场迎战一对乱序窗口尺寸PRF限制其他x86处理器那些使用PRF。

事实上,由于对退休寄存器文件限制读端口,P6系列(通过的PPro的Nehalem)实际上可以读取最近没有写,在一个发布组太多的寄存器时停止。 (见昂纳雾的microarch指南,寄存器读摊位。)但我不认为这是对其他uarches一个典型问题,如乱序任何ARM内核。在寄存器中设置常量/循环不变外循环和内自由使用。


脚注1:这是在所有体系结构大致如此,但也有例外。我知道,如果是一个相当特殊情况下,只有一个:最近的英特尔的x86 CPU(在64位模式下)mov eax, eax(1级周期的延迟)小于mov ecx, eax(0循环等待时间)时用于截断在64位寄存器32慢一点,因为MOV,只有消除不同寄存器之间的作品。 (Can x86's MOV really be "free"? Why can't I reproduce this at all?


2
投票

在由人谁知道多了很多关于理论方面,使用更多的寄存器可以更快被击落的风险 - 这是一个原因,为什么会出现在一个架构设计的压力,包括更多的寄存器(比较T32 / A32 / A64的寻址芯的范围作为体系结构实现成本增加寄存器)。

在架构层面,核心寄存器都是等价的(只要操作码可以解决这些问题) - 即某些指令可能只允许低8个寄存器的访问。

在微架构的水平,那将是非常不寻常的给予一定的寄存器优惠待遇。架构层面优惠待遇的一个例子,ARMv7-M架构及相关专业,是个例外推/流行行为。编译器可以利用此优化的相当容易地(通过避免插入一些垫片代码)。

更高性能的处理器实际上包括比结构寄存器更多的物理寄存器,并自动分配这些提供一些具有多个逻辑寄存器的性能优势。

在你的榜样,你的第一个代码段明确表示与第一r1值永远不会在未来使用的CPU。在第二个代码片段中,你已经离开r1 == 5那种封锁的休息时间 - 有没有办法向前看,并预测,如果您曾经将再次使用这个。

所以:

  • 多个寄存器允许更快速的数据(单周期),和潜在的乱序执行
  • 重新使用寄存器可能激活没有寄存器重命名在广泛问题机联锁
  • 重新使用寄存器可以打破依赖链,腾出更高性能的处理器,更多的物理寄存器。

对于A53,我想没有什么区别可言,直到你的软件用完寄存器(除非你想的5该值以后)。


1
投票

与ARM的效率来自调用约定为主,非正常管道的东西(不添加XX,R1,R2有拖延MOV R2,XX来完成)。

这么少的代码块两者都是正确的解决方案,取决于问题。如果试图避免使用堆栈,并采用了时下流行的调用约定重新使用寄存器,而不是燃烧另一可能会或可能不会是正确之内的信息4个寄存器住。

保持不变的所有其他因素,不计算在管道设计什么,有什么魔力手臂,将在这里限制你,它不是一个微编码的设计就像一个CISC,在那里你可以有特定的核心具体表现规则。任何一个处理器都可以有,即使使用单个寄存器文件和无微码管线的规则,但登记应在手臂上相等。

和手臂很容易测试,看看你是否有打到这里表演,但你必须要小心你的基准,不是最终测量别的东西和被测思考其指令。

© www.soinside.com 2019 - 2024. All rights reserved.