Xiaoxia[PG]

为什么会这样子

OS Study、我的生活13 条评论

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

以前看到iceboy喜欢用xor来交换两个数。今晚就写了段程序来比较xor法与第三方变量法的效率,惊奇的发现,xor法竟然输了。
是不是我的程序有问题呢?

以下是测试比较:

1) 当两者进行1亿次交换时,xor法输48ms。
1. a=1 b=2
2. a=1 b=2     ms:688
3. a=1 b=2     ms:640

2)当两者进行10亿次交换时,xor法输249ms。
1. a=100 b=200
2. a=100 b=200 ms:6765
3. a=100 b=200 ms:6516

3)当两者进行1次交换时,rdtsc读取来的时钟周期数显示两者速度相差不大,无比较意义。

对交换部分代码进行反汇编,得到
xor法:

40142d:	81 7d e8 ff e0 f5 05 	cmpl   $0x5f5e0ff,0xffffffe8(%ebp)
401434:	7f 1f                	jg     401455 <_main+0xc5>
401436:	8b 55 ec             	mov    0xffffffec(%ebp),%edx
401439:	8d 45 f0             	lea    0xfffffff0(%ebp),%eax
40143c:	31 10                	xor    %edx,(%eax)
40143e:	8b 55 f0             	mov    0xfffffff0(%ebp),%edx
401441:	8d 45 ec             	lea    0xffffffec(%ebp),%eax
401444:	31 10                	xor    %edx,(%eax)
401446:	8b 55 ec             	mov    0xffffffec(%ebp),%edx
401449:	8d 45 f0             	lea    0xfffffff0(%ebp),%eax
40144c:	31 10                	xor    %edx,(%eax)
40144e:	8d 45 e8             	lea    0xffffffe8(%ebp),%eax
401451:	ff 00                	incl   (%eax)
401453:	eb d8                	jmp    40142d <_main+0x9d>

第三方变量法:

4014e8:	81 7d e8 ff e0 f5 05 	cmpl   $0x5f5e0ff,0xffffffe8(%ebp)
4014ef:	7f 19                	jg     40150a <_main+0x17a>
4014f1:	8b 45 f0             	mov    0xfffffff0(%ebp),%eax
4014f4:	89 45 e4             	mov    %eax,0xffffffe4(%ebp)
4014f7:	8b 45 ec             	mov    0xffffffec(%ebp),%eax
4014fa:	89 45 f0             	mov    %eax,0xfffffff0(%ebp)
4014fd:	8b 45 e4             	mov    0xffffffe4(%ebp),%eax
401500:	89 45 ec             	mov    %eax,0xffffffec(%ebp)
401503:	8d 45 e8             	lea    0xffffffe8(%ebp),%eax
401506:	ff 00                	incl   (%eax)
401508:	eb de                	jmp    4014e8 <_main+0x158>

很明显,xor法生成的汇编代码行数较多,xor可能比mov的时钟周期数要少。但是因为涉及了内存操作,这两段代码在不同的编译器就可能有不同做法,很难比较了。

注意,上面的分析是在gcc没有开启优化选项的情况下进行的。
如果加入了-O参数,xor交换代码会被聪明地替换成只有3行代码:

4013b8:	87 f3                	xchg   %esi,%ebx
4013ba:	42                   	inc    %edx
4013bb:	81 fa ff e0 f5 05    	cmp    $0x5f5e0ff,%edx

而第三方变量法被优化成:

401443:	89 d8                	mov    %ebx,%eax
401445:	89 f3                	mov    %esi,%ebx
401447:	89 c6                	mov    %eax,%esi
401449:	42                   	inc    %edx
40144a:	81 fa ff e0 f5 05    	cmp    $0x5f5e0ff,%edx
401450:	7e f1                	jle    401443 <_main+0x111>

两者都省去了内存相关的操作了。xor法虽然用xchg直接交换了变量,但结果却出乎意料。xchg比mov耗多了一倍时间。测试如下:
1. a=1 b=2
2. a=1 b=2     ms:468
3. a=1 b=2     ms:219

小结,在c、c++里交换变量还是少使用xor法,除非自己一定要装逼,那也无所谓。

附测试程序源代码:

#include "windows.h"
#include "iostream"
using namespace std;

#define record(t) t = GetTickCount()

int main()
{
	int tl1, tl2, tl3;
	int a = 1, b = 2, i, t;
	cout<<"1. a="<<a<<" b="<<b<<endl;
	record( tl1 );

	//exchange  1:
	for( i=0; i<100000000; i++ ){
		a ^= b;
		b ^= a;
		a ^= b;
	}

	record( tl2 );
	cout<<"2. a="<<a<<" b="<<b<<"\tms:"<<tl2-tl1<<endl;
	record( tl2 );

	//exchange 2:
	for( i=0; i<100000000; i++ ){
		t = a;
		a = b;
		b = t;
	}

	record( tl3 );
	cout<<"3. a="<<a<<" b="<<b<<"\tms:"<<tl3-tl2<<endl;
	return 0;
}

13 条评论

  1. sf~~~~~~~~~~~~~~~~~~~

  2. 我一般是在比较难声明变量的地方用 xor 交换两个数

  3. /zan /mb

  4. 强大~~~~~~~~~~~~~~~~~~~~~~~

  5. to iceboy, 发现 a^=b^=a^=b 在gcc 下通过。

  6. 代码优化有问题,直接用汇编写吧

    xchg的cpu周期很长的~

    mov只用寄存器的时候非常快...

  7. xchg不是一个很简单的操作么? 为什么时钟周期会这么长?
    i486上xchg是3个时钟周期。mov是1个时钟周期。也就是说,3个mov指令的时钟周期应该与1个xchg时钟周期的时间相等。
    然而,测试结果表明xchg的时间还是长了一倍。为什么呢?
    是不是说明intel Pentium后的cpu(我的是atom n270)的mov比xchg快很多?

  8. 你先单独测试一下吧~

  9. mov可以借助多条流水线告诉运行的

  10. 还真没注意过这个问题,学习了。

  11. xchg其实就是3次xor操作

    xchg %esi,%ebx这条指令对内存读取了3次,当然会慢

    位运算只有在汇编里才会有效率,高级语言必然涉及到读取内存,想快也快不起来

  12. 猜测是不是由寄存器重命名引起的??

  13. 不懂~~~

    何谓寄存器重命名???

发表评论

评论会经过自动审核,链接请勿超过 3 个。