【问题标题】:Cost of swapping variables through mov, xor通过 mov、xor 交换变量的成本
【发布时间】:2014-04-03 20:15:49
【问题描述】:

让我们交换 2 个变量。

int temp = a;
a = b;
b = temp;

这是一些半优化的 asm 伪代码:

mov eax, dword ptr [rbp+4]
mov ebx, dword ptr [rbp+8]
mov dword ptr [rbp+8], eax
mov dword ptr [rbp+4], ebx

将对象相互异或会更快吗?

a ^= b ^= a ^= b;

asm 伪代码:

mov eax, dword ptr[rbp+4]
xor eax, dword ptr[rbp+8]
xor dword ptr[rbp+8], eax
xor eax, dword ptr[rbp+8]
xor dword ptr[rbp+8], eax
mov eax, dword ptr[rbp+4]

哪些会更快? (欢迎客人)

【问题讨论】:

  • 这也取决于运行的 CPU。
  • 这个问题没有实际意义,考虑到所讨论的指令都不能直接对两个内存操作数进行操作。
  • 真的等一下吗?好吧,无论如何排除xchg,我将把我的伪代码重新制作为mov,以便首先注册
  • 我没有测量,但我猜内存访问比寄存器操作慢得多,所以第一个应该更快。另一个想法:如果你的交换在一个循环中,展开循环一次可能完全摆脱对变量交换的需要。
  • 您的“半优化 asm 伪代码”甚至不起作用。它从不将值存储到内存中。我认为你最后两条指令中操作数的顺序是颠倒的(即应该是mov dword ptr[rbp+8],eax)。此外,您不可能比 4 次更快地执行 6 次内存访问。我强烈怀疑第一个示例,一旦您修复它,将比第二个更快。

标签: performance optimization assembly x86 mov


【解决方案1】:

将其拉入两个寄存器然后写回交换内容可能是最快的解决方案。四个内存周期,四个指令,两个寄存器。假设数据必须从 ram 开始并返回到 ram,那么您通常无法击败这种方法。

假设您可以为源和目标做内存,四个 xor 是每个 xor 三个周期,12 个内存周期,这是一个明确的失败者。使用寄存器来避免两个 mem 操作数只会增加更多指令。

您的 asm 伪代码是 6 个内存周期。 6条指令一个寄存器。四个周期,四个指令,两个寄存器可能更便宜。现在,如果您必须执行两个内存周期来释放这些寄存器,则它变为 6 个周期。最后一个将是释放寄存器的附加值,因此 7. 6 仍然比 7 便宜,5 条指令比 7 便宜,指令大小在此处未计算在内,但会增加内存周期,尽管可能以有效的方式完成获取(在大小合适的对齐块中)。

如果数据已经在寄存器中,那么使用第三个寄存器并执行三个指令tmp = a,a = b,b = tmp是三个寄存器的三个操作并且最快。但是,如果您只是不能保留一个寄存器,那么四个异或会更快。

这都是通用的高级视图,可能存在处理器和缓存情况等,这些情况可能会使一种解决方案看起来更快,但最终肯定会更快,但可能取决于具体情况。

【讨论】:

  • x86 有 xchg 指令来交换值,所以你不需要另一个空闲的临时寄存器
  • 如果全部都在寄存器中,那么这是最好的方法,如果它必须在 ram 中开始和结束,那么交换 reg 内容只是一个浪费的指令。
【解决方案2】:

没有理由说 Xor 方法在任何机器上都会更快。

这两种方法都需要执行两次读取和两次写入,Xor方法有ALU+内存开销。

【讨论】:

    【解决方案3】:

    在支持寄存器移动消除的处理器上(例如 - IvyBridge 或更高版本),如果您可以让编译器将这些值保存在寄存器中(您将拥有检查生成的程序集以确保)。

    这样您不仅可以避免内存访问(尽管读写后读取应该在内部转发,但您仍然会在内存单元中累积延迟),还可以避免执行延迟。 CPU 会简单地在乱序寄存器重命名器中切换寄存器本身的指针。

    即使没有移动消除,仅寄存器移动也应该更快。内存单元有很多必须强制执行的限制(冲突检查、缓存查找等)、更长的管道和更少的常规执行带宽。

    【讨论】:

      猜你喜欢
      • 2010-09-19
      • 2012-06-13
      • 2012-07-04
      • 2019-04-25
      • 1970-01-01
      相关资源
      最近更新 更多