Build time: Visual Studio 2015-2017 build very slow

Jason picture Jason · Nov 12, 2017 · Viewed 7.3k times · Source

For my small (5-6000 lines of code) C++ program I have used both VS 2015 and 2017 and my build times are around 2 minutes on first build. This is obviously incredibly slow but I'm not sure why. In tools->options->projects and solutions->build and run - I've already set the "maximum number of parallel project builds" to 8 but no change occurred.

Are there any other settings or general rules which can be applied to reduce build times?

Answer

user2328447 picture user2328447 · Nov 12, 2017

Compiling takes time... it's a complicated process, especially in large solutions with many files and projects. But there are some things, which can reduce compile times on Visual Studio.

Use good hardware.

An SSD with sufficient empty space, good multi core processor and sufficient RAM is always a good fundament for quicker compiling.

Use Precompiled Headers

Precompiled headers can speed up the build process a lot. They are a bit complicated to set up if they have not been automatically created during project creation, but it's definitely worth the effort in many cases. Here's how to switch them on:

You'll need two files in your project, for example called pch.h and pch.cpp. enter image description here

pch.h contains all defines and headers you want to commonly use in your project., e.g.

#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 contains only one line:

#include "pch.h"

It has a special purpose (see below).

Now add a #include "pch.h" to EVERY cpp in your project, at the VERY TOP position of your cpp files. This is mandatory for precompiled headers.

The next thing is to enable the precompiled headers in your project. Open your project properties, and enter for all configurations and all platforms, that they should "use" the precompiled headers:

enter image description here

This tells the project that you want to use your pch.h as the precompiled headers.

The last step is to change the file properties of your pch.cpp to "create" (that's the special purpose): enter image description here

This means that the pch.cpp will from now on create the binary precompiled header file Visual Studio needs.

Split Projects and maintain a good project hierarchy.

Generally it is not a good idea to place everything in one large project and call each file from each file, neither compile-time-wise nor design-wise. You should split your solution into static libraries of a certain "level".

The lowest level could e.g. be a basic network library, IO library, wrappers, std improvements, convenience helpers etc.

The medium levels could be e.g. a specialized Thread library (which makes use of the lower levels like network, IO and so on)

The highest level would be your application.

Higher levels can access lower levels (preferrably the level directly below), but lower levels can never access higher levels (except via interfaces, if necessary). This ensures that - while you are working on your application - only the application will have to be rebuilt, and not the whole project.

Avoid unneccessary Header-Only-Classes.

Of course you NEED header-only classes, e.g. STL. And also templates are only possible in header-only-classes. But if you're writing a non-template class, it should be classically split into cpp and header to improve the compile times. Also, only short methods (e.g. trivial getters and setters) should be implemented in the header.

Avoid unneccessary includes, use forward-declarations instead

Let's say you have a class in a lower level 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();
  };
}

And you want to store a reference or (smart) pointer of this class in a higher level 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
  };
}

then of course this is valid code, but it's potentially slow to compile. MySuperHelper uses the slow compiling template lib to instantiate his member and thus includes its header. If you now include lowlevel.h, you will include the slow template lib as well. And if a higher class includes your medium class header, it will include the medium level header, the low level header and the template header... and so on.

You can avoid that with 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
  };
}

No need to include the whole header! Since m_helperRef is not a whole instantiated class object but only a smart pointer, and that smart pointer is only used in the CPP, the header doesn't need to know what MySuperHelper exactly is, it just needs a forward declaration. Only the CPP - which instantiates and works with MySuperHelper directly - needs to know exactly what it is and thus has to #include "lowlevel.h" This can speed up compiling a lot. A library/engine, which does this pretty good, is Ogre; if you #include <ogre.h>, you will only include a list of forward declarations, which is quick to compile. If you want to work with Ogre's classes, you then include the specific header in the CPP.

Use multicore parallel compiling

Like I said, compiling is a pretty complicated process, and I have to confess that I'm not very good in the secrets on how to improve parallel compiling (may be someone else can help). Compiling is in many cases a sequencial process of dependencies. Nevertheless, some cases can be compiled in parallel without deeper knowledge, and Visual Studio has some options to do so.

Under Tools/Options/Build and Run you can enter the maximum number of projects to build concurrently. enter image description here

But these are only projects to build in parallel. The projects itself still will be compiled sequentially. But this can also be changed in the project settings of the projects itself (you will have to do this for every project)

enter image description here

Nevertheless, don't expect any wonders from parallel compiling. There are still a lot of cases which have to be handled sequencially.

Analyze your header inclusion

You can switch on "Show includes", which will give you a list of the included header files in the build output: enter image description here (Of course this feature should be switched on only temporarily, because it slows down the build process extremely - which is the opposite of what you want ;)) After the build you can then analyze the output and perhaps find some unneccessary headers you can remove. AFAIK there are also some tools, which can do that automatically for you, but haven't tried out myself yet. Here's a post which states that ReSharper C++ provides a functionality to remove unused headers (also this I haven't tried yet)

Disable virus scanners for build folders

During a build a large amount of files will be created. If the virus scanner accesses these files during build, this can cause a major slowdown. Exclude at least the temporary build folders from virus scanner access.