10

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

#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 毫秒

因此,至少在我的情况下,它花费了更多时间。

4

3 回答 3

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 
于 2017-07-28T09:58:20.060 回答
4

是的,它可以有所作为。

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

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

于 2017-07-28T09:44:56.517 回答
1

我也一直在想这个办法,我心里也有这个问题。(但我是菜鸟...)

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

也许extern需要明确的?

但是,到链接的时候,cpp文件已经编译成了.obj's,但.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;
}

结果(很多其他的省略了)

带有外部声明:

1>               1630 毫秒  Build                                      1 次调用
...
1>      757 毫秒  ClCompile                                  1 次调用
1>      787 毫秒  Link                                       1 次调用

没有外部声明:

1>               1801 毫秒  Build                                      1 次调用
...
1>      774 毫秒  Link                                       1 次调用
1>      955 毫秒  ClCompile                                  1 次调用

(中文版。图例:毫秒:毫秒/毫秒,x 次调用:x 调用/被调用 x 次)

调整电源设置以让 CPU 运行缓慢,以获得更长的时间来避免湍流。

以上只是每种情况的一个样本。尽管如此,它还是很不稳定。这两种情况有时可能会多运行约 200 毫秒。

但是尝试了很多次,平均总是有大约 200ms 的差异。我只能说平均值在 1650 毫秒和 1850 毫秒左右,ClCompile 的时间完全不同。

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

好吧,但是……有更好的方法吗?

于 2018-05-02T12:14:08.770 回答