我一直在為我們的特定產品開發功能。有人要求將同一功能移植到另一產品。該產品基於M16C微控制器,該微控制器傳統上具有64K閃存和2k RAM。
這是一個成熟的產品,因此,僅還有132字節的Flash和2字節的RAM。
要移植所需的功能(功能本身已進行了優化),我需要1400字節的Flash和〜200字節的RAM。
有人對如何通過代碼壓縮來檢索這些字節有任何建議嗎?當我嘗試壓縮已經存在的工作代碼時,我需要尋找什麼具體的東西?
任何想法都會受到讚賞。
謝謝。
我一直在為我們的特定產品開發功能。有人要求將同一功能移植到另一產品。該產品基於M16C微控制器,該微控制器傳統上具有64K閃存和2k RAM。
這是一個成熟的產品,因此,僅還有132字節的Flash和2字節的RAM。
要移植所需的功能(功能本身已進行了優化),我需要1400字節的Flash和〜200字節的RAM。
有人對如何通過代碼壓縮來檢索這些字節有任何建議嗎?當我嘗試壓縮已經存在的工作代碼時,我需要尋找什麼具體的東西?
任何想法都會受到讚賞。
謝謝。
您有兩種選擇:首先是查找冗餘代碼,然後將其移至單個調用中以消除重複;第二個是刪除功能。
仔細看一下.map文件,看看是否有一些可以刪除或重寫的函數。還要確保確實需要使用正在使用的庫調用。
諸如除法和乘法之類的某些事情可以帶來很多代碼,但是使用移位和更好地使用常量可以使代碼更小。還可以看一下字符串常量和 printf
s之類的東西。例如,每個 printf
都會佔用您的rom,但是您可能可以使用幾個共享格式的字符串,而不必一次又一次地重複該字符串常量。
對於內存,請查看是否可以擺脫全局變量,而在函數中使用自動變量。還要避免在主函數中使用盡可能多的變量,因為它們就像全局變量一樣會消耗內存。
始終值得一看的是列出文件(彙編器)輸出,以查找您的特定編譯器特別擅長的事情。
例如,您可能會發現局部變量非常昂貴,並且如果應用程序很簡單,值得冒險,將幾個循環計數器移到靜態變量中可以節省大量代碼。
或者數組索引可能非常昂貴,但指針操作便宜得多。反之亦然。
但是第一步是了解彙編語言。
編譯器優化(例如,GCC中的 -Os
)可在速度和代碼大小之間實現最佳平衡。避免使用 -O3
,因為它可能會增加代碼大小。
對於RAM,請檢查所有變量的範圍-您是否在使用可以使用char的整數?緩衝區大於所需的大小嗎?
代碼壓縮在很大程度上取決於應用程序和編碼樣式。您剩下的金額表明,也許代碼已經經過了一些壓縮,但這意味著幾乎沒有剩餘了。
還要仔細看一下整體功能-是否有未真正使用且可以拋棄的東西?
如果這是一個舊項目,但此後才開發出編譯器,則可能是更新的編譯器可能會生成較小的代碼
始終值得檢查編譯器手冊中提供的用於優化空間的選項。
對於gcc -ffunction-sections
和 -fdata-sections
, -gc-sections
鏈接器標誌非常適合剝離死代碼。
還有其他一些優秀技巧(針對AVR)
您可以檢查分配的堆棧空間和堆空間量。如果這兩個或其中一個或兩個都被過度分配,您也許能夠獲得大量的RAM。
我的猜測是針對一個適合2k RAM的項目,而沒有動態內存分配(使用 malloc
, calloc
等)。如果是這種情況,您可以假設原始作者為堆分配了一些RAM,從而完全擺脫了堆。
您必須非常小心地減小堆的大小,因為這會導致非常嚴重的錯誤。很難找到。首先將整個堆棧空間初始化為一個已知值(除了0x00或0xff以外的其他值,因為這些值已經很常見了)可能會有所幫助,然後運行系統一段時間以查看有多少未使用的堆棧空間。
您的代碼是否使用浮點數學?您也許可以僅使用整數數學來重新實現算法,並消除使用C浮點庫的開銷。例如。在某些應用中,正弦,對數,exp等函數可以用整數多項式逼近代替。
您的代碼是否對任何算法(例如CRC計算)使用大的查詢表?您可以嘗試替換即時計算值的算法的其他版本,而不是使用查找表。需要注意的是,較小的算法很可能較慢,因此請確保您有足夠的CPU週期。
您的代碼是否包含大量常量數據,例如字符串表,HTML頁面或像素圖形(圖標)?如果足夠大(例如10 kB),則可能有必要實施一個非常簡單的壓縮方案來壓縮數據並在需要時即時對其進行解壓縮。
您可以嘗試將代碼重新排列很多,以使樣式更緊湊。這在很大程度上取決於代碼在做什麼。關鍵是要找到相似的事物,然後彼此重新實現。極端的情況是使用像Forth這樣的高級語言,用這種語言比C或彙編語言更容易實現更高的代碼密度。
這裡是 Forth for M16C。
設置編譯器的優化級別。許多IDE都具有允許代碼大小優化的設置,但會浪費編譯時間(在某些情況下甚至可能浪費處理時間)。他們可以通過幾次重新運行優化器,搜索不太常見的可優化模式以及其他一些可能不需要臨時/調試編譯的技巧來完成代碼壓縮。通常,默認情況下,編譯器設置為中等級別的優化。在設置中進行挖掘,您應該能夠找到一些基於整數的優化比例。
如果您已經在使用IAR之類的專業級編譯器,那麼我認為您將很難通過少量的低級代碼調整來節省大量費用-您需要更多地著眼於刪除功能或以更有效的方式進行零件的重大改寫。您需要成為一個比編寫原始版本的人都要聰明的編碼器。至於RAM,您需要非常仔細地了解當前的使用方式,並查看是否有覆蓋相同RAM用於以下用途的範圍。在不同的時間有不同的事情(為此很方便)。我傾向於在ARM / AVR中使用IAR的默認堆大小和堆棧大小,因此我將首先看這些東西。
還有其他需要檢查的內容-一些架構上的某些編譯器將常量複製到RAM-通常在訪問閃存常量的速度較慢/困難(例如AVR)時使用。 IAR的AVR編譯器需要_ _閃光限定符,以不將常量複製到RAM)
如果處理器不支持參數/本地堆棧的硬件,但是編譯器仍然嘗試實現運行時參數堆棧,並且如果不需要重新輸入代碼,則可以通過靜態分配自動變量來節省代碼空間。在某些情況下,必須手動完成;在其他情況下,編譯器指令可以做到這一點。有效的手動分配將需要在例程之間共享變量。必須仔細進行這種共享,以確保沒有例程使用另一個例程認為在“範圍內”的變量,但是在某些情況下,代碼大小的好處可能很明顯。
某些處理器已經調用這些約定可以使某些參數傳遞樣式比其他樣式更有效。例如,在PIC18控制器上,如果例程採用單個1字節參數,則可以將其傳遞到寄存器中;否則,可能會將其傳遞給寄存器。如果花費更多,則所有 i>參數必須在RAM中傳遞。如果例程將使用兩個一個字節的參數,則最有效的做法是在全局變量中“傳遞”一個,然後將另一個作為參數傳遞。通過廣泛使用的例程,可以節省更多的費用。如果通過global傳遞的參數是一個單個標誌,或者通常它的值通常為0或255(因為存在將0或255存儲到RAM中的特殊指令),則它們尤其重要。
1。如果您的代碼依賴於許多結構,請確保將結構成員從占用最多內存的結構成員中排序到最少。
例如:“ uint32_t uint16_t uint8_t”而不是“ uint16_t uint8_t uint32_t”
這將確保最小的結構填充。
2。在適用的情況下將const用於變量。這樣可以確保這些變量位於ROM中而不會耗盡RAM
我在壓縮某些客戶代碼時成功使用了一些技巧(也許很明顯):
將標誌壓縮到位字段或位掩碼中。這可能是有益的,因為通常布爾值存儲為整數,從而浪費內存。這樣可以節省RAM和ROM,通常不會由編譯器完成。
在代碼中尋找冗餘,並使用循環或函數執行重複的語句。
我還通過使用索引數組替換了常量中的許多 if(x == enum_entry)<assignment>
語句來節省了一些ROM,注意將枚舉項用作數組索引
如果可以,請使用內聯函數或編譯器宏,而不要使用小型函數。有傳遞參數的大小和速度開銷,可以通過使函數內聯來解決。
將局部變量更改為與CPU寄存器相同的大小。
如果CPU是32位,即使最大值永遠不會超過255,也請使用32位變量。使用8位變量,編譯器將添加代碼以掩蓋高24位。
我首先要看的是for循環變量。
for(i = 0; i < 100; i ++)
對於8位變量來說這似乎是一個好地方,但是32位變量可能產生的代碼更少。 / p>