12

我想知道在您的代码中使用未使用的函数的开销是多少。

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

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

或者他们只是让可执行文件稍微大一点,否则不会影响性能?例如没有速度影响?或者编译器或链接器是否甚至可能删除不使用的函数?如果编译器或链接器不删除代码,如果 ToString() 函数是内联定义的呢?大概它会尝试内联代码,并且由于从未调用过该函数,它会消失吗?

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

在另一个大致相似的注意事项上,如果编译器选择不内联内联函数,以便内联函数被定义为多个编译单元中的函数,链接器是否会丢弃多余的定义并在最后只链接其中一个?

谢谢

4

4 回答 4

5

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

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

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

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

于 2011-05-26T11:27:34.807 回答
3

1. 关于编译器和链接器

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

通常,可执行文件会删除任何未使用的内容。因此,如果您静态链接(并使用正确的优化选项),这些功能将被删除。

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

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

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

2. 如何解决问题?

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

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

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

于 2011-05-26T11:38:23.223 回答
2

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

你当然是对的,在大多数情况下,链接器是否能很好地删除不需要或多余的东西 - 对一些小函数的可执行文件大小的影响(与 ie 的绝对数量相比)如果您大量使用 STL 或其他模板库生成的代码)是最少的。

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

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

于 2011-05-26T11:27:54.397 回答
0

如果您将非虚拟函数放在库中的单独文件中,并静态链接,则只有在使用时才应将其添加到可执行文件中。但唯一真正的区别在于可执行文件的大小。可以想象,这可能会影响局部性,从而影响性能,但如果它在实践中产生真正的影响,我会感到非常惊讶。所以一般来说,我会说这种技术在应用程序中不值得费心。(另一方面,如果你提供第三方库,你肯定希望每个非虚拟函数都放在一个单独的文件中。)

于 2011-05-26T12:56:09.767 回答