wcochran wcochran - 1 year ago 74
Javascript Question

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

I wrote another version where I use
instead and have a base object
that I inherit from to create new puzzles. Here is the

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
is very fast using Node:

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

The second version using
is consistently over 10x slower:

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

A similar question here noted that
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
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
. Thanks Bergi.

Answer Source

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download