Node: Unexpected performance penalty when using field initialiser

Created on 2 Jun 2020  路  4Comments  路  Source: nodejs/node

I'm getting 2 orders of magnitude performance penalty when using field initialiser. See snippet below (corrected after @devsnek comment):

"use strict";

class A_field_initialized_in_constructor {
  constructor() {
    this.foo = true;
  }

}

class B_field_initialized_as_class_property {
  foo = true;
}

and the results (using https://www.npmjs.com/package/benchmark):

A_field_initialized_in_constructor x 812,776,791 ops/sec 卤0.94% (91 runs sampled)
B_field_initialized_as_class_property x 5,935,129 ops/sec 卤2.31% (86 runs sampled)

Environment:

> uname -a
Darwin Pawels-MBP-3.lan 19.4.0 Darwin Kernel Version 19.4.0: Wed Mar  4 22:28:40 PST 2020; root:xnu-6153.101.6~15/RELEASE_X86_64 x86_64
> node -v
v14.3.0
V8 Engine

All 4 comments

Can you post a reproduction that uses only pure JavaScript?

@pbadenski thanks, i would recommend reporting this to https://bugs.chromium.org/p/v8/issues/list

A_field_initialized_in_constructor x 812,776,791 ops/sec 卤0.94% (91 runs sampled)

I'm pretty sure this is optimized into a noop. Try to do something like this

let instance;

suite.add('A_field_initialized_in_constructor', function() {
  instance = new A_field_initialized_in_constructor();
  return instance;
});

suite.add('B_field_initialized_as_class_property', function() {
  instance = new B_field_initialized_as_class_property();
  return instance;
});

and see if it changes the results.

@lpinca I followed the suggestion - the difference reduced to one order of magnitude, but still surprisingly large:

A_field_initialized_in_constructor x 77,519,027 ops/sec 卤1.85% (90 runs sampled)
B_field_initialized_as_class_property x 6,270,322 ops/sec 卤1.21% (88 runs sampled)

Test:

const { Options, Suite } = require("benchmark");

const runBenchmarkSuite = (suite) => {
  return new Promise((resolve, reject) => {
    suite
      .on("start", (e) => console.log(`Running test: ${e.currentTarget.name}`))
      .on("cycle", (e) => console.log(String(e.target)))
      .on("complete", (e) => console.log(`Fastest is ${e.currentTarget.filter("fastest").map("name")}`))
      .on("complete", resolve)
      .on("error", reject)
      .run();
  });
};

class A_field_initialized_in_constructor {
  constructor() {
    this.foo = true;
  }

}

class B_field_initialized_as_class_property {
  foo = true;
}

let instance;
runBenchmarkSuite(new Suite("fields")
  .add("A_field_initialized_in_constructor", () => {
    instance = new A_field_initialized_in_constructor();
    return instance;
  })
  .add("B_field_initialized_as_class_property", () => {
    instance = new B_field_initialized_as_class_property();
    return instance;
  })
);

I'll report to v8.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

srl295 picture srl295  路  3Comments

seishun picture seishun  路  3Comments

mcollina picture mcollina  路  3Comments

fanjunzhi picture fanjunzhi  路  3Comments

danielstaleiny picture danielstaleiny  路  3Comments