未使用代码中的开销

Overhead in unused code

本文关键字:开销 代码 未使用      更新时间:2023-10-16

我想知道在代码中使用未使用的函数会带来什么开销。

例如,假设您有一些调试日志,然后为大多数对象提供调试日志中使用的ToString()函数。

在未使用调试日志记录的发布版本中。那么,删除那些ToString()函数的源代码值得吗?(例如通过Macro?)

或者它们只是使可执行文件稍微大一点,否则不会影响性能?例如没有速度影响?或者编译器或链接器是否可能在不使用函数的情况下删除这些函数?如果编译器或链接器没有删除代码,那么如果ToString()函数是内联定义的呢?大概它会尝试内联代码,但由于函数从未被调用,它会消失吗?

我想每个函数都需要保留在静态库中,但一旦编译成可执行文件,肯定会有很多东西被链接器忽略?

另一个大致相似的注意事项是,如果编译器选择不内联内联函数,从而将内联函数定义为几个编译单元中的函数,那么链接器会丢弃多余的定义,并在最后只链接其中一个吗?

感谢

这取决于编译器,我想还取决于优化级别。

G++和MSVC++删除未使用的内联函数,但保留未使用的非内联函数。例如,在普通程序中只使用STL的一小部分。所有未使用的函数都会被删除,因为它们被定义为内联函数。

另一方面,GCC保留所有函数,甚至是未使用的内联函数。

回答你的另一个问题:如果一个函数以某种方式定义在多个编译单元中,链接器会皱眉头拒绝链接,除非它被定义为内联。

1。关于编译器和链接器

这实际上取决于您如何创建可执行文件。

通常,可执行文件会被剥离掉任何未使用的内容。因此,如果静态链接(并使用正确的优化选项),则函数将被删除。

但是,如果您动态链接,它们就会在那里,因为就库而言,它们是导出的,因此会被使用。

至于多重定义,则取决于符号是否弱。如果它很弱,链接器会选择其中一个定义,否则就会阻塞它

最后,它们可能只代表程序的一个边缘部分。

2.如何解决问题

这是一个难题,你总是可以使用预处理器来删除一些东西,但充斥着预处理器指令的代码读起来真的很烦人。

就我个人而言,我不会打扰。。。尤其是因为我也登录了Release(否则如何跟踪生产问题?)。

解决方案可以是在一个单独的文件中定义有问题的函数,而不是在Release中链接它们注意:我认为它不适用于虚拟函数,因为它们至少在vtable

中使用

链接器确实删除了重复的函数,也删除了未引用的数据(Microsoft链接器提供了/OPF:REF/OPT:ICF开关来调整这些设置)。

你肯定是对的,在大多数情况下,链接器是否能很好地删除不需要或多余的东西并不重要——对一些小函数的可执行文件大小的影响(与广泛使用STL或其他模板库时生成的代码量相比)是最小的。

也就是说,如果你需要你的可执行文件尽可能小(或者如果你发现你的调试代码真的占用了大部分图像大小),那么#ifdefing一切是强制不包含某些函数的最简单方法。它使代码读起来有点难看,但它的优点是,您不会在发布版本中意外错过调试代码的几个位置,因为任何调用不存在的函数的尝试都会导致编译器错误。

#ifdef的另一个优点是它是可移植的,不依赖于特定的编译器系统:-/

如果将非虚拟函数放在库中的单独文件中,并且静态链接,只有当它是习惯于但唯一真正的区别在于可执行;这可能会影响当地,因此表现,但如果它真的有什么不同,我会感到非常惊讶在实践中。一般来说,我认为这种技术不值得在应用程序中遇到麻烦。(如果您提供的是第三方库,另一方面,您肯定希望单独的文件。)