并发模型是用来实现不同应用场景中并发任务的编程模型,通过合理地使用多线程,可以缩减应用程序的开发和维护成本,同时还能更好地提升应用程序在多核设备中的运行性能。随着IoT时代下应用场景的不断复杂、多核设备的不断增多,并发模型显得举足轻重,本期我们将为大家带来方舟编译器对传统Actor并发模型的轻量级优化。
一、什么是并发模型?
在操作系统中,并发是任务在不影响最终执行结果的情况下无序或者按部分顺序执行的能力,如图1所示,在一个时间段中可能有多个任务都处于已启动运行到运行完毕之间,同时,并发单元也可以在多核设备下并行执行,可以极大地提高多核设备的运行性能。
图1 并发单元的并行执行
在日常开发中,由于并发任务多种多样,任务拆分方式可能不同,线程间的通信方式也可能不同,所以不同场景下的多线程并发任务可以通过不同的并发编程模型来实现。常见的并发模型又分为内存共享的并发模型和消息通信的并发模型。其中,基于内存共享的并发模型存在数据竞争,往往需要锁或者其它同步机制来保护共享的可变数据。而基于消息通信的并发模型,不需要开发者去面对锁带来的一系列复杂偶发的问题,同时并发度也相对较高。
作为基于消息通信并发模型的典型代表,Actor并发模型深受广大开发者的追捧。下面,我们将为大家带来Actor并发模型的解析。
二、Actor并发模型
Actor是一种历史悠久的分布式并发模型,基于事件(消息)机制传递数据,能有效地避免线程中资源争夺、死锁等情况。本节我们将为大家介绍Actor并发模型的交互原理以及在JS中的应用。
交互原理
如图2所示,在典型的Actor交互流程中,各个Actor并发地处理主线程任务,每个Actor内部都有一个消息队列及单线程执行模块,消息队列负责接收主线程及其他Actor的请求,单线程执行模块则负责串行地处理请求、向其他Actor发送请求以及创建新的Actor。由于Actor采用的是异步方式,各个Actor之间相互隔离没有数据竞争,因此Actor可以高并发运行。
图2 Actor交互流程
2. 应用场景
Actor并发模型被广泛应用于Erlang、Haskell、Akka(Java)、JS等编程语言,下面我们将介绍Actor并发模型在JS中的应用。
(1) Worker介绍
众所周知,JS从诞生起就是单线程,为解决因单线程造成的I/O阻塞问题,JS通过异步回调的方式并结合事件机制,充分提高了单线程下对于轻量级事件的响应速度。但是如果遇到某些比较复杂的任务,比如CPU密集型运算任务、I/O密集型任务、同步任务等,仍采用单线程执行就显得有点力不从心,无法解决复杂任务的线程阻塞问题。所以,JS需要引入多线程任务支持。
Worker是较为典型的JS多线程解决方案,基于Actor并发模型实现,为JS创造多线程并发环境。如图3所示,在Worker的交互流程中,JS主线程可以创建多个Worker子线程,各个Worker线程间相互隔离,并通过序列化传递对象,等到 Worker 线程完成计算任务,再把结果返回给主线程。图3 Worker交互流程
(2) Worker缺陷
Worker实现了复杂JS应用的多线程并发执行,在一定程度上提升了复杂JS应用的运行效率。但是,由于每个Worker线程都拥有独立的虚拟机实例,且各个实例之间不共享任何数据,使得JS Worker启动速度较慢、内存占用较高。三、Lite Actor
为了让JS应用能充分利用多核设备的计算能力更好地提升性能,方舟编译器提出了Lite Actor概念,并针对Worker的缺陷进行了优化。
1. 原理介绍
方舟编译器JS运行时在传统Actor并发模型的基础上,通过共享Actor实例中的不可变对象,以减少每个Actor实例承载的数据,提升了每个Actor的运行性能,从而实现Actor并发模型的轻量级优化。图4 Lite Actor
2. Worker优化
方舟编译器JS运行时基于Lite Actor概念对Worker进行了优化,具体优化如下:
● 方舟编译器JS运行时通过内置的字节码文件管理器,已完成了对多个Worker间缓存的字节码文件的共享,大大减少了字节码的预加载处理时间以及内存占用。
● 方舟编译器JS运行时结合TS类型分析系统与对象分离技术,已经实现了Worker中部分TS类型信息与准静态类型信息的识别与共享,在一定程度上减少Worker线程的内存使用以及Worker线程间的信息传递耗时。同时,由于已共享的部分无需进行序列化拷贝传递,所以Worker的序列化时长也得到了相应的优化。目前,剩余未共享部分数据仍存在很大的提升空间,这部分工作仍会持续展开,让我们共同期待。
● 针对外部内存的数据,例如字节码中的字符串,由于不受JS内存管理,未来也可以实现共享。图5 Worker优化
3. 性能对比
使用方舟编译器的Lite Actor优化后,Worker的性能得到了显著的增长,如图6所示,不难看出,相较于传统Actor并发模型,Worker的启动时长及内存占用均优化了50%以上。图6 性能及内存对比
以上就是本期全部内容,方舟编译器JS运行时通过对传统Actor并发模型的轻量级优化,极大地提高了Worker的启动性能。当前Lite Actor仍有很大的探索空间,期待广大开发者加入我们,共同见证万物互联的无限可能。