在预编译头文件中实例化模板会减少编译时间吗?

时间:2017-07-28 09:40:43

标签: c++ compile-time precompiled-headers translation-unit

实施例: 假设我包含在我的预编译头文件中:

#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测试真实代码库以及常用模板类的一些实例。

  1. 实例化常见的模板类:71731 ms
  2. 没有实例化:68544 ms
  3. 因此,至少在我的情况下,需要花费更多时间。

3 个答案:

答案 0 :(得分:4)

有趣的是,但至少对于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 

答案 1 :(得分:3)

它可以有所作为。

翻译单元中的实例化可以利用预编译头中的数据,编译器可以比C ++标准库头更快地读取它。

但是你必须维护一个实例化列表,所以这个编译时优化可能比它的价值更麻烦 - 如果你有不再需要的实例化,你的想法可能会产生相反的效果

答案 2 :(得分:1)

我也在考虑这种方式,我也有这个问题。 (但我是否......)

另一个参考:https://msdn.microsoft.com/en-us/library/by56e477.aspx

可能需要明确extern吗?

但是,当需要链接时,cpp文件已编译为.obj,但.pch不是.obj ... 然后,模板函数的实例化将在哪里?链接器是否能够从.pch

中读取内容

或者我们需要另一个专门用于实例化它们的.cpp,同时将所有客户端引用声明为extern

并且..链接时代码生成?

有一些尝试

它有点工作。使用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;
}

结果(许多其他遗漏)

使用extern声明:

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次调用:x调用/调用x次)

调整功率设置以使CPU运行缓慢,以便获得更长的时间以避免湍流。

上面只是每个案例的一个样本。它仍然很不稳定。两种情况有时可能会持续约200ms。

但是多次尝试,在平均上总是有大约200毫秒的差异。我可以说平均值大约是1650毫秒和1850毫秒,而且在ClCompile的时间里差别很大。

当然还有更多调用其他模板成员函数,只是我没有时间弄清楚所有那些类型的签名......(谁能告诉我它将使用哪个(const)迭代器? )

那么,那么....有更好的方法吗?