【问题标题】:Swapping two numbers - Processor behavior交换两个数字 - 处理器行为
【发布时间】:2014-10-05 08:55:02
【问题描述】:

我一直在寻找交换两个数字的替代选项,结果发现了链接 How to swap two numbers

在 cmets 部分中提到使用临时变量更好。以下是我从链接中复制的评论

如果我们从 CPU 指令的角度来看问题,使用 tmp 会比以上 3 种方法更好,我已经对所有这 4 种方法运行了基准测试(包括第 4 种方法?使用 temp 变量)。毫无疑问,第 4 种方法胜过所有上述 3 种方法。原因是CPU如何将变量移入寄存器以及我们需要使用多少个寄存器。

但我无法找到它如何工作的线索。有人可以解释一下它在处理器级别是如何工作的以及为什么 temp 变量更好(如果是的话)?

【问题讨论】:

标签: c coding-style swap processor


【解决方案1】:

“无临时变量”解决方案除了赋值之外,每个语句都有一个算术或逻辑运算。 例如,

      x = x + y;  
      y = x - y; 
      x = x - y; 

临时变量解决方案只会有赋值。

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

所以临时变量解决方案在CPU方面显然会有更好的性能。

【讨论】:

  • 投反对票:先生,您错了。我编译了您的示例,它产生了与另一个示例完全相同的机器代码。不要小看编译器。
  • 确认@jforberg 对gcc and clang 的观点:交换两个整数全局变量的内容只会编译为两个加载/两个存储。 temp 方式更安全,并且仍然使用浮点数进行优化,因此非常可取。但主要原因显然是可读性/源匹配操作。 (编译器输出更像是使用临时源的源代码。)
【解决方案2】:

如果我们使用下面的方法

x = x + y;  
y = x - y; 
x = x - y; 

这些指令包括算术运算和赋值。它需要更多的 cpu 周期才能完成交换。

如果您使用临时变量来交换两个值,则不需要任何算术运算。它只是通过分配值来做到这一点。与上述解决方案相比,这需要更少的 CPU 周期。

【讨论】:

  • 你确定它们相对于分配来说成本高吗?
  • 我要再次强调,这个答案假设编译器根本没有优化,这是一个非常可悲的情况,优化你的代码。
【解决方案3】:

查看在这个级别发生了什么样的优化的唯一方法是编译和反汇编。事实证明,编译器已经非常擅长删除或重新解释您的代码以使其更快。

我使用 MS C 编译器编译了这段代码:

int main() 
{
        int a = 1;
        int b = 2;
        int c;

        // Force use of the variables so they aren't optimized away
        printf("a = %d, b = %d\n", a, b);

        c = b;
        b = a;
        a = c;

        // Force use again
        printf("a = %d, b = %d\n", a, b);

        return 0;
}

这是优化后的实际输出,为简洁而编辑:

; 4    :    int a = 1;
; 5    :    int b = 2;
; 6    :    int c;

; OPTIMISED AWAY

; 8    :    printf("a = %d, b = %d\n", a, b);

    push    2
    push    1
    push    pointer_to_string_constant
    call    DWORD PTR __imp__printf

; 10   :    c = b;
; 11   :    b = a;
; 12   :    a = c;

; OPTIMISED AWAY

; 14   :    printf("a = %d, b = %d\n", a, b);

    push    1
    push    2
    push    pointer_to_string_constant
    call    DWORD PTR __imp__printf

; 16   :    return 0;

    xor eax, eax ; Just a faster way of saying "eax = 0;"

; 17   : }

    ret 0

所以你看,编译器决定在这种情况下根本不使用任何变量,而只是将整数直接压入堆栈(这与将参数传递给 C 中的函数相同)。

这个故事的寓意是不要在涉及微优化时对编译器进行第二次猜测。

【讨论】:

  • 谢谢。这很有帮助。你能帮我了解一下异或交换的细节吗?
  • @Tamil 为什么不在自己的编译器上自己尝试一下呢?无论如何,这是您每次考虑进行微优化时都应该做的事情。如果你不知道怎么做,我可以帮忙,告诉我你用的是什么编译器。
  • 我不熟悉 Xcode 但这个答案可能会有所帮助stackoverflow.com/questions/5433935/…
  • 我可以在 Xcode 中查看汇编代码。从菜单栏:产品 -> 执行操作 -> 组装 。我在下面添加了组装说明。看起来 temp 变量比 xor 更好(可能特定于 xcode 编译器)
  • @Tamil 好。如果您觉得有帮助,请考虑接受我的回答。
【解决方案4】:

感谢大家的宝贵意见。我写了一个简单的程序并查看了汇编输出(它是一个用 Xcode 编译的 C 程序)。看起来 XOR 操作的成本是使用临时变量的两倍。附上两个生成的汇编代码图像。当我使用临时变量时,每个操作(a = b & so)都需要两条指令。但是当我使用 XOR 时,每个操作(a ^= b & so)需要 4 条指令。下面是我的程序供参考。截图只包含main中行的汇编代码(除了return 0)

#include <stdio.h>

int main(int argc, const char * argv[])
{

int a = 100;
int b = 200;
int c;

c = a;
a = b;
b = c;

return 0;
}

#include <stdio.h>

int main(int argc, const char * argv[])
{

int a = 100;
int b = 200;

a ^= b;
b ^= a;
a ^= b;

return 0;
}

这些结果可能特定于 MAC 运行时和/或 Xcode 编译方式。如果您认为我在此过程中遗漏了什么,请发表您的评论。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-15
  • 1970-01-01
相关资源
最近更新 更多