dahong
dahong copied to clipboard
深入JS引擎:如何让你的JS更快
Know Your Engines
How to Make Your JavaScript Fast
看到了一篇旧文,是Mozilla公司的JS引擎工程师Dave的PPT,很多JS开发者都知道JS的效率很低,虽然JS无类型的特性让我们代码写的很爽,比如
var a = '22222';
a = 9;
我们根本不用管a初始的类型是string
还是number
,直接赋上我们想要的值就行,但是js引擎处理这些无类型的代码却并不轻松,当然这在JIT之前,但是JIT也并不是万能的
要处理这样一段代码x = y + z;
JS引擎处理无类型的代码要进行一个boxed(装箱)和unbox(解箱)的行为
要处理这样一段代码
- 从内存中读取x = y + z的操作符
- 从内存中读取y、z并装箱
- 检查y、z的类型并确定操作
- y、z解箱拿出
- 执行操作
- x装箱
- x装箱完毕后将x写入内存
看到上面的JS引擎处理代码的步骤,一眼就能看出其实我们想要的就是第五步,我们写代码不就是想让引擎帮我们执行操作么,但是JS无类型的特性让引擎不得不进行一些boxed和unbox行为,有了JIT之后
-
从内存中读取x = y + z的操作符 -
从内存中读取y、z并装箱 - 检查y、z的类型并确定操作
- y、z解箱拿出
- 执行操作
- x装箱
-
x装箱完毕后将x写入内存
1,2两步CPU帮我们做了,第7步JIT将代码保存到寄存器里面去 通常情况下JIT帮我们快速的编译代码,并且正则有专门的JIT编译,所以正则通常比手动搜索要快
IC(inline caching)
我们要找到一个obj里面叫a的属性,我们知道JS对象是基于prototype的,所以会一层链一层链的往上找,这是非常慢的,但是一旦找到了,获得属性的值是非常快的 所以有对Object处理的一个mini JIT,称它为IC,IC会为对象首次搜索后建立cache
总结
- 千万不要使用
eval
和with
- 减少属性或方法在原型链中寻找的链数
- 变量被赋值后避免改变其数据类型(需要重编译)
- 能用正则尽量用正则进行操作
- 避免深层次的作用域嵌套
- substring也会很快, O(1)
- 避免造成稀疏数组
var arr = []; arr[10000] = 1;
- 迭代效率for>reduce>for in
- 函数调用效率f()> f.call()> f.apply(),并且arguments也是很慢的,但是可变
- 判断效率稠密>if>稀疏
- 避免使用throw/catch
较新版本的V8已经对throw/try-catch进行了优化, 具体可以看老司机 @justjavac 的博客, 戳戳戳
@xxmyjk 并且现在的JIT把boxed和unbox的完善了,直接执行操作了,感谢提供的博客,知识很好
关于总结的第三点,请问下,变量赋null之后再进行赋值,这样算不算改变了数据类型?
@MarvinWilliam 如果赋值的是对象,不算,null本身也是对象,且是原型链的最顶层
@Redshao 哦,明白了. 我看了下原始类型有String,Number,Boolean,Symbol.也就是说初始化的时候都应该用这几个类型的值.
那如果我们未对变量初始化,那就是undefined了,这个在赋值之后应该不算改变数据类型吧?
@MarvinWilliam string
number
boolean
undefined
null
object
和ES6的symbol
构成了JS的数据类型,undefined和null一般都表示空值,一个变量声明但未定义这时候他的数据类型就是undefined,又或者一个function没有return值,那么这个function的返回值也是undefined,很抱歉我上面的理解错了,一般undefined用来占位,等待被合适的值填充,而null代表此处不应该有值,希望没有误解到你
@Redshao 完全明白了.谢谢. : )
请问“判断效率稠密>if>稀疏”具体是指什么?
感谢 @xxmyjk 提到我的博客,但是我的博客并没有同步更新 V8 的知识。至于提到的 throw/catch 已经可以优化,是在最新的 V8 实现中,使用 TurboFan 引擎替换了 Crankshaft。
推荐我专栏中的两篇文章吧:
我之所以开始写 V8 专栏,就是因为网站关于 V8 的文章都过于古老,等到翻译成中文,就更古老了。但是 V8 的发展非常迅速,可能是因为 JS 发展太过迅速了吧。包括引入了解释器(之前 V8 没有解释器,只有通用编译器和优化编译器),更换了新的优化引擎,增加了对 wasm 的支持等。。。。
@FailLone 判断的效率是指你要判断对象的数据稠密度,稀疏的数据中显示的个数不代表所拥有的真实数据,所以查找的效率上就低,数据稠密用switch的效率比if要高,最差的就是稀疏的数据
@justjavac 是的,这是一篇比较老的PPT,近两年JavaScript的开发者以倍数增长,刺激引擎的更新乃至迭代不可避免,只是很多JSer包括我自己知其然却不知其所以然,所以看到一些好的文章拿出来当做一个读后笔记
感谢楼主!收获很大~