1. 背景
Zephyr項目Flash和Ram空間比較緊張,有着非常強烈的優化需求。
優化的前提是量化標的,那么如何量化Flash和Ram的使用量呢?
在量化之后,首先要對量化結果進行分析,然后采取措施進行空間優化。
2. 基於ELF信息和linker.cmd分析Flash/Ram使用量
linker.cmd文件中規定了不同section在Flash還是在Ram中,還是兼而有之。
這是一個很有用的信息,基於此我們只需要去羅列每個section的symbol,然后統計大小;就可以知道section的信息;進而知道都有那些symbol在(Flash, Ram)中,都與多大。
分析ELF文件可以獲得Sections和Symbols的詳細信息。
Sections信息可以將,Sections的Index和Name對應起來。
Symbols信息可以將Symbol的Name、Size、Index和Sections的Index對應起來。
這樣子就可以對ELF文件形成ELF-->Sections-->Symbols的樹形結構關系。
分析腳本在:elf_analyze_pro.ipynb。
輸出結果是每個sections中symbol大小降序排列的csv文件,和顯示最高top_counts個大小列表。
3. 分析Flash/Ram使用情況
3.1 總體使用情況分析
從Flash/Ram總大小使用情況,可以看出Ram空間告急,Flash空間也不樂觀。
由於每個Section按降序排列了所有符號表,所以從最大入手效果最明顯。
同時不同Section都有自己的特性,是A.僅在Ram中,還是B.僅在Flash中,還是C.兩者都占用。
優化的首要目標是C情況,如果能將其從Ram中移出,僅在Flash中使用,那最好不過了。不過肯定會降低速度。
其次優化A情況,靜態變量改成動態分配。針對變量分配浪費情況:不需要的結構體成員、變量類型緊湊等等。
最后是B情況,去掉冗余Log信息,將inline類型函數改成普通函數等等。
3.2 逐section、symbol分析
輸出詳細信息到Excel中,便於逐個排除。
3.3 查看symbol細節匯編和C混合
提供查看某一符號詳細信息:
#################################### iface_cb #################################### 0ffc299c <iface_cb>: { ffc299c: b510 push {r4, lr} router = net_if_ipv6_router_find_default(iface, NULL); ffc299e: 2100 movs r1, #0 ffc29a0: f7fa fb4e bl ffbd040 <net_if_ipv6_router_find_default> } ffc29a4: bd10 pop {r4, pc} 0ffc29a6 <net_shell_cmd_mem>:
4. 優化記錄
4.1 通過const修飾變量,將變量從datas轉移到rodata
由於Section datas既占用了Flash又占用了Sram,存在一些變量可以修改成const類型,即只讀變量。
就可以將此Symbol轉移到rodata區域,使用的時候從Flash讀取。
4.2 通過k_malloc從mem pool中動態申請內存
申請靜態大變量,簡單省事不易錯,但是浪費了有限的Ram空間。
如果可以通過k_malloc從Mem pool中申請,將有助於提高Sram的利用率。
4.3 刪除冗余結構體成員
比如struct uart_driver_api中很多成員,沒有實現,也不會使用到。將其中部分成員注釋掉,有助於降低結構體實例大小。
4.4 使用盡量小的數據類型
一個標志位這種情況就沒有必要使用int32這樣的類型了。
4.5 inline類型函數的廢棄
在CPU速度較慢但是ROM空間較大的系統中,使用inline有助於利用空間換時間。
但是在空間非常緊張的系統中,這就變成了缺點。將inline修飾符去掉,該成普通函數,將節省空間,雖然會增加函數調用開銷。
4.6 控制Log使用量
Log存在分級,所以不需要的Log就不需要編譯。
在量產的時候,關閉Log,節省的空間非常可觀。
4.7 控制宏函數的使用
和inline類型函數相似,宏函數也是用空間換時間。這在空間緊張的系統反而是個劣勢。
5. 結語
當然優化的路沒有盡頭,邊走邊記錄吧。
6 補充
1.《C語言程序代碼優化11種使用方法》