1. 程式人生 > >MDK編譯優化筆記

MDK編譯優化筆記

func 視圖 緩沖區 The section 編譯器 自動 靜態 ram

在一次使用MDk的編譯優化等級比較高的時候發現編譯不優化時功能正常,開了優化等級02就出現異常,調試中看了很多博客總結一下。

  • 一個變量,如果你的主程序要用到,同時中斷還要用到,要加volatile修飾。告訴編譯器這個變量是可能隨時發生變化的,使得編譯器編譯程序的時候,每次都從RAM裏面讀取數據,而不是使用之前緩存到寄存器裏面的值。
  • 對於多任務的程序,如果一個公共變量被多個任務用到也要加volatile修飾。
  • 同時變量定義的時候用了關鍵字volatile修飾,但是在其他文件引用時不加volatile變量修飾一樣會被編譯器優化掉。現則反過來想想,原因還是很簡單的,MDK編譯多個文件時是分別編譯,最後再用鏈接器鏈接,當編譯的時候一個模塊引用另外一個模塊的變量,完全是靠的變量聲明,如果聲明都不加volatile,那麽引用的模塊肯定會把變量當成普通變量的,再反推一下,如果原變量沒有加volatile,但是聲明的時候加了volatile,是不是引用的模塊會將這個變量當成volatile型變量呢

  C編譯器是以每個C文件作為基本編譯單元的,稱為模塊,被編譯為obj;而模塊之間的函數或變量訪問都是通過標號來實現的,標號本身沒有任何屬性,只是提供給鏈接器使用的一個符號名稱而已,標號的屬性完全就靠調用的地方的原型聲明來決定的!因此,你在一個.C模塊中定義為volatile,僅僅是在.C模塊中告訴編譯器不要優化而已,在另外的模塊內使用了這個變量,而它們是不知道該變量是什麽屬性的,所以只有靠原型聲明來告訴編譯器這些信息了。

最後簡單的給總結一下:別小看原型聲明,它是聯系各個模塊的橋梁,是各種輸出符號的屬性信息所在。編譯原理不懂就是比較虧啊。。。

最後補充一下MDK中關於編譯的一些選項說明:

USE Cross-Module Optimization

跨模塊優化從先前的構建中獲取信息,並使用它將UNUSED函數放入其中在相應的目標文件中擁有自己的ELF部分。 此選項也稱為鏈接器反饋,需要構建程序兩次以利用它來減少代碼大小。

USE MicroLIBMicroLIB

是一個針對嵌入式平臺優化過的C庫可以減少應用程序的大小。他是標準C庫的一個子集,以提供功能和代碼大小之間的權衡,比如一些標準C庫的memcpy函數應用到微控制器上就運行速度來說比較慢。原文(Some of the standard C library functions such as memcpy() are slower, while some features of the default library are not supported.)這裏還說默認的庫還不支持一些特性:

包括:

  1. 操作系統功能,例如 abort(),exit(),time(),system(),getenv()
  2. 寬字符和多字節支持,例如 mbtowc(),wctomb()
  3. stdio文件I/O函數,stdin,stdout和stderr除外
  4. 位置無關且線程安全的代碼

Link-Time Code Generation(這個選項新版好像沒有了)
鏈接時代碼生成指示編譯器以中間格式創建對象,以便鏈接器可以執行進一步的代碼優化。 這使代碼生成器可以同時查看所有對象的跨文件依賴性,從而允許它應用更高級別的優化。 鏈接時代碼生成可以減少代碼大小,並使您的應用程序運行得更快。
Optimization Levels

不同的優化級別允許您在級別之間進行權衡已編譯代碼中可用的調試信息以及代碼的性能。以下優化級別可用:
-O0應用最小優化。
大多數優化都被關閉,生成的代碼具有最佳的調試視圖。
-O1應用受限優化。
例如,刪除未使用的內聯函數和未使用的靜態函數。在這個優化級別,編譯器還應用自動優化,例如刪除冗余代碼和重新排序指令以避免互鎖情況。生成的代碼經過合理優化,具有良好的調試視圖。
-O2應用高優化(這是默認設置)。
在此級別應用的優化利用了ARM對處理器體系結構的深入了解,利用給定目標的特定於處理器的行為。它生成優化良好的代碼,但有限調試視圖。
-O3應用最積極的優化。
優化符合用戶的-Ospace / -Otime選擇。默認情況下,多文件編譯是啟用,這會導致更長的編譯時間,但會提供最高級別的優化。

Optimize for Time
“優化時間”復選框使編譯器進行優化,更加註重實現最佳效果檢查時的性能(-Otime)或未選中時的最小代碼大小(-Ospace)。
取消選中Optimize for Time就意味著選擇-Ospace選項,該選項指示編譯器執行優化以可能增加的執行時間為代價來減小Image文件大小。例如,使用非內聯函數調用而不是大型結構副本的內聯代碼。 這是默認選項。從中運行編譯器時命令行,使用‘-Ospace‘調用此選項選中Optimize for -Otime選項,該選項指示編譯器以最快的速度優化代碼執行時間,有可能增加image文件大小。建議編譯時間關鍵部分您的代碼使用-Otime,其余使用-Ospace指令。

Split Load and Store Multiples
指示編譯器將涉及大量寄存器的LDM和STM指令拆分為一系列較少多個寄存器的加載/存儲。這意味著16個寄存器的LDM可以分成4個獨立的LDM,每個LDM由4個寄存器組成。此選項有助於減少沒有緩存或寫緩沖區的ARM系統上的中斷延遲,以及使用零等待狀態32位內存的系統。
例如,ARM7和ARM9處理器只能在指令邊界上執行異常。如果在無緩存的ARM7和ARM9系統中的16個寄存器的LDM開始時發生異常,則系統將在獲取異常之前完成對存儲器的16次訪問。根據存儲器仲裁系統,這可能導致非常高的中斷延遲。將LDM拆分為4個寄存器的4個獨立LDM意味著處理器在加載最多4個寄存器後將采用異常,從而大大減少中斷延遲。選擇此選項可提高系統的整體性能。
One ELF Section per Function
選項告訴編譯器將所有函數放入它們各自的ELF部分。 這允許鏈接器刪除未使用的函數.ELF代碼部分通常包含許多函數的代碼。 鏈接器通常只能刪除未使用的ELF部分,而不是未使用的函數。 只有當所有內容都未使用時,才能刪除ELF部分。因此,將每個函數拆分為自己的ELF部分允許編譯器輕松識別哪些未使用,並將其刪除。選擇此選項會增加編譯代碼所需的時間,但可以提高性能。

常見優化目標選項

最小的目標代碼:

選中
? The MicroLIB C library
? Cross-module optimization
? Optimization level 2 (-O2)

最好的代碼表現性能:

選中
? Cross-module optimization
? Optimization level 3 (-O3)
? Optimize for time

最後總結一句話,只要代碼邏輯夠嚴密無論是多高的編譯優化等級都是不會出問題的,所以在資源不是出現了必須要優化的時候不建議優化代碼,自己增加實現難度而已,反之如果你追求功能的穩定可以在優化等級下發現0級優化下存在的問題進行完善在改回0級優化編譯此時代碼的邏輯漏洞會減少。

MDK編譯優化筆記