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
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.