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();

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

审核编辑:黄飞

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

    关注

    5082

    文章

    19100

    浏览量

    304642
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10850

    浏览量

    211518
  • 函数
    +关注

    关注

    3

    文章

    4326

    浏览量

    62557
  • 编译器
    +关注

    关注

    1

    文章

    1623

    浏览量

    49103

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

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

收藏 人收藏

    评论

    相关推荐

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

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

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

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

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

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

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

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

    嵌入式实时程序设计中C/C++代码优化

    本文简单介绍了嵌入式实时程序设计的特点和嵌入式系统设计中语言的选择,着重介绍了以下几种在嵌入式实时程序设计中优化 C/
    发表于 08-07 08:47 15次下载

    嵌入式程序设计中C/C++代码优化

    本文介绍了在嵌入式程序设计中几种提高C/C++代码效率的方法,通过对例子的分析,探讨了影响程序效率的原因。关键词:c语言,
    发表于 08-14 08:53 25次下载

    大神教你:嵌入式系统C++代码的变成技巧

    嵌入式软件技术中,C++语言具有较高的编程效率。但是,要实现高效率,还有许多问题需要特别注意。首先,应该正确理解C++的工作原理,逐步利用它的各种强大功能,把专业经验集成到对象中,并
    发表于 05-25 09:20 3598次阅读

    如何将嵌入式代码优化

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

    嵌入式系统C语言的特点及程序设计中代码优化的技巧

    目前,在嵌入式系统开发中可使用的语言很多,其中 C语言应用得最广泛。虽然用 C 语言编程具有许多优点,但基于嵌入式系统的C语言和标准
    的头像 发表于 09-02 09:14 2939次阅读

    嵌入式外中断c语言代码

    嵌入式外中断c语言代码(arm嵌入式开发实例)-嵌入式外中断c语言
    发表于 07-30 11:29 4次下载
    <b class='flag-5'>嵌入式</b>外中断<b class='flag-5'>c</b>语言<b class='flag-5'>代码</b>

    嵌入式项目实战经验

    嵌入式项目实战经验分享,C/C++、Linux、STM32、51单片机、FPGA、IoT、OpenCV、数字图像处理、通信、算法!
    发表于 11-03 12:36 23次下载
    <b class='flag-5'>嵌入式</b>项目实战<b class='flag-5'>经验</b>

    嵌入式C++编程

    编程特性来构建嵌入式系统您将了解如何将您的系统与外部外围设备以及使用驱动程序的有效方式集成指导您测试和优化代码以获得更好的性能并实现有用的设计模式将了解如何使用 Qt,这是用于构建嵌入式
    发表于 11-04 10:36 10次下载
    <b class='flag-5'>嵌入式</b><b class='flag-5'>C</b>++编程

    嵌入式系统安全实用技巧

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

    嵌入式代码高效运行指南

    嵌入式C语言之所以经久不衰,在于它的运行效率很高,想要高效运行代码,除了编译器帮忙优化,关键还要靠自己“优化
    的头像 发表于 01-06 15:32 876次阅读

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

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