浮点 DSP 运算效率不高
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
浮点 DSP 运算效率不高
问题:
该问题由某客户提出,发生在STM32F407IGT6器件上。据其工程师讲述:由于在其产品中,需要使用STM32进行大量的浮点数以及浮点DSP运算,所以针对STM32的浮点数运算能力及 DSP 运算能力做了相关的测试,但测试结果不理想。STM32F407 在144MHz 主频下,对于表(一)程序的运算耗时为:9105uS。没有体现出硬件浮点运算应有的运算能力。
调研:
使用 Keil MDK4.21 创建工程对表(一)的程序进行测试。在工程设置中,选择支持浮点运算指令。将编译器的优化等级设置为 LEVEL1,然后编译运行。通过示波器测量主程序在调用该函数之前和在该函数返回之后在 I/O 管脚上所发出的脉冲之间的时间差,来判断 STM32 运行该函数所花费的时间。
1. 当 STM32 的主频为 168MHz,使用 SRAM2 存贮变量的条件下,测得时间消耗为:7840uS。修改工程设置,将内存的使用由 SRAM2 转成 CCM,重新编译、下载、运行,测得时间消耗为:7840uS。
2. 修改工程设置,将优化等级由 LEVEL1 换成 LEVEL3。为了避免编译器把整个测试函数优化掉,修改该函数如表(二)。重新编译、下载、运行,测得时间消耗为:7660uS。
3. 修改程序代码,将序函数 sin()和 cos()分别替换成 ARM DSP library 中的arm_sin_f32()和arm_cos_f32(),如表(三)所示。重新编译、下载、运行,测得时间消耗为:748uS。
4. 修改代码,将计算中所用到的小数常量,表示成单精度,如表(四)所示。重新编译、下载、运行,测得时间消耗为:130uS。
结论:
三个方面的原因导致了计算效率降低:
1. 编译器的优化等级不够,以至生成的代码本身的效率低下;
2. 表达式上的漏洞,造成部分计使用了双精度浮点运算;
3. 三角函数库的选取不当,导致在计算正弦、余弦过程中引入了大量的双精度浮点运算;处理:
1. 合理选用编译器的优化等级,提高代码的执行效率;
2. 在计算表达式中,强制常量为单精度浮点数,以避免引入双精度浮点数动算;
3. 选用 ARM 公司提供的,采用单精度浮点数优化的三角函数库,以避免由普通的数学函数库引入的双精度浮点数运算;
建议:
从应用角度看,Cortex-M4 可以认为是 Cortex-M3 的加强版。相对 Cortex-M3,Cortex-M4主要从两个方面进行了改进:
1. 增加了 DSP、SIMD 指令;
2. 增加了单精度浮点运算单元;
第 1 点使得 Cortex-M4 拥有高速的定点 DSP 处理能力,第 2 点则使得 Cortex-M4 拥有强大的数学运算能力。受益于 Cortex-M4 的优异的处理性能,以及高效的总线与存贮器接口,STM32F4 系列 MCU 在数据处理方面有着非凡的表现。不过,要使硬件上的性能得以充分的发挥,除了对算法本身进行优化以外,对一些相关因素也要仔细斟酌:
1. 恰当的设置 Flash 缓冲区的参数。在 STM32F4 中,为了匹配 Flash 存贮器与CPU 之间的对数据、指令的吞吐速率,设有指令缓冲区和数据缓冲区。复位后缓冲区是不工作的,需要软件予以开启,并设置恰当的等待周期数。没有缓冲区的参与,运行在 Flash 中的程序,在运效率上会大幅度的降低。
2. 选择高效的存贮器来存放数据。快速的数据存取是保证 CPU 不间断的执行指令的前提。这一点上,不仅要考查存贮器本身的速率,还要看是否有其它的处理单元与 CPU 分享该存贮器。比如,在STM32F4 中,将数据放在 CCM 存贮器中,要比放在 SRAM1 中更能保证 CPU 对数据的存取速率,因为CCM 存贮器是 CPU独享的,而 SRAM1 还可能被 DMA 访问。
3. 根据 CPU 指令集的特点,合理的选取计算的数据类型。比如,要计算 16 位的DSP 运算,最好把变量和常量定义成 16 位数据,这样有利于编译器使用 SIMD指令对代码进行优化。在单精度浮点数能够满足要求的情况下,将变量或常量定义成单精度类型,有利于编译器使用单精度浮点运算指令,对代码优化。
4. 选择针对 Cortex-M4 进行优化的数学函数库。ARM 公司为 Cortex_M4 的提供了一整套的 DSP、浮点数运算库,其效率远高于编译器自带的函数库。
5. 编译器的优化等级是一个重要的设置选项,不同的优化等级下,所生成的代码的效率是有很大差别的。
6. 对于高频次使用的数据,要考虑放在寄存器类型的变量中。通常,CPU 存取操作数的最快捷的方式是寄存器寻址,可以做到零时钟花费。
7. 合理安排计算次序,考虑是否可以以乘法代替除法。在数学中,乘法和除法是一对逆运算,除以一个数与乘于这个数的倒数可以等同起来。然而在 CPU 的指令实现中,乘法和除法的计算速度上是有很大差别的,通常乘法的计算速度远高于除法的计算速度。所以,有必要在运算中,使用乘法来代替除法。比如:可以使用来代替。
8. 找出算法中的重复计算,将其合并,只计算一次。对这样的计算,完全可以在第一次计算之后,将结果放在中间变量中,而在后面的计算中直接引用。
9. 对于复杂的计算,考虑是否能用查表来代替。查表是一种快速得出结果的好方法,它以牺牲存贮器空间来换取速度。在存贮器空间不是很紧张的情况下,用查表代替计算还是很划算的。