【问题标题】:Why don't people use xor swaps? [closed]为什么人们不使用异或交换? [关闭]
【发布时间】:2013-05-08 06:40:59
【问题描述】:

我在一个网站上读到,使用异或交换速度很快,因为它不使用临时变量。这是一个例子:

#include <stdio.h>

int main(void)
{        
    int a=234,b=789;
    b=b^a;
    a=b^a;
    b=b^a;
    printf("a=%d,b=%d",a,b);
    return 0;
}

为什么人们不在现实生活中的代码中使用这种技术?只是风格差吗?有什么没有明确定义的吗?这是我的编译器可能会自动从更清晰的代码中产生的优化吗?

【问题讨论】:

  • 我怀疑是否有很多证据表明它通常更快(如果有的话)。
  • @xaxxon 你想提醒我注意页面的哪个部分?
  • 闲聊、开放式问题会降低我们网站的实用性,并将其他问题推到首页。
  • 我提出的所有问题都有直截了当的答案,而且大部分都是封闭式问题,只需要“是”或“否”的答案。这个问题建设性的,因为它很可能会鼓励未来的访问者使用比异或交换更便携的方法。我认为您不会基于这些理由来解决这个问题。即使你这样做,我也会很快重新打开它;)
  • 我认为,一旦您说“人们为什么不...”,您就偏离了 stackoverflow 的话题。此外,询问您在堆栈溢出的核心任务之外的风格趋势。这种类型的问题属于某处的博客,我很确定这不是一个真正的问题。你已经知道答案了,你正在半拖着寻找你认为在stackoverflow上回答的常见问题的答案,但stackoverflow特别不相信它们属于这里。我真的认为您应该创建一个编程博客并在那里发布此类内容。

标签: c swap xor premature-optimization


【解决方案1】:

使用 tmp 变量更快并且现代编译器和 CPU 的可读性更高。 2x 加载到寄存器中,然后 2x 存储回原始位置。

或者如果一个或两个变量已经在寄存器中,编译器可能会完全优化临时变量。如果 xor-swapping 在某些假设机器上更快,那么一个好的编译器会在优化 tmp = a; a = b; b = tmp; 时为您使用它,所以您不需要显式编写它。这就是为什么您使用 C 而不是在 asm 中手动编写的原因。

此外,xor-swap 仅适用于整数。如果你想交换浮点数怎么办?字符串?自定义对象?等等

【讨论】:

  • 这几乎涵盖了它。对此进行扩展:编译器在优化第 2 点的交换方面会比异或交换做得更好;通常这将被转换为仅一条指令(或有时为零,因为可能会交换顺序指令),而异或交换仍为三。此外,异或交换的某些方面定义不明确;它们可能会产生错误的结果或陷阱表示,尤其是在使用负数时......更不用说专注于这个优化是愚蠢的;通过分析可能会发现更重要的问题。
  • @xaxxon 然而你坚持认为我的回答是错误的。这就是问题所在。
  • @xaxxon 考虑如果有填充位或负零陷阱表示会发生什么。 n1570.pdf(C11 标准草案)的第 6.5p5 节说:如果在评估表达式期间出现异常情况(即,如果结果未在数学上定义或不在其类型的可表示值范围内),行为未定义。
  • 无论如何,它绝不限于任何特定类型的数据。它将交换任何两个等长的二进制表示。
  • @xaxxon 据我所知,^ 只能对整数类型进行操作,floatA &amp; floatB 是编译器错误,因此您必须执行类似*(int *)&amp;floatA ^ *(int *)&amp;floatB 之类的操作,这会破坏@ 987654321@.
【解决方案2】:

所有答案都已经存在,认为它只是一个补充-

->如果两个值都用于相同的内存地址-结果将为零

->编译器可以优化掉原始交换中的临时变量

->现代 CPU 努力通过指令管道并行执行指令 但是使用 XOR 技术比使用临时变量进行交换要慢得多,因为每个操作都取决于前一个的结果

->x+Y 可能会发生整数溢出

【讨论】:

    【解决方案3】:
    1. 虽然没有显式临时变量,但在写入寄存器之前,结果实际上存储在隐式临时变量中。

    2. 使用异或交换,您需要确保被交换的变量不相同。否则,两者都应评估为 0。

    【讨论】:

    • #2 不正确。如果 a == b,则值将保持不变。
    • @devnull:不。 b 将被设置为 0。然后 a 将被设置为 0^a,即 a。然后 b 将被设置为 b^a,仍然是 a。
    • 我认为#2指的是完全相同的内存空间,不仅仅是相同的值,但我不确定。
    • 如果,正如@xaxxon 建议的那样,在ab 以某种方式存在于同一地址的特殊情况下(在标准C 中不容易像ints 那样实现),结果是b 将在第一个b=b^a; 之后变为0,最后ab 将为0。这种特殊情况也适用于tmp = a; a = b; b = tmp;,应该共享地址。但是在这个代码示例中,a & b 和在不同的位置,ab 将在给定任何初始值的情况下很好地交换。
    • #2 场景更有可能发生在交换指向对象的函数中。
    【解决方案4】:

    性能增益通常非常小,以至于“可理解代码”的成本高于获得的速度优势。

    【讨论】:

    • 获得的性能实际上是不存在的。编译器会为你处理所有这些——事实上,你的代码工作得越“正常”,编译器就能做得越好。而且编译器比您更擅长针对您的平台进行优化。
    • 编译器只有在不懂汇编的情况下才能更好地优化。
    • 即便如此,编译器很可能会经常打败你。这些东西非常聪明。
    • 在大多数情况下,像这样的小“优化”不会为您节省大量时间。坚持使用可读的代码而不是试图比编译器更聪明。
    • @John3136:我同意这样一个小序列的增益是不存在的。对于较长的序列,编写良好(与可读性差别不大)的代码提供了一种优化方案,编译器可以很好地处理它。编译器对糟糕的代码(垃圾输入=垃圾输出)无能为力,结果是代码很慢。因此,好的代码意味着好的速度。
    猜你喜欢
    • 1970-01-01
    • 2016-03-12
    • 2013-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    相关资源
    最近更新 更多