引出問題:
一個好的工程項目代碼,特別是開源類的,如果能做到各種優化等級通吃,是一種非常好的工程案例,這樣別人借鑒的時候,可以方便的適配到自己工程里。但實際項目中,針對一款產品代碼,我們一般不會這么干,因為非常耗精力,意義也不大,一般是追求最高性能,最小代碼量或者更高的穩定性,我們會選擇一個合理的優化等級。
但是隨着工程的復雜,特別是一些第3方組件的加入,很容易碰到不耐優化的情況。也就是這個組件沒法適配到我們當前的優化等級里面。甚至有時候我們還會遇到高優化等級能用,改成0級優化反倒不能用了。
本期帖子我們就分享一種方法來解決這個問題,合理的設置不同代碼的不同優化等級,即一種優化為主優化等級,其它代碼設置到能用的優化等級上,以此來達到通吃的目的。
如果采用這種辦法可以一步一步的鎖定具體問題所在,並將工程文件全部設置到同一個優化等級是最好的。
MDK設置方法(AC5和AC6):
分兩個方向:
1、開啟優化后,部分功能不正常
解決思路是把這部分的文件繼續設置為低優化等級,整體工程設置為高優化等級(這種方法可以鎖定有問題的文件,然后鎖定具體有問題的函數)。
2、開啟優化后,直接整體卡死
解決思路是整體工程設置為低優化等級,逐步開啟工程文件的優化等級。具體到某些函數的優化也是可以單獨開啟測試的。
AC5設置方法:
比如設置函數優化等級為0
https://www.keil.com/support/man ... hr1359124988971.htm
#pragma push #pragma O0 void function(void){ ... // Optimized at O0 } #pragma pop
AC6設置方法:
這里設置無優化
void function(void) _attribute__((optnone)) { ... // Optimized none }
IAR設置方法:
IAR和MDK的設置是一樣的,同樣我們也分為兩個方向:
1、開啟優化后,部分功能不正常
解決思路是把這部分的文件繼續設置為低優化等級,整體工程設置為高優化等級(這種方法可以鎖定有問題的文件,然后鎖定具體有問題的函數)。
2、開啟優化后,直接整體卡死
這種的解決思路是整體工程設置為低優化等級,逐步開啟工程文件的優化等級。具體到某些函數的優化也是可以單獨開啟測試的。
比如設置函數無優化:
https://netstorage.iar.com/SuppDB/Public/UPDINFO/004916/arm/doc/EWARM_DevelopmentGuide.ENU.pdf (253頁)
#pragma optimize=none void foo(void) { /* Do something, but don't optimize this function */ }
GCC設置方法:
GCC的話,我們這里以Embedded Studio為例進行說明,同樣我們也分為兩個方向:
1、開啟優化后,部分功能不正常
解決思路是把這部分的文件繼續設置為低優化等級,整體工程設置為高優化等級(這種方法可以鎖定有問題的文件,然后鎖定具體有問題的函數)。
2、開啟優化后,直接整體卡死
這種的解決思路是整體工程設置為低優化等級,逐步開啟工程文件的優化等級。具體到某些函數的優化也是可以單獨開啟測試的。
比如設置函數無優化:
#pragma GCC push_options #pragma GCC optimize ("O0") void foo(void) { /* Do something, but don't optimize this function */ } #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里面有一大批,導致產生巨大的棧需求。
/** @brief Get UART clok division factor from clock prescaler value. * @param __CLOCKPRESCALER__ UART prescaler value. * @retval UART clock division factor */ #define UART_GET_DIV_FACTOR(__CLOCKPRESCALER__) \ (((__CLOCKPRESCALER__) == UART_PRESCALER_DIV1) ? 1U : \ ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV2) ? 2U : \ ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV4) ? 4U : \ ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV6) ? 6U : \ ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV8) ? 8U : \ ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV10) ? 10U : \ ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV12) ? 12U : \ ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV16) ? 16U : \ ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV32) ? 32U : \ ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV64) ? 64U : \ ((__CLOCKPRESCALER__) == UART_PRESCALER_DIV128) ? 128U : \ ((__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。