有没有技术可以大大改善3D应用程序的C++构建时间?

jok*_*oon 22 c++ makefile c++11

有许多超薄笔记本电脑,价格便宜,使用方便.编程具有在任何存在沉默和舒适的地方完成的优点,因为长时间集中是能够进行有效工作的重要因素.

我有点老式,因为我喜欢我的静态编译的C或C++,这些语言可以很长时间在那些功耗受限的笔记本电脑上编译,特别是C++ 11和C++ 14.

我喜欢做3D编程,我使用的库可能很大而且不会宽容:子弹物理,Ogre3D,SFML,更不用说现代IDE的功耗了.

有几种解决方案可以使构建更快:

  • 解决方案A:不要使用那些大型库,并提出更轻松的东西来减轻编译器的负担.编写适当的makefile,不要使用IDE.

  • 解决方案B:在其他地方设置构建服务器,在肌肉发达的机器上设置makefile,并自动下载生成的exe.我不认为这是一个随意的解决方案,因为你必须瞄准你的笔记本电脑的CPU.

  • 解决方案C:使用非官方的C++模块

  • ???

还有其他建议吗?

Mat*_*jek 19

如果你知道如何,编译速度是可以真正提升的.仔细考虑项目的设计(特别是在大型项目的情况下,由多个模块组成)并对其进行修改总是明智的,因此编译器可以有效地生成输出.

1.预编译头文件.

预编译头是一个普通头(.h文件),它包含最常见的声明,typedef和includes.在编译期间,它只被解析一次 - 在编译任何其他源之前.在此过程中,编译器生成一些内部(最可能是二进制)格式的数据,然后,它使用此数据来加速代码生成.

这是一个示例:

#pragma once

#ifndef __Asx_Core_Prerequisites_H__
#define __Asx_Core_Prerequisites_H__

//Include common headers
#include "BaseConfig.h"
#include "Atomic.h"
#include "Limits.h"
#include "DebugDefs.h"
#include "CommonApi.h"
#include "Algorithms.h"
#include "HashCode.h"
#include "MemoryOverride.h"
#include "Result.h"
#include "ThreadBase.h"
//Others...

namespace Asx
{

    //Forward declare common types
    class String;
    class UnicodeString;

    //Declare global constants
    enum : Enum
    {
        ID_Auto     = Limits<Enum>::Max_Value,
        ID_None     = 0
    };

    enum : Size_t
    {
        Max_Size            = Limits<Size_t>::Max_Value,
        Invalid_Position    = Limits<Size_t>::Max_Value
    };

    enum : Uint
    {
        Timeout_Infinite    = Limits<Uint>::Max_Value
    };

    //Other things...

}

#endif /* __Asx_Core_Prerequisites_H__ */
Run Code Online (Sandbox Code Playgroud)

在项目中,当使用PCH时,每个源文件通常都包含#include在此文件中(我不知道其他文件,但在VC++中这实际上是一个要求 - 每个附加到配置为使用PCH的项目的源必须以:开头#include PrecompiledHedareName.h).预编译头的配置非常依赖于平台,超出了本答案的范围.

请注意一个重要问题:PCH中定义/包含的内容只有在绝对必要时才会更改 - 每个chnge都会导致整个项目(以及其他依赖模块)的重新编译!

关于PCH的更多信息:

Wiki
GCC Doc
MSDN

2.前瞻性声明.

当您不需要全类定义时,转发声明它以删除代码中不必要的依赖项.这也意味着在可能的情况下广泛使用指针和引用.例:

#include "BigDataType.h"

class Sample
{
protected:
    BigDataType _data;
};
Run Code Online (Sandbox Code Playgroud)

你真的需要存储_data价值吗?为什么不这样:

class BigDataType; //That's enough, #include not required

class Sample
{
protected:
    BigDataType* _data; //So much better now
};
Run Code Online (Sandbox Code Playgroud)

这对于大型类型尤其有利.

3.不要过度使用模板.

元编程是开发人员工具箱中非常强大的工具.但是当它们没有必要时,不要试图使用它们.

它们非常适合于特征,编译时评估,静态反射等.但他们引入了很多麻烦:

  • 错误消息 - 如果您曾经看到由于不正确使用std::迭代器或容器(尤其是复杂的等std::unordered_map)导致的错误,那么您知道这是什么.
  • 可读性 - 复杂的模板很难读取/修改/维护.
  • 怪癖 - 许多技术,模板用于,并不是那么着名,因此维护这些代码可能更难.
  • 编译时间 - 对我们来说最重要的是:

请记住,如果您将函数定义为:

template <class Tx, class Ty>
void sample(const Tx& xv, const Ty& yv)
{
    //body
}
Run Code Online (Sandbox Code Playgroud)

它会被编译为每一个独特的组合TxTy.如果经常使用这样的函数(对于许多这样的组合),它确实会减慢编译过程.现在想象一下,如果你开始过度使用全班的模板,会发生什么......

4.使用PIMPL习语.

这是一项非常有用的技术,它允许我们:

  • 隐藏实施细节
  • 加快代码生成
  • 轻松更新,无需破坏客户端代码

它是如何工作的?考虑包含大量数据的类(例如,代表人).它可能看起来像这样:

class Person
{
protected:
    string name;
    string surname;
    Date birth_date;
    Date registration_date;
    string email_address;
    //and so on...
};
Run Code Online (Sandbox Code Playgroud)

我们的应用程序不断发展,我们需要扩展/更改Person定义.我们添加一些新字段,删除其他字段......并且所有内容都崩溃:人员更改大小,字段名称更改...大灾变.特别是,Person需要更改/更新/修复依赖于定义的每个客户端代码.不好.

但我们可以聪明地做到这一点 - 隐藏Person的细节:

class Person
{
protected:
    class Details;
    Details* details;
};
Run Code Online (Sandbox Code Playgroud)

现在,我们做了很多好事:

  • 客户端无法创建代码,这取决于如何Person定义
  • 只要我们不修改客户端代码使用的公共接口,就不需要重新编译
  • 我们减少了编译时间,因为定义stringDate不再需要存在(在以前的版本中,我们必须为这些类型包含适当的头,这会增加额外的依赖性).

5. #pragma once指令.

虽然它可能不会提速,但它更清晰,更不容易出错.它与使用包含警卫基本相同:

#ifndef __Asx_Core_Prerequisites_H__
#define __Asx_Core_Prerequisites_H__

//Content

#endif /* __Asx_Core_Prerequisites_H__ */
Run Code Online (Sandbox Code Playgroud)

它可以防止同一文件的多次解析.虽然#pragma once不是标准的(事实上,没有编译指示 - 编译指示保留用于特定于编译器的指令),但它得到了广泛的支持(例如:VC++,GCC,CLang,ICC)并且可以毫无顾虑地使用 - 编译器应该忽略未知的编译指示(或多或少默默地).

6.不必要的依赖性消除.

非常重要的一点!当代码被重构时,依赖性经常会改变.例如,如果您决定进行一些优化并使用指针/引用而不是值(此答案的第2点和第4点),则某些包含可能变得不必要.考虑:

#include "Time.h"
#include "Day.h"
#include "Month.h"
#include "Timezone.h"

class Date
{
protected:
    Time time;
    Day day;
    Month month;
    Uint16 year;
    Timezone tz;

    //...
};
Run Code Online (Sandbox Code Playgroud)

此类已更改为隐藏实现详细信息:

//These are no longer required!
//#include "Time.h"
//#include "Day.h"
//#include "Month.h"
//#include "Timezone.h"

class Date
{
protected:
    class Details;
    Details* details;

    //...
};
Run Code Online (Sandbox Code Playgroud)

跟踪这样的冗余包含是很好的,可以使用大脑,内置工具(如VS Dependency Visualizer)或外部实用程序(例如GraphViz).

Visual Studio还有一个非常好的选项 - 如果你在任何文件上点击RMB,你会看到一个选项'生成包含文件的图形' - 它将生成一个漂亮,可读的图形,可以很容易地分析并用于跟踪不必要的依赖.

在我的String.h文件中生成的示例图:

示例图

  • 我建议提到`#pragma once`是特定于编译器的.[传统包括警卫](http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0CB8QFjAA&url=http%3A%2F%2Fen.wikipedia.org %2Fwiki%2FInclude_guard&ei = KbYmVdfZKpXdoASY2IDwDQ&usg = AFQjCNE-LLUHx6rseVMmcC0fY3qeVOBVDA&sig2 = zkpDxMDpD-EJc8mx87xxdQ&bvm = bv.90491159,d.cGU)更具可移植性,而不依赖于编译器. (2认同)