You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
15 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 期末答疑与总结 | 再次审视学习编译原理的作用
你好,我是宫文学。到这里,咱们这门课程的主要内容就要结束了。有的同学在学习课程的过程中呢,提出了他感兴趣的一些话题,而我自己也会有一些想讲的话题,这个我也会在后面,以加餐等方式再做一些补充。接下来,我还会给你出一套期末测试题,帮你检测自己在整个学习过程中的所学所得。
那么,在今天这一讲,我们就来做个期末答疑与总结。在这里,我挑选了同学们提出的几个有代表性的问题,给你解答一下,帮助你更好地了解和掌握本课程的知识内容。
## 问题1学习了编译原理对于我学习算法有什么帮助
> @无缘消受人间富贵:老师,想通过编译器学算法,单独学算法总是不知道有什么意义,每次都放弃,老师有什么建议吗?但是看到评论说用到的都是简单的数据结构,编译器用不到复杂的数据结构和算法?
针对这位同学提出的问题,我想谈一谈我对算法学习的感受。
前一阵,我在跟同事聊天时,提到了一个观点。我说,大部分的程序员,其实从来都没写过一个像样的算法。他们写的程序,都是把业务逻辑简单地翻译成代码。那么,如果一个公司写出来的软件全是这样的代码,就没有什么技术壁垒了,很容易被复制。
反之,一些优秀的软件,往往都是有几个核心的算法的。比如,对于项目管理软件,那么网络优化算法就很关键;对于字处理软件,那么字体渲染算法就很关键,当年方正的激光照排系统,就是以此为基础的;对于电子表格软件,公式功能和自动计算的算法很关键;对于视频会议系统,也必须掌握与音视频有关的核心算法。这样,因为有了算法的技术壁垒,很多软件就算是摆在你的面前,你也很难克隆它。
所以说,作为一名软件工程师,你就必须要有一定的算法素养,这样才能去挑战那些有难度的软件功能。而作为一个软件公司,其实要看看自己在算法上有多少积淀,这样才能构筑自己的技术壁垒。
**那么,编译原理对于提升你的算法素养,能带来什么帮助呢?**我给你梳理一下。
编译原理之所以硬核,也是因为它涉及了很多的算法。
在**编译器前端**主要涉及到的算法有3个
* **有限自动机构造算法**:这是在讲[词法分析](https://time.geekbang.org/column/article/243685)时提到的。这个算法可以根据正则文法自动生成有限自动机。它是正则表达式工具的基础也是像grep等强大的Linux命令能够对字符串进行模式识别的核心技术。
* **LL算法**:这是在讲[自顶向下的语法分析](https://time.geekbang.org/column/article/244906)时涉及的。根据上下文无关文法LL算法能够自动生成自顶向下的语法分析器中间还涉及对First和Follow集合的计算。
* **LR算法**:这是在讲[自底向上的语法分析](https://time.geekbang.org/column/article/244906)时涉及的。根据上下文无关文法LR算法能自动生成自底向上的语法分析器中间还涉及到有限自动机的构造算法。
总的来说,编译器前端的算法都是判断某个文本是否符合某个文法规则,它对于各种文本处理工作都很有效。有些同学也在留言区里分享,他在做全文检索系统时就会用到上述算法,使得搜索引擎能更容易地理解用户的搜索请求。
在**编译器后端**主要涉及到的算法也有3个
* **指令选择算法;**
* **寄存器分配算法;**
* **指令重排序(指令调度)算法。**
这三个算法也有共同点它们都是寻找较优解或最优解而且它们都是NP CompleteNP完全的。简单地说就是这类问题能够很容易验证一个解对不对多项式时间内但求解过程的效率却可能很低。对这类问题会采用各种方法求解。在讲解[指令选择算法](https://time.geekbang.org/column/article/274909)时,我介绍了**贪婪策略和动态规划**这两种不同的求解思路;而寄存器选择算法的图染色算法,则采用了一种**启发式算法**这些都是求解NP完全问题的具体实践。
在日常工作中,我们其实也会有很多需要求较优解或最优解的需求。比如,在文本编辑软件中,需要把一个段落的文字分成多行。而如何分行,就需要用到一个这样的算法。再比如,当做一个报表软件,并且需要分页打印的时候,如何分页也是同类型的问题。
其他类似的需求还有很多。如果你没有求较优解或最优解的算法思路,对这样的问题就会束手无策。
而在**编译器的中端**部分涉及的算法数量就更多了但是由于这些算法都是对IR的各种分析和变换所以IR采用不同的数据结构的时候算法的实现也会不同。它不像前端和后端算法那样在不同的编译器里都具有很高的一致性。
不过IR基本上就是三种数据结构树结构、图结构和基于CFG的指令列表。所以这些算法会训练你处理树和图的能力比如你可以在树和图中发现一些模式以此对树和图进行变换等等。这在你日常的很多编程工作中也是非常重要的因为这两种数据结构是编程中最常使用的数据结构。
那么,总结起来,认真学好编译原理,一定会给你的算法素养带来不小的提升。
## 问题2现代编程语言这么多我们真的需要一门新语言吗
> @蓝士钦前不久看到所谓的国产编程语言“木兰”被扒皮后发现是Python套层壳真的是很气愤。想要掌握编译原理设计一门自己的语言但同时又有点迷茫现代编程语言这么多真的需要再多一门新语言吗从人机交互的角度来看任何语言都是语法糖。
关于是否需要一门新语言的话题,我也想跟你聊聊我自己的看法,主要有三个方面。当然,你也可以在此过程中思考一下,看看有没有什么跟我不同的见解,欢迎与我交流讨论。
**第一,编程语言其实比我们日常看到的要多,很多的细分领域都需要自己的语言。**
我们平常了解的都是一些广泛流行的通用编程语言而进入到每个细分领域其实都需要各自领域的语言。比如SaaS的鼻祖Salesforce就设计了自己的Apex语言用于开发商业应用。华为的实验室在研发方舟编译器之前也曾经研发了一门语言Cm服务于DSP芯片的研发。
**第二,中国技术生态的健康发展,都需要有自己的语言。**
每当出现一个新的技术生态的时候总是有一门语言会成为这个技术生态的“脚本”服务于这个技术生态。比如C语言就是Unix系统的脚本语言JavaScript、Java、PHP等等本质上都是Web的脚本语言而Objective-C和Swift显然是苹果设备的脚本语言Android虽然一开始用了Java但最近也在转成Kotlin这样Google更容易掌控。
那么,从这个角度看,当中国逐步发展起自己的技术生态的时候,也一定会孕育出自己的语言。以移动计算生态而言,我们有全球最大的移动互联网用户群和最丰富的应用,手机的制造量也是全球最高的。而位于应用和硬件之间的应用开发平台,我们却没有话语权,这会使中国的移动互联网技术生态受到很大的掣肘。
我在[第40讲](https://time.geekbang.org/column/article/286097)也已经分析过了Android系统经过了很多年的演化但技术上仍然有明显的短板使得Android平台的使用体验始终赶不上苹果系统。为了弥补这些短板各个互联网公司都付出了很大的成本比如一些头部应用的核心功能采用了C/C++开发。
并且Android系统的编译器在支持新的硬件上也颇为保守和封闭让中国厂商难以参与。这也是华为之所以要做方舟编译器的另一个原因。因为华为现在自研的芯片越来越多要想充分发挥这些芯片的能力就必须要对编译器有更大的话语权。方舟编译器的问世也证明了我们其实是有技术能力的可以比国外的厂商做得更好。既然如此我们为什么要受别人的制约华为方舟编译器团队其实也很渴望在方舟编译器之后推出自己的语言。至于华为内部是否已经立项这就不太清楚了但我觉得这是顺理成章的事情。
另外除了在移动端的开发上会受到很多掣肘在云端其实也一样。比如说Java是被大量后端开发的工程师们所掌握的语言但现在Java是被Oracle掌控的。你现在使用Java的时候可能已经多多少少感受到了一种不愉快。先不说Java8之后的收费政策就说我们渴望的特性如协程、泛型中支持基础数据类型等一直没有被满足就会感觉不爽。
我在讲到[协程](https://time.geekbang.org/column/article/280269)的时候就指出Java语言目前支持协程其实是很别扭的一种状态它都是一些第三方的实现并没有官方的支持。而如果Java的技术生态是由我们主导可能就不是这样了。因为我国互联网的并发用户数如此之多我们对更好的并发特性其实是更关切的。到目前为止像微信团队解决高并发的问题是用C++加上自己开发的协程库才实现的。而对于很多没有如此强大的技术能力的公司来说,就只能凑合了。
**第三,实现一款优秀的软件,一定会用到编译技术。**
每一款软件当发展到极致的时候都会变得像一个开发平台。这也是《黑客与画家》的作者保罗·格雷厄姆Paul Graham表达的思维。他原来的意思是每个软件写到最后都会包含一个Lisp的变种。实际上他所要表达的意思就跟我说的一样。
我前一段时间在北京跟某公司的老总探讨一个优秀的行业应用软件。这个软件在上世纪90年代就被开发出来了也被我国广泛采用。一方面它是一个应用软件另一方面它本身也是一个开发平台。所以它可以经过定制满足不同行业的需求。
但是,我们国内的软件行业的情况是,在去客户那里实施的时候,几乎总是要修改源代码,否则就不能满足用户的个性化需求。
很多软件公司想去克隆一下我刚才说的那套软件,结果都放弃了。除了有对领域模型理解的困难以外,缺少把一个应用软件做成软件开发平台的能力,是其中很大的一个障碍。
实际上,目前在很多领域都是这样。国外的软件就是摆在那里,但中国的工程师就是做不出自己的来。而对于编译技术的掌握和运用,就是能够提升国内软件水平的重要途径。
我在开头跟同事交流的时候,也提出了软件工程师技术水平修养提升的几个境界。其中一个境界,就是要能够利用编译技术,做出在更大范围内具有通用性的软件。如果你能达到这个境界,那么也一定有更大的发展空间。
## 问题3如何判断某门语言是否适合利用LLVM作为后端
> @ヾ(◍°∇°◍)ノ゙老师很多语言都声称使用LLVM提升性能但是在Lua领域好像一直是LuaJIT无法超越
这个问题涉及到了如何利用后端工具的问题,比较有代表性。
LLVM是一个通用的后端工具。在它诞生之初首先是用于支持C/C++语言的。所以一门语言在运行机制上越接近C/C++语言用LLVM来做后端就越合适。
比如Rust用LLVM就很成功因为Rust语言跟C/C++一样,它们的目标都是编写系统级的程序,支持各种丰富的基础数据类型,并且也都不需要有垃圾收集机制。
那么如果换成Python呢你应该记得Python不会对基础数据类型进行细粒度的控制不需要把整型区分成8位、16位、32位和64位的它的整型计算可以支持任意长度。这种语义就跟C/C++的相差比较远所以采用LLVM的收益相对就会小一些。
而对于JavaScript语言来说浏览器的应用场景要求了编译速度要尽量地快但在这方面LLVM并没有优势。像我们讲过的隐藏类Shapes和内联缓存Inline Caching这样的对JavaScript很重要的机制LLVM也帮不上忙。所以如果在项目时间比较紧张的情况下你可以暂时拿LLVM顶一顶Safari浏览器中的JavaScript引擎之前就这么干过。但是要想达到最好的效果你还是编写自己的后端更好一些。
那对于Lua语言其实你也可以用这个思路来分析一下是采用LLVM还是自己写后端会更好一些。不过由于Lua语言比较简单所以实现后端的工作量应该也相对较小。
## 小结
这一讲,我主要回答了几个比较宏观的问题,它们都涉及到了编译原理这门课的作用。
第一个问题,我是从提升算法素养的角度来展开介绍的。编译原理知识里面涉及了大量的算法,我把它总结成了三大类,每类都有自己的特点,希望能对你宏观把握它们有所帮助。
第二个问题,其实是这门课程的一条暗线。我并没有在课程里去情绪化地鼓吹,一定要有自己的编译器、自己的语言。我的方式其实是想做一点具体的事情,所以在第二个模块中,我带着你一起探究了现有语言的编译器都是怎么实现的,破除你对编译器的神秘感、距离感;在第三个模块,我们又一起探讨了一下实现一门语言中的那些关键技术点,比如垃圾收集、并行等,它们都是如何实现的。
在课程最后呢,我又带你了解了一下具有中国血统的方舟编译器。我想说的是,其实我们不但能做出编译器和语言来,而且可能会做得更好。虽然我们对方舟编译器的分析还没有做完,但通过分析它的技术思路,你应该或多或少地感受到了它的优秀。所以,针对“我们真的需要一门新语言吗”这个问题,我的回答是确定的。并且,即使你不去参与实现一门通用的语言,在实现自己领域的语言,以及把自己的软件做得更具通用性这点上,编译原理仍然能发挥巨大的作用,对你的职业生涯也会有切实的帮助。
好,请你继续给我留言吧,我们一起交流讨论。同时我也希望你能多多地分享,做一个知识的传播者。感谢你的阅读,我们下一讲再见。