经常被要求要写出高效的代码,但却不知道到底哪些方面的改进可以优化代码,提高编译的速度,看了几天的书,现在终于知道一些皮毛了。
这里我主要总结的是人们在写代码的时候可以做的优化,是人为可以改变的事情,当然,处理器也会进行优化。
程序性能的优化在循环内部会更明显。首先,我们要减少代码的移动,即识别要执行多次但是结果不会改变的计算。比如我们有一个求长度的函数get_length,然后我们要根据这个长度打印所有的元素,我们可以这样写代码:
for(int i=0;i<get_length;i++)
{
printf("...");
}
我们会发现,每进行一次循环,都要进行一次比较,每次比较的时候都要调用一次get_length函数,也就是说循环要进行多少次,这个函数就要被调用多少次。我们知道,函数有一长串的现场保护和现场恢复的过程,所以这就大大降低了运行的效率。可是如果我们这样写:
int len = get_length;
for(int i=0;i<len;i++)
{
printf("...");
}
这样,无论循环要进行多少次,get_length函数都只会被调用一次,这样就很明显会提高效率。
第二,减少过程调用。先看这样一段代码:
typedef struct _DATAS //申请一个结构体
{
int len;
int *data;
}DATA;
//根据index的值求出//该下标所对应的数组的元素,并将其存放在*m中。
int get_value(DATA *p,int index,int *m)
{
if(index < 0 || index > p->len)
{
return -1;
}
*m = p->data[value];
return 0;
}
void add(DATA *p,int *m)
{
*m = 0;
for(int i=0;i<p->len;i++)
{
int val;
get_value(p,i,&val);
*m = *m + val;
}
}
这段代码实现的功能就是:从data这个数组中读取元素,然后存放在val中,并将每个结果累加,存放在*m中。
注意get_value(p,i,&val)这句代码,每次取元素的时候都要调用一次这个函数,在这个函数内部,每次都要进行一次边界检查,这会导致代码的效率大大降低,我们可以这样改:
int *get_start(DATA *p)
{
return p->data; //返回数组的首地址
}
void add(DATA *p,int *m)
{
int *data = get_start(p); // 将元素直接存在数组中
*m = 0;
for(int i=0;i<p->len;i++)
{
*m = *m + data[i];
//直接通过访问数组中的元素就可以
}
}
如果改成这样,则可以减少get_start函数的调用,但是它对性能并不会有很大的提高,而且有些人认为这种方法破环了程序的模块性,所以不太用。
第三,消除不必要的存储器引用。就像上面的代码,对于*m,我们每次都要先从寄存器中读一次它的值,然后写一次,然后再将它存回到寄存器中,这样的读写很浪费,因为每次迭代开始读出来的值都是上一次迭代存入的值。这个时候,基于上面的代码,我们可以这样改进:
void add(DATA *p,int *m)
{
int *data = get_start(p); // 将元素直接存在数组中
*m = 0;
int acc = 0;
for(int i=0;i<p->len;i++)
{
acc = acc + data[i];
}
*m = acc;
}
其实很简单,就是定义了一个临时变量,先用它来存值,然后在循环结束之后将值赋给*m,这样就只进行了一次存储器的读写,减少了那些没必要的读写。
暂时总结的就这些了,以后有收获了再来补吧。