前言
GCC和LLVM编译器提供了各种各样的优化和检测技术。编译器中的优化可以提高性能或减少代码体积,同时各种插装技术可以帮助了解系统的内部结构。本文将介绍一些常用的编译器优化。
性能
一般谈论应用程序的性能时,通常指的是完成某个任务所花费的时间。应用程序需要在合理的时间内执行任务,才能真正发挥作用。在许多情况下,我们希望应用程序运行得尽可能快。有许多方法可以提高应用程序的性能,而其中之一就是利用编译器优化技术来提高性能。需要注意的是,并不是程序的所有部分都需要有性能,一般只有某些被称为瓶颈的部分需要尽可能地被优化从个人提高性能。
编译器优化
编译器会对源代码进行转换,其中有些转换对于生成机器代码是必要的,但大多数转换是为了提高程序的性能或是减少代码体积,而这些转换被称为编译器优化。
编译选项
GCC和LLVM这样的工业级编译器里有数百个影响编译器行为的编译选项,为方便理解,我们可以简单的分一下类:
- 优化选项
例如-O2、-O3、-funroll-loops选项可以被归为优化选项,会在编译的不同阶段对源代码进行优化。 - 诊断选项
例如-Wall、-Werror、-Wnull-dereference等选项会影响编译器的诊断输出。 - 调优参数
例如–param max-inline- inns -small=70选项可以接收不同的值(通常为数值),可以调节某个特定优化的优化程度。 - 仪表选项
例如- fininstrument -function、 -profile-generate这样的选项可以启用编译器插装。被测试的二进制文件将收集运行时配置文件,这些配置文件有助于优化、检测bug等。 - 链接选项
例如-lpthread,是链接器用于查找符号定义、做出优化决策等的选项。 - 赋值选项
例如-D、-fprofile-use、-stdlib=libstdc++选项可以为编译器提供了额外的输入,可以帮助进行优化、诊断、检测等。
性能优化
简单介绍以下常见的编译优化等级:
- -O0
没有做编译优化。但是一些标准要求的特定的语言优化依然会被执行,例如c++标准要求的编译时长计算。添加-g选项后,该优化级别特别适合在debug阶段使用,因为O0没有做任何编译优化,编译时长最短更加适合迭代开发。 - -O1
循环展开、内联、指令调度等优化已经打开。这个选项很少使用,因为有更加积极的优化级别。 - -O2
打开所有O1优化,并包含了更加积极的优化,例如寄存器分配、指令调度、部分冗余消除等。这个级别适用于跳转主导的代码,例如操作系统。 - -O3
打开所有O2优化,并包含了一些现代优化,例如向量化。O3对于多数应用可以将性能最大化,主要用于benchmarking(因为包含所有现实应用相关的编译优化)。 - -Ofast
O3加-ffast-math。-ffast-math告诉编译器放开一些浮点运算的相关要求。对于许多应用,相比带来的性能提升,放开要求后导致的误差是可容忍的。不打开-ffast-math,许多浮点操作的循环将无法被向量化。 - -Os
主要对代码体积进行优化。多数会增加代码大小的优化在这个级别会不那么激进。适用于嵌入式系统和移动应用(因为代码体积影响很大)。 - -g
为了调试时能有源码注释,编译器需要在二进制文件中添加额外的信息。没有-g,调试器只能显示全局符号名字和反汇编,无法将汇编与程序联系在一起。 - -Og
像-g一样启用了调试功能。相比-g,还启用了一些利于调试的编译优化。最好用-Og代替O0加-g,因为-Og允许一些优化,被测试的应用程序比-O0运行得更快,所以测试的周转时间可能会更好。 - -finstrument-functions
该标志用于检测函数的进入和退出(跟踪函数的调用关系)。定义两个函数__cyg_profile_func_enter和__cyg_profile_func_exit,它们分别在每次函数调用的入口和出口被调用。如果有不应该被插装的函数,可以向它们添加__attribute_((no_instrument_function))。 - -fprofile-generate, -fprofile-arcs, -pg
这些选项是用来给程序装备测量仪器从而收集不同程序点的运行时配置文件。这样编译器在后续编译中可以使用配置文件引导优化。根据使用的选项,不同类型的测试仪器会被插桩到程序中。 - -fstack-protector, -fstack-protector-all, -fstack-protector-strong
这些选项通过在堆栈中插入保护变量来检测脆弱的函数。在函数返回之前,检查保护变量以确保它没有被覆盖,从而确保堆栈没有损坏。这是改进缓冲区溢出攻击的一种简单的方法。然而,这可能会增加应用程序的代码体积。如果产生开销较大,可以只用在应用程序中安全至关重要的部分。
References:
https://man7.org/linux/man-pages/man1/gcc.1.html
https://developer.arm.com/documentation/101754/0619/armclang-Reference/armclang-Command-line-Options/-fstack-protector—fstack-protector-all—fstack-protector-strong—fno-stack-protector
若文章中有出错或者不清楚的地方欢迎大家讨论指教!!!