构建时间:Visual Studio 2015-2017 构建非常慢
Build time: Visual Studio 2015-2017 build very slow
对于我的小型(5-6000 行代码)C++ 程序,我使用了 VS 2015 和 2017,第一次构建时我的构建时间约为 2 分钟。这显然非常慢,但我不确定为什么。在工具->选项->项目和解决方案->构建和 运行 - 我已经将 "maximum number of parallel project builds" 设置为 8 但没有发生任何变化。
是否有任何其他设置或一般规则可用于减少构建时间?
编译需要时间...这是一个复杂的过程,尤其是在包含许多文件和项目的大型解决方案中。
但是有些东西可以减少 Visual Studio.
上的编译时间
使用好的硬件。
一个足够空space、良好的多核处理器和足够RAM的SSD始终是更快编译的良好基础。
使用预编译Headers
预编译 headers 可以大大加快构建过程。如果在项目创建期间没有自动创建它们,那么设置它们会有点复杂,但在很多情况下绝对值得付出努力。
以下是打开它们的方法:
您的项目中需要两个文件,例如 pch.h 和 pch.cpp。
pch.h 包含您希望在项目中常用的所有定义和 header,例如
#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"
它有特殊用途(见下文)。
现在向项目中的每个 cpp 添加一个 #include "pch.h"
,在 cpp 文件的最顶部位置。这对于预编译 headers.
是强制性的
接下来是在您的项目中启用预编译的 header。
打开您的项目属性,并为所有配置和所有平台输入它们应该“使用”预编译的 headers:
这告诉项目你想使用你的 pch.h 作为预编译的 headers。
最后一步是将 pch.cpp 的文件属性更改为“创建”(这是特殊用途):
这意味着 pch.cpp 将从现在开始创建二进制预编译 header 文件 Visual Studio 需要。
拆分项目并保持良好的项目层次结构。
一般来说,将所有内容都放在一个大项目中并从每个文件中调用每个文件并不是一个好主意,compile-time-wise 和 design-wise 都不是。
您应该将您的解决方案拆分为某个“级别”的静态库。
最低级别可以是,例如是一个基本的网络库、IO 库、包装器、std 改进、便利助手等。
中等水平可以是例如一个专门的线程库(它利用了网络、IO 等较低级别)
最高级别是您的申请。
上层可以访问下层(最好是下层),但下层永远不能访问上层(必要时通过接口除外)。
这确保 - 当您处理您的应用程序时 - 只有应用程序需要重建,而不是整个项目。
避免不必要的 Header-Only-Classes.
当然你需要 header-only classes,例如STL。而且模板只能在 header-only-classes 中使用。
但是,如果您正在编写 non-template class,则应该 class 将其拆分为 cpp 和 header 以缩短编译时间。此外,只应在 header.
中实现短方法(例如普通的 getter 和 setter)
避免不必要的包含,改用forward-declarations
假设你有一个 class 在较低的级别 header:
#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();
};
}
并且您想将此 class 的引用或(智能)指针存储在更高级别 class header:
#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 使用缓慢编译的模板库来实例化他的成员,从而包含它的 header。如果您现在包含 lowlevel.h,您也将包含慢速模板库。如果更高的class包括你的中等classheader,它将包括中等header、低等header和模板header ]...等等。
您可以使用 forward-declarations 来避免这种情况。
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
};
}
无需包含整个 header!由于m_helperRef不是一个完整实例化的classobject而只是一个智能指针,而且那个智能指针只在CPP中使用,所以header不需要知道MySuperHelper 究竟是什么,它只需要一个前向声明。只有 CPP - 它直接实例化和使用 MySuperHelper - 需要确切地知道它是什么,因此必须 #include "lowlevel.h"
这可以加快编译速度。 library/engine,它做得很好,是 Ogre;如果你 #include <ogre.h>
,你将只包含一个前向声明列表,编译速度很快。如果您想使用 Ogre 的 classes,则可以在 CPP 中包含特定的 header。
使用多核并行编译
就像我说的,编译是一个相当复杂的过程,我不得不承认我对如何改进并行编译的秘密不是很了解(也许有人可以提供帮助)。在许多情况下,编译是一个依存关系的顺序过程。然而,有些案例可以在没有更深入知识的情况下并行编译,Visual Studio 有一些选项可以这样做。
在 Tools/Options/Build and Run
下,您可以输入要同时构建的最大项目数。
但这些只是并行构建的项目。项目本身仍将按顺序编译。但这也可以在项目本身的项目设置中更改(您必须为每个项目执行此操作)
尽管如此,不要期望并行编译有任何奇迹。还有很多案件需要依序处理
分析您的 header 包容性
您可以打开“显示包含”,这将在构建输出中为您提供包含的 header 文件的列表:
(当然,这个功能应该只是暂时打开,因为它会极大地减慢构建过程——这与你想要的相反;))
构建完成后,您可以分析输出,也许可以找到一些不需要的 header 可以删除。
AFAIK 还有一些工具可以自动为您完成,但我自己还没有尝试过。 Here's a post which states that ReSharper C++ 提供了删除未使用的 header 的功能(我还没有尝试过)
为构建文件夹禁用病毒扫描程序
在构建期间将创建大量文件。如果病毒扫描程序在构建期间访问这些文件,这可能会导致严重的速度下降。至少从病毒扫描程序访问中排除临时构建文件夹。
对于我的小型(5-6000 行代码)C++ 程序,我使用了 VS 2015 和 2017,第一次构建时我的构建时间约为 2 分钟。这显然非常慢,但我不确定为什么。在工具->选项->项目和解决方案->构建和 运行 - 我已经将 "maximum number of parallel project builds" 设置为 8 但没有发生任何变化。
是否有任何其他设置或一般规则可用于减少构建时间?
编译需要时间...这是一个复杂的过程,尤其是在包含许多文件和项目的大型解决方案中。 但是有些东西可以减少 Visual Studio.
上的编译时间使用好的硬件。
一个足够空space、良好的多核处理器和足够RAM的SSD始终是更快编译的良好基础。
使用预编译Headers
预编译 headers 可以大大加快构建过程。如果在项目创建期间没有自动创建它们,那么设置它们会有点复杂,但在很多情况下绝对值得付出努力。 以下是打开它们的方法:
您的项目中需要两个文件,例如 pch.h 和 pch.cpp。
pch.h 包含您希望在项目中常用的所有定义和 header,例如
#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"
它有特殊用途(见下文)。
现在向项目中的每个 cpp 添加一个 #include "pch.h"
,在 cpp 文件的最顶部位置。这对于预编译 headers.
接下来是在您的项目中启用预编译的 header。 打开您的项目属性,并为所有配置和所有平台输入它们应该“使用”预编译的 headers:
这告诉项目你想使用你的 pch.h 作为预编译的 headers。
最后一步是将 pch.cpp 的文件属性更改为“创建”(这是特殊用途):
这意味着 pch.cpp 将从现在开始创建二进制预编译 header 文件 Visual Studio 需要。
拆分项目并保持良好的项目层次结构。
一般来说,将所有内容都放在一个大项目中并从每个文件中调用每个文件并不是一个好主意,compile-time-wise 和 design-wise 都不是。 您应该将您的解决方案拆分为某个“级别”的静态库。
最低级别可以是,例如是一个基本的网络库、IO 库、包装器、std 改进、便利助手等。
中等水平可以是例如一个专门的线程库(它利用了网络、IO 等较低级别)
最高级别是您的申请。
上层可以访问下层(最好是下层),但下层永远不能访问上层(必要时通过接口除外)。 这确保 - 当您处理您的应用程序时 - 只有应用程序需要重建,而不是整个项目。
避免不必要的 Header-Only-Classes.
当然你需要 header-only classes,例如STL。而且模板只能在 header-only-classes 中使用。 但是,如果您正在编写 non-template class,则应该 class 将其拆分为 cpp 和 header 以缩短编译时间。此外,只应在 header.
中实现短方法(例如普通的 getter 和 setter)避免不必要的包含,改用forward-declarations
假设你有一个 class 在较低的级别 header:
#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();
};
}
并且您想将此 class 的引用或(智能)指针存储在更高级别 class header:
#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 使用缓慢编译的模板库来实例化他的成员,从而包含它的 header。如果您现在包含 lowlevel.h,您也将包含慢速模板库。如果更高的class包括你的中等classheader,它将包括中等header、低等header和模板header ]...等等。
您可以使用 forward-declarations 来避免这种情况。
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
};
}
无需包含整个 header!由于m_helperRef不是一个完整实例化的classobject而只是一个智能指针,而且那个智能指针只在CPP中使用,所以header不需要知道MySuperHelper 究竟是什么,它只需要一个前向声明。只有 CPP - 它直接实例化和使用 MySuperHelper - 需要确切地知道它是什么,因此必须 #include "lowlevel.h"
这可以加快编译速度。 library/engine,它做得很好,是 Ogre;如果你 #include <ogre.h>
,你将只包含一个前向声明列表,编译速度很快。如果您想使用 Ogre 的 classes,则可以在 CPP 中包含特定的 header。
使用多核并行编译
就像我说的,编译是一个相当复杂的过程,我不得不承认我对如何改进并行编译的秘密不是很了解(也许有人可以提供帮助)。在许多情况下,编译是一个依存关系的顺序过程。然而,有些案例可以在没有更深入知识的情况下并行编译,Visual Studio 有一些选项可以这样做。
在 Tools/Options/Build and Run
下,您可以输入要同时构建的最大项目数。
但这些只是并行构建的项目。项目本身仍将按顺序编译。但这也可以在项目本身的项目设置中更改(您必须为每个项目执行此操作)
尽管如此,不要期望并行编译有任何奇迹。还有很多案件需要依序处理
分析您的 header 包容性
您可以打开“显示包含”,这将在构建输出中为您提供包含的 header 文件的列表:
为构建文件夹禁用病毒扫描程序
在构建期间将创建大量文件。如果病毒扫描程序在构建期间访问这些文件,这可能会导致严重的速度下降。至少从病毒扫描程序访问中排除临时构建文件夹。