Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Node's Object.create(foo) much slower than new Foo()?

I wrote a simple Sudoku solver using backtracking in JS. In effort to be "purely functional" all my 9x9 puzzle arrays are immutable thus a new array is created whenever a new number is inserted.

Version 1 using new SudokuPuzzle

In the first version I use the new Puzzle(puzzle) approach to clone the object:

function SudokuPuzzle(obj) {
   if (obj instanceof SudokuPuzzle) {
      this.grid = obj.grid.slice(0);  // copy array
   } // ...
}

Then whenever I update the array I do the following:

SudokuPuzzle.prototype.update = function(row, col, num) {
    var puzzle = new SudokuPuzzle(this); // clone puzzle
    puzzle.grid[row*9 + col] = num;      // mutate clone
    return puzzle;                       // return clone
}

Version 2 using Object.create()

I wrote another version where I use Object.create() instead and have a base object sudokuPuzzle that I inherit from to create new puzzles. Here is the clone() method:

sudokuPuzzle.clone = function() {
   var puzzle = Object.create(this); // create puzzle from sudokuPuzzle
   puzzle.grid = this.grid.slice(0); // copy array
   return puzzle;                    // return newly minted puzzle
}

My update method in this case is

sudokuPuzzle.update = function(row, col, num) {
   var puzzle = this.clone();       // clone puzzle
   puzzle.grid[row*9 + col] = num;  // mutate clone
   return puzzle;                   // return clone
}

Speed Tests

The first version using new is very fast using Node:

$ time node Sudoku.js
real    0m0.720s
user    0m0.699s
sys     0m0.016s

The second version using Object.create() is consistently over 10x slower:

$ time node Sudoku2.js
real        0m7.746s
user        0m7.647s
sys         0m0.091s

A similar question here noted that Object.create() is a lot slower in browsers, while I am also seeing a large disparity with node.js as well. I can certainly see timing discrepancies between JS engines, but a > 10x difference?!? Does anyone know why the difference is over an order of magnitude?!

You can find the source code here.

Update with Annotated Answer

Thanks to Bergi's answer below I changed the line in the clone method from

 var puzzle = Object.create(this); 

to this:

 var puzzle = Object.create(sudokuPuzzle); 

which avoids long and complex inheritance chains (i.e., I always inherit from the same base object) and I now get speed results comparable to using new. Thanks Bergi.

like image 714
wcochran Avatar asked Mar 23 '16 16:03

wcochran


People also ask

Which is the correct way to create an object in Javascript?

To create an object, use the new keyword with Object() constructor, like this: const person = new Object(); Now, to add properties to this object, we have to do something like this: person.

How many ways object can be created in Javascript?

You can create an object in three different ways: Using object literal. By creating instance of Object directly. By using constructor function.


1 Answers

You've already established that V8 fails to optimise Object.create as good as new (in contrast to SpiderMonkey). Or at least did historically. See also this blog post for details.

However, there is a second reason for this being a lot slower: your two codes have different results. You would have to use

SudokuPuzzle.prototype.clone = function() {
   var puzzle = Object.create(SudokuPuzzle.prototype); // a new instance, without `new`
   puzzle.grid = this.grid.slice(0); // copy array (usually initialised in constructor)
   return puzzle;                    // return newly minted puzzle
};

to create clones that are equivalent to those created using new SudokuPuzzle().

The problem is that when you use Object.create(this), you are creating a new object that has a different prototype - namely, the this instance. Cloning very often from each other, you are creating a very complex inheritance hierarchy. And all these objects, with different prototypes, will have different hidden classes - which prevents optimisations.

like image 164
Bergi Avatar answered Oct 05 '22 22:10

Bergi