与c#和Java相比,编译c++文件需要很长时间。编译一个c++文件比运行一个正常大小的Python脚本花费的时间要长得多。我目前使用vc++,但它与任何编译器是一样的。为什么会这样?
我能想到的两个原因是加载头文件和运行预处理器,但这似乎不能解释为什么需要这么长时间。
与c#和Java相比,编译c++文件需要很长时间。编译一个c++文件比运行一个正常大小的Python脚本花费的时间要长得多。我目前使用vc++,但它与任何编译器是一样的。为什么会这样?
我能想到的两个原因是加载头文件和运行预处理器,但这似乎不能解释为什么需要这么长时间。
当前回答
解析和代码生成实际上相当快。真正的问题是打开和关闭文件。记住,即使使用include守卫,编译器仍然打开. h文件,读取每一行(然后忽略它)。
有一次,我的一个朋友(在工作无聊的时候)把他公司的应用程序——所有的源文件和头文件——放到一个大文件中。编译时间从3小时下降到7分钟。
其他回答
解析和代码生成实际上相当快。真正的问题是打开和关闭文件。记住,即使使用include守卫,编译器仍然打开. h文件,读取每一行(然后忽略它)。
有一次,我的一个朋友(在工作无聊的时候)把他公司的应用程序——所有的源文件和头文件——放到一个大文件中。编译时间从3小时下降到7分钟。
最大的问题是:
1)无限头解析。已经提到过。缓解(如#pragma once)通常只适用于每个编译单元,而不是每个构建。
2)事实上,工具链经常被分离成多个二进制文件(make、预处理器、编译器、汇编器、归档器、impdef、链接器和dll工具),这些二进制文件必须在每次调用(编译器、汇编器)或每一对文件(归档器、链接器和dll工具)时重新初始化和重新加载所有状态。
请参见关于comp.compilers: http://compilers.iecc.com/comparch/article/03-11-078的讨论,特别是这个:
http://compilers.iecc.com/comparch/article/02-07-128
请注意,comp.compilers的主持人John似乎也同意这一点,这意味着如果完全集成工具链并实现预编译的头文件,那么C语言也应该可以达到类似的速度。许多商业C编译器在某种程度上都这样做。
请注意,Unix将所有内容分解为单独的二进制文件的模型对于Windows来说是一种最坏的情况模型(其进程创建缓慢)。在比较Windows和*nix之间的GCC构建时间时,这是非常明显的,特别是当make/configure系统还调用一些程序只是为了获取信息时。
在大型面向对象项目中,重要的原因是c++很难限制依赖关系。
私有函数需要在它们各自的类的public头文件中列出,这使得依赖关系比它们需要的更具传递性(传染性):
// Ugly private dependencies
#include <map>
#include <list>
#include <chrono>
#include <stdio.h>
#include <Internal/SecretArea.h>
#include <ThirdParty/GodObjectFactory.h>
class ICantHelpButShowMyPrivatePartsSorry
{
public:
int facade(int);
private:
std::map<int, int> implementation_detail_1(std::list<int>);
std::chrono::years implementation_detail_2(FILE*);
Intern::SecretArea implementation_detail_3(const GodObjectFactory&);
};
如果在头文件的依赖树中重复使用这种模式,就会产生一些间接包含项目中大部分头文件的“神头文件”。它们就像上帝对象一样无所不知,只是在绘制它们的包含树之前,这一点并不明显。
这会以两种方式增加编译时间:
它们添加到包含它们的每个编译单元(.cpp文件)的代码量很容易比cpp文件本身多很多倍。从这个角度来看,catch2.hpp是18000行,而大多数人(甚至是ide)开始难以编辑超过1000-10000行的文件。 编辑头文件时必须重新编译的文件数量不包含在依赖它的真实文件集中。
是的,有一些缓解措施,比如前向声明,它有缺点,或者pimpl习惯用法,它是非零成本抽象。尽管c++在你能做的事情上是无限的,但如果你偏离了它的本意,你的同事会想知道你到底在吸什么。
最糟糕的是:如果你仔细想想,在它们的公共头中声明私有函数的需求甚至是不必要的:成员函数的道德等效可以在C中被模仿,而且通常也被模仿,这不会重现这个问题。
任何编译器的减速都不一定相同。
我没有使用过Delphi或Kylix,但在MS-DOS时代,Turbo Pascal程序几乎可以立即编译,而等效的Turbo c++程序只能爬行。
两个主要的区别是一个非常强大的模块系统和允许单次编译的语法。
编译速度当然可能不是c++编译器开发人员的优先考虑事项,但C/ c++语法中也有一些固有的复杂性,这使得处理起来更加困难。(我不是C方面的专家,但Walter Bright是,在构建了各种商业C/ c++编译器之后,他创建了D语言。他的改变之一是强制使用上下文无关的语法,使语言更容易解析。)
此外,您还会注意到,makefile通常设置为每个文件都单独用C编译,因此如果10个源文件都使用相同的包含文件,则该包含文件将被处理10次。
我能想到有两个问题可能会影响c++程序的编译速度。
POSSIBLE ISSUE #1 - COMPILING THE HEADER: (This may or may not have already been addressed by another answer or comment.) Microsoft Visual C++ (A.K.A. VC++) supports precompiled headers, which I highly recommend. When you create a new project and select the type of program you are making, a setup wizard window should appear on your screen. If you hit the “Next >” button at the bottom of it, the window will take you to a page that has several lists of features; make sure that the box next to the “Precompiled header” option is checked. (NOTE: This has been my experience with Win32 console applications in C++, but this may not be the case with all kinds of programs in C++.)
POSSIBLE ISSUE #2 - THE LOCATION BEING COMPILED TO: This summer, I took a programming course, and we had to store all of our projects on 8GB flash drives, as the computers in the lab we were using got wiped every night at midnight, which would have erased all of our work. If you are compiling to an external storage device for the sake of portability/security/etc., it can take a very long time (even with the precompiled headers that I mentioned above) for your program to compile, especially if it’s a fairly large program. My advice for you in this case would be to create and compile programs on the hard drive of the computer you’re using, and whenever you want/need to stop working on your project(s) for whatever reason, transfer them to your external storage device, and then click the “Safely Remove Hardware and Eject Media” icon, which should appear as a small flash drive behind a little green circle with a white check mark on it, to disconnect it.
我希望这对你有帮助;如果有,请告诉我!:)