Пример:
Скажем, я включаю в свой предварительно скомпилированный заголовочный файл:
#include <vector>
Поскольку несколько экземпляров вектора, таких как std :: vector, std :: vector и т. Д., Часто используются в моем проекте, это уменьшит время компиляции, если я создам их экземпляры также в предварительно скомпилированном заголовке следующим образом:
#include <vector>
template class std::vector<float>;
template class std::vector<int>;
Если пойти дальше, будет ли целесообразным даже добавлять фиктивные функции в предварительно скомпилированные заголовки, которые используют несколько функций:
namespace pch_detail {
inline auto func() {
auto&& v = std::vector<float>{};
v.size();
v.begin();
v.front();
}
}
Я очень не уверен в том, как на самом деле работают блоки перевода и шаблоны, поэтому мне кажется, что если я создаю их экземпляры в предварительно скомпилированных заголовках, это должно означать, что их не нужно создавать для каждого файла .cpp.
Обновить
Протестировано на реальной базе кода с Visual Studio 2017 и некоторыми экземплярами часто используемых шаблонных классов.
Следовательно, по крайней мере, в моем случае это заняло немного времени.
Забавная вещь, но хотя бы для clang
(4.0.1) ваш вариант увеличивает время компиляции:
1. no pch
real 0m0,361s
user 0m0,340s
sys 0m0,021s
2. pch, no explicit instantiate
real 0m0,297s
user 0m0,280s
sys 0m0,017s
3. pch, explicit instantiate
real 0m0,507s
user 0m0,474s
sys 0m0,033s
Я использую такой код:
#include <iostream>
#include "test.h"
int main() {
std::vector<float> a = {1., 2., 3.};
for (auto &&e : a) {
std::cout << e << "\n";
}
std::vector<int> b = {1, 2, 3};
for (auto &&e : b) {
std::cout << e << "\n";
}
}
кейс 2 test.h
#pragma once
#include <vector>
случай 3
#pragma once
#include <vector>
template class std::vector<float>;
template class std::vector<int>;
и такой скрипт компиляции:
echo "no pch"time clang++ -std=c++11 main.cpp
echo "pch, no explicit instantiate"clang++ -std=c++11 -x c++-header test.h -o test.pch
time clang++ -std=c++11 -include-pch test.pch main.cpp
echo "pch, explicit instantiate"clang++ -std=c++11 -x c++-header test2.h -o test2.pch
time clang++ -std=c++11 -include-pch test2.pch main2.cpp
Это может иметь значение, да.
Инстанциация в единицах перевода может затем использовать данные в предварительно скомпилированном заголовке, и компилятор может прочитать это быстрее, чем заголовки стандартной библиотеки C ++.
Но вам придется вести список экземпляров, так что эта оптимизация во время компиляции может оказаться более сложной задачей, чем она того стоит — ваша идея может иметь обратный эффект, если у вас есть экземпляры, которые больше не нужны.
Я тоже думал об этом, и у меня тоже есть этот вопрос. (Но я нуб …)
Еще одна ссылка: https://msdn.microsoft.com/en-us/library/by56e477.aspx
Может быть явным extern
нужно?
Однако, когда пришло время связать, файлы cpp были скомпилированы в .obj
х, но .pch
это не .obj
…
Тогда, где будет происходить реализация функций шаблона? Будет ли компоновщик читать вещи из .pch
?
Или нам нужен еще один отдельный .cpp
выделенный для их создания, при этом все клиентские ссылки объявляются как extern
?
И .. Генерация кода Link-Time?
Это работает немного. Тестирование с VS2012. Включите профилирование компилятора и просмотрите выходные данные компилятора.
// stdafx.h
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <vector>
#include <set>
#include <deque>
// stdafx.cpp
#include "stdafx.h"
using namespace std;
template class set<int>;
template set<int>::set();
template set<int>::_Pairib set<int>::insert(const int&);
template class deque<int>;
template deque<int>::deque();
template void deque<int>::push_back(const int&);
template class vector<int>;
template vector<int>::vector();
template void vector<int>::push_back(const int&);
// playcpp.cpp, the entry point
#include "stdafx.h"
using namespace std;
// toggle this block of code
// change a space in the "printf", then build (incrementally)
/*
extern template class set<int>;
extern template set<int>::set();
extern template set<int>::_Pairib set<int>::insert(const int&);
extern template class deque<int>;
extern template deque<int>::deque();
extern template void deque<int>::push_back(const int&);
extern template class vector<int>;
extern template vector<int>::vector();
extern template void vector<int>::push_back(const int&);
*/
int _tmain(int argc, _TCHAR* argv[])
{
set<int> s;
deque<int> q;
vector<int> v;
for(int i=0;i<10000;i++){
int choice=rand()%3;
int value=rand()%100;
switch(choice){
case 0: s.insert(value); break;
case 1: q.push_back(value); break;
case 2: v.push_back(value); break;
}
}
for(const auto &i:s)
printf("%d",i);
for(const auto &i:q)
printf("%d ",i);
for(const auto &i:v)
printf("%d ",i);
return 0;
}
результаты (многие другие опущены)
с внешними декларациями:
1> 1630 毫秒 Build 1 次调用
...
1> 757 毫秒 ClCompile 1 次调用
1> 787 毫秒 Link 1 次调用
без внешних деклараций:
1> 1801 毫秒 Build 1 次调用
...
1> 774 毫秒 Link 1 次调用
1> 955 毫秒 ClCompile 1 次调用
(Китайская версия. Легенды: ms : мс / миллисекунды , x 次 调用 : х звонит / звонит х раз)
Настройки питания настраиваются таким образом, чтобы позволить процессору работать медленно, чтобы увеличить время, чтобы избежать турбулентности.
Выше только один образец для каждого случая. Тем не менее, это довольно нестабильно. Оба случая могут иногда работать на ~ 200 мс больше.
Но пытаясь много раз, всегда есть разница около 200 мс в среднем. Я могу просто сказать, что средние значения составляют около 1650 мс и 1850 мс, со всей разницей во времени ClCompile.
Конечно, есть и другие вызовы других функций-членов шаблона, просто у меня не было времени выяснить все эти сигнатуры типов … (кто-нибудь может сказать, какой (постоянный) итератор он будет использовать?)
Ну, но тогда …. Есть ли лучшие способы сделать это?