异或交换真的比开一个tmp快吗?
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异或交换真的比开一个tmp快吗?相关的知识,希望对你有一定的参考价值。
在以前学习Java的时候,leetcode上有几道位运算的题目,利用位运算的特性很好的解决。
之后有前辈老师在讲到异或^的时候,对我们说有不用开第三个变量tmp,来交换两个变量值的方法,说这种方法速度快,又节省了内存。受到之前leetcode题目和老师威信的影响下,我当然相信了,之后在写数据结构大作业的时候,又受到了学长异或交换两个值更快的提醒,然后特地把课程设计里面的算法涉及到交换的部分变成了位运算的写法,看起来逼格好像确实高了很多 =-=。
然后在今天,几个大佬又在讨论这个问题。
我们用代码证明了事实并非如此?!
结果证明tmp开第三个比异或时间上要快的多啊,喂。。。
现代机器的编译器其实在背后做了很多事啊。。
翻译成汇编的代码,跟我们想的有很多出入啊?!。。
两种写法:
{ int tmp; tmp = a; a = b; b = tmp; }
按语言本身的特性来想,这些代码做以下这些工作:
- 在栈上分配为整型变量tmp分配空间;
- 将a的值放入tmp中;
- 将b的值放入a中;
- 将tmp的值放入b中;
- 释放为tmp分配的栈空间。
而实际上呢?我们来看看生成的汇编代码
movl b, %eax ;将b从内存载入到寄存器eax movl a, %edx ;将a从内存载入到寄存器edx movl %eax, a ;将eax的内容存入到内存a中 xorl %eax, %eax ;将eax清零 movl %edx, b ;将edx的内容存入到内存b中
而另一种写法,
{
a ^= b;
b ^= a;
a ^= b;
}
这个写法在-O2和-O3翻译成汇编:
movl b, %eax ;将b从内存载入寄存器eax movl a, %ecx ;将a从内存载入寄存器ecx movl %eax, %edx ;将eax的值保存到edx中 xorl %ecx, %edx ;ecx与edx异或 xorl %edx, %eax ;edx与eax异或 xorl %eax, %edx ;eax与edx异或 movl %eax, b ;将eax的值存入到内存b中 xorl %eax, %eax ;将eax置0:设置返回值,与上例中一样 movl %edx, a ;将edx的值存入到内存a中
怎么样,结果跟你想的是否有出入呢,这样的话,第二种写法对内存的读写是肯定多于第一种的。
1. 这个所谓的“技巧”在现代的机器上只会更慢(我甚至怀疑它从来就不可能比原始办法快)。原始办法是两次内存读和写,这个"技巧"是六读三写加三次异或(或许编译器可以优化成两读三写加三次异或)。
2. 同样也不能节省内存,因为中间变量 tmp 通常会是寄存器(稍后有汇编代码供分析)。就算它在函数的局部堆栈(stack)上,反正栈已经开在那儿了,也没有进一步的函数调用,根本节约不了一丁点内存。
3. 相反,由于计算步骤较多,会使用更多的指令,编译后的机器码长度会增加。(这不是什么大问题,短的代码不一定快,后面有另外一个例子。)
详细可见:http://blog.csdn.net/do2jiang/article/details/4549679
http://blog.csdn.net/solstice/article/details/5166912
关于优化,引用其他博主的观点:
但是面试的时候可能会出这个无聊的题目,记住位运算交换之前一定要判断两个值是否相等,这是个小陷阱。
以上是关于异或交换真的比开一个tmp快吗?的主要内容,如果未能解决你的问题,请参考以下文章