Go 我不懂,下面以 C++ 和 C# 对比来说明为什么 C++ 编译慢和 C# 编译快。
C 和 C++ 文件的编译经过几个主要步骤:
- 处理续行符处理(“\”)之类的杂事
- 词法分析,解析出 tokens 来
- 预处理,宏展开,处理 #include ,然后对 #include 包含的文件又重复 1~3 步骤。
- 重新词法分析
- 语法分析生成抽象语法树 AST
- 语义分析
- 优化生成代码
C# 的步骤:
- 处理续行符处理(“\”)之类的杂事
- 词法分析,解析出 tokens 来
- 语法分析生成抽象语法树 AST
- 语义分析
- 优化生成代码
首先,直观的看,从编译阶段上来说 C# 就比 C++ 少,这是一个原因。
另一个原因是语言的编译速度跟语法关系很大,高级语言编译一般是经过词法分析=>语法分析=>语义分析=>优化生成代码几步,如果语言的语法上没有歧义,编译的每个阶段都是独立的,那么编译速度自然就快。
如果不是这样,语法上有歧义,比如 C++ 嵌套模板的">>"和位操作的“>>”,当编译器遇到嵌模板">>"的时候只有先放着,然后往前多看几步才能确定到底是模板的尖括号还是移位,自然比没有语法歧义的语言要慢啦。
再举个例子,C++调用模板函数实例化时可以省略<>让编译器自己去推导最合适的重载,C# 泛型方法调用也可以省略 <>。类似这样的语法元素使得编译器在语法分析阶段就不能够确定一个函数调用是否是函数模板实例化(对于 C# 来说是泛型方法调用)还是一个普通的函数调用,而要等到语义分析阶段才能确定,所以在这一点上 C++ 和 C# 其实都选择牺牲编译速度来为人类减少击键次数做贡献并略微提高了代码的可读性。
还有一个方面是C和C++的编译时代码生成问题产生的:头文件和宏定义会在编译时生成代码,宏展开和引入头文件相当于硬插入了一大段文本,让编译器需要重新从词法分析开始分析,模板什么的虽然不是直接插入文本,但处理起来也很麻烦,消耗了不少的编译时间。如果 C/C++ 不是用头文件而是某种形式的编译模块,多个源文件(编译单元)引用同一个模块那么编译器只需分析一次该模块生成 AST 反复使用,那编译速度也能提升不少。
比起 C/C++ 笨笨的文本插入形式的 #include,C# 编译器完全省去了头文件的编译,你引用的 assembly 直接提供了类型和方法神马的元数据,编译器直接拿来用就好。
最后,因为 C# 没有宏和模板之类的代码生成手段,泛型也是运行时处理的,进一步加快了编译速度。
----------
题外话
关于编译器自举跟编译速度关系不大,目前(VS2013)的 C# 编译器应该是 C++ 写的,编译速度很快,但是 Mono 的编译器是 C# 写的,感觉并不慢,至少没有数量级上的差异。现在连 M$ 新的 C# 编译器 Rosylin 也完全用 C# 写了,还很模块化,性能也不差哦。
说了这么多,其实我的意思是 C++ 和 C# 比 PHP 的编译速度差远了,PHP 是最好的语言!