为什么本机ES6承诺比bluebird更慢且占用更多内存?


195

此基准测试中,与Bluebird Promise相比,套件完成ES6 Promise的时间要长4倍,并且使用的内存是3.6倍。

JavaScript库如何比用C编写的v8本机实现更快,更轻巧?Bluebird Promise与本机ES6 Promise具有完全相同的API(以及大量额外的实用程序方法)。

是本机实现编写得不好,还是我缺少其他方面?


请记住,现代JavaScript实现已进行了优化,甚至可能使用JIT 在本地运行

1
根据此基准,BlueBirdJS实际上比Native Promises慢。但是,PromiseMeSpeedJS实际上超过了两者。PromiseMeSpeedJS以此证明的众多事情之一是,对Promise的主要表现元凶是滥用new操作员,因为PromiseMeSpeedJS没有使用new
杰克·吉芬

1
@JackGiffin Chrome 67:PromiseMeSpeedJS慢46%,蓝鸟慢61%。
FINDarkside

Answers:


272

蓝鸟作者在这里。

V8承诺实现是用JavaScript而不是C 编写的。所有JavaScript(包括V8自己的JavaScript)都被编译为本机代码。此外,在可能的情况下(并且值得)对用户编写的JavaScript进行优化,然后再编译为本机代码。使用C编写承诺的实现并不会带来任何好处或根本没有任何好处,实际上,这只会使它变慢,因为您所做的只是操纵JavaScript对象和通信。

V8实现根本不如bluebird优化,它为实例分配了promises处理程序的数组。当每个promise还必须分配几个数组时,这会占用大量内存(基准创建了总共80k个promise,因此分配了160k个未使用的数组)。实际上,99.99%的用例从未多次承诺过,因此针对此常见用例进行优化可以获得巨大的内存使用改进。

即使V8实现了与bluebird相同的优化,它仍然会受到规范的阻碍。基准测试必须使用new Promise(蓝鸟中的反模式),因为没有其他方法可以在ES6中创建根本承诺。new Promise创建承诺的方法非常慢,首先执行程序函数分配一个闭包,其次将两个单独的闭包作为参数传递给它。每个promise分配了3个闭包,但是闭包已经比优化promise更昂贵。

Bluebird可以使用promisify它进行大量优化,并且是使用回调API的更为便捷的方法,并且可以将整个模块转换为一行(promisifyAll(require('redis'));)中的基于Promise的模块。


10
“仍然受到规范的阻碍”-不知道那是什么意思。您是说ES6遵循的是一种本质上较慢的规范,如果是这样的话,是否就意味着bluebird没有遵循相同的规范(如果是这样,它遵循的是另一种规范吗?而且,有什么理由导致ES6不能更好地创建根Promise new Promise或改善实例化以使其更便宜(例如不为每个实例创建3个闭包)?
安东尼

12
这听起来根本不好(对于JS)。我真的不想在内部实现时使用Promise库。如果这是真的,对每个人来说这都是不幸的情况。但是无论如何,我已经看不到Promise-hype的麻烦了,我已经编写了100,000个LoC JS应用程序,但是我仍然没有真正的需要,这对我来说是一个非常小的改进,主要是在错误处理方面,没有回调处理方面的改进(我的编码风格从未经历过“回调地狱”)。
Mörre

19
在ES6中,您不能Promise.resolve()用来创建“根本承诺”吗?
zetlen

10
@MörreNoseshine(续)多年后,ES6的作者走了过来,并说:“嘿,让我们指定JS引擎必须开箱即用地提供通用的Promises / A +兼容实用程序,因此人们总是可以使用基本的Promise工具”。这是一个很好的便利(不必为了执行快速操作Promise.resolve()或其他操作而导入库),但这是一个非常基本的实现,它的存在不应该让您使用更严重的与Promise相关的工具(如bluebird)!
卡伦

11
@MörreNoseshine100k LOC Javascript应用程序,可能从来没有任何异步功能。祝您好运,使用不带bluebird的mysql / redis库编写100k LoC JS游戏。
NiCk Newman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.