开发者体验和运行时效率
在没有太多接触dart的时候,我想当然以「既生瑜何生亮」为由不喜欢这门语言。因为尝试flutter而「不得不」使用dart后,我开始慢慢欣赏这门语言。dart和我之前使用过的很多语言都不太一样:有些语言顾及到开发时效率,如python/javascript/elixir,却付出了运行时效率作为代价;有些语言顾及到了运行时效率,却让开发效率受到损伤,如c/golang/rust。在我们平时的观念里,开发效率和运行时效率,就像鱼和熊掌,二者不可兼得。而dart是少见地想把两者都占全了。
当我们讲一门语言的性能时,我们往往谈及的是:
1.更小的代码体积
2.更快的启动时间
3.更高的吞吐量
4.更低的延迟
这些要素在开发时和运行时的要求是不一样的,而我们往往只考虑了运行时的需求而忽视了开发时的需求:
更小的代码体积:对于开发时而言,代码体积并不重要。
更快的启动时间:对于开发时而言,启动时间很重要,尤其是重新加载所花费的启动时间,以及恢复到上一次运行状态的时间。
更高的吞吐量:对于开发时而言,吞吐量也不重要。
更低的延迟:对于开发时而言,程序在修改之后,到改动得以体现之间的延时更为重要。
dart为两种截然不同的需求提供了截然不同的解决方案:
开发时:JIT编译器,如dartVM,dartdevc。
运行时:AOT编译器,如dart2native,dart2js。
JIT编译器的目的很单纯,把你刚刚撰写的代码尽快编译成目标平台的代码。因为要快,所以它会牺牲很多解析,分析和优化的步骤,对于开发者来说,JIT可以带来更低的开发延迟,而对于用户来说,JIT没有太多好处,效率不高,冷启动速度还慢,对用户不太友好。而AOT编译器则要把编译原理课程里的所有步骤都走一遍,甚至有些步骤要来回走很多遍(比如rust)。这对用户非常友好,大大提升了冷启动的速度,运行效率更高,然而AOT对开发者很不友好——看看下图额外的启动时间和运行时间:
如此一来,从产品的角度来看,开发者和开发者的用户的利益都兼顾到了,用户体验非常美妙,可是dart团队需要做的工作就多了很多。一个新的语言特性需要被添加到不同的编译器之中,需要考虑不同的场景下的优化方法。随着dart对原生平台的支持力度越来越大,支持的平台越来越多,这样的工作会越来越繁琐。我想,这也是大部分语言只照顾一头的原因。
语言特性
大部分时候,flutter中使用到的dart都是在画UI,而这部分的语法,有编程基础的人看着例子十分钟内都能上手。但既然因为尝试flutter而使用dart,那么dart的语言特性还是需要大致了解一下的。
dart面向对象的特性没有太多可说的,如果你有java/C#背景,里面的接口,泛型和类型系统都不难理解,大家基本大同小异。如果你来自前端世界,有kotlin/swift背景,或者出道于后端,是rust/scala/haskell的拥趸,那你大概率会对dart的类型系统有些失望,因为dart在语言层面没有完整支持ADT(algebradatatypes),只有producttype(class),却没有sumtype(taggedunion),使得你不太容易优雅地表述复杂的,带有「或」关系的数据结构。而模式匹配,因为它往往和sumtype是孪生兄弟,在dart里也没有支持。
dart的并发模型非常讨喜,至少,对我的胃口。它受erlang的影响不小,提供了类似于erlangprocess的isolate。在dart里,每个isolate都有自己的栈和堆,isolate之间sharenothing,只能通过发送和接收消息来传递数据。每个isolate自己单独做GC,这和erlang的GC也非常类似,因而内存的分配和回收无需加锁,很大程度上避免了Java的STW问题。dart里isolate之间的通讯见下图,熟悉erlangVM的小伙伴估计都会会心一笑:
dart每个isolate内部,运行一个eventloop,处理这个isolate上的事件。和javascript一样,dart里的每个异步事件都是一个future对象,语言本身提供async/await作为语法糖。在web环境下,isolate会被webworker执行;而在原生环境下,isolate可能会被某个线程调用,但要注意的是:同一个线程不能在同一时间处理两个不同的isolate。
dart还有一个有意思的特性是snapshot。顾名思义,snapshot允许dart保存并序列化当前VM的上下文,下次可以从snapshot中恢复运行。snapshot目前主要用作加快dart应用的启动,但也许未来可以用于很多有意思的场合:比如bug的复现——复杂的bug可以通过保存snapshot给程序员轻松复现。
运行时
在开发环境下,dart运行时包括公共前端(CommonFront-End,CFE)和VM。dartCFE提供了代码编译服务(