一、布尔代数和环
利用^(异或)的环属性实现变量的交换。
void inplace_swap(int *x, int *y){
*x = *x ^ *y;
*y = *x ^ *y;
*x = *x ^ *y;
}
正如上面这段程序,看上去是来来回回的同样一种运算甚至连运算数都一样。但是就是这样三行代码达到了交换x和y指向的数据值的效果。
让我们来简单分析一下这个程序的具体过程吧:
步骤 |
*x |
*y |
初始 |
a |
b |
第一步 |
a^b |
b |
第二步 |
a^b |
a^b^b=a^0=a |
第三步 |
a^b^a=a^a^b=0^b=b |
a |
不错这样看来,这个程序从表象三看好像是省去了一个空间,但是从汇编码等底层实现上看没有任何性能上的优势。下面我们看看这段代码的汇编码:
汇编代码 写道
.file "inplace_swap.c"
.text
.globl inplace_swap
.type inplace_swap, @function
inplace_swap:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
movl (%eax), %edx
movl 12(%ebp), %eax
movl (%eax), %eax
xorl %eax, %edx
movl 8(%ebp), %eax
movl %edx, (%eax)
movl 8(%ebp), %eax
movl (%eax), %edx
movl 12(%ebp), %eax
movl (%eax), %eax
xorl %eax, %edx
movl 12(%ebp), %eax
movl %edx, (%eax)
movl 8(%ebp), %eax
movl (%eax), %edx
movl 12(%ebp), %eax
movl (%eax), %eax
xorl %eax, %edx
movl 8(%ebp), %eax
movl %edx, (%eax)
popl %ebp
ret
.size inplace_swap, .-inplace_swap
.globl swap
.type swap, @function
swap:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl 8(%ebp), %eax
movl (%eax), %edx
movl -4(%ebp), %eax
movl %edx, (%eax)
movl 12(%ebp), %eax
movl (%eax), %edx
movl 8(%ebp), %eax
movl %edx, (%eax)
movl -4(%ebp), %eax
movl (%eax), %edx
movl 12(%ebp), %eax
movl %edx, (%eax)
leave
ret
.size swap, .-swap
.section .rodata
.LC0:
.string "Old: x=%d,y=%d\n"
.LC1:
.string "New: x=%d,y=%d\n"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
movl $1, -8(%ebp)
movl $2, -12(%ebp)
movl -12(%ebp), %eax
movl %eax, 8(%esp)
movl -8(%ebp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
movl -12(%ebp), %eax
movl -8(%ebp), %edx
movl %eax, 4(%esp)
movl %edx, (%esp)
call inplace_swap
movl -12(%ebp), %eax
movl %eax, 8(%esp)
movl -8(%ebp), %eax
movl %eax, 4(%esp)
movl $.LC1, (%esp)
call printf
movl $0, %eax
addl $36, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.2.4 (Ubuntu 4.2.4-1ubuntu1)"
.section .note.GNU-stack,"",@progbits
从这个问题上,我们获得启发:
- 分析程序的性能不能简单通过源程序判断,需要深入底层看看汇编码甚至于和OS硬件有关的指令执行状况
- 数学之美无处不在,一种根本无法想像的东西通过数学上代数环的性质迎刃而解
- 对于程序的分析如果发现逻辑不清,应当借助表格等工具静心一步一步的分析
- 配置Gedit让汇编码高亮 http://qianjigui.iteye.com/blog/248085
二、浮点运算的不精确性
需要非常小心地使用浮点运算,因为浮点运算的范围和精度有限,而且浮点运算不遵守普通的算术性质,比如结合性。
未完待续