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

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 sec
  • 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 classes连接transitions来发现蓬松不成形的JavaScript对象中的静态 struct .

隐藏类描述对象的 struct ,转换将隐藏类链接在一起,描述在对象上执行特定操作时应使用哪个隐藏类.

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

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

在此处输入图像描述

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

现在,当属性fn用于存储函数时,V8try 对该属性进行特殊处理:而不是仅仅在隐藏类中声明对象包含属性fn V8 puts function into the hidden class.

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

在此处输入图像描述

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

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

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

因为V8无法遵循这种转换,所以V8将决定将函数升级到隐藏类的决定是不正确和浪费的.情况将会改变

在此处输入图像描述

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

为什么在隐藏类上有函数很重要?因为这使V8的优化编译器信息达到inline method calls.如果调用目标存储在隐藏类本身上,它只能内联方法调用.

现在让我们把这些知识应用到上面的例子中.

因为在转换bar.fnfoo.fn之间存在冲突,所以转换bar.fnfoo.fn将成为普通属性——函数直接存储在这些对象上,V8无法内联foo.fn的调用,从而导致性能降低.

它能在打电话之前接通吗?Yes.改变如下:在旧版本的V8 there was no deprecation mechanism中,即使在发生冲突并重新路由fn转换之后,foo也没有迁移到隐藏类中,其中fn成为正常属性.相反,foo仍然保留隐藏类,其中fn是直接嵌入到隐藏类中的常量函数属性,允许优化编译器内联它.

如果在较旧的 node 上try 计时bar.fn,您将看到它的速度较慢:

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

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

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

Node.js相关问答推荐

如何在Angular jest测试中调用Nodejs的垃圾收集? node v20的测试速度慢且内存泄漏

如何在Reaction应用程序中查看存储的斑点图像?

即使卷已设置,Docker Nodemon 也不会热重载

Node js 处理回调和 Promise

Node.js中使用ffmpeg如何获取视频截图的位置?

我正在try 在公共目录中使用 Express.js 项目部署 Angular 静态构建

node_modules/preact/src/jsx.d.ts:2145:22 - 错误 TS2304:找不到名称SVGSetElement

如何在mongodb中获取对象数组内部的数据

(Mongoose) 删除 TTL 字段失败

如何使用来自前一阶段的另一个数组的聚合mongoose 在数组中添加字段

Sharp JS 依赖关系 destruct 了 Elastic Beanstalk 上的 Express Server

即使部署成功,也不会触发 Firebase 函数来填充 Firestore 集合.为什么?

编写可维护的事件驱动代码

ChildProcess 关闭、退出事件之间的区别

使用 NodeJS 将新对象附加到 DynamoDB 中的 JSON 数组

__dirname 未在 Node 14 版本中定义

Passport 的 req.isAuthenticated 总是返回 false,即使我硬编码 done(null, true)

Node应用程序中相同npm包的两个版本

什么时候应该将函数存储到变量中?

promise 回调返回promise