Для моей маленькой (5-6000 строк кода) программы на C ++ я использовал VS 2015 и 2017, и время первой сборки составляет около 2 минут. Это очевидно невероятно медленно, но я не уверен почему. В tools-> options-> projects and solutions-> build and run — я уже установил «максимальное количество параллельных сборок проекта» на 8, но никаких изменений не произошло.
Существуют ли другие настройки или общие правила, которые можно применять для сокращения времени сборки?
Компиляция требует времени … это сложный процесс, особенно в больших решениях с большим количеством файлов и проектов.
Но есть некоторые вещи, которые могут сократить время компиляции в Visual Studio.
Твердотельный накопитель с достаточным пустым пространством, хорошим многоядерным процессором и достаточным объемом оперативной памяти всегда является хорошей основой для более быстрой компиляции.
Предварительно скомпилированные заголовки могут значительно ускорить процесс сборки. Их немного сложно настроить, если они не были созданы автоматически при создании проекта, но во многих случаях это определенно стоит усилий.
Вот как их включить:
Вам понадобятся два файла в вашем проекте, например, с именами 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 файлов. Это обязательно для предварительно скомпилированных заголовков.
Следующая вещь — включить предварительно скомпилированные заголовки в вашем проекте.
Откройте свойства вашего проекта и введите для всех конфигураций и всех платформ, что они должны «использовать» предварительно скомпилированные заголовки:
Это говорит проекту, что вы хотите использовать ваш pch.h в качестве предварительно скомпилированных заголовков.
Последний шаг — изменить свойства файла вашего pch.cpp на «создать» (это специальное назначение):
Это означает, что отныне pch.cpp будет создавать двоичный предварительно скомпилированный заголовочный файл, необходимый Visual Studio.
Как правило, не стоит помещать все в один большой проект и вызывать каждый файл из каждого файла, ни во время компиляции, ни в дизайне.
Вы должны разбить свое решение на статические библиотеки определенного «уровня».
Самый низкий уровень может, например, быть базовой сетевой библиотекой, библиотекой ввода-вывода, оболочками, улучшениями std, вспомогательными средствами и т. д.
Средние уровни могут быть, например, специализированная библиотека потоков (которая использует более низкие уровни, такие как сеть, IO и т. д.)
Наивысшим уровнем будет ваше приложение.
Более высокие уровни могут получить доступ к более низким уровням (предпочтительно уровень непосредственно ниже), но более низкие уровни никогда не смогут получить доступ к более высоким уровням (кроме как через интерфейсы, если это необходимо).
Это гарантирует, что — пока вы работаете над своим приложением — придется перестраивать только приложение, а не весь проект.
Конечно, вам нужны классы только для заголовков, например STL. А также шаблоны возможны только в заголовочных классах.
Но если вы пишете не шаблонный класс, он должен быть классически разделен на cpp и 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();
};
}
И вы хотите сохранить ссылку или (умный) указатель этого класса в заголовке класса более высокого уровня:
#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 использует медленную компиляцию шаблона lib для создания экземпляра своего члена и, таким образом, включает его заголовок. Если вы сейчас включите lowlevel.h, вы также включите медленный шаблон lib. И если более высокий класс включает ваш заголовок среднего класса, он будет включать заголовок среднего уровня, заголовок низкого уровня и заголовок шаблона … и так далее.
Вы можете избежать этого с помощью предварительных деклараций.
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"
Это может значительно ускорить компиляцию. Библиотека / движок, который делает это довольно хорошо, великан-людоед; если ты #include <ogre.h>
, вы включите только список предварительных деклараций, который быстро компилируется. Если вы хотите работать с классами Ogre, тогда вы включаете конкретный заголовок в CPP.
Как я уже сказал, компиляция — довольно сложный процесс, и я должен признаться, что я не очень хорош в секретах о том, как улучшить параллельную компиляцию (может быть, кто-то еще может помочь). Во многих случаях компиляция является последовательным процессом зависимостей. Тем не менее, некоторые случаи могут быть скомпилированы параллельно без более глубоких знаний, и Visual Studio имеет некоторые возможности для этого.
Под Tools/Options/Build and Run
Вы можете ввести максимальное количество проектов для одновременной сборки.
Но это только проекты, которые нужно строить параллельно. Сам проект все равно будет составлен последовательно. Но это также может быть изменено в настройках проекта самого проекта (вам придется делать это для каждого проекта)
Тем не менее, не ожидайте чудес от параллельной компиляции. Есть еще много случаев, которые должны рассматриваться последовательно.
Вы можете включить «Показать включенные», что даст вам список включенных заголовочных файлов в выводе сборки:
(Конечно, эта функция должна быть включена только временно, потому что она сильно замедляет процесс сборки — что противоположно тому, что вы хотите;))
После сборки вы можете проанализировать вывод и, возможно, найти ненужные заголовки, которые вы можете удалить.
AFAIK Есть также несколько инструментов, которые могут сделать это автоматически для вас, но я еще не пробовал. Вотпост, в котором говорится, что ReSharper C ++ предоставляет функциональность для удаления неиспользуемых заголовков (также это я еще не пробовал)
Во время сборки будет создано большое количество файлов. Если антивирусный сканер получает доступ к этим файлам во время сборки, это может привести к значительному замедлению работы. Исключите по крайней мере временные папки сборки из доступа антивирусного сканера.
Других решений пока нет …