在JavaScript运行时领域,长期以来,Node.js一直是无可争议的霸主,但近几年来,不断涌现出新的挑战者,先是基于Rust语言的Deno,接着是Zig语言的Bun。如今,又一位新进者LLRT(L ow L atency R untime)进入了舞台。
LLRT(低延迟运行时)是AWS(亚马逊)最近开源的一个JavaScript运行时,专为在AWS的边缘位置执行JavaScript代码而设计。它特别适用于较小的Serverless(无服务器)函数,如数据转换、实时处理、AWS服务集成、授权、验证等任务。这是一个实验性的、轻量级的JavaScript运行时,旨在满足对快速高效的无服务器应用程序日益增长的需求。
设计初衷
AWS为什么要耗费人力物力开发这样一个新的JavaScript运行时呢?
这是因为Node.js、Bun和Deno等JavaScript运行时是通用的,但它们并未专门针对Serverless环境进行设计。Serverless环境的特点是短暂的运行时实例,需要快速启动和高效执行。这些现有运行时依赖于即时编译器(Just-In-Time compiler,JIT)来进行动态代码编译和优化,尽管JIT编译提供了长期性能优势,但却带来了计算和内存开销。
相比之下,LLRT并不包含JIT编译器,这个战略决策带来了两个重要的优势:一是降低了系统复杂性和运行时大小,二是节省了CPU和内存资源,从而实现了更快的应用程序启动时间。
LLRT的启动速度远超其它JavaScript运行时,据称可以达到其它在AWS Lambda上运行的JavaScript运行时的10倍以上,并且成本降低了两倍。它是用Rust构建(没错,与Deno一样)的,利用QuickJS作为JavaScript引擎,保证了内存的有效利用和快速启动。
关于内存占用,AWS的博文《AWS 的新 Lambda 运行时 LLRT 怎么样》中提到:
LLRT 在大多数配置中得分都非常好, 通常是第三快的运行时,仅次于 C++ 和 Rust。在这种情况下,它甚至比 Golang 还要快。
当然,你要记住,C++和Rust 是非常成熟的生态系统,相对而言也是如此,而且这仍然是一个实验性的测试产品。
在基准测试中,我们还可以看到内存使用情况的差异,如果我们将 LLRT 与 Node 20 进行比较,我们会发现,我们的内存使用量为 24 MB,而 Node 20 为63 MB, 因此大约是同一 Lambda 函数所需内存的三分之一。
此外,LLRT与Lambda和AWS SDK(v3)集成,为开发者提供了更加便捷的开发体验。
以下是LLRT的GitHub首页列出的在128M的ARM虚机上的性能对比,这是LLRT的: 这是Node.js 20的:
不同观点
然而,对于LLRT的评价存在一些不同的声音,并非所有用户都买账。
在Reddit的一个帖子中,一位回复者解释道:
速度提升主要来自于一种限制:LLRT要求将代码和依赖项打包到一个单独的.js文件中,从而消除了Node模块解析阶段发生的所有文件系统查找。
此外,他们预先打包、预编译(为字节码)和预加载AWS SDK的部分内容,然后将其与需要在应用程序启动时加载AWS SDK库的通用运行时(如Node或Bun)进行比较。我敢打赌,这就是99%的性能收益来源。
Hacker News的一个帖子中,一位回复者评论道:
这似乎非常适合用Lambda作为粘合剂的场景,比如用于路由请求或授权策略决策。但对于一个做大量思考的应用程序,记住v8 jitless比QuickJS快大约3倍,有JIT时比QuickJS快大约30倍。我很想知道在各种工作负载中比较这两种方法的收支平衡点,以及与Bun的比较数据,后者比Node启动更快,但具有可比较的顶级JIT性能。
一位程序员兼软件架构师 Zemnytskyi Dmytro 在推特上写道:
这将进一步复杂化Lambda开发。不兼容的依赖和工具。更多的供应商锁定。我对此持怀疑态度。我宁愿用Rust完整地编写Lambda。
关于这些缺点,LLRT并不否认,在其GitHub说明文档里也写道:
与JIT(Just-In-Time)技术相比,LLRT在处理大数据、蒙特卡洛模拟或执行数十万甚至数百万次迭代的任务时性能明显不足。 LLRT的设计目的是作为现有组件的补充,而不是全面替代一切。 值得注意的是,由于其支持的API基于Node.js规范,切换回替代解决方案只需要进行最少量的代码调整。
总结
总的来说,LLRT是一个实验性的、轻量级的、专注于Serverless环境的JavaScript运行时。它使用Rust开发,利用 QuickJS 作为 JavaScript 引擎,确保高效的内存使用和快速启动。它的赛道相对单一,大部分情况下与其它运行时并不形成竞争关系。