• STM32个人笔记-嵌入式C语言优化


    忘记在哪里摘抄的笔记,总结一下。 

    嵌入式C语言优化

    定义匹配变量类型

    不同的数据类型所生成的机器代码长度相差很大,变量类型选取的范围越小运行速度越快,占用的内存越快。

    比如,能用char型的变量,就没必要使用int型来定义;能用int型的变量,就没必要使用long int型来定义;能不使用float型,就不用。能用unsigned int型的变量,就没必要使用int型来定义;

    因为有些处理器处理无符号比处理有符号更有效率。使用float类型,需要借助FPU(浮点运算单元)或者浮点型运算库,而int型可以直接被处理器运算。

    注:在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C编译器并不会报错,但程序运行结果此时已经错了。 

    算法优化-短而有效率

    算法优化指对程序时空复杂度的优化。

    在PC机上进行程序设计时一般不必过多关注程序代码的长短,只需考虑功能实现。但在嵌入式系统上,就必须考虑系统的硬件资源。

    在程序设计时,在不影响程序功能的需求下,应考虑代码尽可能短的算法。

    函数调用要使用系统的栈来保存数据,同时CPU在函数调用时需要保存和恢复当前的现场,进行对栈的push和pop操作,所以函数调用实际上也需要CPU时间。

    宏定义仅仅作为预先写好的代码嵌入到当前程序上,不产生函数调用,所占用的仅仅是一些空间,省去了参数压栈,生成汇编语言的call调用,返回参数,执行return等过程,从而提高程序的执行速度。

    内嵌汇编

    程序上对时间要求苛刻的部分可以用内嵌汇编来重写,提高执行速度。汇编较难,慎重使用。

    循环语言

    在多重循环中,尽量不超过三层循环。

    在多重循环中,应将最长的循环放在最内层,最短的循环放在最外层。这样可以减少CPU跨切循环的次数。

    1. int i=0,j=0
    2. for(i;i<10;i++) //效率高于下方
    3. {
    4. for(j;j<30;j++)
    5. {
    6. }
    7. }
    8. for(j;j<30;j++) //效率低于上方
    9. {
    10. for(i;i<10;i++)
    11. {
    12. }
    13. }

    无限循环

    while(1)和for(;;)起一样效果,无限循环。

    while(1)编译后

    1. mov eax,1
    2. test eax,eax
    3. je foo+23h
    4. jmp foo+18h

    for(;;)编译后

    jmp foo+23h

    for(;;)指令少,不占用寄存器,而且没有判断,跳转。CPU效率上比while(1)好。

    SWITCH语句

    当switch语句的case标号很多时,为了减少比较的次数,可以把发生的频率性相对高的条件放到第一位或者把整个switch语句转化为嵌套switch语句。把发生频率高的case标号放在最外层的switch语句中,发生频率较低的case标号放在另外的switch语句中。

    如,把发生频率高的case标号放在最外层的switch语句中,发生频率较低的case标号放在缺省(default)的内层switch语句中。

    1. switch (表达式)
    2. {
    3. case1:
    4. 语句1;
    5. break;
    6. case2
    7. 语句2;
    8. break;
    9. ……
    10. /*把发生频率低的放在内层的switch语句中*/
    11. default:
    12. switch (表达式)
    13. {
    14. case 值n:
    15. 语句n;
    16. break;
    17. case 值m:
    18. 语句m;
    19. break;
    20. ……
    21. }
    22. }

    标准库-避免使用

    使用C语言标准库可以加快开发速度,但由于标准库需要设法去处理用户所有遇到的情况,所以很多标准库代码很大。

    比如,标准库的sprintf函数非常大,很大一部分用于处理浮点数,如果程序中不需要格式化浮点数值,程序设计人员就可以根据实际情况用少量的代码实现这个功能。

    运算符求值-高效

    乘法比除法更有效率,移位比乘法更有效率。

    a=b*2 -> a=b<<1;
    a=b/4 -> a=b>>2;
    a=b%8 -> a=b&7;

    a=b/8*8+b%4 -> a=((b>>3)<<3)+(b&3);
    a=b*15 -> a=(b<<4)-b;

    存储器分配

    由于成本限制,嵌入式系统存储器容量有限。程序上所有的变量,包含的库函数以及堆栈等都使用有限的内存。

    全局/静态变量进入main函数之前就被创建,生命周期为整个源程序,存储在全局/静态存储区。

    局部变量,在栈中分配。在函数被调用时才被创建,生命周期为函数内。 

    因此,在程序中应尽量使用局部变量,提高内存使用效率。

    程序中堆大小受限于所有全局数据和栈空间都分配后的剩余量。如果堆太小,程序就不能够在需要的时候分配内存。

    因此在使用malloc函数申请内存之后一定要用free函数释放内存,防止内存泄漏,造成内存碎片。

    使用Memoization,以避免递归重复计算

    考虑Fibonacci(斐波那契)问题,Fibonacci(斐波那契)问题是可以通过简单的递归方法来解决。

    1. int fib ( n )
    2. {
    3. if ( n == 0 || n == 1 )
    4. {
    5. return 1;
    6. }else
    7. {
    8. return fib( n - 2 ) + fib ( n - 1 );
    9. }
    10. }

    注:考虑Fibonacci(斐波那契)系列从1开始,因此,该系列看起来:1,1,2,3,5,8...

    从递归树,我们计算fib(3)函数2次,fib(2)函数3次。这是相同函数的重复计算。如果n非常大,fib函数的效率会比较低。

    Memoization是一个简单的技术,可以被用在递归,加强计算速度。fibonacci 函数Memoization的代码如下:

    1. int calc_fib ( int n )
    2. {
    3. int val[ n ] , i;
    4. for ( i = 0; i <=n; i++ )
    5. {
    6. val[ i ] = -1;
    7. }
    8. val[ 0 ] = 1;
    9. val[ 1 ] = 1;
    10. return fib( n , val );
    11. }
    12. int fib( int n , int* value )
    13. {
    14. if ( value[ n ] != -1 )
    15. {
    16. return value[ n ];
    17. }else
    18. {
    19. value[ n ] = fib( n - 2value ) + fib ( n - 1value );
    20. }
    21. return value[ n ];
    22. }

    硬件设备

    如利用DMA传输方式减少中断次数,因为DMA传输没有用到CPU,而中断用到CPU。

  • 相关阅读:
    Conda创建软件安装环境
    python之subprocess模块详解
    【进阶篇】Java 实际开发中积累的几个小技巧(二)
    Scala系列-5、scala中的泛型、actor、akka
    常用linux解压命令
    excel 如何自动调整行间距 vba
    Android学习笔记 48. Android 多媒体技术——MediaRecorder录制视频
    云计算与大数据第12章 商用云计算平台习题带答案
    数据结构之顺序表习题讲解
    解决Huggingface被墙下载模型的问题
  • 原文地址:https://blog.csdn.net/weixin_47077788/article/details/126084564