隧道窑厂家
免费服务热线

Free service

hotline

010-00000000
隧道窑厂家
热门搜索:
技术资讯
当前位置:首页 > 技术资讯

针对嵌入式SoC应用的C编程优化

发布时间:2020-06-30 21:41:38 阅读: 来源:隧道窑厂家

开发运行在SoC内的嵌入式处理器内核的程序时,工程师有两个主要目的:运行得足够快,使处理器运行的频率降到最低;消耗尽量少的内存,使内存开销降到最小。

本文引用地址:对于不同的项目,有时候这两个因素的重要性会不一样。下面两个关键因素极大地影响着设计团队满足这些目标的能力:开发源程序的编译器对代码的优化效率以及用于开发源代码的编程风格。本文将深入地讨论这两种因素,并提出一些创建小而快的C程序的建议。

编译器原理

编译器通常是由前端和后端两部分组成。前端通常是指语法和语义的处理过程,后端通常是指优化、代码生成,以及针对特定处理器的优化过程。很多好的编译器后端依赖于多层的中间表述(IR)。优化和代码生成从高层(类型输入程序的句法)到低层逐级地传递中间表述。与处理器无关的优化一般倾向于在编译过程的早期在较高IR层上实现,而针对特定处理器的优化一般倾向于在编译过程的后期在低层IR上来实现。信息通过不同IR层向下传递,这样低层优化可以充分利用编译器早期处理得到的高层信息。

Tensilica针对其Xtensa可配置处理器和Diamond标准处理器的XCC/C++编译器包含四个基本的优化级,从-O0到-O3,对应着不断提高的优化级别。表1描述了这些级别及其相对应的代码大小和内部过程分析(IPA)。缺省情况下,XCC编译器一次优化一个文件,但是它也可以执行内部过程分析(通过加入IPA的编译选项)。当在多个原文件上优化整个应用程序时,优化将会被延迟到链接的步骤之后进行。表2描述了当前编译器(包括 XCC编译器)支持的优化内容部分列表。

XCC编译器还可以利用编译产生的性能分析数据。性能分析的反馈可以帮助编译器减轻分支跳转的延迟。另外,反馈可以让编译器只是插入那些最常用的函数(inline),并且妥善处理常用代码段中寄存器溢出的问题。因此,性能分析反馈允许XCC编译器在所有地方进行正常优化的同时,还可以通过优化应用中的临界部分进行加速。

一些有用的C编码规则

为了利用编译器得到最好的性能,编程人员需要像编译器一样思考问题,并且理解C语言和目标处理器之间的关系。下面的一些基本原则可以帮助所有嵌入式编程人员在不需很大努力的情况下获得性能好很多的编译代码。

1. 观察编译得到的代码

完全理解编译器对全部代码如何编译是不可能的。如果XCC编译器设置了—S或者-save-temps编译选项,编译将产生汇编输出并且还有一些为了理解而添加的注释。对于那些性能要求很高的代码,你可以观察编译结果是否符合你的期望。如果不是,请考虑以下规则。

2. 了解混淆发生的情况

C语言允许任意地使用指针,这增加了混淆出现的机会,这允许程序用很多种方法去引用同一数据对象。如果全局变量的地址被作为子程序的参数传递,这个变量可以通过它的名字或者通过指针被引用。这就是一种混淆,编译器必须保守地把这样的数据对象保存在内存中而不是寄存器中,并且仔细地保持代码中可能引起混淆的变量的访问顺序。考虑下面的代码:

void foo(int *a, int *b)

{

int i;

for (i=0; i100; i++) {

*a += b[i];

}

}

您会设想编译器应该产生代码是在循环开始前将*a保存到一个寄存器里面,并且在循环中把b[i]保存到一个寄存器里面然后将它加到*a所在的寄存器里。但事实上却是,编译器产生的结果是*a被放置在内存里面,因为a和b可以产生混淆情况,*a也许是b数组的一个元素。虽然看起来在这个例子中不太可能出现这种混淆,但是编译器是没法确定这种情况是否会发生的。有几个技巧可以针对混淆的情况,帮助编译器能做到更好的编译工作:你可以使用-IPA 编译选项进行编译,你可以用全局变量代替参数,你可以使用特殊编译选项进行编译,或者可以在声明变量中使用_restrict属性。

3. 指针常常引起混淆

编译器识别指针指向的目标对象经常会遇到问题。程序员可以通过使用本地变量帮助编译器去避免混淆,具体方法是使用本地变量去存储依据指针访问获得的值,因为不直接的操作和调用影响指针引用的值而不是本地变量的值。因此,编译器会把本地变量放到寄存器里面去。

下面的例子显示如何正确使用指针以避免混淆从而产生更好的编译代码。在这个例子里面,优化者不知道*p++=0是否会修改len,所以它不能把len放到寄存器里面去获得性能提升。相反每个循环中,len都被放到了内存里面。

int len = 10;

void

zero(char *p)

{

int i;

for (i=0; i

}

通过使用本地变量而不是全局变量,可以避免混淆。

int len = 10;

void

zero(char *p)

{

int local_len = len;

int i;

for (i=0; i local_len; i++) *p++ = 0;

}

4. 使用const和restrict限定词

_restrict限定词告诉编译器可以假设有资格的指针是唯一访问某内存或数据对象的方式。通过这个指针的Load和Store操作不会引起与这个函数内部其它Load和Store操作的混淆,除非通过这个指针的访问。例如:

float x[ARRAY_SIZE];

float *c = x;

void f4_opt(int n, float * __restrict a, float * __restrict b)

{

int i;

/* No data dependence across iterations because of __restrict */

for (i = 0; i n; i++)

a[i] = b[i] + c[i];

}

linux操作系统文章专题:linux操作系统详解(linux不再难懂)

吉林制做职业装

青岛防静电工作服制作

辽宁工作服制做