问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

ARM_MATH_LOOPUNROLL:提升代码性能的秘密武器

创作时间:
作者:
@小白创作中心

ARM_MATH_LOOPUNROLL:提升代码性能的秘密武器

引用
github
10
来源
1.
https://github.com/ARM-software/CMSIS_5/issues/1300
2.
https://m.blog.csdn.net/emily0626/article/details/72633005
3.
https://www.csdn.net/tags/MtjaUg4sMjc5NjEtYmxvZwO0O0OO0O0O.html
4.
https://m.edu.iask.sina.com.cn/jy/3f6G7TeCFGf.html
5.
https://blog.csdn.net/qq_18460067/article/details/119742664
6.
https://wenku.csdn.net/answer/44iouay1ve
7.
https://arm-software.github.io/CMSIS-DSP/
8.
https://community.st.com/t5/stm32-mcus-embedded-software/cmsis-dsp-library-performance/td-p/132955
9.
https://www.cnblogs.com/armfly/p/12653719.html
10.
https://web.eece.maine.edu/~hummels/classes/ece486/docs/CMSIS/Documentation/DSP/html/ChangeLog_pg.html

在嵌入式开发领域,特别是在数字信号处理(DSP)应用中,代码性能优化至关重要。ARM公司提供的CMSIS-DSP库中包含了一个名为ARM_MATH_LOOPUNROLL的预处理宏,这个宏通过循环展开(loop unrolling)技术显著提升代码执行效率。本文将深入探讨这个宏的作用原理、使用方法以及实际性能优化效果。

01

循环展开技术原理

在程序中,循环结构虽然简洁,但每次迭代都需要执行循环控制指令,如比较、跳转等,这些额外指令会带来性能开销。循环展开技术通过减少循环控制的频率来优化性能。

具体来说,循环展开是将循环体复制多次,每次处理多个数据元素,从而减少总的迭代次数。例如,将每次处理1个元素的循环改为每次处理4个元素,可以将循环控制开销降低到原来的1/4。

循环展开的另一个好处是隐藏内存访问延迟。现代处理器通常具有指令流水线,如果每次只处理一个元素,流水线可能会因为等待数据从内存加载而停滞。通过一次处理多个元素,可以充分利用处理器的并行性,减少等待时间。

02

ARM_MATH_LOOPUNROLL的作用

ARM_MATH_LOOPUNROLL是ARM CMSIS-DSP库中的一个预处理宏,用于控制是否启用循环展开优化。当这个宏被定义时,库函数会自动应用循环展开技术,从而减少循环控制开销,提高执行效率。

这个优化特别适合处理小批量数据的场景。在DSP应用中,很多算法需要频繁处理短小的数据块,如滤波、FFT等。通过循环展开,可以显著减少这些算法的执行时间。

03

使用示例

下面以求绝对值函数arm_abs_f32为例,展示ARM_MATH_LOOPUNROLL如何影响代码生成:

void arm_abs_f32(const float32_t * pSrc, float32_t * pDst, uint32_t blockSize) {
    uint32_t blkCnt = blockSize;

#if defined(ARM_MATH_LOOPUNROLL)
    // 当 ARM_MATH_LOOPUNROLL 定义时,每次循环处理4个元素
    blkCnt = blockSize >> 2U;
    while (blkCnt > 0U) {
        *pDst++ = fabsf(*pSrc++);
        *pDst++ = fabsf(*pSrc++);
        *pDst++ = fabsf(*pSrc++);
        *pDst++ = fabsf(*pSrc++);
        blkCnt--;
    }
    // 处理剩余不足4个的情况
    blkCnt = blockSize & 0x3U;
#else
    // 没有定义 ARM_MATH_LOOPUNROLL 时,逐个处理元素
#endif

    while (blkCnt > 0U) {
        *pDst++ = fabsf(*pSrc++);
        blkCnt--;
    }
}

在这个例子中,如果ARM_MATH_LOOPUNROLL被定义,代码会每次从源数组读取4个元素,并计算它们的绝对值后写入目标数组。这样减少了循环控制的开销,提高了执行效率。

04

性能优化效果

循环展开技术可以带来显著的性能提升,特别是在处理小批量数据时。但是,实际优化效果会受到多种因素的影响,包括:

  1. 编译器优化设置:不同的编译器优化级别可能会影响循环展开的效果。例如,GCC编译器的-funroll-loops选项可以自动进行循环展开,如果与ARM_MATH_LOOPUNROLL配合不当,可能导致过度优化或优化不足。

  2. 数据缓存:如果数据不在CPU缓存中,频繁的内存访问仍然会成为瓶颈。在某些情况下,循环展开可能导致更多的数据加载延迟,反而降低性能。

  3. 处理器架构:不同的处理器架构对循环展开的响应不同。例如,ARM Cortex-M系列处理器的流水线结构和NEON SIMD指令集会影响优化效果。

下面是一个实际测试案例,展示了ARM_MATH_LOOPUNROLL带来的性能提升:

void main() {
    unsigned int N = 100000000;
    unsigned int blockSize = 32;

    float32_t vinp1[32] __attribute__((aligned (128)));
    float32_t vinp2[32] __attribute__((aligned (128)));
    float32_t voutp[32] __attribute__((aligned (128)));

    while (N--) {
        arm_mult_f32(&vinp1[0], &vinp2[0], &voutp[0], blockSize);
    }
}

在ARM Cortex-A7处理器上,使用GCC 9.3.0编译器,开启ARM_MATH_LOOPUNROLL后,这段代码的运行时间从13.39秒减少到10.56秒,性能提升了约21%。

05

使用场景和注意事项

ARM_MATH_LOOPUNROLL最适合以下场景:

  • 需要频繁处理小批量数据的DSP算法
  • 对性能要求极高,但代码体积不是主要考虑因素的应用
  • 处理器支持NEON SIMD指令集,可以进一步提升并行处理能力

但是,在使用时需要注意以下几点:

  1. 循环展开会增加代码体积,可能导致内存占用增加
  2. 过度展开可能导致指令缓存失效,反而降低性能
  3. 需要根据具体应用场景和处理器架构调整展开程度
  4. 编译器的优化设置需要与循环展开配合得当

总结来说,ARM_MATH_LOOPUNROLL是ARM CMSIS-DSP库中一个强大的性能优化工具。通过循环展开技术,它可以显著减少循环控制开销,提升代码执行效率。在实际应用中,开发者需要根据具体需求和硬件环境合理使用这个宏,以达到最佳的性能优化效果。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号