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