硬汉嵌入式论坛

 找回密码
 立即注册
查看: 2188|回复: 6
收起左侧

[STM32H7] 实战技能分享,如何让工程代码各种优化等级通吃,含MDK AC5,AC6,IAR和GCC

  [复制链接]

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
发表于 2021-11-25 01:27:29 | 显示全部楼层 |阅读模式
引出问题:

    一个好的工程项目代码,特别是开源类的,如果能做到各种优化等级通吃,是一种非常好的工程案例,这样别人借鉴的时候,可以方便的适配到自己工程里。但实际项目中,针对一款产品代码,我们一般不会这么干,因为非常耗精力,意义也不大,一般是追求最高性能,最小代码量或者更高的稳定性,我们会选择一个合理的优化等级。

但是随着工程的复杂,特别是一些第3方组件的加入,很容易碰到不耐优化的情况。也就是这个组件没法适配到我们当前的优化等级里面。甚至有时候我们还会遇到高优化等级能用,改成0级优化反倒不能用了。

本期帖子我们就分享一种方法来解决这个问题,合理的设置不同代码的不同优化等级,即一种优化为主优化等级,其它代码设置到能用的优化等级上,以此来达到通吃的目的。

如果采用这种办法可以一步一步的锁定具体问题所在,并将工程文件全部设置到同一个优化等级是最好的。

MDK设置方法(AC5和AC6):

分两个方向:
1、开启优化后,部分功能不正常


解决思路是把这部分的文件继续设置为低优化等级,整体工程设置为高优化等级(这种方法可以锁定有问题的文件,然后锁定具体有问题的函数)。



2、开启优化后,直接整体卡死

解决思路是整体工程设置为低优化等级,逐步开启工程文件的优化等级。具体到某些函数的优化也是可以单独开启测试的。

AC5设置方法:

比如设置函数优化等级为0
https://www.keil.com/support/man ... hr1359124988971.htm

  1. #pragma push
  2. #pragma O0
  3. void function(void){
  4.     ...                 // Optimized at O0
  5. }
  6. #pragma pop
复制代码

AC6设置方法:
这里设置无优化
  1. void function(void) __attribute__((optnone))

  2. {
  3.     ...   // Optimized none
  4. }
复制代码

IAR设置方法:

IAR和MDK的设置是一样的,同样我们也分为两个方向:

1、开启优化后,部分功能不正常

解决思路是把这部分的文件继续设置为低优化等级,整体工程设置为高优化等级(这种方法可以锁定有问题的文件,然后锁定具体有问题的函数)。



2、开启优化后,直接整体卡死

这种的解决思路是整体工程设置为低优化等级,逐步开启工程文件的优化等级。具体到某些函数的优化也是可以单独开启测试的。

比如设置函数无优化:
https://netstorage.iar.com/SuppDB/Public/UPDINFO/004916/arm/doc/EWARM_DevelopmentGuide.ENU.pdf (253页)

  1. #pragma optimize=none
  2. void foo(void)
  3. {
  4.     /* Do something, but don't optimize this function */
  5. }
复制代码

GCC设置方法:

GCC的话,我们这里以Embedded Studio为例进行说明,同样我们也分为两个方向:

1、开启优化后,部分功能不正常

解决思路是把这部分的文件继续设置为低优化等级,整体工程设置为高优化等级(这种方法可以锁定有问题的文件,然后锁定具体有问题的函数)。




2、开启优化后,直接整体卡死

这种的解决思路是整体工程设置为低优化等级,逐步开启工程文件的优化等级。具体到某些函数的优化也是可以单独开启测试的。

比如设置函数无优化:
  1. #pragma GCC push_options
  2. #pragma GCC optimize ("O0")
  3. void foo(void)
  4. {
  5.     /* Do something, but don't optimize this function */
  6. }
  7. #pragma GCC pop_options
复制代码


不同优化最容易出问题的地方:

延迟类函数最容易出问题,特别是像for循环这种简单实现的延迟。可以考虑使用DWT时钟周期计数器做延迟。

http://www.armbbs.cn/forum.php?mod=viewthread&tid=89128


不迷信编译器:

即使再强劲的编译器,有触摸不到的天花板。

MDK AC6的0级优化在这方面的设计问题最明显。比如MDK AC6.14使用0级优化编译HAL库的n级条件表达式会产生巨大的栈需求。

现象:

使用MDK5.30 AC6.14的0级优化测试RTX5的模板程序,发现启动任务需要高达2000字节的栈需求。

原因分析:

通过不断的调试和查看map,htm等文件,最终锁定是H7的HAL库函数UART_SetConfig导致的。



进一步的排查,锁定是下面这种n级条件表示导致的,下面这种类型的表达式偏偏在函数UART_SetConfig里面有一大批,导致产生巨大的栈需求。

  1. /** @brief  Get UART clok division factor from clock prescaler value.
  2.   * @param  __CLOCKPRESCALER__ UART prescaler value.
  3.   * @retval UART clock division factor
  4.   */
  5. #define UART_GET_DIV_FACTOR(__CLOCKPRESCALER__) \
  6.   (((__CLOCKPRESCALER__) == UART_PRESCALER_DIV1)   ? 1U :       \
  7.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV2)   ? 2U :       \
  8.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV4)   ? 4U :       \
  9.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV6)   ? 6U :       \
  10.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV8)   ? 8U :       \
  11.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV10)  ? 10U :      \
  12.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV12)  ? 12U :      \
  13.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV16)  ? 16U :      \
  14.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV32)  ? 32U :      \
  15.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV64)  ? 64U :      \
  16.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV128) ? 128U :     \
  17.    ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV256) ? 256U : 1U)
复制代码

解决办法:

使用AC6中0以外的其它优化就解决了,或者使用AC5的任何优化等级也都可以解决。

又比如:
如果用AC6的优化等级0,没有选择使用微库的话(底层做了C标准库重定向),偶尔会造成脱机(调试仿真下可以使用,拔掉下载器运行就失败)执行失败,将微库勾上即可解决:

这坑也非常容易遇到。


各种优化等级通吃的实战案例分享:

那么问题来了,有没有不需要设置不同优化等级的综合Demo分享?  有的,早期为V6板子设计的二代示波器Demo,可以各种优化等级通吃,并且开启了时间优化。无需采用本帖的特别设置方法,直接切换优化等级就可以使用,大家有兴趣可以看看工程代码:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=45785



实际项目中让程序代码在所有优化等级下都可以正常运行来检查各种奇葩问题,也是一种非常有效的检测手段,确实可以找到程序里面的一些隐形bug。


回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
 楼主| 发表于 2021-11-25 02:04:13 | 显示全部楼层
终于整了完毕。
回复

使用道具 举报

73

主题

1193

回帖

1412

积分

至尊会员

积分
1412
发表于 2021-11-25 08:59:39 | 显示全部楼层
牛逼了
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

积分
2
发表于 2021-11-25 09:44:03 | 显示全部楼层
这个太好了。昨天就是因为这优化问题,O3下写入16位的数据只有低11位的是正常。指定那个写入函数为O0就正常了。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
 楼主| 发表于 2021-11-25 12:06:01 | 显示全部楼层
itsun 发表于 2021-11-25 09:44
这个太好了。昨天就是因为这优化问题,O3下写入16位的数据只有低11位的是正常。指定那个写入函数为O0就正常 ...

是的,很多时候还是很好使的。
回复

使用道具 举报

0

主题

12

回帖

12

积分

新手上路

积分
12
发表于 2021-12-23 22:07:02 | 显示全部楼层
AC6设置优化的关键字 __attribute__ , 前面是两个下滑线,文中漏了一个。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
 楼主| 发表于 2021-12-24 01:13:39 | 显示全部楼层
xdj0818 发表于 2021-12-23 22:07
AC6设置优化的关键字 __attribute__ , 前面是两个下滑线,文中漏了一个。

谢谢,已经修正过来了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-28 23:28 , Processed in 0.272463 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表