微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 嵌入式系统超标量体系CPU的高效软件优化技术

嵌入式系统超标量体系CPU的高效软件优化技术

时间:01-23 来源:3721RD 点击:

引言

  公司基于超标量体系结构的某款PowerPC芯片开发出了电力系列自动化装置,它对实时性要求很高。但软件的运行效率低,这就需要我们针对该芯片的超标量体系结构特点进行软件优化。实践中,在针对性优化后进行对比实验,装置软件运行效率大大提高,实际效果良好。

1 超标量体系结构PowerPC芯片特点

1.1 超标量体系结构芯片

  PowerPC芯片属于超标量体系结构。超标量体系结构是一种微处理器设计模式,它能够在一个时钟周期内执行多条指令。在超标量体系结构设计中,处理器或指令编译器判断指令能否独立于其他顺序指令而执行,或是依赖于另一指令,必须按顺序执行。然后处理器使用多个执行单元并行执行两个或更多独立指令。

1.2 PowerPC芯片特点

1.2.1 流水线机制

  该芯片一条指令,可简单分为取指、译码、执行,提交4个时钟周期操作。同一周期,CPU的不同部件可并行执行多条指令的不同操作,从而达到指令并行,提高CPU的吞吐率。

1.2.2 总线频率

  该芯片的主频达到400 MHz,但访问内存的总线频率是100 MHz,只有主频的1/4。由此可见,当访问内存数据时,其运行时间比执行计算程序慢多了。当系统大量访问内存时,系统运行速度会明显下降。

1.2.3 16 KB的指令Cache和16 KB的数据Cache

  PowerPC芯片中指令Cache和数据Cache中访问指令和数据的速度与主频一样。同样,当读取指令和数据时在Cache中读取的速度约是内存中读取速度的4倍。

(1) 指令Cache运作机制

  每次指令运行时若指令未在指令Cache中,即指令Cache未命中,则一次从内存中读出待执行的连续32字节(32字节相当于8个浮点数)指令到指令Cache。同时将指令Cache中最久未访问的代码淘汰出Cache。32字节相当于3~5条普通C语言代码。

(2) 数据Cache运作机制

  每次访问数据时,若数据未在数据Cache中,即数据Cache未命中,则一次从内存中读出连续32字节数据到数据Cache。同时将数据Cache中最久未访问的数据淘汰出Cache。

2 从超标量流水线机制的角度进行优化

2.1 超标量流水线机制对程序效率的分析

  从前面的流水线机制可以看到,若指令能达到尽可能的并行,程序运行效率会明显提高。这就需要优化代码,让编译器优化成并行指令。

2.2 从提高指令并行和流水线不被打断的角度进行优化

  要提高指令并行,主要就要提高代码并行可能性。防止流水线不被打断,就是要尽量避免跳转。

2.2.1 循环体代码并行执行的优化

  代码举例1:
for(i=0;i<1000;i++) {
Y[i]=Y[i]+Y[i-1];
}

  该代码循环体代码之间因为存在相关数据,导致代码无法被CPU并行执行,需要避免类似代码。

  代码举例2:
for(i=0;i<1000;i++) {
Y[i]=X[i]+Z[i];
}

  该代码循环体代码之间不存在相关,能被CPU并行执行。CPU执行时代码如下:
Y[0]=X[0]+Z[0],
Y[1]=X[1]+Z[1],
Y[2]=X[2]+Z[2],
Y[3]=X[3]+Z[3],

2.2.2 代码顺序执行避免跳转的优化

  跳转的语句主要有if_else结构、switch_case结构、循环结构等。

  if_else结构可以将选择概率最大的语句放到if语句之后。因为取指时,紧接着if语句的指令会被取到。这样发生跳转的次数降低,流水线被中断的概率降低。

  尽量降低循环嵌套层数和循环次数,这样发生跳转的次数也降低。

2.2.3 避免小段程序代码循环的优化

  比如2~3句的小循环,可以适当展开。
  一是可以提高循环内指令并行的可能性。
  二是可以减少跳转次数。
  循环体代码超过10句普通C语言代码,可以不要展开。

3 从指令Cache的角度进行优化

3.1 指令Cache对程序效率的分析

  从前面分析可知,若程序取指环节能从指令Cache中读取,而不是每次都从内存中读取,则能显著提高程序执行速度。

3.2 从提高指令Cache命中的角度进行优化

① 尽量使程序顺序执行。
② 避免大量相似的代码重复实现、分散调用。
③ 尽量将相同的代码在一个地方循环执行,提高指令Cache的命中率。不要分散执行,导致多次读取同一段代码到指令Cache中。

3.2.1 多个相似函数的优化

  代码举例3:
{
FuncA;//3个相似函数连续调用
FuncB;
FuncC;
}
优化为
for(i=0;i<3;i++) {
Func(i);
}

  这样相同的代码一次即可从内存读到Cache中,另外2次指令都是从Cache中读取。

3.2.2 大函数拆分的优化

  由于函数体较大,超出了指令Cache的大小,导致第1次循环结束、第2次循环开始时,函数体前面内容已经被调出Cache。同样代码又重新从内存中读取到Cache中,如此反复,实际的结果是函数体Func代码被三次从内存中读取到Cache中,导致效率大大降低。

  代码举例4:
for(i=0;i<3;i++) {
Func(i);
}
被优化为:
for(i=0;i<3;i++) {
Func1(i);
}
for(i=0;i<3;i++) {
Func2(i);
}
for(i=0;i<3;i++) {
Func3(i);
}

  将函数体Func分成几个单独的子函数:Func1、Func2、Func3,然后分别循环。这样Func1循环时,由于代码量较小,整个函数体都在Cache中。Func2、Func3类似。这样的结果是,函数体Func1、Func2、Func3都只从内存被读一次到Cache中。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top