0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

嵌入式C代码优化:实用技巧与经验分享

电子电路开发学习 来源:电子电路开发学习 2024-03-28 10:53 次阅读

嵌入式代码优化是一个复杂的过程,它不仅取决于代码本身,还取决于目标硬件平台、编译器以及优化的目标(例如速度、内存使用、功耗等)。

不过,有一些通用的技巧可以在编写嵌入式代码时考虑到:

使用查表法

在内存空间较为充足的情况下,有时候可以牺牲一些空间来换取程序的运行速度。查表法就是 以空间换取时间 的典型例子。

比如:编写程序统计一个4bit(0x0~0xF)数据中1的个数。

使用查表法:

staticinttable[16]={0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
intget_digits_1_num(unsignedchardata)
{
intcnt=0;
unsignedchartemp=data&0xf;

cnt=table[temp];

returncnt;
}

优于:

intget_digits_1_num(unsignedchardata)
{
intcnt=0;
unsignedchartemp=data&0xf;

for(inti=0;i< 4; i++)
 {
  if (temp & 0x01)
  {
   cnt++;
  }
  temp >>=1;
}

returncnt;
}

查表法把0x0~0xF中的所有数据中每个数据的1的个数都记录下来,存放到一个表中。这样一来,数据数据中1的个数就建立起了一一对应关系,就可以通过数组索引来获取得到结果。常规法使用for循环的方式来实现,缺点是占用了不少处理器的时间。

特别地,对于越复杂地运算,查表法较常规法更有优势。另一方面,查表法的代码往往比常规法要简洁些。

使用柔性数组

C99中,结构体中的最后一个元素允许是未知大小的数组,这就叫作 柔性数组

254de50c-ec37-11ee-a297-92fbcf53809c.png

柔性数组的特点:

结构体中柔性数组成员前面必须至少有一个其他成员。

sizeof返回的这种结构大小不包括柔性数组的内存。

包含柔性数组成员的结构用malloc()函数进行内存的动态分配。

在C99标准环境中,使用柔性数组:

typedefstruct_protocol_format
{
uint16_thead;
uint8_tid;
uint8_ttype;
uint8_tlength;
uint8_tvalue[];
}protocol_format_t;

优于使用指针:

typedefstruct_protocol_format
{
uint16_thead;
uint8_tid;
uint8_ttype;
uint8_tlength;
uint8_t*value;
}protocol_format_t;

柔性数组的方式结构体占用较指针的方式少。

柔性数组的方式相对与指针的方式更为简洁,给结构体申请空间的同时也给柔性数组申请空间,柔性数组的方式只需要申请一次空间,是一块连续内存,连续的内存有益于提高访问速度;而指针的方式,除了给结构体申请空间之外,还得给结构体里的指针成员申请空间。

使用指针的方式写代码会比柔性数组的方式会繁琐一些,特别地,如果在释放内存的时候把顺序弄反了,则结构体里的指针成员所指向的内存就释放不掉,会造成内存泄露。

使用位操作

1、使用位域

有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。

2558021c-ec37-11ee-a297-92fbcf53809c.png

比如:管理一些标志位。

使用位域:

struct{
unsignedcharflag1:1;
unsignedcharflag2:1;
unsignedcharflag3:1;
unsignedcharflag4:1;
unsignedcharflag5:1;
unsignedcharflag6:1;
unsignedcharflag7:1;
unsignedcharflag8:1;
}flags;

优于:

struct{
unsignedcharflag1;
unsignedcharflag2;
unsignedcharflag3;
unsignedcharflag4;
unsignedcharflag5;
unsignedcharflag6;
unsignedcharflag7;
unsignedcharflag8;
}flags;

2、使用位操作代替除法和乘法

使用位操作:

uint32_tval=1024;
uint32_tdoubled=val<< 1; 
uint32_t halved = val >>1;

优于:

uint32_tval=1024;
uint32_tdoubled=val*2
uint32_thalved=val/2

循环展开

有时候,可以牺牲一点代码的简洁度、减少循环控制语句的执行频率以提高性能。

无依赖的循环展开:

process(array[0]);
process(array[1]);
process(array[2]);
process(array[3]);

优于:

for(inti=0;i< 4; i++) 
{
    process(array[i]);
}

有依赖的循环展开:

longcalc_sum(int*a,int*b)
{
longsum0=0;
longsum1=0;
longsum2=0;
longsum3=0;

for(inti=0;i< 250; i += 4)
 {
  sum0 += arr0[i + 0] * arr1[i + 0];
  sum1 += arr0[i + 1] * arr1[i + 1];
  sum2 += arr0[i + 2] * arr1[i + 2];
  sum3 += arr0[i + 3] * arr1[i + 3];
 }
 
 return (sum0 + sum1 + sum2 + sum3);
}

优于:

longcalc_sum(int*a,int*b)
{
longsum=0;

for(inti=0;i< 1000; i ++)
 {
  sum += arr0[i] * arr1[i];
 }
 
 return sum;
}

尽可能把长的有依赖的代码链分解成几个可以在流水线执行单元中并行执行的没有依赖的代码链,提高流水线的连续性。通常4次展开为最佳方式。

使用内联函数

使用内联函数替换重复的短代码,一方面,可以避免函数的回调,加速了程序的执行,利用指令缓存,增强局部访问性;另一方面,可以方便代码管理。

如:翻转led的操作。

staticinlinevoidtoggle_led(uint8_tpin)
{
PORT^=1<< pin;
}

// 这会减少函数调用的开销,因为函数体会直接嵌入到调用点
toggle_led(LED_PIN);

使用合适的数据类型

首先使用合适的数据类型。

比如几种数据类型都满足需求的情况下,更小的可能并不是最合适的。

比如:素组索引的变量类型。

数组索引应尽量采用int类型。

inti;
for(i=0;i< N; i++)
{
 // ...
}

优于:

chari;
for(i=0;i< N; i++)
{
 // ...
}

定义为char类型,一般会有溢出的风险,因此编译器需要使用多余的指令判断是否溢出;而使用int类型,一般编译器默认不会超过这么大的循环次数,从而减少了不必要的指令。

其它情况下,在满足数据范围的情况下,能够使用字符型(char)定义的变量,就不要使用整型(int)变量来定义;能够使用整型变量定义的变量就不要用长整型(long int),能不使用浮点型(float)变量就不要使用浮点型变量。

多重循环优化

长循环在最内层:

for(col=0;col< 5; col++)
{
 for (row = 0; row < 100; row++)
 {
  sum = sum + a[row][col];
 }
}

优于长循环在最外层:

for(row=0;row< 100; row++)
{
 for(col=0; col < 5; col++ )
 {
  sum = sum + a[row][col];
 }
}

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

尽早退出循环

通常,循环并不需要全部都执行。

例如,如果我们在从数组中查找一个特殊的值,一经找到,我们应该尽可能早的断开循环。例如:如下循环从10000个整数中查找是否存在-99。

charfound=FALSE;
for(i=0;i< 10000; i++)
{
    if (list[i] == -99)
    {
        found = TRUE;
    }
}
 
if (found) 
{
    printf("Yes, there is a -99. Hooray!
");
}

这段代码无论我们是否查找得到,循环都会全部执行完。更好的方法是一旦找到我们查找的数字就终止继续查询。把程序修改为:

found=FALSE;
for(i=0;i< 10000; i++)
{
    if (list[i] == -99)
    {
        found = TRUE;
        break;
    }
}
 
if (found) 
{
    printf("Yes, there is a -99. Hooray!
");
}

假如待查数据位于第23个位置上,程序便会执行23次,从而节省9977次循环。

结构体内存对齐

必要时,手动对齐结构体的内存排列。

比如:

typedefstructtest_struct
{
chara;
shortb;
charc;
intd;
chare;
}test_struct;

该结构体在32bit环境中,该结构体所占的字节数为16。

可以手动调整各成员的位置来进行空白字节填充以达到对齐的效果。如:

typedefstructtest_struct
{
chara;
charc;
shortb;
intd;
chare;
}test_struct;

则结构体变量test_s所占的字节数变为12字节,比原来的16字节省下了4个字节。

优化中断处理

确保中断处理快速且尽可能短。

//中断例程应该尽量简短
voidISR()
{
flag=true;
}

利用硬件特性

使用硬件模块或特有指令来减轻CPU负担。

//比如,直接使用DMA传输而不经由CPU
DMA_Config(&src,&dest,length);
DMA_Start();

以上就是本次的分享。一些优化可能会增加代码的复杂性或降低可读性或其它方面的影响,因此在决定应用优化时,需权衡不同方面的影响。

审核编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    4982

    文章

    18281

    浏览量

    288459
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10442

    浏览量

    206564
  • 函数
    +关注

    关注

    3

    文章

    3868

    浏览量

    61309
  • 编译器
    +关注

    关注

    1

    文章

    1577

    浏览量

    48617

原文标题:实用的嵌入式C代码优化技巧与经验

文章出处:【微信号:mcu149,微信公众号:电子电路开发学习】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    嵌入式C语言代码优化经验与方法

    在本篇文章中,收集了很多经验和方法。应用这些经验和方法,可以帮助我们从执行速度和内存使用等方面来优化C语言代码
    发表于 02-02 09:17 230次阅读

    嵌入式C语言代码优化经验与方法

    在本篇文章中,收集了很多经验和方法。应用这些经验和方法,可以帮助我们从执行速度和内存使用等方面来优化C语言代码
    发表于 03-08 13:27 155次阅读

    如何成为一名嵌入式C语言高手?

    系统中,如何调试和优化程序,如何处理实时性要求等。同时,还可以学习如何使用调试工具和硬件仿真器来辅助调试和测试。 四、积极参与开源项目和技术社区加入一些嵌入式开源项目的社区,与其他开发者交流经验
    发表于 03-25 14:12

    如何成为一名嵌入式C语言高手?

    系统中,如何调试和优化程序,如何处理实时性要求等。同时,还可以学习如何使用调试工具和硬件仿真器来辅助调试和测试。 四、积极参与开源项目和技术社区加入一些嵌入式开源项目的社区,与其他开发者交流经验
    发表于 04-07 16:03

    嵌入式系统编程中代码优化

    System)的广泛使用,高级语言编程已是嵌入式系统设计的必然趋势。但是 不排除一些软件模块仍用汇编语言来写,这可以使程序更加有效。虽然C/C++编译器对代码进行了
    发表于 02-23 10:47

    如何为嵌入式应用编写优秀的C++程序代码

    成员安全地共享这些专业经验。‧使用针对嵌入式应用做过优化的设计工具。虽然许多公司提供嵌入式程序工具,但相对而言,一些工具更能满足嵌入式开发者
    发表于 09-22 16:29

    诚聘嵌入式软件工程师

    、分析并解决嵌入式系统运行过程中出现各类软硬件问题。任职资格:1、电子、通信、计算机相关专业本科以上学历,三年以上开发经验;2、精通CC++,有良好的开发习惯文档规范及编码习惯;3、
    发表于 03-10 10:31

    嵌入式系统内存优化使用

    嵌入式系统功能的提高,占用了较大内存空间,继而时常出现运行无响应。基于用户方面看,由于系统内存问题影响运行,针对系统内存与进程应用状态研究,可以调整系统数值与执行文件elf分析,进行系统优化进而确保
    发表于 11-04 06:23

    嵌入式C++编程的相关资料分享

    编程特性来构建嵌入式系统您将了解如何将您的系统与外部外围设备以及使用驱动程序的有效方式集成指导您测试和优化代码以获得更好的性能并实现有用的设计模式将了解如何使用 Qt,这是用于构建嵌入式
    发表于 11-09 08:26

    嵌入式C语言优化小技巧是什么

    嵌入式C语言优化小技巧
    发表于 12-15 07:23

    C嵌入式程序项目经验总结相关资料分享

    C嵌入式程序项目经验总结这里总结一些项目经验,比如程序调试经验代码设计模式
    发表于 12-21 06:04

    嵌入式应用程序进行性能优化

    满足嵌入式应用的性能需求。1 嵌入式程序优化的类型嵌入式应用程序优化,指在不改变程序功能的情况下,通过修改原来程序的算法、结构,并利用软件开
    发表于 12-22 07:46

    如何将嵌入式代码优化

    嵌入式代码优化,除了最基本的函数实现细节算法优化外,还有一些细节的处理。
    发表于 09-25 09:34 1211次阅读

    嵌入式系统安全实用技巧

    嵌入式系统安全实用技巧
    的头像 发表于 12-28 09:51 512次阅读

    嵌入式C语言代码优化经验与方法

    在本篇文章中,收集了很多经验和方法。应用这些经验和方法,可以帮助我们从执行速度和内存使用等方面来优化C语言代码。 简介 在最近的一个项目中,我们需要开发一个运行在移动设备上但不保证图像
    的头像 发表于 02-09 01:21 399次阅读