1. 程式人生 > 其它 >【轉】[STM32H7] 實戰技能分享,如何讓工程程式碼各種優化等級通吃,含MDK AC5,AC6,IAR和GCC

【轉】[STM32H7] 實戰技能分享,如何讓工程程式碼各種優化等級通吃,含MDK AC5,AC6,IAR和GCC

來源

引出問題:

一個好的工程專案程式碼,特別是開源類的,如果能做到各種優化等級通吃,是一種非常好的工程案例,這樣別人借鑑的時候,可以方便的適配到自己工程裡。但實際專案中,針對一款產品程式碼,我們一般不會這麼幹,因為非常耗精力,意義也不大,一般是追求最高效能,最小程式碼量或者更高的穩定性,我們會選擇一個合理的優化等級。

但是隨著工程的複雜,特別是一些第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 {
4     ...   // Optimized none
5 }

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。
再牛逼的夢想也架不住傻逼似的堅持