Chez Scheme 的传说.docx

上传人:b****2 文档编号:2465135 上传时间:2022-10-29 格式:DOCX 页数:5 大小:47.63KB
下载 相关 举报
Chez Scheme 的传说.docx_第1页
第1页 / 共5页
Chez Scheme 的传说.docx_第2页
第2页 / 共5页
Chez Scheme 的传说.docx_第3页
第3页 / 共5页
Chez Scheme 的传说.docx_第4页
第4页 / 共5页
Chez Scheme 的传说.docx_第5页
第5页 / 共5页
亲,该文档总共5页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

Chez Scheme 的传说.docx

《Chez Scheme 的传说.docx》由会员分享,可在线阅读,更多相关《Chez Scheme 的传说.docx(5页珍藏版)》请在冰豆网上搜索。

Chez Scheme 的传说.docx

ChezScheme的传说

  

ChezScheme的传说

在上一篇博文的最后,我提到了Lisp编译器的问题。

由于早期的Lisp编译器生成的代码效率普遍低下,成为了Lisp失败的主要原因之一。

而现在的高性能Lisp编译器(比如ChezScheme),其实已经可以生成非常高效的代码,甚至可以匹敌C程序的速度。

如果你看得到我脑子里的东西,就会明白这完全不是吹牛,而是科学的结论。

我在这里介绍一下我写Scheme编译器的经历,也许你就会从根本上明白为什么我会对此这么自信。

这里的介绍其实不止针对函数式语言,而且针对所有语言的编译器。

编译器是一种神秘,有趣,又无聊的的程序。

说它神秘,是因为只有非常少的人知道如何写出优秀的编译器。

这些会写编译器的人,就像身怀绝技的武林高手一样神出鬼没。

说它有趣,是因为编译器的技术里面含有大量的“哲学问题”和深刻的理论(比如partialevaluation)。

但为什么又说它无聊呢?

因为你一旦掌握了编译器技术里面最精华的原理,就会发现其实说来说去就那么点东西。

编译器代码里面的“创造性含量”其实非常低。

里面有些固定的“模式”,几十年都不变。

这是因为编译器只是一种“工具”,而不是最终的“目的”。

它就像做菜的锅一样,只有屈指可数的那几种形状。

设计应用程序才是程序员的最终目的。

只有应用程序才能有无穷无尽的创造性。

这就像厨师用同样的锅,却能做出无穷变化的菜肴来。

然而,我并不是说普通程序员不应该学习写编译器。

相反,编译器的原理是非常重要的知识。

不理解编译原理的应用程序设计者,就像不理解菜锅组成原理的厨师。

先来说一说为什么早期的Lisp编译器生成的代码效率低下吧。

在函数式语言的早期,由于它比普通的语言多了一些表达力强大的构造(比如函数作为值传递),人们其实都不知道如何实现它的编译器。

很多Scheme的编译器其实只是把Scheme编译成C,然后再调用C语言的编译器。

Haskell的编译器GHC在早期也是这样的。

而且由于C编译器生成的汇编代码不完全符合Haskell的需求,GHC里面含有一个Perl脚本,专门用于调整这汇编代码的结构。

这个Perl脚本,由于它的工作方式毫无原则,被叫做evilmangler。

现在这个东西已经被去掉了,但从它曾经的存在你可以看出,其实函数式编译器的技术在早期是相当混沌的。

在我看来,早期Lisp编译器出现的主要问题,其实在于对编译的本质的理解,以及编译器与解释器的根本区别。

解释器之所以大部分时候比编译器慢,是因为解释器“问太多的问题”。

每当看到一个构造,解释器就会问:

“这是一个整数吗?

”“这是一个字符串吗?

”“这是一个函数吗?

”……然后根据问题的结果进行不同的处理。

这些问题,在编译器的理论里面叫做“解释开销”(interpretiveoverhead)。

编译的本质,其实就是在程序运行之前进行“静态分析”,试图一劳永逸的回答这些问题。

于是编译后的代码根本不问这种问题,它直接就知道那个位置肯定会出现什么构造,应该做什么事,于是它就直接去做了。

早期的Lisp编译器,以及现在的很多Scheme编译器出现的问题其实在于,它们并没有干净的消除这些问题,甚至根本没有消除这些问题。

当我最早学习Scheme语言的时候,我发现Scheme有太多的“实现”:

PLTScheme(现在叫Racket),MITScheme,Scheme48,Bigloo,Chicken,Gambit,Guile,...让人搞不清楚哪一个更好。

有些Scheme实现显得高级一些,但实际用起来总是感觉不放心,因为你心里总想着,这代码编译出来到底能不能跟C语言代码比?

这也是我后来开始使用CommonLisp的原因,因为CommonLisp似乎有挺多高效的编译器(CMUCL,Lispworks,Allegro等等)。

直到有一天,我发现了ChezScheme,它改变了我对Scheme编译器,以至于整个编译器概念的理解。

当时我只下载了ChezScheme的免费版本,叫做Petite。

Petite与正式版ChezScheme的区别是,它不输出二进制代码,所以你不能把编译后的代码拿去销售。

另外出于商业目的,Petite的出错信息非常的“简约”,以至于有时候你不得不用其它的Scheme实现,才能找到bug的位置。

但是一运行就见分晓,Petite被作为一个“解释器”直接运行Scheme代码,比其他的Scheme实现编译后的代码还要快很多倍。

ChezScheme导致了我命运的改变,我怎么也没有想到,自己最终会见到它的作者R.KentDybvig,并且成为他的学生。

我只能说也许一切都是天意吧。

第一次见到Kent的时候,他安静的对我说,你应该拥有自己的代码,将来有一天,你会发现它的价值。

也就是这个Kent,单枪匹马的创造了ChezScheme,世界上唯一的商业Scheme编译器,并且为此成立了自己的公司(CadenceResearchSystems)。

ChezScheme价格不菲,而且不明码实价,它的价格跟项目的大小和公司的规模成正比。

有些大公司花重金购买ChezScheme用于一些核心的项目。

其中有些公司为了保证这编译器的安全,又花了好几倍的价钱买下了它的源代码。

Kent的公司只有他一个人,不用操心管理,也不用操心销售。

所以他过的非常舒服,基本是一个不愁吃穿,不问世事的人。

Kent是我一生中见过的最神秘,最酷的人。

他几乎从来不表扬任何人,但也不贬低任何人。

从冷漠的言语之中,你仿佛感觉他并不是这个世界上的人。

任何人的喜怒与哀乐,傲慢与偏见,蔑视与奉承,全都不能引起他情绪的变化。

他的心里有许许多多的秘密,你需要一些技巧才能套出他的真言。

他很少发表论文,却把别人的论文全都看得很透。

没有人知道他的核心技术,他也从来不在乎别人是否了解他的水平。

最让人惊奇的是,没有人知道他叫什么名字!

他的全名叫R.KentDybvig,那么R.就应该是他的firstname。

然而,却从来没有人知道那个R.是哪一个名字的简写,所以大家只好叫他的middlename,Kent。

他的照片从来不放在网上,如果你真想知道他长得什么样,我在网上找到一个跟他长得非常相似的人的照片:

ChezScheme生成的“目标代码”效率之高,我还没有见到任何其它Scheme编译器可以与之匹敌。

而它的“编译速度”之快,没有任何语言的任何编译器可以相提并论(注意我去掉了“Scheme”这个限定词)。

ChezScheme可以在5秒钟之内完成从头到尾的自我编译。

想想编译GCC或者GHC需要多少时间,你就明白差距了。

另外值得一提的是,ChezScheme从头到尾都是Kent一个人的作品。

它的工作原理是从Scheme源程序一直编译到机器代码,而不依赖任何其他语言的编译器。

它甚至不依赖第三方的汇编器,所有三种体系构架(Intel,ARM,SPARC)的汇编器,都是Kent自己写的。

为什么这样做呢?

因为几乎没有其它人的编译器代码能够达到他的标准。

连Intel自己给自己的处理器写的汇编器,都不能满足他的要求。

如果你上了Kent的课,再来看看普通的编译器书籍(比如有名的DragonBook),或者LLVM的代码,你就会发现Kent的水平其实远在这些知名的大牛之上。

我为什么可以这么说呢?

因为如果你的水平不如这些人的话,你自己都会对这种判断产生怀疑。

而如果你超过了别人,他们的一言一行,他们的每一个错误,都像是处于你的显微镜底下,看得一清二楚。

这就是为什么有一天我拿起DragonBook,感觉它变得那么的幼稚。

而其实并不是它变幼稚了,而是我变成熟了。

实话实说吧,在编译器这个领域,我觉得Kent很有可能就是世界的No.1。

如果你不了解Scheme的编译器里面有什么东西,也许就会轻视它的难度。

Scheme是比C语言高级很多的语言,所以它的编译器需要做比C语言的编译器多很多的事情。

在Kent的编译器课程的前半段,我们其实本质上是在实现一个C语言的编译器,把一种基于“S表达式”的中间语言,编译为X64汇编代码。

在后半学期的课程中,我们才加入了各种Scheme的先进功能,比如函数作为值(需要进行closureconversion以及closure优化),尾递归优化(tail-calloptimization),等等。

另外,我还自己为它加入了一种非常漂亮的技术,叫做onlinepartialevaluation。

这种技术可以在一个pass就完成普通编译器需要好几个pass才能完成的优化。

在这些先进的优化技术之下,几乎所有的冗余代码都会被编译器消除掉。

这些优化的智能程度,在很多方面拥有人类思维没法达到的准确性和深度。

如果你的程序没有使用到Scheme特有的功能,那么生成的目标代码就会跟C语言编译后的代码没有什么两样。

比如,如果你的代码没有把函数作为值传递,或者你的函数里面没有“自由变量”,或者你的函数里虽然有自由变量,但是你却没法在函数外部改变它的值,那么生成的代码里面就不会含有“闭包”,也就不会产生多余的内存数据交换。

你有时甚至会得到比C程序编译之后更好的代码,因为我们的“后端”编译器其实比GCC,LLVM之类的C编译器先进。

Kent的课程编译器有很好的结构,它被叫做“nanopass编译器构架”。

它的每一个pass只做很小的一件事情,然后这些pass被串联起来,形成一个完整的编译器。

编译的过程,就是将输入程序经过一系列的变换之后,转化为机器代码。

你也许发现了,这在本质上跟LLVM的构架是一样的。

但是我可以告诉你,我们的课程编译器比LLVM干净利落许多,处于远远领先的地位。

每一节课,我们都学会一个pass。

每一个讲义,都非常精确的告诉你需要干什么。

每一次的作业,提交的时候都会经过上百个测试(当然Kent不可能把ChezScheme的测试都给我们),如果没有通过就会被拒绝接受。

这些测试也可以下载,用于自己的调试。

有趣的是,每一次作业我们都需要提交一些自己写的新测试,目的是用于“破坏”别人的编译器。

所以我们每次都会想出很刁钻的输入代码,让同学的日子不好过。

当然是开玩笑的,这种做法其实大大的提高了我们对编译器测试的理解和兴趣,以及同学之间的友谊。

这比起我曾经在Cornell选过(然后drop掉)的编译器课程,真是天壤之别。

在课程的最后,我们做出了一个完整的编译器,它可以把Scheme最关键的子集编译到X64汇编代码,然后通过GNU的汇编器转化成机器代码。

在最后的一节课,Kent对我们的学期做了一个令人难忘的总结。

他说:

“你们现在写出的这个编译器里面含有很多先进的技术。

也许过一段时间再回头看这段代码,你们才会发现它的价值。

如果你们觉得自己已经成为了编译器的专家,那我就告诉你们,你们提交的最快的编译器,编译速度比ChezScheme慢了700倍。

但是不要灰心,我告诉你们哪些地方可以改进……”

只有极少数的人见到过ChezScheme的源代码,我也没有看见过。

但是见到过它的人告诉我,ChezScheme里面其实只有很少几个pass,而不是像我们的课程编译器有50个左右的pass,这节省了很多用于“遍历”代码树所需要的时间。

ChezScheme只使用了一些非常简单的算法,没有使用论文里很炫很复杂的方法,这也是它速度快的原因之一。

比如它的寄存器分配,没有使用通常的“图着色”(graphcoloring)方法,而是使用非常简单的一种类似linearscan的算法,生成的代码效率却更高。

另外

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 基础医学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1