代码之家  ›  专栏  ›  技术社区  ›  JoshD

forward声明对编译时间有多大影响?

  •  22
  • JoshD  · 技术社区  · 14 年前

    forward声明与full-include相比能在多大程度上改变编译时间?

    #include "myClass.h"
    

    与。

    class myClass;
    

    有什么研究可以检验这一点吗?

    我特别担心的项目大约有1200个文件。每个cpp平均包含5个标题。每个报头平均包含5个报头。这大约倒退4个层次深。似乎对于编译的每个cpp,都必须打开和解析大约300个头,有时需要多次。(include树中有许多副本。)有保护,但文件仍处于打开状态。每个cpp都是用gcc单独编译的,因此没有头缓存。

    谢谢你提供任何信息。

    5 回复  |  直到 14 年前
        1
  •  16
  •   Goz    14 年前

    前向声明可以使代码更简洁易懂,这肯定是任何决策的目标。

    再加上这样一个事实:当涉及到类时,两个类很可能相互依赖,这使得不使用forward声明而不造成恶梦有点困难。

    头中类的等转发声明意味着您只需要在实际使用这些类的cpp中包含相关的头。这实际上减少了编译时间。

    编辑 :考虑到您上面的评论,我会指出包含头文件总是比转发声明慢。任何时候当你包含一个头部,你需要从磁盘加载,往往只发现头部保护意味着什么都不会发生。这将浪费大量的时间,真的是一个非常愚蠢的规则。

    编辑2 :很难获得硬数据。坊间传闻,我曾经做过一个项目,对它的头包含并不严格,在512MB RAM P3-500Mhz上的构建时间大约是45分钟(这是一段时间以前的事情)。在花了两周时间减少include噩梦(通过使用forward声明)之后,我设法在不到4分钟的时间内构建了代码。随后,尽可能使用forward声明成为一种规则。

    编辑3

    我还注意到许多其他人赞美预编译头(pch)的优点。他们有自己的位置,他们真的可以提供帮助,但他们真的不应该被用作替代适当的向前声明。否则,对头文件的修改可能会导致重新编译大量文件(如上所述)以及触发PCH重建的问题。pch可以为诸如预构建的库之类的东西提供一个巨大的胜利,但是它们没有理由不使用正确的forward声明。

        2
  •  10
  •   the_mandrill    14 年前

    Large Scale C++ Design 书——我 认为 他有一些前瞻性声明的数字,看看如果你包括N个头部M层深。

    如果不使用forward声明,那么除了增加来自干净源树的总生成时间之外,它还大大增加了增量生成时间,因为头文件被不必要地包括在内。假设你有四个班,A,B,C和D。C使用A和B 在实施过程中 C.cpp )D在实现中使用C。由于此“无转发声明”规则,D的接口被迫包含C.h。同样,C.h必须包含A.h和B.h,因此每当A或B被改变时,D.cpp必须被重建,即使它没有直接的依赖关系。随着项目规模的扩大,这意味着如果你 任何

    (在我看来)有一条规则不允许转发声明确实是非常糟糕的做法。这将浪费大量的时间给开发人员,毫无益处。一般的经验法则应该是如果 接口 B类依赖于A类,则应包含A.h,否则转发声明。实际上,“依赖”表示继承自、用作成员变量或“使用的任何方法”。Pimpl习惯用法是一种广泛使用且易于理解的方法,用于将实现隐藏在接口中,并允许您大大减少代码库中所需的重建量。

    如果你找不到拉科斯的数据,那么我建议你自己做实验,并花时间向你的管理层证明这条规则是绝对错误的。

        3
  •  5
  •   Matthieu kelly    14 年前
    #include "myClass.h"
    

    是1…n行

    class myClass;
    

    除非所有标题都是一行,否则可以节省时间。由于对编译本身没有影响(前向引用只是对编译器说特定符号将在链接时定义,并且只有当编译器不需要来自该符号的数据(例如数据大小)时才有可能),每次用转发引用替换一个文件时,都将保存所包含文件的读取时间。由于这是每个项目的值,因此没有常规的度量方法,但对于大型c++项目,这是一种推荐的做法(请参见 Large-Scale C++ Software Design /John Lakos获得更多关于在c++中管理大型项目的技巧的信息,即使其中一些项目已经过时)

    另一种限制编译器在头上传递时间的方法是预编译头。

        4
  •  2
  •   Roger Pate Roger Pate    14 年前

    你问了一个很一般的问题,得到了一些很好的一般性答案。但你的问题不是关于你的实际问题:

    我们有一些关于这个项目的信息,但还不够:

    我特别担心的项目大约有1200个文件。每个cpp平均包含5个标题。每个报头平均包含5个报头。这大约倒退4个层次深。似乎对于编译的每个cpp,都必须打开和解析大约300个头,有时需要多次。(include树中有许多副本。)有保护,但文件仍处于打开状态。每个cpp都是用gcc单独编译的,因此没有头缓存。

    对于使用gcc的预编译头,您做了什么?它在编译时有什么区别?

    现在编译一个干净的构建需要多长时间?您的典型(非清理/增量)构建有多长时间?如果,正如James McNellis在comments中的示例,构建时间不到一分钟:

    我工作的最后一个大型C++项目是100万个SLoc(不包括第三方库)的顺序。我们完全没有使用forward声明,整个过程只需10分钟。增量重建按秒的顺序进行。

    那么,避免使用include可以节省多少时间并不重要:对于许多项目来说,从构建中节省几秒钟肯定无关紧要。

    从你的项目中拿出一小部分有代表性的内容,然后把它转换成你想要的样子。测量该示例的未转换版本和转换版本之间的编译时间差异。记住要接触(或等效于make--assempt new)各种文件集,以表示工作时遇到的实际构建。

    显示

        5
  •  1
  •   Keynslug    14 年前

    嗯,这个问题还不清楚。这取决于,简单地说。

    在一个任意的场景中,我认为翻译单元不会变得更短,更容易编译。前向声明最受重视的目的是为程序员提供方便。