使用Python分析ELF文件优化Flash和Sram空间的案例
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种使用方法》