[Javascript Performance] Optimisation and deoptimization

The optimizing compiler optimizes for what it’s seen. If it sees something new, that’s problematic. 

Seleting properties has some strange implications on performacne.

 

We have seen that runing this code, interpertor can optimze js runtime:

const { performance } = require('perf_hooks');

let iterations = 1e7;

const a = 1;
const b = 2;

const add = (x, y) => x + y;

performance.mark('start');

while (iterations--) {
  add(a, b);
}

performance.mark('end');

performance.measure('My Special Benchmark', 'start', 'end');

const [ measure ] = performance.getEntriesByName('My Special Benchmark');
console.log(measure);

 

 

So what if we do:

performance.mark("start");

while (iterations--) {
  add(a, b);
}

add("foo", "bar"); // added this line

performance.mark("end");

So when we call add('foo', 'bar')we see deoptimizing happens.

 

If we do:


performance.mark("start");

while (iterations--) {
  add(a, b);
}

iterations = 1e7;

while (iterations--) {
  add(a, b);
}

performance.mark("end");

 We can see the result:

entryType: 'measure',
startTime: 41.05515700019896,
duration: 15.343078000470996,

 

If we add:

performance.mark("start");

while (iterations--) {
  add(a, b);
}

add('foo', 'bar') // add this back

iterations = 1e7;

while (iterations--) {
  add(a, b);
}

performance.mark("end");

Result:

  entryType: 'measure',
  startTime: 40.564633999951184,
  duration: 32.49434900004417,

It doubles the runtime from 18ms to 32ms.

 

If we add:

performance.mark("start");

%NeverOptimizeFunction(add); // add this line

while (iterations--) {
  add(a, b);
}

add("foo", "bar");

iterations = 1e7;

while (iterations--) {
  add(a, b);
}

performance.mark("end");

Run: node --allow-natives-syntax benchmark.js  

We can see the result:

  entryType: 'measure',
  startTime: 44.788716999813914,
  duration: 143.67414100002497,

If we don't allow JS engine to optimize our code, then it would be pretty slow

 

function add(x, y) {
  return x + y;
}

add(1, 2);
%OptimizeFunctionOnNextCall(add);
add(3, '4');

Run:  node --trace-opt --trace-deopt --allow-natives-syntax add.js

[manually marking 0x28a977194161 <JSFunction add (sfi = 0x28a97f871b41)> for non-concurrent optimization]
[compiling method 0x28a977194161 <JSFunction add (sfi = 0x28a97f871b41)> (target TURBOFAN) using TurboFan]
[optimizing 0x28a977194161 <JSFunction add (sfi = 0x28a97f871b41)> (target TURBOFAN) - took 0.278, 0.635, 0.037 ms]
[bailout (kind: deopt-soft, reason: Insufficient type feedback for binary operation): begin. deoptimizing 0x28a977194161 <JSFunction add (sfi = 0x28a97f871b41)>, opt id 0, bytecode offset 2, deopt exit 0, FP to SP delta 32, caller SP 0x7ff7b125e810, pc 0x000113c86050]

 

How does this works?

  • We use an interpreter because the optimizing compiler is slow to get started
  • Also: it needs some information before it knows what work it can either optimize or skip out on all together
  • So, the interpreter starts gathering feedback about what it sees as the function is used.

But what if a string slips in there?

The optimizing compiler optimizes for what it's seen. If it sees something new, that's problematic.

 

Let's mesure this code:

const { performance } = require("perf_hooks");

let iterations = 1000000;

class Point {
  constructor(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
}

performance.mark("start");

while (iterations--) {
  var point = new Point(1, 2, 3);
  //point.x = undefined;
  //delete point.x;
  //point.y = undefined
  //delete point.y
  //point.z = undefined
  //delete point.z
}

performance.mark("end");

performance.measure("My Special Benchmark", "start", "end");

const [measure] = performance.getEntriesByName("My Special Benchmark");
console.log(measure);

Run it to get baseline:

without point.x = undefined delete point.x point.y = undefined delete point.y point.z = undefined delete point.z
290.2347559928894 269.04967099428177 709.9430350065231 266.5608630031347 704.3972899913788 267.9879929870367 305.1125900000334
  • Deleting x & y, it is slow
  • Deleting z, it is faster

 

posted @ 2022-12-03 23:11  Zhentiw  阅读(54)  评论(0编辑  收藏  举报