编程语言的发展趋势及未来方向(4):动态语言

原文来自编程语言的发展趋势及未来方向(4):动态语言

2010-05-23 22:16 by 老赵, 10587 visits

这是Anders Hejlsberg(不用介绍这是谁了吧)在比利时TechDays 2010所做的开场演讲。由于最近我在博客上关于语言的讨论比较多,出于应景,也打算将Anders的演讲完整地听写出来。在上一部分中,Anders谈及了声明式编程的另一个重要组成部分:函数式编程,并使用.NET平台上的函数式编程语言F#进行了演示。在这一部分中,Anders讨论了动态语言及JavaScript的相关内容,“动态性”也是Anders眼中编程语言的发展趋势之一。

如果没有特别说明,所有的文字都直接翻译自Anders的演讲,并使用我自己的口语习惯表达出来,对于Anders的口误及反复等情况,必要时在译文中自然也会进行忽略。为了方便理解,我也会将视频中关键部分进行截图,而某些代码演示则会直接作为文章内容发表。

(听写开始,接上篇

我下面继续要讲的是动态语言,这也是我之前提到的三种趋势之一。

我还是尝试着去找到动态语言的定义,但是你也知道……一般地说,动态语言是一些不对编译时和运行时进行严格区分的语言。这不像一些静态编程语言,比如C#,你先进行编译,然后会得到一些编译期错误,稍后再执行,而对于动态语言来说这两个阶段便混合在一起了。我们都熟悉一些动态语言,比如JavaScript,Python,Ruby,LISP等等。

动态语言有一些优势,而静态语言也有着另一些优势,这也是两个阵营争论多年的内容。老实讲,我认为结果不是两者中的任意一个,它们都有各自十分重要的优点,而长期来看,我认为结果应该是两者的杂交产物,我认为在语言发展中也可以看到这样的趋势,这两部分内容正在合并。

许多人认定动态语言执行起来很慢,也没有类型安全等等。我想在这里观察并比较一下,究竟是什么原因会让静态语言和动态语言在这方面有不同的性质。这里有一段有趣的代码,它的语法在JavaScript和C#里都是正确的,这样我们便能比较两种语言是如何处理这段代码的。

首先我们把它看作是一段C#代码,它只是用for循环把一堆整数相加,你肯定不会这么做,这只是一个示例。在C#中,当我们使用var关键字时,它表示“请为我推断这里的类型”,所以在这里a和i的类型都是int。

这断代码在执行的时候,这两个值都是32位整数,而for循环只是简单的使用ADD指令即可,执行起来自然效率很高。

但如果从JavaScript或是动态语言的角度来看……或者说对于动态类型的语言来说,var只代表了“一个值”,它可以是任意类型,我们不知道它究竟是什么。所以当我们使用var a或var i时,我们只是定义了两个值,其中包含了一个“类型”标记,表明在运行时它是个什么类型。在这里它是一个int,因此包含了存储int值的空间。但有些时候,例如要存储一个double值,那么可能便需要更多的空间,还可能是一个字符串,于是便包含一个引用。

所以两者的区别之一便是,表示同样的值在动态语言中会有一些额外的开销,代价较高。而在如今的CPU中,“空间”便等于“速度”,所以较大的值便需要较长时间进行处理,这里便损失了一部分效率。

在JavaScript中,我们如果要处理a加i,那么便不仅仅是一个ADD指令。首先它必须查看两个变量中的类型标记,然后根据类型选择合适的相加操作。于是再去加载两个值,然后再进行加法操作。这里还需要进行越界检查,因为在JavaScript中一旦越界了便要使用double,等等。很明显在这里也有许多开销。一般来说,动态语言是使用解释器来执行的,因此还有一些解释器需要的二进制码。你把这些开销全部加起来以后,便会发现执行代码时需要10倍到100倍的开销。

不过由于近几年来出现的一些动态虚拟机或引擎,目前这些情况改善了许多。比方说,这是传统的情况(上图左),如在IE 6或IE 7里使用的非常缓慢的解释器。目前的情况是,大部分的JavaScript引擎使用了JIT编译器(上图中),于是便省下了解释器的开销,这样性能损失便会减小至3到10倍。而在过去的两三年间,JIT编译器也变得越来越高效,浏览器中新一代的适应性JIT编译器(上图右),如TraceMonkeyV8,还有如今微软在IE 9中使用的Chakra引擎。这种适应性的JIT编译器使用了一部分有趣的技术,如Inline Caching、Type Specialization、Hidden Classes、Tracing等等,它们可以将开销降低至2到3倍的范围内,这种效率的提升可谓十分神奇。

在我看来,JavaScript引擎可能已经接近了性能优化的极限,我们在效率上可以提升的空间已经不多。不过我同样认为,如今JavaScript语言的性能已经足够快了,完全有能力统治Web客户端。

有人认为,JavaScript从来不是一种适合进行大规模编程的语言。如今也有一些有趣的工具,如Google Web Tookit,在微软Nikhil Kothari也创建了Script#,让你可以编写C#或Java代码,然后将代码编译成JavaScript,这就像是将JavaScript当作是一种中间语言。Google Wave的所有代码都用GWT写成,它的团队坚持认为用JavaScript不可能完成这样的工作,因为复杂度实在太高了。如今在这方面还有一些有趣的开发成果,我不清楚什么时候会结束。不过我认为,这些都不算是大规模的JavaScript开发方案,而编写C#或Java代码再生成JavaScript的方式也不能算是完全正确的做法。我们可以关注这方面的走向。

在.NET 4.0的运行时进行动态编程时,我们引入了一个新功能:动态语言运行时。可以这样理解,CLR的目的是为静态类型的编程语言提供一个统一的框架或编程模型,而DLR便是在.NET平台上为动态语言提供了统一的编程模型。CLR本身已经有一些支持动态编程能力,如反射,Emit等等。不过在.NET上实现动态语言的时候,总会一遍又一遍地去实现某些功能,还有如动态语言如何与静态语言进行交互,这些都由DLR来提供。DLR的特性包含了,如表达式树、动态分发、Call Site缓存,这可以提高动态代码的执行效率。

在.NET 4.0中我们使用了DLR,不仅仅是IronPython和IronRuby,还有C# 4和VB.NET 10,它们使用DLR实现动态分发功能。因此我们共享了语言的动态能力实现方式,于是这些语言之间可以轻松地进行交互。同样我们可以与其他多样性的技术进行交互,例如使用JavaScript操作Silverlight的DOM,或是与Ruby、Python代码沟通,甚至用来控制Office等自动化服务。

(未完待续)

 

Leave a Reply