概述
本页用来纪录各个开源 JS 引擎(SpiderMonkey、V8、JavaScriptCore)在算法、未来趋势上的比较。除非额外说明,内存相关的数据假设系统为 32 位。
类、函数、词汇比较表
为了让懂得其中一个 JS 引擎的程序员迅速了解另外一个引擎,以下整理这些引擎共同概念的实现比较(把鼠标放在类/函数上面可以找到定义该类/函数的文件):
常见
SpiderMonkey | V8 | 备注 |
---|---|---|
Value |
MaybeObject * |
代表一个 JS 值(数值、字符串、对象……)的类。SpiderMonkey 是用 nun-boxing,总是一个 double 的大小(因此在 32 位的系统下,传值需要两个 cycle)。V8 是用 tagged pointer,总是一个指针的大小,浮点数存在堆上。MaybeObject 还是很多非 JS 语言对象(代码块等等)的父类。 |
ObjectImpl |
JSObject |
JS 对象(obj instanceof Object )的实现类。SpiderMonkey 与 V8 皆是把部分属性存放在对象行内,部分用外链的一个数组储存的模式。SpiderMonkey 也将小数组的数据存在行内(但是不会分两半),而 V8 的 JS 数组对象(貌似)都是将数据存在外链的数组上。在 SpiderMonkey 中,只有在 new space 的 JS 对象的外链数组的空间由虚拟机分配,其他情况的外链数组的空间由 malloc 分配(因此内存会比较分裂?)。ObjectImpl 除了有连接到 Shape 的指针之外,还有一个链到 TypeObject 的指针:16 B(ObjectImpl )vs. 12 B(JSObject )。在 SpiderMonkey 中,数据用 Value 存,也就是说,对于一个有 k 个行内属性的 JS 对象来说,内存占用是:16 B + k * 8 B(ObjectImpl )vs. 12 B + k * 4 B(JSObject ),行外的情形 V8 则会再多用 8 B(FixedArray 的标头)。 |
Shape 、TypeObject、 Class |
Map |
隐藏类的实现类。在 SpiderMonkey 中,属性名存在 Shape 上(因此带有 V8 的 DescriptorArray 的作用),JS 对象的原型存在 TypeObject 上,特殊属性的处理方法存在 Class 上(Class 对象不是 JS 堆里的对象,大部分都共用:JSObject::class_ 、JSFunction::class_…… )。由于 V8 的 Map 有 SM 三个类的作用,因此占用内存也比较多:24 B(Shape )vs. 40 B(Map )。 |
内存布局与对象创建
运行时
SpiderMonkey | V8 | 备注 |
---|---|---|
Interpret |
(无) |
解释器的主回圈。V8 没有字节码与解释器。 |
堆与 GC
SpiderMonkey | V8 | 备注 |
---|---|---|
Nursery |
NewSpace |
调优
一个容易调优的 JS 引擎应该具备有以下条件(从重要的到不重要的):
- 能透过 profiler 够找到应用的热点函数(当前内联过的函数是怎么处理的?)。
- 调整各个常量方便。
- 能(透过内置函数扩展等等)得到以下影响性能的数据:
- 内联缓存(inline cache)中缓存的隐藏类个数。
- 函数运行状态(哪个阶段的 JIT?)与机械码。
- 一个 JS 函数已内联的函数。
- 一个 JS 函数调用运行时 C++ 函数的个数。
- 一段时间中 CPU 运行的指令个数与种类(SpiderMonkey 里有个
PerfMeasurement
,这个好用么?)。
- 能得到 bailout 的原因。
- 代码易读,容易调试。
参见
- ARE WE FAST YET — 每天更新的 JS 引擎性能比较(使用 kraken、sunspider、octane 三个 benchmark)。
- How to Choose a JavaScript Engine for iOS and Android Development — 不太具有技术分析的文章,不过有一个各类似 Phonegap 的产品用什么 JS 引擎的列表。
- 各JavaScript引擎的简介,及相关资料/博客收集帖 — R 大经典的整理帖,下面有一个比较高层级的各引擎比较表。
跟 “JS 引擎” 比较无关,但是中文的 JS 引擎相关的博文也比较少,这里搜集一下:
- 编译路慢慢 — SpiderMonkey 相关的比较多。
代码美观
这件事实在不应该是决定使用哪个 JS 引擎的因素,不过……
如果(不幸的)需要以 SpiderMonkey 作为基础调优则会碰到以下问题:
- C 与 C++ 代码混用。SpiderMonkey 原先是 C 写成的,后来大规模的以 C++ 改进,但是留下了很多残骸:宏、不是宏但是名称是大写的函数。(参见
JS::Value
的说明。) - 成员变量、方法命名缺乏规则
。ObjectImpl::slots
与ObjectImpl::shape_
同时存在(前面那个应该加底线)。这个或许是可以修复的 bug。 - 混入 JavaScriptCore、V8 代码。SpiderMonkey 的组译器是 JavaScriptCore 的,那些类的成员变量是
m_buffer
等等,又增加了成员变量命名的。另外还有WTF_*
宏,真是 WTF。再批。妈的,WTF_CPU_ARM_THUMB2
跟本不可能为真,这里有大量的死代码(所有代码完全没有用到JSC::
MacroAssembler
,用到的是js::jit::
)。另外,MacroAssembler
- 混乱的名称空间。
struct
、class
混用。- 文档命名缺少规则。C 时代的档名是
jsobj.h
,C++ 时代的档名是Value.h
。请自重。 - 过渡滥用宏。