gitbook/编译原理实战课/docs/242308.md
2022-09-03 22:05:03 +08:00

100 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 开篇词 | 在真实世界的编译器中游历
你好我是宫文学一名技术创业者现在是北京物演科技的CEO很高兴在这里跟你见面。
我在IT领域里已经工作有20多年了。这其中我个人比较感兴趣的也是倾注时间精力最多的是做基础平台类的软件比如国内最早一批的BPM平台、BI平台以及低代码/无代码开发平台(那时还没有这个名字)等。这些软件之所以会被称为平台,很重要的原因就是拥有很强的定制能力,比如流程定制、界面定制、业务逻辑定制,等等。而这些定制能力,依托的就是编译技术。
在前几年我参与了一些与微服务有关的项目。我发现前些年大家普遍关注那些技术问题比如有状态的服务Stateful Service的横向扩展问题在云原生、Serverless、FaaS等新技术满天飞的时代不但没能被很好地解决反而更恶化了。究其原因就是状态管理还是被简单地交给数据库而云计算的场景使得数据库的压力更大了数据库原来在性能和扩展能力上的短板就更加显著了。
而比较好的解决思路之一,就是大胆采用新的计算范式,发明新的计算机语言,所以我也有意想自己动手搞一下。
我从去年开始做设计已经鼓捣了一阵了采用了一些很前卫的理念比如云原生的并发调度、基于Actor的数据管理等。总的目标是要让开发云原生的、有状态的应用像开发一个简单的单机应用一样容易。那我们就最好能把云架构和状态管理的细节给抽象掉从而极大地降低成本、减少错误。而为编程提供更高的抽象层次从来就是编译技术的职责。
Serverless和FaaS已经把无状态服务的架构细节透明掉了。但针对有状态的服务目前还没有答案。对我而言这是个有趣的课题。
在我比较熟悉的企业应用领域ERP的鼻祖SAP、SaaS的鼻祖SalesForce都用自己的语言开发应用很可惜国内的企业软件厂商还没有做到这一点。而在云计算时代设计这样一门语言绕不过去的一个问题就是解决有状态服务的云化问题。我希望能为解决这个问题提供一个新工具。当然这个工具必须是开源的。
正是因为给自己挖了这么大一个坑,也促使我更关心编译技术的各种前沿动态,也非常想把这些前沿的动态、理念,以及自己的一些实战经验都分享出来。
所以去年呢,我在极客时间上开了一门课程[《编译原理之美》](https://time.geekbang.org/column/intro/219?utm_term=zeusGI5E3&utm_source=app&utm_medium=geektime&utm_campaign=219-end&utm_content=xuexizhinan0601),帮你系统梳理了编译技术最核心的概念、理论和算法。不过在做第一季的过程中呢,我发现很多同学都跟我反馈:我确实理解了编译技术的相关原理、概念、算法等,但是有没有更直接的方式,能让我更加深入地把知识与实践相结合呢?
## 为什么要解析真实编译器?
说到把编译技术的知识与实践相结合,无外乎就是解决以下问题:
* 我已经知道,语法分析有自顶向下的方法和自底向上的方法,但要自己动手实现的话,到底该选择哪个方法呢?是应该自己手写,还是用工具生成呢?
* 我已经知道,在语义分析的过程中要做引用消解、类型检查,并且会用到符号表。那具体到自己熟悉的语言,这些工作是如何完成的呢?有什么难点和实现技巧呢?符号表又被设计成什么样子呢?
* 我已经知道编译器中会使用IR但实际使用中的IR到底是什么样子的呢使用什么数据结构呢完成不同的处理任务是否需要不同的IR呢
* 我已经知道,编译器要做很多优化工作,但针对自己熟悉的语言,这些优化是如何发生的?哪些优化最重要?又要如何写出便于编译器优化的代码呢?
类似的问题还有很多,但总结起来其实就是:**真实世界的编译器,到底是怎么写出来的?**
那弄明白了这个问题,到底对我们有什么帮助呢?
**第一,研究这些语言的编译机制,能直接提高我们的技术水平。**
一方面深入了解自己使用的语言的编译器会有助于你吃透这门语言的核心特性更好地运用它从而让自己向着专家级别的工程师进军。举个例子国内某互联网公司的员工就曾经向Oracle公司提交了HotSpot的高质量补丁因为他们在工作中发现了JVM编译器的一些不足。那么你是不是也有可能把一门语言吃得这么透呢
另一方面IT技术的进化速度是很快的作为技术人我们需要迅速跟上技术更迭的速度。而这些现代语言的编译器往往就是整合了最前沿的技术。比如Java的JIT编译器和JavaScript的V8编译器它们都不约而同地采用了“Sea of Nodes”的IR来做优化这是为什么呢这种IR有什么优势呢这些问题我们都需要迅速弄清楚。
**第二,阅读语言编译器的源码,是高效学习编译原理的重要路径。**
传统上,我们学习编译原理,总是要先学一大堆的理论和算法,理解起来非常困难,让人望而生畏。
这个方法本身没有错,因为我们学习任何知识,都要掌握其中的原理。不过,这样可能离实现一款实用的编译器还有相当的距离。
那么根据我的经验,学习编译原理的一个有效途径,就是阅读真实世界中编译器的源代码,跟踪它的执行过程,弄懂它的运行机制。因为只要你会写程序,就能读懂代码。既然能读懂代码,那为什么不直接去阅读编译器的源代码呢?在开源的时代,源代码就是一个巨大的知识宝库。面对这个宝库,我们为什么不进去尽情搜刮呢?想带走多少就带走多少,没人拦着。
当然,你可能会犯嘀咕:**编译器的代码一般都比较难吧?以我的水平,能看懂吗?**
是会有这个问题。当我们面对一大堆代码的时候,很容易迷路,抓不住其中的重点和核心逻辑。不过没关系,有我呢。在本课程中,我会给你带路,并把地图准备好,带你走完这次探险之旅。而当你确实把握了编译器的脉络以后,你对自己的技术自信心会提升一大截。这些计算机语言,就被你摘掉了神秘的面纱。
俗话说“读万卷书,行万里路”。如果说了解编译原理的基础理论和算法是读书的过程,那么探索真实世界里的编译器是什么样子,就是行路的过程了。根据我的体会,**当你真正了解了身边的语言的编译器是怎样编写的之后,那些抽象的理论就会变得生动和具体,你也就会在编译技术领域里往前跨出一大步了。**
## 我们可以解析哪些语言的编译器?
那你可能要问了,在本课程中,**我都选择了哪些语言的编译器呢?选择这些编译器的原因又是什么呢?**
这次我要带你解析的编译器还真不少包括了Java编译器javac、Java的JIT编译器Graal、Python编译器CPython、JavaScript编译器V8、Julia语言的编译器、Go语言的编译器gc以及MySQL的编译器并且在讲并行的时候还涉及了Erlang的编译器。
我选择剖析这些语言的编译器,有三方面的原因:
* 第一它们足够有代表性是你在平时很可能会用到的。这些语言中除了Julia比较小众外都比较流行。而且虽然Julia没那么有名但它使用的LLVM工具很重要。因为LLVM为Swift、Rust、C++、C等多种语言提供了优化和后端的支持所以Julia也不缺乏代表性。
* 第二它们采用了各种不同的编译技术。这些编译器有的是编译静态类型的语言有的是动态类型的语言有的是即时编译JIT有的是提前编译AOT有高级语言也有DSLSQL解释执行的话有的是用栈机Stack Machine有的是用寄存器机等等。不同的语言特性就导致了编译器采用的技术会存在各种差异从而更加有利于你开阔视野。
* 第三,通过研究多种编译器,你可以多次迭代对编译器的认知过程,并通过分析对比,发现这些编译器之间的异同点,探究其中的原因,激发出更多的思考,从而得到更全面的、更深入的认知。
看到这里,你可能会有所疑虑:**有些语言我没用过,不怎么了解,怎么办?**其实没关系。因为现代的高级语言,其实相似度很高。
一方面,对于不熟悉的语言,虽然你不能熟练地用它们来做项目,但是写一些基本的、试验性的程序,研究它的实现机制,是没有什么问题的。
另一方面,学习编译原理的人会练就一项基本功,那就是更容易掌握一门语言的本质。特别是我这一季的课程,就是要帮你成为钻到了铁扇公主肚子里的孙悟空。研究某一种语言的编译器,当然有助于你通过“捷径”去深入地理解它。
## 我是如何规划课程模块的?
这门课程的目标,是要让你对现代语言的编译器的结构、所采用的算法以及设计上的权衡,都获得比较真切的认识。其最终结果是,如果要你使用编译技术来完成一个项目,你会心里非常有数,知道应该在什么地方使用什么技术。因为你不仅懂得原理,更有很多实际编译器的设计和实现的思路作为你的决策依据。
为了达到本课程的目标,我仔细规划了课程的内容,将其划分为预备知识篇、真实编译器解析篇和现代语言设计篇三部分。
在**预备知识篇**,我会简明扼要地帮你重温一下编译原理的知识体系,让你对这些关键概念的理解变得更清晰。磨刀不误砍柴工,你学完预备知识篇后,再去看各种语言编译器的源代码和相关文档时,至少不会被各种名词、术语搞晕,也能更好地建立具体实现跟原理之间的关联,能互相印证它们。
在**真实编译器解析篇**我会带你研究语言编译器的源代码跟踪它们的运行过程分析编译过程的每一步是如何实现的并对有特点的编译技术点加以分析和点评。这样我们在研究了Java、Java JIT、Python、JavaScript、Julia、Go、MySQL这7个编译器以后就相当于把编译原理印证了7遍。
在**现代语言设计篇**,我会带你分析和总结前面已经研究过的编译器,进一步提升你对相关编译技术的认知高度。学完这一模块以后,你对于如何设计编译器的前端、中端、后端、运行时,都会有比较全面的了解,知道如何在不同的技术路线之间做取舍。
好了,以上就是这一季课程的模块划分思路了。你会发现,这次的课程设计,除了以研究真实编译器为主要手段外,会更加致力于扩大你的知识版图、增加你的见识,达到“行万里路”的目的。
可以说,我在设计和组织这一季课程时,花了大量的时间准备。因此这一季课程的内容,不说是独一无二的,也差不多了。你在市面上很少能找到解析实际编译器的书籍和资料,这里面的很多内容,都是在我自己阅读源代码、跟踪源代码执行过程的基础上梳理出来的。
## 写在最后
近些年编译技术在全球范围内的进步速度很快。比如你在学习Graal编译器的时候你可以先去看看市面上有多少篇围绕它的高质量论文。所以呢作为老师我觉得我有责任引导你去看到、理解并抓住这些技术前沿。
我也有一个感觉在未来10年左右中国在编译技术领域也会逐步有拿得出手的作品出来甚至会有我们独特的创新之处就像我们当前在互联网、5G等领域中做到的一样。
虽然这个课程不可能涵盖编译技术领域所有的创新点,但我相信,你在其中投入的时间和精力是值得的。你通过我课程中教给你的方法,可以对你所使用的语言产生更加深入的认知,对编译器的内部结构和原理有清晰理解。最重要的是,对于如何采用编译技术来解决实际问题,你也会有能力做出正确的决策。
这样,这个课程就能起到抛砖引玉的作用,让我们能够成为大胆探索、勇于创新的群体的一份子。未来中国在编译技术的进步,就很可能有来自我们的贡献。我们一起加油!
**最后,我还想正式认识一下你。**你可以在留言区里做个自我介绍,和我聊聊,你目前学习编译原理的最大难点在哪?或者,你也可以聊聊你对编译原理都有哪些独特的思考和体验,欢迎在留言区和我交流讨论。
好了,让我们正式开始编译之旅吧!