Open 981377660LMT opened 9 months ago
js里number的范围对执行效率影响也很大。
同一份代码,令 INF = 2**31
需要 8500ms,
令 INF = 2**31 - 1
需要 3500ms
正好是int32
// !1.数字大小的影响
// INF = 2**31 需要 8500ms,
// INF = 2**31 - 1 需要 3500ms
// (参考v8优化方案)
// !2.数组与对象的性能差异(对象快于数组)
// type E = [min: number, max: number] // 6500ms, 138MB
// type E = { min: number; max: number } // 3500ms, 108MB
// !3.number与对象的性能差异
// type E = { min: number; max: number } // 3500ms, 108MB
// type E = number // 800ms, 82MB
// !4.将普通数组number[]换成Float64Array
// number[] // 800ms, 82MB
// Float64Array // 550ms, 70MB
// !类型数组的数字不受范围影响(例如INF的取值影响效率)
得出优化结论:
https://zhuanlan.zhihu.com/p/84453627 同样是占32个坑,凭啥你float就比int的范围更大?
https://blog.csdn.net/opk8848/article/details/103240945 int32 float 在c++中同样都是32位,但是存储结构完全不同。 int32 先不说 float是这样表达的。关键词IEEE 754 float在内存中 分三部分 s+e+m s 1位 0代表正数 1 负数 e 8位 代表 移多少位 m 23位 是小数的具体数字 (24位最高位总为1,所以丢弃不存) 对应float64: s 1 e 11 m 52
https://lyn-ho.github.io/posts/4d26265b/ https://zhuanlan.zhihu.com/p/591353951
https://panzhongxian.cn/cn/2021/04/02-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code/
https://zhuanlan.zhihu.com/p/29638866?from_voters_page=true JavaScript 在 V8 中的元素种类及性能优化
const array = [1, 2, 3];
// 元素类型: PACKED_SMI_ELEMENTS
array.push(4.56);
// 元素类型: PACKED_DOUBLE_ELEMENTS
array.push('x');
// 元素类型: PACKED_ELEMENTS
为了获得最佳性能,请避免不必要的不具体类型 - 坚持使用符合您情况的最具体的类型。
JavaScript高性能的秘密———探索V8引擎 https://zhuanlan.zhihu.com/p/368320375
js中的Number类型看起来十分简单,不需要关心什么Int、Float、32、64,统统都是Number。看起来js引擎内部只要选择一个最大范围的类型(比如Float64)就可以表示js中的所有Number,但在v8内部可没这么简单。
Smi vs HeapNumber
在V8内部,会把Number类型分成两大类型,一种是Smi(small integer的缩写)和 Heap Number。 Smi,小整数,顾名思义用来表示一个小范围内的整数类型: -(2^30) ~ 2^30 - 1 范围内的整数,我们知道Int32类型的范围是 -(2^31) ~ 2^31 - 1, 为什么Smi类型会比Int32小呢,这是因为在V8中,Sim类型的值是根据它的地址直接得出的,为了区分Smi类型和普通的指针,Smi类型都存储在最低位为0的地址中,所以Smi的范围实际上是Int31类型的范围。
V8中对于32位系统和64位系统中的指针也有不同处理,简单来说V8把内存分为一个个4GB大小的区块,因为4GB正好是2 ^ 32byte, 于是可以不用关心一个地址的前32位,根据末位是否为0来判断一个地址是Smi还是普通指针,如果是Smi则读取它的值,如果是普通指针则加上当前区块的offset。这样做的好处是可以在64位的系统上使用32位的空间来储存一个地址,避免了很多空间的浪费,也正好符合许多浏览器中JS最高使用内存4GB的限制。
HeapNumber 与Sim对应,HeapNumber则用来表示无法用Smi表示其他Number类型,包括有小数点的数值, 超过Smi范围的整数, Number.NaN, Infinity等任何不能用Smi表示的Number类型。HeapNumber在V8内部是一个对象,储存在堆内存上,它的名字也体现出了这一点。HeapNumber类型的值是不可变,如果要修改,会创建一个新的HeapNumber并赋值。
JavaScript中把数据类型分为基本类型和引用类型2种,实际在基本类型的背后,有很多是用像HeapNumber这样的对象来表示的,只不过在js使用者看来并不能感受到这样的隐藏逻辑。
一切对象的原型-Object类型 Object是js中十分常用的数据类型。由于JS采用原型继承,一切对象都最终继承Object。在JS中Object可以简单地声明、添加和删除属性,而且value可以是任意类型,看起来表现的很像hash table。但是在V8引擎内部,并不是简单的用hash table来表示Object的,为了兼顾内存占用和执行效率,V8为Object类型做了很多复杂的工作。
Object中的属性 首先,通常情况下,虽然Object表现得像一个字典数据结构,但是在V8中Object中的属性值是存储在一个独立的数组中的。而这样存储的属性值是无法直接通过key直接访问的,因此还需要一些其他的元数据,V8中使用了一种叫做隐藏类型的机制来储存这些元数据,每个js对象都有一个对应的隐藏类型(HiddenClass)。隐藏类型存储的信息包括:对象的形态、从属性的key到属性值在数组中位置的映射。
好的方法是当成golang写
获取o.x的流程为:查找对象 o 的隐藏类,通过隐藏类查找属性x的偏移量,然后通过偏移量(相对对象o的)获取属性值。这里经过三个步骤,在for循环中反复执行了loadX函数去获取 o.x,是否有办法去简化获取过程?这个时候就该内联缓存(Inline Cache)登场了。
V8 在执行函数的过程会观察一些调用点上的关键“中间数据”,然后将这些数据缓存起来,下次再执行该函数的时候,V8就可以直接利用这些“中间数据”,以此节约再次获取这些数据的过程。
https://cloud.tencent.com/developer/article/1083755
这也解释了为什么js的位运算只支持in32