Bluebird 的 util.toFastProperties 函数如何使对象的属性变得“快速”?

2024-02-03

在蓝鸟的util.js file https://github.com/petkaantonov/bluebird/blob/7454401269cfa47e5b001354388c062509103de7/src/util.js#L180,它具有以下功能:

function toFastProperties(obj) {
    /*jshint -W027*/
    function f() {}
    f.prototype = obj;
    ASSERT("%HasFastProperties", true, obj);
    return f;
    eval(obj);
}

由于某种原因,返回函数后面有一个语句,我不确定为什么它在那里。

而且,这似乎是故意的,因为作者已经压制了 JSHint 的警告:

“return”后无法访问“eval”。 (W027)

这个函数具体有什么作用呢?做util.toFastProperties真的能让对象的属性“更快”吗?

我在 Bluebird 的 GitHub 存储库中搜索了源代码中的任何注释或问题列表中的解释,但我找不到任何内容。


2017 更新:首先,对于今天到来的读者 - 这是一个适用于 Node 7 (4+) 的版本:

function enforceFastProperties(o) {
    function Sub() {}
    Sub.prototype = o;
    var receiver = new Sub(); // create an instance
    function ic() { return typeof receiver.foo; } // perform access
    ic();
    ic();
    return o;
    eval("o" + o); // ensure no dead code elimination
}

没有一两个小的优化 - 以下所有内容仍然有效。


让我们首先讨论它的作用以及为什么它更快,然后讨论它为什么有效。

它能做什么

V8 引擎使用两种对象表示:

  • 词典模式- 其中对象存储为键值映射作为hash map //en.wikipedia.org/wiki/Hash_table.
  • 快速模式- 对象存储在其中structs //en.wikipedia.org/wiki/Struct_(C_programming_language),其中属性访问不涉及计算。

Here is 一个简单的演示 //jsperf.com/test-dictionary-mode这表明了速度差异。这里我们使用delete语句强制对象进入慢速字典模式。

只要有可能,并且通常在执行大量属性访问时,引擎都会尝试使用快速模式 - 但有时它会陷入字典模式。处于字典模式会带来很大的性能损失,因此通常需要将对象置于快速模式。

此 hack 的目的是强制对象从字典模式进入快速模式。

  • 蓝鸟的佩特卡本人在这里谈论它 //github.com/petkaantonov/bluebird/wiki/Optimization-killers#52-the-object-being-iterated-is-not-a-simple-enumerable.
  • 这些幻灯片(回溯机) //web.archive.org/web/20170729185544/http://mrale.ph/s3/nodecamp.eu/#1维亚切斯拉夫·叶戈罗夫(Vyacheslav Egorov)也提到了这一点。
  • 问题“*https://stackoverflow.com/questions/23455678/pros-and-cons-of-dictionary-mode*” https://stackoverflow.com/questions/23455678/pros-and-cons-of-dictionary-mode*%22和它接受的答案也相关。
  • 这篇文章有点过时了 //jayconrod.com/posts/52/a-tour-of-v8-object-representation仍然是一本相当不错的读物,可以让您很好地了解 v8 中如何存储对象。

为什么它更快

在 JavaScript 中,原型通常存储在许多实例之间共享的函数,并且很少动态变化。因此,非常希望将它们置于快速模式,以避免每次调用函数时产生额外的损失。

为此 - v8 会很乐意放置以下对象.prototype快速模式下函数的属性,因为它们将被通过调用该函数作为构造函数创建的每个对象共享。这通常是一种聪明且理想的优化。

怎么运行的

让我们首先浏览一下代码并弄清楚每一行的作用:

function toFastProperties(obj) {
    /*jshint -W027*/ // suppress the "unreachable code" error
    function f() {} // declare a new function
    f.prototype = obj; // assign obj as its prototype to trigger the optimization
    // assert the optimization passes to prevent the code from breaking in the
    // future in case this optimization breaks:
    ASSERT("%HasFastProperties", true, obj); // requires the "native syntax" flag
    return f; // return it
    eval(obj); // prevent the function from being optimized through dead code
    // elimination or further optimizations. This code is never
    // reached but even using eval in unreachable code causes v8
    // to not optimize functions.
}

我们不have要自己找到代码来断言 v8 进行了此优化,我们可以改为阅读 v8 单元测试 //github.com/v8/v8/blob/d52280b1a7a867ffb350c4f193cf8692861855dd/test/mjsunit/fast-prototype.js:

// Adding this many properties makes it slow.
assertFalse(%HasFastProperties(proto));
DoProtoMagic(proto, set__proto__);
// Making it a prototype makes it fast again.
assertTrue(%HasFastProperties(proto));

阅读并运行这个测试向我们表明,这种优化在 v8 中确实有效。然而 - 很高兴看到如何做到这一点。

如果我们检查objects.cc我们可以找到以下函数(L9925):

void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
    if (object->IsGlobalObject()) return;

    // Make sure prototypes are fast objects and their maps have the bit set
    // so they remain fast.
    if (!object->HasFastProperties()) {
        MigrateSlowToFast(object, 0);
    }
}

Now, JSObject::MigrateSlowToFast只是显式地获取 Dictionary 并将其转换为快速的 V8 对象。这是一本值得一读的书,也是对 v8 对象内部结构的有趣见解——但这不是这里的主题。我还是热烈推荐你在这里读到的 https://github.com/v8/v8/blob/3235f3f8b5930de07a240f61386f21d55040dbf8/src/objects.cc#L4617-L4751因为这是了解 v8 对象的好方法。

如果我们检查一下SetPrototype in objects.cc,我们可以看到它在第12231行被调用:

if (value->IsJSObject()) {
    JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
}

依次调用FuntionSetPrototype这就是我们得到的.prototype =.

Doing __proto__ = or .setPrototypeOf也可以工作,但这些是 ES6 函数,并且 Bluebird 可以在 Netscape 7 以来的所有浏览器上运行,因此这里不可能简化代码。例如,如果我们检查.setPrototypeOf我们可以看到:

// ES6 section 19.1.2.19.
function ObjectSetPrototypeOf(obj, proto) {
    CHECK_OBJECT_COERCIBLE(obj, "Object.setPrototypeOf");

    if (proto !== null && !IS_SPEC_OBJECT(proto)) {
        throw MakeTypeError("proto_object_or_null", [proto]);
    }

    if (IS_SPEC_OBJECT(obj)) {
        %SetPrototype(obj, proto); // MAKE IT FAST
    }

    return obj;
}

直接是哪个Object:

InstallFunctions($Object, DONT_ENUM, $Array(
...
"setPrototypeOf", ObjectSetPrototypeOf,
...
));

所以 - 我们已经走过了从 Petka 编写的代码到裸机的道路。这很好。

免责声明:

请记住,这是所有实现细节。像 Petka 这样的人都是优化狂。永远记住,97% 的情况下,过早优化是万恶之源。 Bluebird 经常做一些非常基本的事情,因此它从这些性能黑客中获益匪浅 - 与回调一样快并不容易。你rarely必须在不支持库的代码中执行类似的操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Bluebird 的 util.toFastProperties 函数如何使对象的属性变得“快速”? 的相关文章

  • npm WARN 已弃用 [email protected]:改用 uuid 模块

    当我尝试时 npm install g cordova latest总是得到npm 警告已弃用 电子邮件受保护 cdn cgi l email protection 使用 uuid 模块代替 mac 操作系统塞拉利昂 10 12npm v
  • 通过 Javascript 更改 Webkit 属性?

    请帮助我 可能是因为我对 CSS 动画和 Javascript 相当陌生 但我使用的代码应该更改它的属性 当我运行代码时 它会执行代码中的所有其他操作 除了更改所需 div 的 CSS 属性 我已经尝试了所有这四种方法 但似乎都不起作用 它
  • 如何从对象数组中删除所有重复项?

    This is a large array of objects e g let totalArray id rec01dTDP9T4ZtHL4 fields user id 170180717 user name abcdefg even
  • 光标:IE 8 和 9 中的自动行为

    我想要的是为整个正文标记指定cursor pointer 这样页面的背景是可点击的 但我也希望页面的其余部分像以前一样工作 所以我尝试为div设置cursor auto 其中包含这一页 在 FF Chrome 和 safari 中 它工作得
  • Chrome 跨域 PATCH 请求不起作用

    我有一个带有 REST Api 的网站 现在我正在创建一个浏览器扩展 它将从某些页面收集数据并将它们发送回 REST Api 因为我希望我的扩展能够与 Firefox 和 Chrome 兼容 并且易于维护 所以我将实际代码作为脚本标记注入到
  • 在Javascript中将RGB数组转换为RGBA数组的快速方法

    我正在使用的模拟器在内部存储 RGB 值的一维帧缓冲区 但是 HTML5 画布在调用 putImageData 时使用 RGBA 值 为了显示帧缓冲区 我当前循环遍历 RGB 数组并以某种方式创建一个新的 RGBA 数组与此类似 https
  • 处理时区转换的 JavaScript 库

    是否有一个 JavaScript 库可以处理时区转换 并考虑 DST 规则和此类内容 我知道有类似的问题 但我见过的问题似乎都没有真正适合我的问题的答案 我想在时区 A 创建一个日期并能够对其进行操作 添加天数 小时等 然后将其转换为另一个
  • 获取输入图像类型选择的图片并加载到图像标签中

    所以 我有一个用于上传 img 文件的输入框 我想要做的是从该数据 或选定的源 中获取数据并将其路由到图像标签的 src 属性中 像这样的东西 http jsfiddle net QC2c4 http jsfiddle net QC2c4
  • UpdatePanel 启动脚本未执行

    我正在编写一个在 SharePoint 网站中使用的 ASP NET Web 部件 并尝试使用 UpdatePanel 来呈现查询结果 我想使用 JQuery 插件来修改从异步回发返回的表 但我无法让启动脚本在异步更新上执行 我发现这个帖子
  • 如何获取 Spotify API 的访问令牌?

    我已经研究 Spotify api 和示例源代码几天了 但我仍然不知道如何获取访问令牌来访问用户的播放列表数据 我已经到达了拉起登录窗口 用户登录 然后收到授权码的地步 此时 我尝试做这样的事情 window open https acco
  • 如何在socket.io Nodejs服务器上列出房间

    在问题取得进展后如何创建socket io多播组 https stackoverflow com questions 6616922 how to create socket io multicast groups 6624604 6624
  • 在 R 中,为什么 sum 与其他方法(例如 cumsum)相比如此慢?

    我正在尝试实现一个需要非常快的函数 主要是因为它一遍又一遍地处理巨大的数据帧 R 总是让我感到困惑 为什么它有时有点慢 而有时又慢得离谱 不幸的是 它从来都不快 不管怎样 我一直认为 如果可能的话 当以某种方式推入 apply sapply
  • Ajax 函数在重定向后不保存滚动位置

    正如标题所述 我编写了一个 ajax 函数 该函数应该滚动到用户在重定向之前所在的位置 我写了一个alert对于测试场景 它确实触发了 但滚动不断回到顶部 我在这里做错了什么 JavaScript ajax type GET url Adm
  • jQuery 模板插件:如何创建双向绑定?

    我开始使用 jQuery 模板插件 微软创建的 但现在我面临这个问题 模板用于绑定到对象数组的一堆表单 当我更改其中一个表单上的某些内容时 我希望更新绑定的对象 但我不知道如何自动执行该操作 这是一个简单的例子 现实生活中的模板和对象要复杂
  • 如何使 4.X Typescript 项目与旧版本的 Typescript(如 3.X)兼容?

    如何使基于 TS 4 X 构建的软件包与 3 X 兼容 例如 如果我有较新的版本 则使用新功能 否则使用any or unknown或旧版本支持的任何内容 有没有可能使用指令 https www typescriptlang org doc
  • javascript 闭包和对象引用

    我的情况有点晦涩难懂 主要是因为我认为我已经掌握了闭包 所以基本上我想要的是将集合重置为默认值 假设我有一个集合 它具有带有对象参数数组的构造函数 var c new collection x y z 然后集合定期更新 因为我没有保留数组的
  • “memset”没有 DLL 那么如何 ctype 它

    如何使用memset在 jsc 类型中 没有对应的 DLL 我搜索 搜索了 js ctype 代码 但找不到要破解的示例 如果你只是想memset一个数组为零字节 然后我有 好消息 大家 js ctypes 会将新数组初始化为零 否则 最简
  • 为什么 phantomjs 不能在 MacOS Sierra 中工作?

    我们正在使用phantomjs 1 9 1 macosx phantomjs 2 0 0 macosx哪一个工作得很好OS X 埃尔卡皮坦更新后macOS 塞拉利昂它会引发以下错误 phantomjs 1 9 1 macosx phanto
  • 文件和目录条目 API 在 Chrome 中损坏?

    我正在尝试使用文件和目录条目 API 创建一个文件上传器工具 该工具允许我将文件和目录的任意组合放入浏览器窗口中 以供读取和上传 我完全意识到 可以通过使用文件输入元素来实现类似的功能webkitdirectory已启用 但我正在测试一个用
  • 尽管 getBoundingClientRect() 是假的,但如何将事件坐标转换为 SVG 坐标?

    我正在尝试根据鼠标的位置在 SVG 元素上动态绘制内容 不幸的是 我很难将 mousemove 事件中的鼠标坐标转换为 SVG 元素的坐标空间 这是我一直在测试的一个有缺陷的函数 CylinderDemo prototype handleM

随机推荐