chakra-core / chakra-core/ChakraCore

Optimize class constructor calls

Open
#3,869 3 comments 0 reactions 0 assignees View on GitHub
Performance Task
Dominant language
JavaScript
Stars
9.3k
Forks
1.2k
PR merge metrics
No merged PRs in 30d

Description

We have seen in some benchmarks that class constructor calls can be extremely slow compared to object literal assignment.

For example, replacing calls to `new Point()` with `{x:0, y:0}` improves perf by 50% in the benchmark below (brought up by #3828):

````js
class Point {
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
};
}

class Param {
constructor(x1 = 0, x2 = 0, a = 0, b = 0, c = 0, d = 0) {
this.x1 = x1;
this.x2 = x2;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
};
}

class Spline {
constructor(input) {
const clen = input.length - 1;

this.last = 0;
this.params = [];

let ds = new Float64Array(clen);
let ms = new Float64Array(clen);

for (let i = 0; i < clen; i++) {
let c = input[i];
let n = input[i + 1];

const dx = n.x - c.x;
const dy = n.y - c.y;

ds[i] = dx;
ms[i] = dy / dx;

const p = new Param();

if (i) {
c = ms[i - 1];
n = ms[i];

if ((c * n) <= 0) {
p.c = 0.0;
} else {
const pdx = ds[i - 1];
const com = pdx + dx;

p.c = 3.0 * com / ((com + dx) / c + (com + pdx) / n);
}
} else {
p.c = ms[0];
}

this.params[i] = p;
}

this.params[clen] = new Param();
this.params[clen].c = ms[clen - 1];

for (let i = 0; i < clen; i++) {
const p = this.params[i];
const c = input[i];
const n = input[i + 1];
const m = ms[i];

p.x1 = c.x;
p.x2 = n.x;
p.d = c.y;

const inv = 1.0 / ds[i];
const com = p.c + this.params[i + 1].c - m - m;

p.b = (m - p.c - com) * inv;
p.a = com * inv * inv;
}

this.params[clen - 1].x2 = Number.MAX_VALUE;
};

evaluate(x) {
let c = this.params[this.last];

if (x < c.x1 || x > c.x2) {
for (let i = 0, len = this.params.length; i < len; i++) {
if (x <= this.params[i].x2) {
this.last = i;
c = this.params[i];
break;
}
}
}

const dif = x - c.x1;
const out = new Point();

out.x = ((c.a * dif + c.b) * dif + c.c) * dif + c.d;
out.y = (3.0 * c.a * dif + 2.0 * c.b) * dif + c.c;

return out;
};
}

class Dac {
constructor(bits = 8) {
this.dac = new Float32Array(bits);
};

kinked(model) {
const len = this.dac.length;
const inf = 1e6;

let r2 = 2.20;
let term = false;
let vsum = 0;

if (model == 2) {
r2 = 2.00;
term = true;
}

for (let i = 0; i < len; i++) {
let vn = 1;
let rn = (term) ? r2 : inf;
let b, ti;

for (b = 0; b < i; b++) {
rn = (rn == inf) ? (1 + r2) : (1 + r2 * rn / (r2 + rn));
}

if (rn == inf) {
rn = r2;
} else {
rn = r2 * rn / (r2 + rn);
vn = vn * rn / r2;
}

for (++b; b < len; b++) {
rn++;
ti = vn / rn;
rn = r2 * rn / (r2 + rn);
vn = rn * ti;
}

vsum += vn;
this.dac[i] = vn;
}

vsum /= (1 << len);

for (let i = 0; i < len; i++) {
this.dac[i] /= vsum;
}
};

output(input) {
let value = 0;

for (let i = 0, len = this.dac.length; i < len; i++) {
if (input & (1 << i)) {
value += this.dac[i];
}
}

return value;
};
}

class OpAmp {
constructor(opamp, kvddt) {
this.opamp = new Spline(opamp);
this.kvddt = kvddt;
this.vmin = opamp[0].x;
this.vmax = opamp[opamp.length - 1].x;

this.x = 0;
};

reset() {
this.x = this.vmin;
};

solve(n, vi) {
let ak = this.vmin;
let bk = this.vmax;

const a = n + 1;
const b = this.kvddt;

let bvi = b - vi;
if (bvi < 0) { bvi = 0; }

const c = n * (bvi * bvi);

do {
const xk = this.x;

let out = this.opamp.evaluate(xk);
let bvx = b - xk;
let bvo = b - out.x;

if (bvx < 0) { bvx = 0; }
if (bvo < 0) { bvo = 0; }

const f = a * (bvx * bvx) - c - (bvo * bvo);

this.x -= (f / (2 * (bvo * out.y - a * bvx)));

if (Math.abs(this.x - xk) < 1e-8) {
out = this.opamp.evaluate(this.x);
return out.x;
}

if (f < 0) {
bk = xk;
} else {
ak = xk;
}

if (this.x <= ak || this.x >= bk) {
this.x = (ak + bk) * 0.5;
}
} while (true);
};
}

class Filter {
constructor() {
this.gain = null;
this.mixer = null;
this.resonance = null;
this.summer = null;

this.enabled = true;
this.filter = 0;
this.volume = 0;
this.vhp = 0;
this.vbp = 0;
this.vlp = 0;
this.ve = 0;
this.fc = 0;
this.hp = false;
this.bp = false;
this.lp = false;
this.filt1 = false;
this.filt2 = false;
this.filt3 = false;
this.filtE = false;
this.voice3off = false;

this.setEnable = (value) => {
this.enabled = value;

if (value) {
this.resFilt = this.filter;
} else {
this.filt1 = false;
this.filt2 = false;
this.filt3 = false;
this.filtE = false;
}
}
};

set fcLo(value) {
this.fc = (this.fc & 0x7f8) | (value & 7);
this.updateCenterFreq();
};

set fcHi(value) {
this.fc = ((value << 3) & 0x7f8) | (this.fc & 7);
this.updateCenterFreq();
};

set modeVol(value) {
this.volume = value & 0x0f;

this.lp = (value & 0x10) != 0;
this.bp = (value & 0x20) != 0;
this.hp = (value & 0x40) != 0;

this.voice3off = (value & 0x80) != 0;

this.updateMixing();
};

set resFilt(value) {
this.filter = value;
this.updateResonance((value >> 4) & 0x0f);

if (this.enabled) {
this.filt1 = (value & 1) != 0;
this.filt2 = (value & 2) != 0;
this.filt3 = (value & 4) != 0;
this.filtE = (value & 8) != 0;
}

this.updateMixing();
};

reset() {
this.fc = 0;
this.updateCenterFreq();

this.modeVol = 0;
this.resFilt = 0;
};
}

class Model6581 {
constructor() {
const voltage = [
new Point( 0.81, 10.31),
new Point( 2.40, 10.31),
new Point( 2.60, 10.30),
new Point( 2.70, 10.29),
new Point( 2.80, 10.26),
new Point( 2.90, 10.17),
new Point( 3.00, 10.04),
new Point( 3.10, 9.83),
new Point( 3.20, 9.58),
new Point( 3.30, 9.32),
new Point( 3.50, 8.69),
new Point( 3.70, 8.00),
new Point( 4.00, 6.89),
new Point( 4.40, 5.21),
new Point( 4.54, 4.54),
new Point( 4.60, 4.19),
new Point( 4.80, 3.00),
new Point( 4.90, 2.30),
new Point( 4.95, 2.03),
new Point( 5.00, 1.88),
new Point( 5.05, 1.77),
new Point( 5.10, 1.69),
new Point( 5.20, 1.58),
new Point( 5.40, 1.44),
new Point( 5.60, 1.33),
new Point( 5.80, 1.26),
new Point( 6.00, 1.21),
new Point( 6.40, 1.12),
new Point( 7.00, 1.02),
new Point( 7.50, 0.97),
new Point( 8.50, 0.89),
new Point(10.00, 0.81),
new Point(10.31, 0.81)
];

const ut = 26.0e-3;
const vdd = 12.18;
const vth = 1.31;
const wlvcr = 9;

this.voltrange = 1.5;
this.dcvoltage = 5;
this.c = 470e-12;
this.k = 1;
this.kvddt = this.k * (vdd - vth);
this.ucox = 20e-6;
this.wlsnake = 1 / 115;

this.vmin = voltage[0].x;
this.vmax = voltage[0].y;

if (this.kvddt > this.vmax) {
this.vmax = this.kvddt;
}

this.denorm = this.vmax - this.vmin;
this.norm = 1 / this.denorm;
this.n16 = this.norm * 65535;
this.voiceDc = (this.n16 * (this.dcvoltage - this.vmin)) >> 0;
this.scale14 = ((this.norm * 16383) * this.voltrange) >> 0;

this.gain = [];
this.mixer = [];
this.summer = [];

this.dacbits = 11;
this.daczero = 6.65;
this.dacscale = 2.63;

this.dac = new Dac(this.dacbits);
this.dac.kinked(1);

this.opampRev = new Uint16Array(65536);
this.vcrkvg = new Uint16Array(65536);
this.vcrnTerm = new Uint16Array(65536);

const scaled = [];

for (let i = 0, len = voltage.length; i < len; i++) {
let p = new Point();
let o = voltage[i];

p.x = (this.n16 * (o.x - o.y) + 65536) / 2;
p.y = this.n16 * (o.x - this.vmin);

scaled[i] = p;
}

const s = new Spline(scaled);

for (let i = 0; i < 65536; i++) {
const o = s.evaluate(i);
if (o.x < 0) { o.x = 0; }
this.opampRev[i] = o.x + 0.5;
}

const model = new OpAmp(voltage, this.kvddt);

for (let i = 0; i < 5; i++) {
const idiv = 2 + i;
const size = idiv << 16;
const narr = new Uint16Array(size);

model.reset();

for (let vi = 0; vi < size; vi++) {
const vin = this.vmin + vi / this.n16 / idiv;
narr[vi] = ((model.solve(idiv, vin) - this.vmin) * this.n16) + 0.5;
}

this.summer[i] = narr;
}

for (let i = 0; i < 8; i++) {
let idiv = 1;
let size = 1;

if (i) {
idiv = i;
size = i << 16;
}

const narr = new Uint16Array(size);
const n = i * 8 / 6;

model.reset();

for (let vi = 0; vi < size; vi++) {
const vin = this.vmin + vi / this.n16 / idiv;
narr[vi] = ((model.solve(n, vin) - this.vmin) * this.n16) + 0.5;
}

this.mixer[i] = narr;
}

for (let i = 0; i < 16; i++) {
const narr = new Uint16Array(65536);
const n = i / 8;

model.reset();

for (let vi = 0; vi < 65536; vi++) {
const vin = this.vmin + vi / this.n16;
narr[vi] = ((model.solve(n, vin) - this.vmin) * this.n16) + 0.5;
}

this.gain[i] = narr;
}

const nkvddt = this.n16 * this.kvddt;
const nvmin = this.n16 * this.vmin;

const kvt = this.k * vth;
const is = 2 * this.ucox * ut * ut / this.k * wlvcr;
const n15 = this.norm * 32767;
const nis = n15 * 1.0e-6 / this.c * is;
const ut2 = 2 * ut;

for (let i = 0; i < 65536; i++) {
const log = Math.log1p(Math.exp((i / this.n16 - kvt) / ut2));
this.vcrnTerm[i] = (nis * log * log) + 0.5;

this.vcrkvg[i] = (this.k * (nkvddt - Math.sqrt(i << 16)) - nvmin) + 0.5;
}
};
}

console.time("worker");
const sid6581 = new Model6581();
console.timeEnd("worker");
````

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.