Skip to content

Instantly share code, notes, and snippets.

@chrisdickinson
Last active December 21, 2015 13:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisdickinson/7d344ca7454adfd11a15 to your computer and use it in GitHub Desktop.
Save chrisdickinson/7d344ca7454adfd11a15 to your computer and use it in GitHub Desktop.

Run these with time WHICH=exN node run.js, where N is 1, 2, or 3.

For the first run, compare GC:

$ time WHICH=ex1 node --trace_gc run.js
$ time WHICH=ex2 node --trace_gc run.js
$ time WHICH=ex3 node --trace_gc run.js

Note that the "fast" examples don't more than twice-ish. Weird. Why would the closure one GC so much more that the others? The only garbage being generated is (potentially) boxed doubles + whatever it has to allocate for call itself.

...unless after a certain point, it doesn't need to allocate for the call, because the call has been inlined.

$ time WHICH=ex1 node --trace_inlining --trace_osr run.js
$ time WHICH=ex2 node --trace_inlining --trace_osr run.js
$ time WHICH=ex3 node --trace_inlining --trace_osr run.js

ex1's distance function can't be inlined! here's the code in v8 that's preventing it..

Were the loop in the same context, it could inline distance entirely, but since it's not, we have to keep the function call around and its associated garbage generation.

module.exports = Points
// with closure
function Points(x0, y0, x1, y1) {
this.distance = function distance() {
var x = x1 - x0
var y = y1 - y0
return Math.sqrt(x * x + y * y)
}
}
module.exports = Points
// this is the replaced version.
// note: the function represented
// by `Point#distance` is only ever
// allocated *once ever*, in comparison to
// the previous example's *once per class instantiation*.
function Points(x0, y0, x1, y1) {
this._points = {
x0: x0
, y0: y0
, x1: x1
, y1: y1
}
}
Points.prototype.distance = function distance() {
var p = this._points
, x = p.x1 - p.x0
, y = p.y1 - p.y0
return Math.sqrt(x * x + y * y)
}
module.exports = Points
// a modified form that ends up
// performing about the same as ex2.js
// NB: I'm not encouraging this style -- if
// you're writing a class, please use the `prototype`
// for methods!
function Points(x0, y0, x1, y1) {
this.x0 = x0
this.x1 = x1
this.y0 = y0
this.y1 = y1
this.distance = function distance() {
var x = this.x1 - this.x0
var y = this.y1 - this.y0
return Math.sqrt(x * x + y * y)
}
}
var Points = require('./' + (process.env.WHICH || 'ex1'))
var rand = Math.random
, iter = 1e8
// only allocate one "Points" -- we're not measuring allocation
// time, we just want to see why "distance" using closures might
// be slower than "distance" with object lookup.
var p = new Points(rand(), rand(), rand(), rand())
for(var j = 0; j < iter; ++j) {
p.distance()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment