未使用的功能会得到优化吗?
Posted
技术标签:
【中文标题】未使用的功能会得到优化吗?【英文标题】:Do unused functions get optimized out? 【发布时间】:2011-06-02 14:19:54 【问题描述】:一个相当简单的问题...如今的编译器倾向于进行大量优化。他们是否还会从最终输出中删除未使用的功能?
【问题讨论】:
只包含函数原型还是包含整个函数?? 【参考方案1】:这取决于编译器。 Visual C++ 9 可以做到这一点 - 未使用的 static
函数在编译阶段被删除(甚至还有一个 C4505 warning ),具有外部链接的未使用函数可以在链接阶段删除 depending on linker settings。
【讨论】:
【参考方案2】:如果您使用/Gy
编译并使用/OPT:REF
链接,MSVC(Visual Studio 编译器/链接器)可以做到这一点。
如果您使用-ffunction-sections -fdata-sections
编译并使用--gc-sections
链接,GCC/binutils 可以做到这一点。
不了解其他编译器。
【讨论】:
请注意,/OPT:REF
会导致非标准行为:它将优化仅获取地址的函数,从而使地址无效。 GCC 不会发生这种情况。【参考方案3】:
一般来说,答案是:
是:用于未使用的 static
函数。
否: 用于未使用的全局可用函数。
编译器不知道是否有其他编译单元引用它。此外,大多数对象模块类型不允许在编译后删除函数,也没有为链接器提供判断是否存在内部引用的方法。 (链接器可以判断是否有 external 链接器。)一些链接器可以做到这一点,但很多事情都与此相反。
当然,任何链接器都不会不必要地加载其自身模块中的函数,除非它是共享库的一部分。 (因为它可能会在未来的运行时被引用,显然。)
【讨论】:
我相信现代链接器在使用之前不会加载函数。至少,dlopen
和 RTLD_LAZY
暗示这是可能的。【参考方案4】:
许多编译器都会这样做,但这取决于特定的实现。调试版本通常会包含所有函数,以允许从调试器中调用或检查它们。由于我不完全理解的原因(*),许多嵌入式系统编译器会将所有函数都包含在目标文件中(如果它们包含任何函数),但会完全忽略任何根本不使用的目标文件。
请注意,在支持反射的语言(例如 Java、C#、vb.net 等)中,给定函数的名称,即使代码中不存在引用,也可以在运行时创建对它的引用。例如,一个例程可以从控制台接受一个字符串,以某种方式处理它,并生成一个对该名称的函数的调用。编译器或链接器无法知道可能会生成哪些名称,因此无法知道可以从代码中安全地省略哪些函数。然而,在 C 或 C++ 中不存在这样的困难,因为在代码中不存在显式引用的情况下,代码没有定义方法来创建对函数、变量或常量的引用。一些实现可能会安排一些事情,以便连续声明的常量或变量将连续存储,因此可能会通过向较早声明的常量或变量添加偏移量来创建对稍后声明的常量或变量的引用,但这种技巧的行为是明确的不受 C 或 C++ 标准的保证。
(*)我知道它使编译和链接变得更容易,但是今天的计算机运行比过去几十年更复杂的编译和链接算法应该没有问题。如果不出意外,两遍预编译/预链接/编译/链接方法可以在预编译/链接阶段产生一个使用的东西列表,然后在“真正的”编译/链接阶段省略那些不是。
【讨论】:
虽然我猜一些令人头疼的内联汇编可能会调用一个可以安全地从代码中省略的函数? @Andy;一般来说,只有当它使用相关函数的地址时。【参考方案5】:使用 gcc,如果您打开优化,它可以删除未使用的函数和死代码。
更多关于 gcc 优化的内容请见here
【讨论】:
【参考方案6】:很多时候,是的。它通常称为链接器剥离。
【讨论】:
【参考方案7】:对于 MS,链接器 在链接阶段负责处理此问题,编译器可能会警告您未使用的静态函数(文件范围)。如果您希望链接器删除未使用的函数,请使用 /OPT:REF 选项:
【讨论】:
【参考方案8】:在 MSVC 和全局函数或变量下,您可以使用 __declspec( selectany )。
如果选择了链接器选项 /OPT:REF(优化),如果代码中没有引用该函数或变量,它将删除该函数或变量。
【讨论】:
以上是关于未使用的功能会得到优化吗?的主要内容,如果未能解决你的问题,请参考以下文章