Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The main difference between #4 and all other cases is that the first time when you use a closure as a constructor is always quite expensive. </p> <ol> <li><p>It is always handled in V8 runtime (not in generated code) and transition between compiled JS code and C++ runtime is quite expensive. Subsequent allocations usually are handled in generated code. You can take a look at <code>Generate_JSConstructStubHelper</code> in <code>builtins-ia32.cc</code> and notice that is falls through to the <code>Runtime_NewObject</code> when closure has no initial map. (see <a href="http://code.google.com/p/v8/source/browse/trunk/src/ia32/builtins-ia32.cc#138" rel="noreferrer">http://code.google.com/p/v8/source/browse/trunk/src/ia32/builtins-ia32.cc#138</a>)</p></li> <li><p>When closure is used as a constructor for the first time V8 has to create a new map (aka hidden class) and assign it as an <em>initial map</em> for that closure. See <a href="http://code.google.com/p/v8/source/browse/trunk/src/heap.cc#3266" rel="noreferrer">http://code.google.com/p/v8/source/browse/trunk/src/heap.cc#3266</a>. Important thing here is that maps are allocated in a separate memory space. This space can't be cleaned by a fast partial <em>scavenge</em> collector. When map space overflows V8 has to perform relatively expensive full <em>mark-sweep</em> GC. </p></li> </ol> <p>There are couple of other things happening when you use closure as a constructor for the first time but 1 and 2 are the main contributors to the slowness of test case #4.</p> <p>If we compare expressions #1 and #4 then differences are: </p> <ul> <li><h1>1 does not allocate a new closure every time;</h1></li> <li><h1>1 does not enter runtime every time: after closure gets initial map construction is handled in the fast path of generated code. Handling the whole construction in generated code is much faster then going back and forth between runtime and generated code;</h1></li> <li><h1>1 does not allocate a new initial map for each new closure every time;</h1></li> <li><h1>1 does not cause a mark-sweeps by overflowing map space (only cheap scavenges).</h1></li> </ul> <p>If we compare #3 and #4 then differences are:</p> <ul> <li><h1>3 does not allocate a new initial map for each new closure every time;</h1></li> <li><h1>3 does not cause a mark-sweeps by overflowing map space (only cheap scavenges);</h1></li> <li><h1>4 does less on JS side (no Function.prototype.call, no Object.create, no Object.prototype lookup etc) more on C++ side (#3 also enters runtime every time you do Object.create but does very little there).</h1></li> </ul> <p>Bottom line here is that the first time you use closure as a constructor is expensive compared to subsequent construction calls of the <em>same</em> closure because V8 has to setup some plumbing. If we immediately discard the closure we basically throw away all the work V8 has done to speedup subsequent constructor calls. </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload