图解 Google V8
李兵
前盛大创新院高级研究员
26763 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 25 讲
图解 Google V8
15
15
1.0x
00:00/00:00
登录|注册

16 | 答疑: V8是怎么通过内联缓存来提升函数执行效率的?

属性的偏移量
隐藏类(map)的地址
状态(state)
类型(type)
索引(slot index)
实际项目中的影响因素
避免多态和超态
IC提升代码执行速度
尽量保持单态
单态、多态、超态的执行性能
查找反馈向量中的中间信息
CALL类型操作
STORE类型操作
LOAD类型操作
插槽(Slot)
缓存关键中间数据
观察函数中的调用点
思考题
总结
最佳实践
利用缓存数据
中间数据写入
反馈向量(FeedBack Vector)
原理
提升函数执行效率
V8内联缓存(IC)
V8内联缓存(IC)知识关系脑图

该思维导图由 AI 生成,仅供参考

你好,我是李兵。
上节我们留了个思考题,提到了一段代码是这样的:
function loadX(o) {
return o.x
}
var o = { x: 1,y:3}
var o1 = { x: 3 ,y:6}
for (var i = 0; i < 90000; i++) {
loadX(o)
loadX(o1)
}
我们定义了一个 loadX 函数,它有一个参数 o,该函数只是返回了 o.x。
通常 V8 获取 o.x 的流程是这样的:查找对象 o 的隐藏类,再通过隐藏类查找 x 属性偏移量,然后根据偏移量获取属性值,在这段代码中 loadX 函数会被反复执行,那么获取 o.x 流程也需要反复被执行。我们有没有办法再度简化这个查找过程,最好能一步到位查找到 x 的属性值呢?答案是,有的。
其实这是一个关于内联缓存的思考题。我们可以看到,函数 loadX 在一个 for 循环里面被重复执行了很多次,因此 V8 会想尽一切办法来压缩这个查找过程,以提升对象的查找效率。这个加速函数执行的策略就是内联缓存 (Inline Cache),简称为 IC。
这节课我们就来解答下,V8 是怎么通过 IC,来加速函数 loadX 的执行效率的。

什么是内联缓存?

要回答这个问题,我们需要知道 IC 的工作原理。其实 IC 的原理很简单,直观地理解,就是在 V8 执行函数的过程中,会观察函数中一些调用点 (CallSite) 上的关键的中间数据,然后将这些数据缓存起来,当下次再次执行该函数的时候,V8 就可以直接利用这些中间数据,节省了再次获取这些数据的过程,因此 V8 利用 IC,可以有效提升一些重复代码的执行效率。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

V8通过内联缓存(IC)来提升函数执行效率,通过观察关键的中间数据并将其缓存起来,以便下次执行函数时直接利用这些数据,从而提升执行效率。IC会为每个函数维护一个反馈向量,记录函数执行过程中的关键中间数据,包括操作类型、隐藏类的地址、属性偏移量等。V8会缓存LOAD、STORE和CALL类型的操作中间数据。当对象的形状不固定时,V8会根据对象的隐藏类记录在反馈向量中,以及属性值的偏移量来处理。IC的最佳实践是尽量保持单态,避免多态和超态的情况,以提升执行效率。文章通过分析IC的工作原理介绍了它如何提升代码执行速度,强调了在实际项目中需要尽量避免多态或者超态的情况。最后,文章提出了思考题,引发读者思考两段代码的执行效率高低。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《图解 Google V8》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(29)

  • 最新
  • 精选
  • 王楚然
    思考题: 认为第一种方式效率更高。 第一种方式中,每一个item类型一样,后续几次调用toString可以直接命中,是单态。 第二种方式中,由于item类型间错不同,经常变换,就要同时缓存多个类型,是多态。

    作者回复: 回答很赞

    2020-04-21
    6
    28
  • 内联合缓存 IC 的缓存向量只针对函数调用吗?

    作者回复: 可以这么说,其实你在全局环境下执行一段代码,V8也会将其封装成一个匿名(anonymous)函数,所以IC对所有代码都是有效的

    2020-04-21
    7
  • 王子晨
    老师有个疑问,当采用IC机制的时候,函数内每一行的代码都可以理解IC里面的一个插槽么?在多次重复调用该函数时,可能会出现某一行的map地址跟上一次比对不一样,于是将新的map地址添加到该插槽内,形成多态或超态;那IC中是所有插槽都是多态称为多态还是只要有一个插槽是多态即该IC为多态IC?

    作者回复: 不能看js代码,要看字节码或者汇编代码,有时候一行代码可以拆分为多行字节码,比如字节码中,字节码中最后的【n】就代表了插槽

    2020-04-30
    6
  • 翰弟
    前者效率更高,因为数组元素一样会很好的利用到IC

    作者回复: 是的,尽量使用同一种数据类型的数据

    2020-04-28
    1
  • chris
    精彩。终于基本搞懂v8的IC了。 两个问题不知老师有空解答吗: 1. 感觉查feedback vector的方式还是不够高效,请问代码被jit后o.x中x的偏移量是否就可以直接硬编码到机器码中去了。 2. 关于CALL类型的IC,feedback vector里面存的是什么信息?

    作者回复: 是的,机器代码都是直接将偏移地址写进去,这样就更加高效了。 call、一样的,函数也是一个对象,存放函数对象隐藏类的一些基础信息

    2020-04-21
    1
  • 咪呐!哦哈哟嘶!٩(ˊᗜˋ*)...
    State 里的MONO是什么意思?

    作者回复: 是单态 (monomorphic)的缩写

    2020-04-21
    3
    1
  • 咪呐!哦哈哟嘶!٩(ˊᗜˋ*)...
    第一次执行时 loadX 时,V8 会将 o 的隐藏类记录在反馈向量中,并记录属性 x 的偏移量。那么当再次调用 loadX 函数时,V8 会取出反馈向量中记录的隐藏类,并和新的 o1 的隐藏类进行比较,发现不是一个隐藏类,那么此时 V8 就无法使用反馈向量中记录的偏移量信息了。 发现不是同一个隐藏类

    作者回复: 是的

    2020-04-21
    1
  • 天天
    Typescript用起来
    2020-04-21
    19
  • 天天
    一个疑问是,既然Inline cache是为了提升性能,而它也只是缓存了偏移量,v8也是先从反馈向量中取出这个偏移量,然后在到内存中去取值,那和原来的偏移量放在map中,从map中获取到偏移量后去再去内存中取值,这两个过程感觉差不多,有多大的性能区别吗?
    2020-08-31
    1
    3
  • champ可口可乐了
    老师,有办法能直接查看反馈向量里面的内容吗?想知道你那个反馈向量的表格是从哪里获取的。
    2020-04-21
    3
收起评论
显示
设置
留言
29
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部