如何添加一个新类的原型方法JS V8优化的一种形式?(How is adding a new cla

2019-09-26 02:43发布

我读通过温斯顿的代码库,并有在他们的评论DerivedLogger班线22 ,上面写着:

创建一个新的类派生记录了该级别可以连接到的原型。 这是众所周知的,增加的原型功能性能V8优化。

从我在这里聚集,他们说,增加一个新的类(DerivedLogger),其中增加的原型方法是V8优化的一个众所周知的形式? 它是如何从只需添加方法的类的原型不同的Logger ,而无需创建一个新的类? 有人可以帮助我理解这个概念,或者纠正我,如果我在这里误解有何评论? 谢谢!

Answer 1:

嗯,这是一个有趣的一个。

前言

如果没有对“众所周知”的“增加[上]性能的任何细节,”我们只能猜测什么意思。

记录仪的历史

当我第一次看到你的问题,我的代码实现了代码注释必须是过时的。

class DerivedLogger extends Logger {
  /**
   * Create a new class derived logger for which the levels can be attached to
   * the prototype of. This is a V8 optimization that is well know to increase
   * performance of prototype functions.
   * @param {!Object} options - Options for the created logger.
   */
  constructor(options) {
    super(options);
    this._setupLevels();
  }
  // ...
}

module.exports = (opts = { levels: config.npm.levels }) => (
  new DerivedLogger(opts)
);

资源

这是错误的一种方式,因为通过_setupLevels()在构造函数调用,方法是在实例中定义,而不是在原型。 请参见这里或这里关于该主题的详细信息。

所以,我透过历史挖出来找到不变的代码注释中第一次出现...

这是看着中加入上述评论时,像什么原代码:

// Create a new instance of a winston Logger. Creates a new
// prototype for each instance.
module.exports = function (opts) {
  // ...

  //
  // Create a new prototypal derived logger for which the levels
  // can be attached to the prototype of. This is a V8 optimization
  // that is well know to increase performance of prototype functions.
  //
  function DerivedLogger(options) { Logger.call(this, options); }
  util.inherits(DerivedLogger, Logger);

  // ...

  DerivedLogger.prototype[level] = function (msg) {

资源

当前的代码以另一种方式改变:在DerivedLogger不再与每一个记录器实例创建,但只有一次,当模块被加载。

分析

直到这里,我没有意识到的是,温斯顿作者在记录仪的功能创建新创建原型:

// Create a new instance of a winston Logger. Creates a new
// prototype for each instance.
//
module.exports = function (opts) {

资源

所以,当是要创建一个新的记录,不仅是一个实例制成, 而是创造了一个全新的原型了

               [Logger]  (A)
                   ^
                   |
         +---------+--------+
         |                  |
 [DerivedLogger #1] [DerivedLogger #2]  (B)
         |                  |
      logger #1          logger #2

派生记录仪都不要再用。

结论

原意明确是防止修改/污染Logger每当创建了新的记录器实例(A)。

虽然创建一个原型记录器的方法,以防止重复实例方法浪费内存(见之初链接的问题)似乎是由新的原型重复创作受挫。

我相信,即使通过创建原型持有日志方法比直接定义他们的实例所创作的原型对象的吞噬获得的性能。

不过,我没有信心100%,该讨论的解释是原意和我打开更正和澄清。

奖金

(我在我的研究,可能无关的上述温斯顿代码中发现这一点。)

由于困扰我的是,原作者声称对样机将优化东西V8定义方法,我开始对这个话题搜索更新,我发现的由V8开发马蒂亚斯Bynens一篇文章: JavaScript引擎基本面:优化原型 。

他在讨论如何最JavaScript引擎(不仅V8!)内部存储对象以及它们是如何处理的属性访问。 您可能还需要阅读另由他Shape对象的文章 。

我不会完全回顾一下,在这里详细,但似乎有一个独特的细节V8如何沿着原型链的处理访问:

V8对待原型塑造了专门用于这一目的。 每个原型具有不与任何其它对象共享一个唯一的形状(具体地不与其他原型),并且每个这些原型形状具有特殊ValidityCell与它相关联。
ValidityCell每当有人改变了相关的原型或它上面的任何原型无效。
[...]
接下来的时间内联缓存被击中,发动机必须检查实例和形状ValidityCell 。 如果它仍然有效,发动机能直接接触到的偏移的原型,跳过额外的查找。

(由我粗体文字。)

所以,唯一的V8似乎是事实,他们跟踪的原型是否仍然是“形状”。 这允许V8减少参与原型链处理的检查。



文章来源: How is adding a new class with prototype methods a form of V8 optimization in JS?