是否有一种自动方法来优化 C++ 中头文件的包含,从而缩短编译时间? “自动”这个词是指一种工具或程序。是否可以找到哪些头文件已过时(例如未使用公开的功能)?
编辑:让每个包含头“仅包含一次是一件重要的事情,但是有没有办法甚至更改文件的内容,以便经常使用的“功能”位于特定包含和不常用的功能上是在其他包含上吗?不幸的是,我们正在谈论包含数千个文件的现有代码库,这可能是我真正要求的重构工具吗?
Is there an automatic way to optimize inclusion of header files in C++, so that compilation time is improved ? With the word "automatic" I mean a tool or program. Is it possible to find which headers files are obsolete (e.g exposed functionality is not used) ?
Edit: Having each include header "included only once is one important thing, but is there a way to even change the contents of files so that frequently used "functionality" is on specific includes and less frequently used functionality is on other includes? Am i asking too much ? Unfortunately, we are talking about an existing code base with thousands of files. Could it be a refactoring tool what I am actually asking for ?
发布评论
评论(10)
更新
我认为你真正想要的是“包含你使用的内容”而不是一组最小的标题。 IWYU 意味着尽可能前向声明,并包含直接声明您使用的符号的标头。您不能盲目地将文件转换为 IWYU 干净文件,因为它可能不再编译。发生这种情况时,您需要找到丢失的标头并添加它。但是,如果每个文件都是 IWYU 干净的,您的编译将是 有意义/自我记录。
正如我之前的回答指出的那样,从技术上讲,包含比 IWYU 所需的更少的标头是可能的,但这通常是浪费时间。
现在如果有一个工具可以完成大部分 IWYU 重构工作就好了:)
我曾考虑过创建/使用一次这样的工具。这个想法是使用二分搜索和重复编译来找到最小的包含集。经过进一步调查,它似乎没有那么有用。
一些问题:
更改包含的头文件可以更改行为,但仍然允许文件编译。特别是一个例子,如果您在单独的头文件中定义了自己的
std::swap
。您可以删除该标头,您的代码仍将使用默认的 std::swap 实现进行编译。然而,std::swap
可能会:效率低下,导致运行时错误,或者更糟糕的是产生微妙的错误逻辑。有时,头文件包含可以用作文档。例如,对于使用
std::foreach
,通常包含
就足以使其编译。使用额外的#include
使代码更有意义。最小编译集可能无法在编译器或编译器版本之间移植。再次使用
std::foreach
示例,不能保证std::foreach
将由
提供。 p>无论如何,最小的包含集可能不会显着影响编译时间。 Visual Studio 和 gcc 支持
#pragma Once
,这使得重复包含基本上不存在性能问题。而且至少 gcc 的预处理器已经过优化,可以非常快地处理包含保护(与#pragma Once
一样快)。Update
I think what you really want is "include what you use" rather than a minimal set of headers. IWYU means forward declare as much as possible, and include headers that directly declare the symbols you use. You cannot mindlessly convert a file to be IWYU clean as it may no longer compile. When that occurs, you need to find the missing header and add it. However, if every file is IWYU clean your compiles will be faster overall even if you have to add headers occasionally. Not to mention you headers will be more meaningful/self-documenting.
As my previous answer points out it is technically possible to include even fewer headers than necessary for IWYU, but it's generally a waste of time.
Now if only there was a tool to most of the IWYU refactoring grunt work for you :)
I had considered a creating/using a tool like this once. The idea is to use binary search and repeated compilation to find the minimal set of includes. Upon further investigation it didn't seem that useful.
Some issues:
Changing the included header files can change the behavior, and still allow the file to compile. One example in particular, if you defined your own
std::swap
in a separate header file. You could remove that header and your code would still compile using the defaultstd::swap
implementation. However, thestd::swap
may be: inefficient, cause a runtime error, or worse produce subtly wrong logic.Sometimes a header file inclusion works as documentation. For instance, to used
std::foreach
, often including<vector>
is sufficient to get it to compile. The code is more meaningful with the extra#include <algorithm>
.The minimal compilation set may not be portable, between compilers or compiler versions. Using the
std::foreach
example again, there is no guarantee thatstd::foreach
will provided in by<vector>
.The minimal set of includes may not affect compile time significantly anyway. Visual studio and gcc support
#pragma once
which make repeated included essentially non-existent performance wise. And at least gcc's preprocessor has been optimized to process include guards very fast (as fast as#pragma once
).大多数编译器都对预编译头文件提供某种支持。
一个删除完全不需要的包含的工具可能会很好。您似乎在暗示您希望看到一个删除所需包含内容的内容,而这些内容被其他包含内容变得多余。我不会是它的忠实粉丝。有一天,有人可以删除另一个多余的包含文件,然后其他一些可怜的懒虫将不得不追踪硬盘上某个位置的包含文件,其中包含所有这些丢失的符号,这些符号突然出现在没有明显原因的情况下。
Most compilers have some kind of support for precompiled header files.
A tool that removes totally unneeded includes might be nice. You seem to be implying that you'd like to see one that removes needed includes that are rendered redundant by other includes. That I wouldn't be a big fan of. Somebody could remove the other redundant include one day, and then some other poor slob will have to track down what include file somewhere on the hard disk has all those missing symbols that suddenly appeared on them for no apparent reason.
PC-Lint 将报告未使用的包含文件。
PC-Lint will report unused include files.
GamesFromWithin 博客有一篇关于 C++ 标头的好文章,甚至还有一个列出包含最多的标头的工具(因此是 pimpl/pch/forwarding 的主要候选者)。很好的工具,尽管是用 Perl 编写的,而且当构建时间让我(太多)发疯时,我实际上已经使用它几次来挖掘一些好的数据。
The GamesFromWithin blog has a good article on C++ headers, and even a tool that lists which headers are included the most (and thus prime candidates for pimpl/pch/forwarding). Good tool, albeit in perl, and I've actually used it a few times to dig up some good data when build times make me (too much) nuts.
GCC 已经就预编译头主题开展了一些工作,但有一些限制(如果我记得的话,每次编译只能包含一个预编译头)。
如果您不使用 GCC,则此解决方案对您没有帮助,因为它不会生成可供其他编译器使用的简化标头。
There has been some work in GCC on the topic of precompiled headers, but there are some restrictions (only one precompiled header can be included per compilation, if I remember).
If you are not using GCC this solution will not help you, because it doesn't produce a simplified header that could be used by another compiler.
Google 的 cppclean 将帮助您删除不必要的头文件。它会产生一些误报,但它提供了一个很好的起点。请参阅我对类似问题和评论的回答此处。
Google's cppclean will help you remove unnecessary header files. It produces some false positives, but it provides a great starting point. See my answer to a similar question and comments here.
我从来没有听说过任何工具跟踪无用的包含。这只是一个微不足道的例子。
但是,无论如何,您都应该注意它。如何组织标头和实现在很大程度上取决于您如何组织代码。将您的项目视为独立的部分很重要,标题将在一定程度上迫使您思考它。
I never heard of any tool tracking useless includes. And this would be only the trivial case.
However, you should pay some attention to it anyways. How to organize the headers and the implementation depends a lot on how you organize you code. Thinking your project as indepents parts is important, and headers will partly enforce you to think about it.
我最近找到了一个工具可以完全满足您的要求。
在 Ben Ziegler 的博客双缓冲上,他发布了一些关于构建系统优化。
结果并不理想。没有明显的构建时间改进。
我仍然坚持我的第一个答案;这是浪费时间,而且不明智。
无论哪种方式,这里都是他的 perl 脚本。您需要对它进行一些修改才能使其在您的代码上工作,但核心应该是相同的。
I recent found a tool to do exactly what you asked.
On Ben Ziegler's blog Double Buffered, he posted about some build system optimizations.
The results weren't stellar. No noticeable build time improvements.
I still stand by my first answer; it's a waste of time, and ill advised.
Either way here is his perl script. You'll need to hack it a bit to get it to work on your code but core should work the same.
我曾经编写过一个非常简单的工具来执行此操作,并且效果非常好,尽管是以最可怕的蛮力方式。
它递归了项目中的所有 h/cpp 文件。
对于每个,它都会搜索#include,并依次在每个#include 前面添加//,然后触发构建。如果构建返回错误,它将取消//。如果构建成功,则包含行会被注释掉。
处理所有 cpp 文件非常快(一个小时左右),但我必须让它整个周末在代码库上运行来处理标头,但它实现起来非常简单并且非常有效。而且每年只需要运行一次左右即可清除积累的绒毛。
它产生了微小但显着的差异(在 70 分钟的构建 IIRC 中大约需要 5 分钟)。
I wrote a trivially simple tool to do this once, and it worked really well, albeit in the most hideously brute force manner.
It recursed over all h/cpp files in the project.
For each, it searched for the #includes, and added // in front of each of them in turn, then triggered a build. If the build returned an error it undid the //. If the build succeeded, it left the include line commented out.
It was pretty quick to process all the cpp files (an hour or so) but I'd have to leave it running over the codebase all weekend to deal with the headers, but it was very simple to implement and extremely effective. And it only needed to be run about once a year to clean away the accumulated fluff.
It made a small but significant difference (about 5 minutes on a 70min build IIRC).
预编译头很糟糕。
这确实是一个链接器问题,除了获得更好的链接器(祝你好运)或以一种对链接器来说更容易的方式编写代码之外,没有什么可做的。
Precompiled headers are awful.
This is really a linker issue, and there's nothing to do about it except either get a better linker (good luck) or make your code in a way that's easier on the linker.