我有一个需要新功能的传统固件应用程序。应用程序的大小已经接近设备的有限闪存容量,并且少数新功能和变量将其推到了边缘。打开编译器优化可以解决这个问题,但客户对这样做很谨慎,因为它们过去曾导致过失败。那么,在重构C代码以产生较小的输出时,需要注意哪些常见的事情呢?
答案 0 :(得分:20)
如果您仍然需要比启用compile with optimizations
更多的空间,请查看生成的程序集与未优化的代码。然后重新编写发生最大变化的代码,以便编译器根据棘手的C重写生成相同的优化,并关闭优化。
例如,您可能有几个'if'语句进行类似的比较:
if(A && B && (C || D)){}
if(A && !B && (C || D)){}
if(!A && B && (C || D)){}
然后创建一个新变量并提前进行一些比较将使编译器免于重复代码:
E = (C || D);
if(A && B && E){}
if(A && !B && E){}
if(!A && B && E){}
如果打开它,这是编译器为您自动执行的优化之一。还有很多很多其他的,如果你想学习如何在C代码中手工完成,你可以考虑阅读一些编译器理论。
答案 1 :(得分:8)
通常:利用您的链接器映射或工具来确定最大/最多的符号是什么,然后可能使用反汇编程序查看它们。你会对这种方式感到惊讶。
使用perl或类似的东西,你可以缩短.xMAP文件或“objdump”或“nm”的结果,并以各种方式对相关信息进行重新排序。
特定于小指令集:注意literal pool用法。从例如改变ARM(每条指令32位)指令设置为THUMB(每条指令16位)指令集在某些ARM处理器上非常有用,它减小了“立即”字段的大小。
突然间,全局或静态的直接负载变得非常间接;它必须首先将全局/静态的地址加载到寄存器中,然后从中加载,而不是直接在指令中编码地址。所以你在文字池中得到一些额外的指令和一个额外的条目,通常是一条指令。
解决这个问题的策略是将全局和静态组合成结构;这样,您只存储一个文字(全局结构的地址)并从中计算偏移量,而不是在访问多个静态/全局变量时存储许多不同的文字。
我们将“单例”类从管理自己的实例指针转换为仅仅是大型“struct GlobalTable”中的成员,并且在某些情况下,它在代码大小(百分之几)和性能方面产生了明显的差异。
否则:留意静态结构和非平凡构造数据的数组。这些中的每一个通常都会生成大量的.sinit代码(“隐藏函数”,如果你愿意的话),它们在main()之前运行,以正确填充这些数组。如果你只能在静力学中使用琐碎的数据类型,那么你的状况会好得多。
这又是通过在“nm”或“objdump”等结果上使用工具可以容易地识别的东西。如果你有很多.sinit的东西,你会想要调查!
哦,并且 - 如果你的编译器/链接器支持它,不要害怕选择性地为某些文件或函数启用优化或更小的指令集!
答案 2 :(得分:2)
重构duplicate code应该对程序的内存占用产生最大的影响。
答案 3 :(得分:0)
注意宏。他们可以从一次宏扩展中生成大量代码。如果你发现了这样的宏 - 尝试重写它们,以便最小化它们的大小,并将功能转移到函数中。
注意重复的代码 - 复制粘贴和逻辑复制。尝试将重复的代码分成函数。
检查编译器是否支持内联,并且可以将其关闭。
答案 4 :(得分:0)
引发错误的编译器优化?真奇怪。 获取程序的地图,看看是否应该定位数据或代码。 寻找重复的代码。寻找具有类似目标的代码。其中一个例子是busybox代码,它的目标是占用大量内存。
它有利于大小而不是可读性,因此它有时会变得非常丑陋,有了等等。
答案 5 :(得分:0)
以上答案声称“打开编译器优化[减小代码大小]”。鉴于我在嵌入式系统 TI DSP编程方面的所有文档和经验,我知道开启优化将增加您的代码大小(对于TI DSP芯片)!
让我解释一下:
TI TMSCx6416 DSP有9个编译器标志,会影响您的代码大小。
对于我的编译器,当您打开优化级别3时,文档说明:
什么是软件流水线?
这就是编译器在汇编中做的事情,它使for循环的执行速度明显加快(速度提高了几倍),但代价是更大的代码。我建议阅读software pipelining at wikipedia(寻找循环展开,prolog和epilog)。
请检查您的文档,确保优化不会使您的代码变大。
另一个建议是查找与代码大小相关的编译器标志。 如果您有代码大小的编译器标志,请确保将它们调高到最高设置。通常编译代码大小意味着您的代码执行速度会变慢......但您可能必须这样做。
答案 6 :(得分:0)
您可以做很多事情,但是这两件事在过去对我有很大帮助 我只想建议一个
1-请勿使用通用标准C库,例如 sprintf ,... 它们非常通用,如果您编写自己的函数,则会释放大量空间
2-如果您有一个char数组的本地声明(如果您知道最大长度),则应明确给出该长度,而不是通过输入参数来获取该长度,例如
如果您有这样的功能
void foo(char* str,uint8_t length){
char local_string[length];
....
}
您最好找到所用的最大长度,然后将其更改为
void foo(char* str,uint8_t length){
char local_string[MAXIMUM_LENGTH];
....
}