【问题标题】:Are unnecessary include files an overhead?不必要的包含文件是开销吗?
【发布时间】:2015-07-15 17:59:28
【问题描述】:

我看到了几个关于如何在 C++ 项目中检测不必要的#include 文件的问题。这个问题经常引起我的兴趣,但我一直没有找到满意的答案。

如果包含了一些头文件,而这些头文件没有在 c++ 项目中使用,那会是开销吗?我理解这意味着在编译之前,所有头文件的内容都会被复制到包含的源文件中,这会导致很多不必要的编译。

这种开销在编译后的目标文件和二进制文件中传播到什么程度?

编译器不能做一些优化来确保这 某种开销不会转移到生成的目标文件中,并且 二进制文件?

考虑到我可能对编译器优化一无所知,我仍然想问这个,以防有答案。

作为一名在工作中使用各种 c++ 库的程序员, 我应该遵循什么样的编程实践来避免 这样的开销?让自己对每一个都非常熟悉 图书馆是唯一的出路?

【问题讨论】:

  • 这取决于包含文件中的内容。结构声明不会产生任何运行时开销。外部变量声明。
  • @Barmar 声明一个变量(即通知编译器这个名称的变量是在别处定义的)具有运行时间成本?真的吗?怎么样?
  • @Barmar 这取决于您包含的依赖项(上游多远)。只需引入一个远在上游的依赖项(取决于您的构建/重建设置和您更改的内容),您就可以将一个 10 分钟的项目重建变成一个 1.5 小时的重建。
  • @Barmar 当变量在另一个 TU 中定义时,它会在那里初始化,而不是你声明它存在的地方。
  • 我觉得你应该去掉C标签。 C 和 C++ 与此完全不同。正如在一些评论中所说,C++ 头文件甚至倾向于定义一些未使用的符号位可能仍然没有被优化。而且编译时间对于 C++ 来说比对于 C 来说更重要。所以两种语言的答案可能会完全不同。

标签: c++ c++11 compilation code-organization


【解决方案1】:

当然,任何#include 都是开销。编译器需要解析那个文件。

所以避开他们。尽可能使用前向声明。

它将加快编译速度。请参阅 Scott Myers 关于该主题的书

【讨论】:

  • 我怀疑他主要对运行时开销感兴趣。
【解决方案2】:

它不会影响二进制文件的性能,甚至不会影响二进制文件的内容,几乎适用于所有标头。 声明根本不生成任何代码,如果不使用内联/静态/匿名命名空间定义,它们会被优化掉,并且任何标题都不应该包含外部可见的定义(如果标题包含在更多一个翻译单元)。

作为@T.C.指出,异常是具有非平凡构造函数的内部可见静态对象。例如,iostream 就是这样做的。程序的行为必须像调用了构造函数一样,而编译器通常没有足够的信息来优化构造函数。

但是,它确实会影响编译需要多长时间以及在更改标头时将重新编译多少文件。对于大型项目,这足以激励去关心不必要的包含。

【讨论】:

  • 有一些例外。标头中的函数定义在包含它们的每个生成的二进制文件中都重复,并且较大的二进制文件需要更长的加载时间,并且重复的函数会干扰指令缓存,从而导致极小的性能损失。
  • @MooingDuck 链接器通常可以将它们丢弃,不是吗?
  • @T.C.:它会在目标文件中抛出一个函数的重复项,因为它们被链接到一个二进制文件中。但是,它不能丢弃在多个 DLL 或 EXE 中使用的函数。
  • @MooingDuck 我以为我在回答中提到了这个问题?这里的假设是 include 是不必要的,即函数没有被使用,因此可以被编译器剥离。您是在谈论外部可见的定义吗?这些与 ODR(以及 C 中的等效规则)冲突,因此它们不能重复。
  • 标头可以定义具有内部链接的对象,在这种情况下,您需要为它们的初始化付费。 (例如,包括<iostream>,必须像定义一个具有静态存储持续时间的ios_base::Init 对象一样。)
【解决方案3】:

简单的答案是肯定的,就编译而言,它是一种开销,但对于运行时而言,它只会产生任何差异。原因是假设您添加了#include <iostream>只是举例)并假设您没有使用它的任何功能,那么 g++ 4.5.2 需要处理一些额外的 18,560 行代码(编译)。但就运行时开销而言,我几乎不认为它会产生性能问题。

您也可以参考Are unused includes harmful in C/C++?,我非常喜欢 David Young 提出的这一点

任何在标头中声明为外部并在 源文件将包含在您的程序中。这显然增加了 内存使用并可能导致性能开销 导致人们更频繁地访问他们的页面文件(不是 现在的问题,因为单例通常是中小型的,并且 因为我认识的大多数人都有 6 GB 以上的 RAM)。

【讨论】:

  • 有趣的是,您使用 #include <iostream> 作为示例,因为包含该标头确实会在运行时产生少量开销。
  • @T.C.:- 确实,但我只是想说明一点。可能我可以添加 #include <boost/asio.hpp> 大约有 74k 行。
【解决方案4】:

除了明显更长的编译时间之外,可能还有其他问题。恕我直言,最重要的是对外部库的依赖。您不希望您的程序依赖于更多必要的库。

然后,您还需要在要构建程序的每个系统中安装这些库。这可能成为一场噩梦,尤其是当下一个程序员需要安装一些数据库客户端库时,尽管该程序从不使用数据库。

此外,尤其是库头文件通常倾向于定义宏。有时这些宏具有非常通用的名称,这会破坏您的代码或与您可能实际需要的其他库头文件不兼容。

【讨论】:

  • 如果您实际上没有使用外部库中的任何内容,您真的可以对外部库拥有依赖吗?例如,如果您 #include 它但从不引用它的任何内容?
  • @Mr.Llama 您可能正在考虑链接器丢弃任何东西,甚至不强迫您链接到实际的库。但是你需要安装库以便找到头文件,否则你的代码将无法编译。从法律上讲,它也可能已经被视为依赖许可。所以你已经依赖于这个库,即使只是为了构建你的项目。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-06
  • 1970-01-01
  • 1970-01-01
  • 2018-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多