gpt4 book ai didi

javascript - 对不同对象中的函数使用相同的键时,V8 中的函数调用缓慢

转载 作者:IT老高 更新时间:2023-10-28 21:52:55 25 4
gpt4 key购买 nike

也许不是因为调用慢,而是查找慢;我不确定,但这里有一个例子:

var foo = {};
foo.fn = function() {};

var bar = {};
bar.fn = function() {};

console.time('t');

for (var i = 0; i < 100000000; i++) {
foo.fn();
}

console.timeEnd('t');

在win8.1上测试

  • firefox 35.01:~240ms
  • chrome 40.0.2214.93 (V8 3.30.33.15):~760ms
  • msie 11:34 秒
  • nodejs 0.10.21 (V8 3.14.5.9):~100ms
  • iojs 1.0.4 (V8 4.1.0.12):~760ms

现在这是有趣的部分,如果我将 bar.fn 更改为 bar.somethingelse:

  • chrome 40.0.2214.93 (V8 3.30.33.15):~100ms
  • nodejs 0.10.21 (V8 3.14.5.9):~100ms
  • iojs 1.0.4 (V8 4.1.0.12):~100ms

最近 v8 出了什么问题?这是什么原因造成的?

最佳答案

第一基础。

V8 使用 hidden classestransitions 连接来发现蓬松无形的 JavaScript 对象中的静态结构。

隐藏类描述对象的结构,转换将隐藏类链接在一起,描述如果对对象执行特定操作,应使用哪个隐藏类。

例如,下面的代码将导致下面的隐藏类链:

var o1 = {};
o1.x = 0;
o1.y = 1;
var o2 = {};
o2.x = 0;
o2.y = 0;

enter image description here

此链是在您构造 o1 时创建的。当构造 o2 时,V8 只遵循已建立的转换。

现在,当使用属性 fn 来存储函数时,V8 会尝试对该属性进行特殊处理:而不是仅仅在隐藏类中声明对象包含属性 fn V8 将函数放入隐藏类

var o = {};
o.fn = function fff() { };

enter image description here

现在这里有一个有趣的结果:如果你将不同的函数存储到同名的字段中,V8 不能再简单地跟随转换,因为函数属性的值与预期值不匹配:

var o1 = {};
o1.fn = function fff() { };
var o2 = {};
o2.fn = function ggg() { };

在评估 o2.fn = ... 赋值时,V8 会看到有一个标记为 fn 的转换,但它会导致一个不适合的隐藏类:它fn 属性中包含 fff,而我们正在尝试存储 ggg。注意:我只是为了简单起见给出了函数名称 - V8 内部不使用它们的名称,而是使用它们的 identity

因为 V8 无法遵循这种转换,V8 将决定其将功能提升到隐藏类的决定是不正确且浪费的。图片会变的

enter image description here

V8 将创建一个新的隐藏类,其中 fn 只是一个简单的属性,不再是一个常量函数属性。它将重新路由转换并标记旧的转换目标已弃用。请记住,o1 仍在使用它。但是下一次代码触及 o1 例如当从中加载一个属性时 - 运行时会将 o1 从已弃用的隐藏类中迁移出来。这样做是为了减少多态性——我们不希望 o1o2 有不同的隐藏类。

为什么在隐藏类上有函数很重要?因为这提供了 V8 的优化编译器信息,它用于内联方法调用。如果调用目标存储在隐藏类本身上,它只能内联方法调用。

现在让我们将这些知识应用到上面的示例中。

因为转换 bar.fnfoo.fn 之间的冲突变成了普通属性 - 函数直接存储在这些对象上,V8 无法内联调用foo.fn 导致性能下降。

之前可以内联调用吗? 是的。变化如下:在旧版 V8 中没有弃用机制,所以即使在我们发生冲突并重新路由 fn 转换之后,foo 也没有迁移到 fn 成为普通属性的隐藏类。相反,foo 仍然保留隐藏类,其中 fn 是一个常量函数属性,直接嵌入到隐藏类中,允许优化编译器内联它。

如果您尝试在旧 Node 上计时 bar.fn,您会发现它更慢:

for (var i = 0; i < 100000000; i++) {
bar.fn(); // can't inline here
}

正是因为它使用了不允许优化编译器来内联 bar.fn 调用的隐藏类。

最后要注意的是,这个基准测试并不衡量函数调用的性能,而是衡量优化编译器是否可以通过内联调用来将此循环减少为空循环。

关于javascript - 对不同对象中的函数使用相同的键时,V8 中的函数调用缓慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28201164/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com