现代嵌入式计算 - 第18章 - 性能优化 (第二部分)
来源:互联网 发布:java 正则替换 编辑:程序博客网 时间:2024/06/06 12:40
译自Modern Embedded Computing. by Peter Barry, Patrick Crowley
代码和设计
本节介绍适用于大多数处理器的一些通用的代码优化方法。在很多情况下,这些优化会减少可读性,可维护性,和移植性。确保你是优化需要优化的代码(见模式– 避免过早的代码优化)。
9. 重新排列结构体
上下文:你已经定位性能瓶颈在数据路径上的某段代码。代码采用了大的结构体。
问题:该结构跨越若干高速缓存行。
解决方案:重新排序结构里的字段,使得经常访问的领域放在一起。如果所有的访问域在一个缓存行上,那么首次访问将把整行刷新到缓存中。
有些架构是对地址对齐敏感的。只要有可能,使用编译器编译指令调整到合适的地址边界。
关键点:
•重新排列结构的做法可能不行。一些结构可能会映射到一个数据包的定义。
•多处理器访问相同高速缓存行将会产生额外的处理器间通信。
如果可能,线程间共享的数据结构应该拆分开,不同的线程使用独立的数据结构。
10. 高速中断服务程序
上下文:您的应用程序使用多个中断接口上的数据并触发数据的处理。
问题:中断服务例程可以与其他ISR和实时线程协同工作,实时线程如,网络包处理的内核线程。
解决方法:保持的ISR简短。并且设计成可重入的。
例如,ISR可以释放一个信号,设置一个标志,推一个数据包进队列。您应该在ISR之外,读取队列和处理数据。这样,您避免了在ISR等待数据的锁。
频繁的ISR处理锁会使整个系统产生不可预知的结果。
关键点:
•在中断服务程序错误通常有一个灾难性的影响。他们保持简单可减少错误的概率。
11. 汇编语言
背景信息:已经鉴定了数据路径中消耗最显著的C函数。
问题:为此函数生成的执行代码并不是最优的,或者对您的处理器而言还可进一步优化。
解决办法:用汇编语言直接重新实现关键功能。
使用最好的编译器(见模式-应用最佳的编译器)来生成初始汇编代码,然后手工优化。
关键点:
•对于复杂的处理器,现代编译器技术已经开始在优化考量上优于人类。
•汇编语言是阅读和维护更加困难。
•汇编语言移植到其他的处理器更加困难。
12. 内联函数
上下文:你已经确定了一个小的C函数被频繁调用,尤其在数据通路上的C函数。
问题:应用程序数据路径频频调用的小函数,进入和退出函数带来的开销会变得很显著。
解决方法:声明函数内联。这样,函数被直接插入到调用函数的代码中。
关键点:
•内联函数会增加应用程序的代码大小,并给代码高速缓冲带来压力。
•有些调试器显示不能调试内联函数。
13. 优化循环
背景信息:您已经确定了某个循环,它是消耗数据通路性能的显著部分。
问题:循环的结构或在其上进行的操作数据可以“捣毁”数据缓存。
解决方法:可以考虑一些循环/数据优化:
• 数组合并--- 如果循环使用了两个或多个数组,把它们合并到一个数组结构中,减少高速缓存的更新。
•感应变量交换。感应变量指的是每次迭代定额增加或减少的变量。(比如循环中的i)
•循环融合
关键点:
•循环优化可能使代码难以阅读,理解和维护
14. 减少局部变量
上下文:你已经确定了需要优化的函数。它含有大量的本地变量。
问题:大量的局部变量可能会增加它们在堆栈上的开销。编译器需要生成代码来设置和恢复帧指针。
解决方案:减少局部变量的数目。使得编译器可以存储所有的本地变量和参数到处理器寄存器中。
关键点:
•删除局部变量会降低代码的可读性,或者需要执行额外计算。
15. 显式使用寄存器
上下文:你已经确定了需要优化的函数。一个局部变量或数据块频繁被使用。
问题:有时,编译器无法识别一个变量可以进行寄存器优化。
解决方法:显式指定寄存器优化局部变量,这是经常使用的方法。
另外比如,数据包的包头在算法路径上被频繁使用,可以进行寄存器优化。在一个实际的应用,这种类型的优化对性能改进大约20%。
或者,你可以用一个局部寄存器变量,缓存经常使用的全局变量。比如,你知道这个全局变量不被任何ISR修改,并且在某个函数中被频繁使用,比如,在一个处理多个包的循环中的统计变量,您可以先把这个全局变量赋值到局部寄存器变量中,然后在退出函数之前,更新这个全局变量。
关键点:
•寄存器(register)关键字对编译器只是一个提示。
16. 优化硬件寄存器操作
上下文:算法路径上,对一个或多个硬件寄存器进行多次读和写操作
问题:对硬件寄存器的”读-操作-写“可导致处理器停顿。
解决方案:首先,分解”读-操作-写“语句来隐藏部分操作硬件寄存器存在的延时
例如:
”读-操作-写“硬件寄存器:
* reg1ptr |=0x0400;
* reg2ptr &=〜0x80;
优化如下:
reg1 = * reg1ptr;
reg2 = * reg2ptr;
reg1 |= 0x0400;
reg2 &= ~0x80;
* reg1ptr = reg1;
* reg2ptr = reg2;
此修改后的代码消除了读相关延迟之一。
第二,合并数据路径中多个写入相同硬件寄存器中的代码。例如,某些应用程序禁用
硬件中断时,多次设置/复位中断使能寄存器。在一个这样的应用中,当我们手动合并这些写指令,性能改良约4%。
关键点:
• 分离”读-操作-写“代码,它也将增加本地变量,进而可能引发帧指针的创建。
17. 避免使用OS缓冲池
上下文:应用程序使用了系统缓冲池。
问题:在一些操作系统,系统缓冲池的管理使用了,锁定中断和使用本地信号来保证同步。这些都非常耗时。设计阶段,要特别注意缓冲管理。尤其是否在数据路径上用到这些OS管理的缓冲池。
解决方案:避免在数据路径上用到或操作OS管理的数据缓冲池。在数据路径之外,预先分配好包缓冲池,并将它们存储在轻量级软件池/队列中。
堆栈或数组在管理缓冲区时通常比链表更有效,因为它们需要较少的内存访问来添加和删除缓冲区。栈还提高了数据高速缓存区的利用率。
关键点:
•OS缓冲池实现缓冲区的管理。另写一个功能上重复了。
18. C语言的优化
背景信息:您已确定的函数或代码段正显著消耗着数据路径上的CPU时钟周期。你可能是使用性能分析工具(见模式-分析工具)定位到代码。
问题:C代码的函数或段需要优化。
解决方法:可以尝试一些C语言级优化:
•使用引用传递大的函数的参数,而不是参数值。参数值需要时间来复制。
• 避免数组索引。使用指针。
•减少循环的嵌套,合并到单一的循环中。
•避免长的IF-THEN-ELSE链。使用switch语句或状态机。
•使用int(处理器的天然字长)来存储变量,而不是char或short。
•尽可能使用unsigned变量和参数。这样做可能会带来一些编译器优化。
• 避免数据路径上的浮点计算。
• 使用递减循环变量,例如,
for(i = 10; i --;){dosomething}
甚至更好
do { something )while (i --);
• 读读你的编译器生成的这段汇编码。
• 调整结构体,大小为2的幂。
•if-else语句中,把最可能出现的语句放在if段里
•使用return返回值,而不是某个指针参数,许多处理器返回值约定存储在一个寄存器中。
关键点:
•良好的编译器做了部分这些优化。
19. 关闭计数器/统计
背景:许多应用程序有大量与数据路径相关联的统计/计数器。您已完成应用程序的集成测试。
问题:软件保留了一些计数器和统计,以便系统整合和调试。这些计数器,通常需要读取和
写,主内存,或高速缓存。
解决方法:找找是否有宏可以禁用这些计数器和统计,还包括一些仅在集成调试阶段用的检查代码。
在一种应用中,使用该模式高达4%的吞吐量提升。
关键点:
•禁用计数器和统计也失去了有用的调试信息。
- 现代嵌入式计算 - 第18章 - 性能优化 (第二部分)
- 现代嵌入式计算 - 第18章 - 性能优化 (第一部分)
- 现代嵌入式计算 - 第18章 - 性能优化 (第三部分)
- 现代嵌入式计算 - 第16章 - 设计实例 (第一部分)
- 现代嵌入式计算 - 第4章 - 嵌入式平台架构 (第2部分, 易失性存储器技术)
- 现代嵌入式计算 - 第4章 - 嵌入式平台架构 (第3部分,非易失性存储器技术)
- 现代嵌入式计算 - 第4章 - 嵌入式平台架构 (第一部分)
- 现代嵌入式计算 - 第16章 - 设计实例 (第2部分)
- 现代嵌入式计算 - 第17章 平台调试
- 现代嵌入式计算(英文版)
- [翻译]现代java开发指南 第二部分
- Domino 6应用程序性能优化指南(第二部分)
- Domino 6应用程序性能优化指南(第二部分)
- Domino 6应用程序性能优化指南(第二部分)
- Domino 6应用程序性能优化指南(第二部分)
- C语言程序设计现代方法(第二版)_______第二、三、四章编程题部分
- 第18章 (第二部分)类加载器机制
- 【数学建模】现代优化算法部分提纲
- Maven学习 (四) 使用Nexus搭建Maven私服
- socket的底层创建与关闭 (A)
- iOS开发 非常全的三方库、插件、大牛博客等等
- 停止Java线程,小心interrupt()方法
- makefile三个常见变量:$@,$^,$<
- 现代嵌入式计算 - 第18章 - 性能优化 (第二部分)
- Unicode, UTF-8, ASCII文件编码格式
- 十三周项目5
- LeetCode[395] Longest Substring with At Least K Repeating Characters
- Robotium学习(三)-执行shell命令的方法
- 2-8 系统调用方式的文件编程
- Gradle学习总结——抓重点学Gradle
- 原子性和可见性有序性
- 利用jmail qq邮箱发邮件 报错 解决方法