1. 依赖清理优化算法详解-基于JavaScript引擎的SMI进一步压榨性能

maxMarkerBits = 30表示仅支持effect嵌套31层,注释中描述该值是因为想让JavaScript影响使用SMI。那么什么是SMI呢?

由于ECMAScript标准约定number数字需要转换为64位双精度浮点数处理,但所有数字都用64位存储和处理是十分低效的,所以V8内部采用其它内存表示方式(如32位)然后向外提供64位表现的特性即可。其中数组合法索引范围是[0, 2^32 - 2],V8引擎就是采用32位的方式来存储这些合法的下标数字。另外,所有在[0, 2^32 - 2]内的数字都会优先使用32位二进制补码的方式存储。

针对32位有符号位范围内的整型数字V8为其定义了一种特殊的表示法SMI(非SMI的数字则被定义为HeapNumber),而V8引擎针对SMI启用特殊的优化:当使用SMI内的数字时,引擎不需要为其分配专门的内存实体,并会启用快速整型操作

对于非SMI的数字

let o = {
  x: 42, // SMI
  y: 4.2 // HeapNumber
}

内存结构为HeapNumber{ value: 4.2, address: 1 }JSObject{ x: 42, y: 1 },由于x值类型为SMI因此直接存储在对象上,而y为HeapNumber则需要分配一个独立的内存空间存放,并通过指针让对象的y属性指向HeapNumber实例的内存空间。

然而在修改值时,然后x为SMI所以可以原地修改内存中的值,而HeapNumber为不可变,因此必须再分配一个新的内存空间存放新值,并修改o.y中的内存地址。那么在没有启用Mutable HeapNumber时,如下代码将产生1.11.21.33个临时实例。

let o = { x: 1.1 }
for (let i = 0; i < 4; ++i) {
  o.x += 1;
}

SMI是带符号位的,那么实际存储数字是31位,因此设置maxMarkerBits = 30且通过if (effectTrackDepth <= maxMarkerBits)判断层级,即当effect嵌套到31层时不再使用无用依赖清理优化算法。而优化算法中采用的是二进制位对上一轮已收集和本轮收集的依赖进行比较,从而清理无用依赖。若nw值所占位数超过31位则内部会采用HeapNumber存储,那么在位运算上性能将有所下降。

其实我们还看到若effectTrackDepth等于31时还会执行trackOpBit = 1 << ++effectTrackDepth,这会导致trackOpBitSMI的存储方式转换为HeapNumber,那是不是可以加个判断修改成下面这样呢!

const maxMarkerBit = 1 << 30

if (trackOpBit & maxMarkerBit !== 1) {
  trackOpBit = 1 << ++effectTrackDepth
}
Copyright © fsjohnhuang 2022 all right reserved,powered by GitbookFile Modify: 2022-04-26 09:47:36

results matching ""

    No results matching ""