Polymorphism overwhelming inline caches - javascript

How I understand inline caches, there is no check for "or further down in the hidden class tree" (would be costly to do without having some trick). Even though an instance property from a base class would always be at the same offset, an access of a base class property encountering many different subclass instances would mean its inline cache gets overwhelmed (or does it? Perhaps I am wrong here already, strongly typed languages usually do cover this case, perhaps there is a trick being used?). This also would apply to objects in general.
This left me wondering: instead of subclasses, wouldn't it sometimes be better, to have two objects, one with the base properties, one with the subclass properties, both having a reference to the other?
Class extension version:
class A {
x = 0;
}
class B1 extends A {
y = 0;
}
class B2 extends A {
z = 0;
}
const b1 = new B1();
const b2 = new B2();
const f = (p: A) => { p.x; };
// Different hidden classes.
f(b1);
f(b2);
Linked objects version:
class A<T> {
x = 0;
extension: T;
constructor(extension: T) { this.extension = extension; }
}
class B1 {
a: A<B1>;
y = 0;
constructor() { this.a = new A(this); }
}
class B2 {
a: A<B2>;
z = 0;
constructor() { this.a = new A(this); }
}
const b1 = new B1();
const b2 = new B2();
const a1 = b1.a;
const a2 = b2.a;
const f = <T,>(p: A<T>) => { p.x; };
// Same hidden class.
f(a1);
f(a2);
Does this have any tangible performance impact? I guess the answer is always "don't care until you measured it's your bottleneck", but I am left wondering.

In short: Yes.
Subclassing can lead to excessive polymorphism when an engine uses hidden classes and equality comparisons on these hidden classes (like V8 does). (And for the record, that doesn't imply that hidden classes are a bad idea -- they just happen to have both benefits and limitations, like any engineering technique.)
Modifying your object layout can be a good way to avoid that. How exactly to do that depends a lot on your requirements. You may or may not need the bidirectional links; e.g. a variant that might be good enough to address some common usage scenarios is:
class A {
x = 0;
extension;
constructor(extension) { this.extension = extension; }
}
function MakeB1() {
return new A({y: 0});
}
function MakeB2() {
return new A({z: 0});
}
// And then the rest is very similar to your first example, just
// with "MakeB1" instead of "new B1":
const b1 = MakeB1();
...
f(b1);
You may want to include a type property in class A, then you can do things like
if (a.type == B1) ProcessB1(a) else if (a.type == B2) ProcessB2(a);.
Does this have any tangible performance impact?
It might, sometimes...
I guess the answer is always "don't care until you measured it's your bottleneck"
...absolutely. Many/most apps don't need to care, but for certain performance-sensitive code, optimizations like these might help quite a bit. Only profiling can tell you which parts of your app might be worth optimizing.

Related

Do derived classes enable property lookup Inline Cache in V8? [duplicate]

How I understand inline caches, there is no check for "or further down in the hidden class tree" (would be costly to do without having some trick). Even though an instance property from a base class would always be at the same offset, an access of a base class property encountering many different subclass instances would mean its inline cache gets overwhelmed (or does it? Perhaps I am wrong here already, strongly typed languages usually do cover this case, perhaps there is a trick being used?). This also would apply to objects in general.
This left me wondering: instead of subclasses, wouldn't it sometimes be better, to have two objects, one with the base properties, one with the subclass properties, both having a reference to the other?
Class extension version:
class A {
x = 0;
}
class B1 extends A {
y = 0;
}
class B2 extends A {
z = 0;
}
const b1 = new B1();
const b2 = new B2();
const f = (p: A) => { p.x; };
// Different hidden classes.
f(b1);
f(b2);
Linked objects version:
class A<T> {
x = 0;
extension: T;
constructor(extension: T) { this.extension = extension; }
}
class B1 {
a: A<B1>;
y = 0;
constructor() { this.a = new A(this); }
}
class B2 {
a: A<B2>;
z = 0;
constructor() { this.a = new A(this); }
}
const b1 = new B1();
const b2 = new B2();
const a1 = b1.a;
const a2 = b2.a;
const f = <T,>(p: A<T>) => { p.x; };
// Same hidden class.
f(a1);
f(a2);
Does this have any tangible performance impact? I guess the answer is always "don't care until you measured it's your bottleneck", but I am left wondering.
In short: Yes.
Subclassing can lead to excessive polymorphism when an engine uses hidden classes and equality comparisons on these hidden classes (like V8 does). (And for the record, that doesn't imply that hidden classes are a bad idea -- they just happen to have both benefits and limitations, like any engineering technique.)
Modifying your object layout can be a good way to avoid that. How exactly to do that depends a lot on your requirements. You may or may not need the bidirectional links; e.g. a variant that might be good enough to address some common usage scenarios is:
class A {
x = 0;
extension;
constructor(extension) { this.extension = extension; }
}
function MakeB1() {
return new A({y: 0});
}
function MakeB2() {
return new A({z: 0});
}
// And then the rest is very similar to your first example, just
// with "MakeB1" instead of "new B1":
const b1 = MakeB1();
...
f(b1);
You may want to include a type property in class A, then you can do things like
if (a.type == B1) ProcessB1(a) else if (a.type == B2) ProcessB2(a);.
Does this have any tangible performance impact?
It might, sometimes...
I guess the answer is always "don't care until you measured it's your bottleneck"
...absolutely. Many/most apps don't need to care, but for certain performance-sensitive code, optimizations like these might help quite a bit. Only profiling can tell you which parts of your app might be worth optimizing.

How to set an object property value to its own variable name (during initialization if possible)?

question
How to set an object property value to its own variable name (during initialization if possible)?
eg
For example, to create a Enum (inside class AA) in Javascript:
class AA {
static Color = {
Red: 'Red',
Green: 'Green',
Blue: 'Blue',
}
}
I have to repeat the String name everytime.
Is there a simpler way to do this, something like eg:
class AA {
static Color = {
Red: this.currentProperty.name,
Green: this.currentProperty.name,
Blue: this.currentProperty.name,
}
}
requirements (not mandatory) & comments
Please make this As Simple As Possible (dont be complicated/cumbersome).
(The hardest requirement. It would be best in the form shown above (during initialization).
I may be a bit picky on this... (I know this is very subjective, and such thing may not even exist)
though, any other thoughts are still welcome)
The variable value & variable name can be refactored at the same time -- without the need to change them separately.
It doesnt have to be an Enum (-- this topic is not limited to Enum only, a normal object will do)
Try to use Es6+
Try to let Jsdoc able to recognize this as an Enum (maybe the use of #emun (?<)), (mainly for autocompletion / Type hint on Vscode)
Try to let Debugger able to recognize this as an Enum & able to view the value as a meaningful string
Im aware of there are some Enum lib in github eg, not sure they are good enough / fit my style.
Im aware of the use of Symbol() on Enum
Im aware of need to make Enum immutable (private + getter / Object.freeze)
I dont think Object.keys() can help. (too cumbersome?)
class AA {
static Color = Object.fromEntries(['Red','Green','Blue'].map(i=>[i,i]))
}
console.log(AA.Color)
or, with a helper method:
function makeEnum(...props) { return Object.fromEntries(props.map(i=>[i,i])) }
class AA {
static Color = makeEnum('Red','Green','Blue')
}
console.log(AA.Color)
this might help with autocompletion:
function makeEnum(obj) { return Object.fromEntries(Object.keys(obj).map(i=>[i,i])) }
class AA {
static Color = makeEnum({Red:'', Green:'', Blue:''})
}
console.log(AA.Color)
or using a proxy:
function makeEnum() {
let o = {}, p = new Proxy(o, {get:(_,i)=>(i==='enum'?o:(o[i]=i,p))})
return p
}
class AA {
static Color = makeEnum().Red.Green.Blue.enum
}
console.log(AA.Color)
including Object.freeze() to prevent reassignment:
function makeEnum() {
let o = {}, p = new Proxy(o, {get:(_,i)=>
(i==='enum'?(Object.freeze(o),o):(o[i]=i,p))})
return p
}
class AA {
static Color = makeEnum().Red.Green.Blue.enum
}
console.log(AA.Color)
AA.Color.Red = 'Yellow'
console.log(AA.Color)
another proxy variant: the new keyword is used to trigger freezing of the object:
function Enum() {
let o={}, p = new Proxy(function() {}, {
construct: () => (Object.freeze(o),o),
get:(_,i)=>(o[i]=i,p)
});
return p;
}
class AA {
static Color = new (Enum().Red.Green.Blue)
}
console.log(AA.Color)
console.log(AA.Color.Red)
AA.Color.Red = 'Yellow' // frozen object can't be changed
console.log(AA.Color.Red)
AA.Color.Orange = 'Orange' // frozen object can't accept new properties
console.log(AA.Color)

Weird behavior when declaring class property with semicolon

class SomeClass {
x: 5;
y = 10;
}
const c = new SomeClass();
alert(c.x + ' : ' + c.y);
Why is the code compilable but the value of c.x is undefined?
What is the effect of declaring a class property with :?
Regarding the x: 5 part, although this is a valid javascript code, there is no much use for it.
This is a javascript label and it used (if any) mostly within loops context.
So to answer your questions:
Why is the code compilable
Because technically this is a valid javascript code (yet not a valid class field).
but the value of c.x is undefined
Because the x is a label and not a class field.
What is the effect of declaring a class property with :
You get a label instead of a class field.
Addendum
Another common mistake, is this code of block:
class SomeClass {
z = () => {
x: 5;
};
}
You would think that z() will return an object with an x key:
`{x:5}`
But actually you have a function with a label of x that just run an expression of 5.
Just for completeness sake, the fix will be either to add an explicit return and another set of curly braces
() => {return {x: 5}}
Or just wrap the whole thing with parentheses
() => ({x: 5})
Edit
As a followup to the comments below:
Just to be clear, your code compiles on several environments that i tested as well as stack-snippets as can be seen below:
class SomeClass {
x: 5;
y = 10;
}
const c = new SomeClass();
console.log(c.x + ' : ' + c.y);
The code is not valid ES6.
You seem to be "compiling" with babel, and have inadvertently enabled the flow syntax extension (and also class properties for the second line). In flow, x: 5 is a class field type annotation. Of course, 5 as a type doesn't make sense, but apparently they allow pretty arbitrary expressions.

Creating millions of Objects in Javascript

Let me be the first to say that this isn't something I normally do, but out of curiousity, I'll see if anyone has a good idea on how to handle a problem like this.
The application I am working on is a simulated example of the game Let's make a Deal featuring the Monty Hall problem.
I won't go into details about my implementation, but it more or less allows a user to enter a number of how many games they want to simulate, and then if an option is toggled off, the player of those x games won't switch their choice, while if it is toggled on, they will switch their choice every single instance of the game.
My object generator looks like this:
const game = function(){
this[0] = null;
this[1] = null;
this[2] = null;
this.pick = Math.floor(Math.random() * 3);
this.correctpick = Math.floor(Math.random() * 3);
this[this.correctpick] = 1;
for (let i=0; i<3; i++){
if ((this[i] !== 1) && (i !== this.pick)){
this.eliminated = i;
break;
}
}
}
const games = arg => {
let ret = [];
for(let i=0; i<arg; i++){
ret.push(new game);
}
return ret;
}
This structure generates an array which i stringify later that looks like this:
[
{
"0": 1,
"1": null,
"2": null,
"pick": 2,
"correctpick": 0,
"eliminated": 1
},
{
"0": null,
"1": null,
"2": 1,
"pick": 2,
"correctpick": 2,
"eliminated": 0
}
]
As sloppy as the constructor for game looks, the reason is because I have refactored it into having as few function calls as possible, where now I'm literally only calling Math functions at the current time (I removed any helper functions that made the code easier to read, in opt for performance).
This app can be ran both in the browser and in node (cross platform), but I have clamped the arg a user can pass into the games function to 5 million. Any longer than that and the process (or window) freezes for longer than a few seconds, or even potentially crashes.
Is there anything else I can do to increase performance if a huge number is given by a user? Also, if you need more information, I will be happy to supply it!
Thanks!
The obvious performance optimisation would be not to create and store 5 million objects at all, relieving memory pressure. Instead you'd create the objects on the fly only when you need them and throw them away immediately after. I'm not sure what your app does, but it sounds like you want to re-use the same game instances when evaluating results with the different options. In that case, you need to store them of course - but I'd advise to re-think the design and consider immediately evaluating each game with all possible options, accumulating only the results for each choice of options but not keeping all games in memory.
Apart from that, I'd recommend to simplify a bit:
You can drop that loop completely and use some clever arithmetic to find the eliminated option: this.eliminated = this.pick == this.correctpick ? +!this.pick : 3 - this.pick - this.correctpick;. Or use a simple lookup table this.eliminated = [1, 2, 1, 2, 0, 0, 1, 0, 0][this.pick * 3 + this.correctpick].
I'd avoid changing the type of the array elements from null (reference) to 1 (number). Just keep them as integers and initialise your elements with 0 instead.
Don't store 6 properties in your object that are completely redundant. You only need 2 of them: pick and correctpick - everything else can be computed on the fly from them when you need it. Precomputing and storing it would only be advantageous if the computation was heavy and the result was used often. Neither of this is the case, but keeping a low memory footprint is important (However, don't expect much from this).
Not sure about your implementation, but do you really need an Array?
How about only using results (see snippet)?
If it's blocking the browser that worries you, maybe delegating the work to a web worker is the solution for that: see this jsFiddle for a web worker version of this snippet.
(() => {
document.querySelector("#doit")
.addEventListener("click", playMontyHall().handleRequest);
function playMontyHall() {
const result = document.querySelector("#result");
const timing = document.querySelector("#timing");
const nOfGames = document.querySelector("#nGames");
const switchDoors = document.querySelector("#switchyn");
// Create a game
const game = (doSwitch) => {
const doors = [0, 1, 2];
const pick = Math.floor(Math.random() * 3);
const correctPick = Math.floor(Math.random() * 3);
const eliminated = doors.filter(v => v !== pick && v !== correctPick)[0];
return {
correctpick: correctPick,
pick: doSwitch ? doors.filter(v => v !== pick && v !== eliminated)[0] : pick,
eliminated: eliminated,
};
};
const getWinner = game => ~~(game.correctpick === game.pick);
// Sum wins using a generator function
const winningGenerator = function* (doSwitch, n) {
let wins = 0;
while (n--) {
wins += getWinner(game(doSwitch));
yield wins;
}
};
// calculate the number of succeeded games
const calculateGames = (nGames, switchAlways) => {
const funNGames = winningGenerator(switchAlways, nGames);
let numberOfWins = 0;
while (nGames--) {
numberOfWins = funNGames.next().value;
}
return numberOfWins;
}
const cleanUp = playOut => {
result.textContent =
"Playing ... (it may last a few seconds)";
timing.textContent = "";
setTimeout(playOut, 0);
};
const report = results => {
timing.textContent = `This took ${
(performance.now() - results.startTime).toFixed(3)} milliseconds`;
result.innerHTML =
`<b>${!results.switchAlways ? "Never s" : "Always s"}witching doors</b>:
${results.winners} winners out of ${results.nGames} games
(${((results.winners/+results.nGames)*100).toFixed(2)}%)`;
};
// (public) handle button click
function clickHandle() {
cleanUp(() => {
const nGames = nOfGames.value || 5000000;
const switchAlways = switchDoors.checked;
report({
switchAlways: switchAlways,
startTime: performance.now(),
winners: calculateGames(nGames, switchAlways),
nGames: nGames
});
});
}
return {
handleRequest: clickHandle
};
}
})();
body {
margin: 2em;
font: normal 12px/15px verdana, arial;
}
#timing {
color: red;
}
<p><input type="number" id="nGames" value="5000000"> N of games</p>
<p><input type="checkbox" id="switchyn"> Always switch doors</p>
<p><button id="doit">Play</button>
<p id="result"></p>
<p id="timing"></p>

How to know if you are writing 'good' javascript- lots of ways to do same thing?

I have only really looked at python before where there is one way to do anything, javascript is really hard to get an order of how to do things for me.
For example, in the elequentjavascript teaching series, one of the module tests was to output the average ages of each century from an 'ancestry' object. My answer was vastly different from the websites, is it just experience which shows you the correct way?
Website solution:
function average(array) {
function plus(a, b) { return a + b; }
return array.reduce(plus) / array.length;
}
function groupBy(array, groupOf) {
var groups = {};
array.forEach(function(element) {
var groupName = groupOf(element);
if (groupName in groups)
groups[groupName].push(element);
else
groups[groupName] = [element];
});
return groups;
}
var byCentury = groupBy(ancestry, function(person) {
return Math.ceil(person.died / 100);
});
for (var century in byCentury) {
var ages = byCentury[century].map(function(person) {
return person.died - person.born;
});
console.log(century + ": " + average(ages));
}
My attempt:
function displayCenturyAges(array, centuryStart, centuryEnd) {
var ages = 0;
var tempObject = {};
for (var i = centuryStart; i <= centuryEnd; i += 100) {
tempObject[i] = array.filter(function (people) {
return (people.died - i) < 100 && (people.died - i) > 0;
}).map(function (people) {
return people.died - people.born;
});
}
for (var key in tempObject) {
ages = 0;
if (tempObject.hasOwnProperty(key)) {
tempObject[key].forEach(function (elements) {
ages += elements;
});
console.log(key + " average age: " + ages / tempObject[key].length);
}
}
}
I would say "good" Javascript is a combination of clarity and optimization, which is dependent on the type of project you are working on. If you are writing code for an enterprise company, then the code should have optimization as its primary concern, which may sacrifice clarity, but good documentation should take care of that. If you are working with a group of friends on a fun project, then I would say not to worry too much about optimization, but make sure your code is clearly understandable so that other people know what is going on. I hope this helps!
In the world of Software, never exist a "correct way to do" something, exist many ways to solve a problem, it depends of each person, and every one that works, can be applied to a correct answer.
When we talk about which one works better, we entered on another face to the software.
I called: "quality of software development proccess" and "Software engineer", those subjects talk about a better way to developt a software.
It depends on your target it have many ways to make your code and software better, some examples:
Clearity:
Software standards (you can make your own standard)
Design patterns (Singletone, Factory, EAV, Observe, etc...)
Models (MVC model, HMVC, view oriented).
Optimization:
Algoritms and data structures
RUn time Optimization
User interface designs
DB Query optimization
Quality:
Implements process models
Test cases
So exist one correct way?, no, exist many ways to solve it, Which is better? deppends on your approach, which is correct? every one that works and fit to your espectations.

Categories

Resources