编写高效代码(之一)

之前一直没有关注编写高效代码是问题,一直以为只要代码写的越短越高效。直到看到关于优化代码方面的文章,瞬间感觉自己知识面太窄了。还是知识不够,基本功不扎实。

关于导读,编者给出了下面这个简单的例子:

for(i=0;i<strlen(pswd);i++){
    
    if(isNUM(pswd[i])){
        j++;
    }
}

刚开始我没看出代码有什么问题,感觉很好啊,还认为strlen()写在循环里面很厉害的样子呢,现在才知道自己的见识短浅。每循环一次程序就需要调用一次strlen函数,这样就浪费了很多资源和时间,可能在小规模的程序里面并不能感觉到什么,但是在工作中接触到的大多是上万行的程序,这样一个写法,估计雇主看到都能气死。tips_1:能调用一次的函数尽量就调用一次。

tips_2:降低数据精度

我们都知道,小数后面的位数越多,数就越精确。但是越精确的数据,所需要的bit数就越多。浮点数有单精度( single)和双精度(double)两种格式,单精度浮点数占32bit,双精度占64bit,处理单精度的数自然也快一点。在 c 中,fabsf() 是计算单精度浮点数绝对值的函数,fabs() 是计算双精度的,所以如果数据是单精度浮点数,使用fabsf() 会比fabs() 快一点。

tips_3:减少函数的使用,不要老是打断我。

函数是结构化程序设计的产物,他能是代码更加模块化,耦合性更低,重用性更高。但是,过多函数的调用会带来额外的开销,除了引起跳转外,还会产生额外的指令。这时我们可以将一些小的函数直接转换成代码。比如说这个求最小值的函数:

int min(int a,int b){
    return a<b? a:b;
}
c = min(a,b);

不如直接写成一个语句:

c = a<b? a:b;

或者也可以将小的函数写成宏定义;如果调用的地方很多,用宏的话会使代码简洁很多:

#define min(a,b) ((a)<(b))? (a):(b)
c = min(a,b);

但是如果还是嫌宏方法麻烦的话,还有一种简单的方法,就是将函数声明成内联函数 inline ,关于内联函数,就是一种小型函数,牺牲空间来节省函数调用的开销,一般用作比较小的函数,它们看起来象函数,运作起来象函数,比宏(macro)要好得多,使用时还不需要承担函数调用的开销,编译时,编译器会自动用函数体覆盖函数调用。内联函数被发明出来就是为了取代 c 中的宏。

inline int min(int a,int b){
    return a<b? a:b;
}
c = min(a,b);
//编译器会自动将代码优化成: 
c = a<b? a:b;

tips_4:空间换时间

程序谁都能实现,但是程序的快慢就不是谁都能实现的了。老师说个这样的比喻,老板让你和小王同时编写一个程序,你们都很快编好了,但是你的代码执行了一分钟出了结果,小王的程序一秒钟就出了结果,这时老板心里会怎么想?之前没有意识到这个问题的重要性。在四轴的飞控程序中,几秒的处理 时间差,将会使飞控效率提升一个档次,更快也更加稳定。比如下面这两个求斐波那契数列的例子:

如果用这个程序的话,简直是慢的吓人,一开始我还以为输出完了呢,但是慢慢的一点点还在输出,没想到这个程序这么慢。

#include "stdio.h"

int f(int n){
    if (n <= 1){
        return 1;
    }
    return f(n - 1) + f(n - 2);
}

int main(){
    int result;
    int i;
    for (i = 0; i < 40; i++)
    {
        result = f(i);
        printf("%d
", result);
    }
}

但是使用下面这个代码的话,在你还没有反应过来的时候,40个斐波那契数列就已经输出完毕了,真是不比不知道,一比吓一跳啊。

int arr[40];//使用数组将数列的值缓存起来
int f(int n){
    int result;
    if (n <= 1){
        result = 1;
    }
    else
    {
        result = arr[n - 1] + arr[n - 2];
    }

    arr[n] = result;
    return result;
}

int main(){
    int result;
    for (int i = 0; i < 40;i++)
    {
        result = f(i);
        printf("%d
", result);
    }
}

第二个程序中,计算  f(n) = f(n-1)+f(n-2)  时就不需要调用递归执行,直接从数组中取值相加即可。

5、少用乘法,用移位代替

定点乘法在DSP中需要两个cycle,而移位操作只要1个cycle,如果是一个数乘以 2 的N 次方,就可以用移位代替乘法。

len = len * 4;

就不如下面的好:

led = led << 2;

6、少用除法,求余

除法,求余需要消耗大量的时间,很多处理器没有相应的指令,是通过软件来实现的,尽量少用。如果要除以一个常数,如下面的浮点数除法:

f = f / 5.0;

可以将它转换位乘法:

#define cof 1.0/5
f = f*cof;

7、尽量减少分支

现在的处理器都是流水线结构,if和switch 等语句会带来跳转,而跳转会打乱流水线的正常运行,影响程序的执行效率。

比如下面这个分别给奇数和偶数赋不同的值:

for (i = 0; i < 100;i++)
{
    if (i % 2 == 0)
    {
        a[i] = x;
    }
    else
        a[i] = y;
}

不如写成下面这个形式更好:

for (i = 0; i < 100;i+=2)
{
    a[i] = x;
    a[i + 1] = y;
}

8、将最有可能的分支放在if中,而不是else中。

其实还有很多优化代码的技巧,我这里只是写出了几个我们理解,并且能用到的,其他的都太高大了,我感觉以我的能力暂时还写不出那么复杂的代码。所以暂时就记录这么多吧。

原文地址:https://www.cnblogs.com/qsyll0916/p/7639104.html