Home > other >  Escape analysis for class instances
Escape analysis for class instances

Time:08-20

Does v8 do escape analysis for class instances, or is there some fundamental roadblock, which makes this excessively harder than for objects? In an example:

class P {
  x;
  y;
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  add(other) {
    return new P(this.x   other.x, this.y   other.y);
  }
  multiply(s) {
    return new P(this.x * s, this.y * s);
  }
}

const f = (a, b) => a.add(b).multiply(5).x;
// These allocations -^^^^^^-^^^^^^^^^^^

Are temporary instances allocated here, and is this avoidable? It is a simplified example for a naive vector implementation.


Following are checks i did, which make me suspect a difference in the first place. I am moderately new to the topic, and also not too proficient in asm. It is likely I made some silly mistake. Comparing the following, I see that the resulting code:

  • with plain objects, is considerably shorter, and has only one multiplication (expected)

  • exchanging with class instances, is around four times as long (just for the function f), and has two multiplications

    vmulsd xmm1,xmm1,xmm2
    -- no jumps here --
    vmulsd xmm0,xmm0,xmm2
    

    and to my naive eyes looks like everything is being allocated.

(put in snippets to collapse)

const add = (p1, p2) => ({ x: p1.x   p2.x, y: p1.y   p2.y });
const multiply = (p1, s) => ({ x: p1.x * s, y: p1.y * s });

const f = (a, b) => multiply(add(a, b), 5).x;

const rnd = () => Math.random() * 1000000;
const rndP = () => ({ x: rnd(), y: rnd() });

%PrepareFunctionForOptimization(f);
for (let i = 0; i < 10000; i  ) f(rndP(), rndP());
%OptimizeFunctionOnNextCall(f);
f(rndP(), rndP());

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

const add = (p1, p2) => new P(p1.x   p2.x, p1.y   p2.y);
const multiply = (p1, s) => new P(p1.x * s, p1.y * s);

const f = (a, b) => multiply(add(a, b), 5).x;

const rnd = () => Math.random() * 1000000;
const rndP = () => new P(rnd(), rnd());

%PrepareFunctionForOptimization(f);
for (let i = 0; i < 10000; i  ) f(rndP(), rndP());
%OptimizeFunctionOnNextCall(f);
f(rndP(), rndP());

node --allow-natives-syntax --print-opt-code --code-comments class.js > classAsm.txt (not on the newest node, but unless something changed in a recent v8 update, it shouldn't matter)

This in itself doesn't prove anything, and i am not proficient enough in the topic to understand the full code, especially with all the ICs etc I already have trouble identifying. I am aware that for most applications, the allocations are likely not going to pose a bottleneck, especially as they will be gen0 for GC. I am simply curious, to push my understanding of the topic, in case it does eventually matter.

CodePudding user response:

Does v8 do escape analysis for class instances

Yes.

Are temporary instances allocated here

No.

with class instances, [the code] has two multiplications

I see only one (for x; the operations on y are all dead-code-eliminated).

(This is with recent V8; I haven't checked older versions.)

  • Related