我想知道代码中包含未使用的函数的开销是多少。
例如,您有一些调试日志记录,然后为大多数对象提供一个在调试日志中使用的 ToString() 函数。
在发布版本中未使用调试日志记录。那么是否值得删除这些 ToString() 函数的源代码? (例如通过宏?)
或者它们只是使可执行文件稍微变大,否则不会影响性能?例如没有速度影响吗?或者,如果不使用这些函数,编译器或链接器是否可能删除它们?如果编译器或链接器不删除代码,如果 ToString() 函数是内联定义的怎么办?据推测,它会尝试内联代码,并且由于该函数从未被调用,所以它会消失?
我想每个函数都需要保留在静态库中,但是一旦编译成可执行文件,肯定很多东西都会被链接器忽略?
另一个大致相似的地方是,如果编译器选择不内联内联函数,从而使内联函数在多个编译单元中定义为函数,链接器是否会丢弃多余的定义并仅在链接处链接其中一个结束?
谢谢
这取决于编译器,我猜,还取决于优化级别。
G++ 和 MSVC++ 删除未使用的内联函数,但保留未使用的非内联函数。 例如,在正常程序中仅使用 STL 的一小部分。所有未使用的函数都被删除,因为它们被定义为内联。
另一方面,GCC 保留所有函数,甚至是未使用的内联函数。
回答你的另一个问题:如果一个函数以某种方式在多个编译单元中定义,链接器将皱起眉头并拒绝链接,除非它被定义为内联。
1。关于编译器和链接器
这实际上取决于您如何创建可执行文件。
通常可执行文件会删除任何未使用的内容。因此,如果您静态链接(并使用正确的优化选项),这些函数将被删除。
但是,如果您动态链接,它们就会在那里,因为就库而言,它们是导出的,因此会被使用。
至于多重定义,要看符号是否弱。如果它很弱,链接器就会选择其中一个定义,否则就会被它阻塞。
最后,它们可能只代表您计划的边缘部分。
2。怎么解决问题?
这是一个难题,你总是可以使用预处理器来删除一些东西,但是充满预处理器指令的代码读起来真的很烦人。
就我个人而言,我不会打扰......特别是因为我也登录了Release(否则如何跟踪生产问题?)。
解决方案可能是在单独的文件中定义有问题的函数,而不是在发布中链接它们。 注意:我认为它不适用于虚拟函数,因为它们至少在 vtable 中使用
链接器确实会删除重复的函数,并且会删除未引用的数据(Microsoft 链接器提供
/OPF:REF
和 /OPT:ICF
开关来调整这些设置)。
你当然是对的,在大多数情况下,链接器是否能很好地删除不需要或多余的东西并不重要——对一些小函数的可执行文件大小的影响(与庞大的数量相比)如果您广泛使用 STL 或其他模板库,则生成的代码的数量)是最少的。
也就是说,如果您需要您的可执行文件尽可能小(或者如果您发现您的调试代码真的占据了大部分图像大小),
#ifdef
所有内容都是强制执行某些功能的最简单方法,而不是被包括在内。它使代码读起来有点难看,但它的优点是您不会意外地错过发布版本中的一些调试代码,因为任何尝试调用不存在的函数都会导致编译器错误。
#ifdef
的另一个优点是它是可移植的并且不依赖于特定的编译器系统:-/
如果将非虚函数放在库中的单独文件中,并且 静态链接,仅应将其添加到可执行文件中 用过的。 但唯一真正的区别在于尺寸 可执行文件;这可能会影响局部性,因此 性能,但如果它确实产生了真正的影响,我会感到非常惊讶 在实践中。 所以一般来说,我会说这种技术不值得 打扰应用程序。 (如果您要提供第三方库, 另一方面,你肯定希望每个非虚拟函数都在一个 单独的文件。)