编译需要时间……这是一个复杂的过程,尤其是在具有许多文件和项目的大型解决方案中。但是有些东西可以减少 Visual Studio 上的编译时间。
使用好的硬件。
拥有足够的空闲空间、良好的多核处理器和足够的 RAM 的 SSD 始终是加快编译速度的良好基础。
使用预编译头文件
预编译的头文件可以大大加快构建过程。如果在项目创建期间没有自动创建它们,设置它们会有点复杂,但在许多情况下绝对值得付出努力。以下是打开它们的方法:
您的项目中需要两个文件,例如 pch.h 和 pch.cpp。

pch.h 包含您希望在项目中常用的所有定义和标头。例如
#ifdef _WIN32
# define _WIN32_WINNT 0x0502
# define WIN32_LEAN_AND_MEAN
# define VC_EXTRALEAN
# define NOMINMAX
#endif
#include <windows.h>
#define OIS_DYNAMIC_LIB
#include "OgreVector3.h"
#include <string>
#include <vector>
etc. pp.
pch.cpp 只包含一行:
#include "pch.h"
它有一个特殊的用途(见下文)。
现在#include "pch.h"
在项目中的每个 cpp 中添加一个,位于 cpp 文件的 VERY TOP 位置。这对于预编译的头文件是强制性的。
接下来是在您的项目中启用预编译头文件。打开您的项目属性,并为所有配置和所有平台输入它们应该“使用”预编译头文件:

这告诉项目你想使用你的 pch.h 作为预编译的头文件。
最后一步是将 pch.cpp 的文件属性更改为“创建”(这是特殊目的):

这意味着 pch.cpp 将从现在开始创建 Visual Studio 需要的二进制预编译头文件。
拆分项目并保持良好的项目层次结构。
通常,将所有内容放在一个大项目中并从每个文件中调用每个文件并不是一个好主意,无论是编译时还是设计方面。您应该将解决方案拆分为某个“级别”的静态库。
最低级别可以是例如基本网络库、IO 库、包装器、标准改进、便利助手等。
中等级别可以是例如一个专门的线程库(它利用较低级别,如网络、IO 等)
最高级别将是您的应用程序。
较高级别可以访问较低级别(最好是正下方的级别),但较低级别永远无法访问较高级别(除非必要,通过接口)。这确保了——当你在你的应用程序上工作时——只需要重建应用程序,而不是整个项目。
避免不必要的 Header-Only-Classes。
当然,您需要仅包含标头的类,例如 STL。而且模板也只能在 header-only-classes 中使用。但是如果你正在编写一个非模板类,它应该被经典地拆分为 cpp 和 header 以缩短编译时间。此外,只有简短的方法(例如普通的 getter 和 setter)应该在标头中实现。
避免不必要的包含,改用前向声明
假设您在较低级别的标题中有一个类:
#include "my_template_lib_which_takes_ages_to_compile.h"
namespace LowLevel {
class MySuperHelper {
my_template<int> m_bla;
public:
MySuperHelper();
virtual ~MySuperHelper();
void doSomething();
};
}
并且您希望将此类的引用或(智能)指针存储在更高级别的类头中:
#include "lowlevel.h"
namespace MediumLevel {
class MyMediumClass {
std::unique_ptr<LowLevel::MySuperHelper> m_helperRef;
public:
MyMediumClass(); //constructor initializes the smart pointer in cpp
virtual ~MyMediumClass();
void work(); // works with the smart pointer in cpp
};
}
那么这当然是有效的代码,但编译起来可能很慢。MySuperHelper 使用慢速编译模板库来实例化他的成员,因此包括它的头文件。如果您现在包含 lowlevel.h,您也将包含慢速模板库。如果一个更高的类包括你的中类头,它将包括中级头,低级头和模板头......等等。
您可以通过前向声明来避免这种情况。
namespace LowLevel {
class MySuperHelper;
}
namespace MyMediumLevel {
class MyMediumClass {
std::unique_ptr<LowLevel::MySuperHelper> m_helperRef;
public:
MyMediumClass(); //constructor initializes the smart pointer in cpp
virtual ~MyMediumClass();
void work(); // works with the smart pointer in cpp
};
}
无需包含整个标题!由于 m_helperRef 不是一个完整的实例化类对象,而只是一个智能指针,并且该智能指针仅在 CPP 中使用,所以头部不需要知道 MySuperHelper 到底是什么,它只需要一个前向声明。只有 CPP——它直接实例化并与 MySuperHelper 一起工作——需要确切地知道它是什么,因此必须#include "lowlevel.h"
这样做可以大大加快编译速度。一个做得很好的库/引擎是Ogre;如果你#include <ogre.h>
,你将只包括一个前向声明列表,它可以快速编译。如果你想使用 Ogre 的类,你可以在 CPP 中包含特定的头文件。
使用多核并行编译
就像我说的,编译是一个相当复杂的过程,我不得不承认我对如何改进并行编译的秘诀不是很了解(可能其他人可以提供帮助)。在许多情况下,编译是依赖关系的顺序过程。尽管如此,有些案例可以在没有更深入的知识的情况下并行编译,Visual Studio 有一些选项可以这样做。
您可以在Tools/Options/Build and Run
下面输入要同时构建的最大项目数。

但这些只是并行构建的项目。项目本身仍将按顺序编译。但这也可以在项目本身的项目设置中更改(您必须为每个项目执行此操作)

尽管如此,不要期望并行编译有什么奇迹。还有很多案件需要依次处理。
分析您的标题包含
您可以打开“显示包含”,它将在构建输出中为您提供包含头文件的列表:(
当然,这个功能应该只暂时打开,因为它会极大地减慢构建过程 - 这是相反的你想要什么;)) 在构建之后,你可以分析输出,也许会找到一些你可以删除的不必要的标题。AFAIK 还有一些工具可以自动为您完成,但我自己还没有尝试过。这是一篇文章,其中指出ReSharper C++提供了删除未使用的标头的功能(这也是我还没有尝试过的)
禁用构建文件夹的病毒扫描程序
在构建期间将创建大量文件。如果病毒扫描程序在构建过程中访问这些文件,这可能会导致严重减速。至少从病毒扫描程序访问中排除临时构建文件夹。