阿里云「飞天发布时刻」2024来啦!新产品、新特性、新能力、新方案,等你来探~ 了解详情
写点什么

JS 引擎中的 Inline Cache 技术内幕,你知道多少?

  • 2020-12-29
  • 本文字数:3094 字

    阅读完需:约 10 分钟

JS 引擎中的 Inline Cache 技术内幕,你知道多少?

JavaScript 以简单易用而著称,NodeJS 的出现使 JavaScript 的影响进一步扩大。JavaScript 是动态类型的语言,动态类型为应用开发者带来了便利,但也为 JavaScript 运行时的性能带来了负担,例如类型的不断变化可能会导致基于类型的某些优化失效。为了解决 JavaScript 由于动态类型导致的运行性能受损问题,各大 JavaScript 引擎几乎都采用了 IC(Inline Cache)技术:即通过缓存上一次对象的类型信息来加速当前对象属性的读写访问。本文从引例入手,以 V8 JavaScript 引擎(主要由于 V8 既是 Chrome 浏览器的 JS 引擎,也是 node 的 JS 引擎)为基础,深入分析 Inline Cache 机制的基本原理。


引例


function Point(x,y) {this.x = x;this.y = y;}var p = new Point(0, 1);var q = new Point(2,3);var r = new Point(4,5);
复制代码


为了避免 API 调用不稳定因素的影响,通过修改 V8 源码,在内部插入时间戳的方式。在 3.2G 8 核机器上,分别测试三次调用 new Point(x,y)时执行 this.x=x 这个语句耗时,结果如下表所示。


执行代码this.x=x耗时统计
var p = new Point(0,1);4.11ns
var q = new Point(2,3);6.63ns
var r = new Point(4,5);0.65ns


从表中的结果可以看出,事实并非想象中的从第二次执行开始,速度就会变快,相反,第二次比第一次还要慢,到第三次的时候速度才会变快。本文后面会通过分析 V8 IC 机制来解释为什么第二次速度最慢,第三次执行速度又变快的原因。


问题分析


1. 对象的隐藏类(Hidden Class)


由于 JavaScript 对象没有类型信息,几乎所有 JS 引擎都采用隐藏类(Hidden Class/Shape/Map 等)来描述对象的布局信息,用以在虚拟机内部区分不同对象的类型,从而完成一些基于类型的优化。


V8 对 JavaScript 对象都使用 HeapObject 来描述和存储,每一种 JavaScript 对象都是 HeapObject 的子类,而每个 HeapObject 都用 Map 来描述对象的布局。对象的 Map 描述了对象的类型,即成员数目、成员名称、成员在内存中的位置信息等。


上述源码中对象 p、q、r 都是由同一个构造函数 Point 生成,因此他们具有同样的内存布局,可以采用同一个 Map 来描述。


2. 隐藏类变迁(Map Transition)


因为 JavaScript 是高度动态的程序设计语言,对象的成员可以被随意动态地添加、删除甚至修改类型。因此,对象的隐藏类在程序的运行过程中可能会发生变化,V8 内部把这种变化叫隐藏类变迁(Map Transition)。


以上文代码为例,当 Point function 被声明时,V8 就会给 Point 创建隐藏类 map0,由于暂时还没有属性,因此 map0 为空。当执行 this.x=x 时,V8 会创建第二个 Hidden Class map1,map1 是基于 map0,并且描述了属性 x 在内存中的位置,此时 this 对象的 Hidden Class 会通过 Map Transition 变为 map1。当执行 this.y=y 时,会重复前面的操作,一个新的 Hidden Class map2 会被创建,此时 this 对象的 Hidden Class 被更新为 map2。this 对象的 Map 从 map0 变为 map1,再变成 map2 的过程就叫做 Map Transition。图一描述了 Point 类对象创建过程中 Map Transition 的过程。


Map Transition示意图


3. 类型反馈向量(type feedback vector)


前面已经提到 IC 机制的原理是:对于某代码语句比如 this.x=x,比较上次执行到该语句时缓存的 Map 和对象当前的 Map 是否相同,如果相同则执行对应的 IC-Hit 代码,反之执行 IC-Miss 代码。那么 V8 是如何组织被缓存的 Map 和 IC-Hit 代码?以上文代码为例,V8 会在 Point 函数对象上添加一个名为 type_feedback_vector 的数组成员,对于该函数中的每处可能产生 IC 的代码,Point 对象中的 type_feedback_vector 会缓存上一次执行至该语句时对象的 Map 和对应的 IC-Hit 代码(在 V8 内部称为 IC-Hit Handler)。上文中的 Point 函数中有两处可能产生 IC 的语句,this.x=x 和 this.y=y。假设某次执行至 this.x=x 时,对象 this 的 Map 是 map0,执行至 this.y=y 时 this 的 Map 是 map1,那么 Point 对象的 type_feedback_vector 数据内容如下所示:


数组下标IC对应的源码缓存的Map和对应的IC-Hit Handler
0this.x=x<map0, ic-hit handler>
1this.y=y<map1, ic-hit handler>


简单来说,type_feedback_vector 缓存了 Map 和与之对应的 IC-Hit handler,这样 IC 相关的逻辑简化为只需要通过访问 type_eedback_vector 就可以判断是否 IC Hit 并执行对应的 IC-Hit Handler。


4. IC 状态机


为了描述 V8 中 IC 状态的变化情况,本节将以状态机的形式描述 V8 中最常见 IC 种类的状态变化情况。V8 中最常用 的 IC 分为五个状态,如图二所示。初始为 uninitialized 状态,当发生一次 IC-Miss 时会变为 pre-monomorphic 态,再次 IC-Miss 会进入 monomorphic 态,如果继续 IC-Miss,则会进入 polymorphic 状态。进入 polymorphic 之后如果继续 IC-Miss 3 次,则会进入 megamorphic 态,并最终稳定在 megamophic 态。


IC状态机


引例中代码会涉及到 IC 状态机的前三种状态。


以 Point 函数走红 this.x=x 语句为例,第一次执行时,由于 Point.type_feedback_vetor 为空,因此此时会发生 IC-Miss,并将该处 IC 状态从 uninitialized 设置为 pre-monomorphic,IC-Miss Handler 会分析出此时 this 对象的 Map 中不包含属性 x,因此会添加成员 x,接着会发生 Map Transition,即前文提到的 this 对象的隐藏类从 map0 变为 map1。由于考虑到大部分函数可能只会被调用一次,因此 V8 的策略是发生第一次 IC-Miss 时,并不会缓存此时的 map,也不会产生 IC-Hit handler;


第二次调用构造函数执行 this.x=x 时,由于 Point.type_feedback_vector 仍然为空,因此会发生第二次 IC-Miss,并将 IC 状态修改为 monomorphic,此次 IC-Miss Hanlder 除了发生 Map Transition 之外,还会编译生成 IC-Hit Handler,并将 map0 和 IC Hit Handler 缓存到 Point.type_feedback_vector 中。由于此次 IC-Miss Handler 需要编译 IC-Hit Handler 的操作比较耗时,因此第二次执行 this.x=x 是最慢的;


第三次调用构造函数中 this.x=x 时,发现 Point.type_feedback_vector 不为空,且此时缓存的 map0 与此时 this 对象的 Map 也是一致的,因此会直接调用 IC-Hit Handler 来添加成员 x 并进行 Map transition。由于此次无需对 map0 进行分析,也无需编译 IC-Hit Handler,因此此时执行效率比前两次都高。


至此,本节已经解释清楚为什么 V8 执行构造函数时,第二遍最慢而第三遍最快的原因。


5. Polymorphic 和 Megamorphic


function f(o) {  return o.x;}f({x:1}) //pre-monomorphicf({x:2}) //monomorphicf({x:3, y:1}) // polymorphic degree 2f({x:4, z:1}) // polymorphic degree 3f({x:5, a:1}) // polymorphic degree 4f({x:6, b:1}) // megamorphic
复制代码


上述代码描述了图二状态机中 polymorphic 态和 megamophic 态的两种情形。上面 3 中提到 type_feedback_vector 会缓存 Map 和 IC-Hit Handler,但是如果 IC 状态太多比如到达 megamorphic 态,此时 Map 和 IC-Hit Handler 便不会再缓存在 Point 对象的 feedback_vector 中,而是存储在固定大小的全局 hashtable 中,如果 IC 态多于 hashtable 的大小,则会对之前的缓存进行覆盖。通过上述分析,可以总结得出不同 IC 态的性能:


  • 如果每次都能在 monomorphic 态 IC-Hit,代码的运行速度是最快的;

  • 在 polymorphic 态 IC-Hit 时,需要对缓存进行线性查找;

  • Megamorphic 是性能最低的 IC-Hit,因为需要每次对 hashtable 进行查找,但是 megamorphic ic hit 性能仍然优于 IC-Miss;

  • IC-Miss 性能是最差的;


综合前文所述,仅从 Inline cache 的角度来分析,如果 JavaScript 开发者在应用开发时能让 IC 态保持在 monomorphic 或者 polymorphic,代码的性能是最好的。特别是对于一些比较注重应用冷启动性能的场景,减少启动过程中的 IC-Miss 会使启动时间大幅缩短。


参考文献




头图:Unsplash

作者:廖彬

原文JS引擎中的Inline Cache技术内幕,你知道多少?

来源:腾讯云中间件 - 微信公众号 [ID:gh_6ea1bc2dd5fd]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2020-12-29 23:352215

评论

发布
暂无评论
发现更多内容

找工作必备AI工具!AI润色简历、AI生成作品集PPT、AI模拟面试、AI绘画、AI绘图等!

彭宏豪95

效率 AI 职场 办公软件 AIGC

CAN总线位时序的介绍

智趣匠

linux IP地址原理,分类,子网划分,VLAN,TRUNK详解

百度搜索:蓝易云

云计算 Linux 运维 IP 云服务器

iA Writer for Mac(mac好用的写作软件)v7.1中文激活版

影影绰绰一往直前

Text Workflow for mac(文本格式转换工具)v1.9.3直装激活版

影影绰绰一往直前

DxO PhotoLab 6 for Mac(智能raw图片编辑器)v6.15.0.68激活版

影影绰绰一往直前

数字藏品开发公司 nft平台搭建

西安链酷科技

数字藏品开发

DAPP去中心化交易质押分红系统开发模式案例

西安链酷科技

Java 抽象类与方法:实现安全性与代码重用

小万哥

Java 程序人生 编程语言 软件工程 后端开发

Boxy SVG for Mac(矢量图编辑器)v4.25.2免激活版

影影绰绰一往直前

BetterMouse for Mac(鼠标增强软件)v1.5 (4612)中文激活版

影影绰绰一往直前

gRPC-第二代rpc服务

京东科技开发者

k8s说一下deployment,statefulset,daemonset的区别

百度搜索:蓝易云

云计算 Linux Kubernetes 运维 云服务器

Java垃圾回收全过程

百度搜索:蓝易云

Java 云计算 Linux 运维 云服务器

DxO FilmPack 7 for Mac(胶片效果渲染工具)v7.5.0.513中文激活版

影影绰绰一往直前

量化合约丨现货丨对冲丨秒合约模式系统开发源码搭建

西安链酷科技

DAPP智能合约交易系统开发 DAPP系统开发

飞书深诺基于Flink+Hudi+Hologres的实时数据湖建设实践

飞书深诺技术团队

大数据 数据仓库

Starknet(strk) 跨链桥教程:手把手教你用bitget钱包跨链

股市老人

Starknet(strk) 跨链桥教程:手把手教你用bitget钱包跨链

长安区块链

《MySQL是怎样运行的:从根儿上理解 MySQL》PDF

程序员李木子

前端和大数据怎么选择?

算法的秘密

mClock调度算法与QoS

算法 存储 Ceph QoS 调度

DxO ViewPoint 4 for Mac(照片修复校正工具)v4.15.0.294激活版

影影绰绰一往直前

直播预告|从一张 CD 说起,关于播放器的前世今生

声网

低代码:数智化助力新农业发展

快乐非自愿限量之名

低代码 数字化 新农业

DAPP去中心化交易质押分红系统开发模式案例

西安链酷科技

DAPP智能合约交易系统开发

HttpServletRequest JSON序列化问题分析

五陵散人

JSON序列化 HTTP请求

体育赛事直播平台,全方位引流策略分享

软件开发-梦幻运营部

Starknet(strk) 跨链桥教程:手把手教你用bitget钱包跨链

股市老人

ai写ppt工具有哪些?这5款值得推荐,办公必备!

彭宏豪95

人工智能 职场 PPT 在线白板 AIGC

当数据遇上代码:程序员的假设检验

数据分析 数学 SLO 概率论

JS 引擎中的 Inline Cache 技术内幕,你知道多少?_语言 & 开发_腾讯云中间件_InfoQ精选文章