1、
存储器别名
这个时候需要考虑程序转化的安全难度:
典型的案例是如下代码:
void twiddle1(int *xp,int *yp)
{
*xp+=*yp;
*xp+=*yp;
}
void twiddle2(int *xp,int *yp)
{
*xp+=2* *yp;
}
以上的代码看上去一样,但是明显twiddle2是twiddle1的优化版本。因为twiddle1中一共执行了load *xp 2次、load *yp两次、store *xp2次,一共6次对存储器的引用。而twiddle2只需要一次load *xp 、一次load *yp、一次store *xp,一共3次对存储器的引用 。
因此我们在编译的时候会觉得twiddle2比twiddle高效。
但是如果我们考虑程序的安全性来看。考虑下xp 等于yp的情况。
此时twiddle1会执行
*xp+=*xp;
*xp+=*xp;
结果是*xp的值会
增加4倍。
此时twiddle2会执行
*xp+=2* *xp;
结果是*XP的值
增加3倍。由此可见不能产生twiddle2作为twiddle1的优化版本。
这种情况称为存储器别名使用(memory aliasing)。即两个指针可能会指向存储器中的同一个位置。
因此安全的优化中必须考虑不同的指针可能指向存储器的同一个位置。
测试代码:
#include
#include
void twiddle1(int *xp,int *yp)
{
*xp+=*yp;
*xp+=*yp;
}
void twiddle2(int *xp,int *yp)
{
*xp+=2* *yp;
}
int main(int argc,char **argv[])
{
int a=3,b=2;
twiddle1(&a,&b);
printf("twiddle1 xp!=yp *xp=%d *yp=%d\n",a,b);
a=3,b=2;
twiddle2(&a,&b);
printf("twiddle2 xp!=yp *xp=%d *yp=%d\n",a,b);
a=5,b=5;
twiddle1(&a,&b);
printf("twiddle1 xp!=yp *xp=%d *yp=%d\n",a,b);
a=5,b=5;
twiddle2(&a,&b);
输出结果:
jie@ubuntu:~/zhang/datastruct$ gcc twiddle.c
./jie@ubuntu:~/zhang/datastruct$ ./a.out
twiddle1 xp!=yp *xp=7 *yp=2
twiddle2 xp!=yp *xp=7 *yp=2
twiddle1 xp!=yp *xp=15 *yp=5
twiddle2 xp!=yp *xp=15 *yp=5
twiddle1 xp=yp *xp=20
twiddle2 xp=yp *xp=15
再比如一下案例:
x=1000; y=3000;
*q=y;
*p=x;
t1=*q;
t1的值取决于指针p和指针q是否指向存储器中同一个位置。
测试代码:
#include
#include
void t1(int *q,int *p)
{
int x=1000,y=3000;
int t;
*q=y;
*p=x;
t=*q;
printf("t=%d\n",t);
}
int main(int argc,char **argv[])
{
int a=5,b=3;
t1(&a,&b);
t1(&a,&a);
}
输出结果:
jie@ubuntu:~/zhang/datastruct$ vi t1_t1.c
jie@ubuntu:~/zhang/datastruct$ ./a.out
t=3000
t=1000
第一次为3000 第二次为1000。原因就是存储器别名。两个指针指向同一个位置。
2、妨碍性能优化的因素是函数调用
比如:
f();
int func1()
{
return f()+f()+f()+f();
}
int func2()
{
return 4*f();
}
看上去结果相同,但是考虑下f()的代码
int f()
{
return count++;
}
count作为全局变量。 func1的返回值将会是
0+1+2+3=6
func2的返回值是4*0=0;
阅读(1965) | 评论(0) | 转发(0) |