【发布时间】:2013-10-29 16:20:40
【问题描述】:
我已经阅读了一些Assembly Programming Videos 以更好地了解如何手动优化使用gcc/g++ -S ... 编译后留下的*.s 文件其中一个主题是Refactoring Redundant Code,它演示了如何移动冗余代码到它自己的以ret结尾的标记块,并将其替换为call。
视频中给出的示例是 2 个块,其中包含:
mov eax,power
mul ebx
mov power,eax
inc count
它用call CalculateNextPower 替换,CalculateNextPower 看起来像:
CalculateNextPower:
mov eax,power
mul ebx
mov power,eax
inc count
ret
出于好奇,为了减小编译大小,我编译了一些 C 和 C++ 项目,使用 -S 和各种优化,包括 -Os、-O2、-O3、-pipe、-combine 和 -fwhole-program,并分析了结果*.s 文件用于冗余使用 duplo 的轻微修补(用于 .s 文件)版本。只有 -fwhole-program (现在已弃用 IIRC) 对消除文件之间的重复代码有显着影响(我假设它的替换 -flto 在链接时会表现类似 - 大致相当于使用 -ffunction-sections -fdata-sections 编译并使用 --gc-sections 链接) 但仍然会遗漏大量代码。
使用 duplo 输出进行手动优化可在随机 C 项目中减少约 10% 的大小,在仅对具有至少 5 个连续重复指令的连续程序块进行重复数据删除时,在随机 C++ 项目中减少近 30%。
我是否缺少一个编译器选项(甚至是一个独立工具),该选项在针对大小进行编译时会自动消除冗余程序集(包括其他编译器:clang、icc 等。)缺少此功能(出于某种原因?)?
如果它不存在,可以修改duplo 以忽略以'.' 开头的行。或者 ';' (和其他人?) 并用重复代码的函数调用替换重复的代码块,但我愿意接受其他直接与编译器的内部表示(最好是 clang 或 gcc)一起工作的建议。
编辑:我修补了 duplo 以识别重复程序集块 here,但目前仍需要手动重构。只要使用相同的编译器生成代码,就有可能(但可能很慢)识别最大的重复代码块,将它们放在自己的“功能”块中,并用对该块的 CALL 替换代码.
【问题讨论】:
-
这反映了我从嵌入式编程中学到的东西:编译器很愚蠢;开发人员很聪明。因此,当真正需要(小于 8 位内存)时,我总是使用可能会严重影响代码可维护性的优化。在您的情况下,您可以尝试查看 --param 值。对于其中一些,它们是在值非常大的情况下对内存或时间使用的警告。第一个要设置的是--param lto-partitions=1,因此您将整个源代码编译为单个c 文件(而不是32)。
-
我想知道如何恢复 -pipe -combine -fwhole-program 的全部功能。谢谢@user2284570。
-
我阅读了整个手册页以继续,你应该这样做...结果是每次我启动 gcc 时,打印的命令占了我完整的一半高清屏幕由于编译器标志和优化的数量。不要忘记使用
-march=native -mtune=native。但不要忘记规则:单个人为优化通常比许多编译器更有效。在一些嵌入式环境中,程序由goto 和labels 组成。这就是为什么代码的可维护性/可读性通常与代码速度和内存使用相对立。
标签: assembly size compiler-optimization code-duplication