ChipmunkJS and Emscripten

I've finally gotten around to compiling Chipmunk to JS using Emscripten to see what happens. It works great.

As a baseline, here's the first benchmark running in C:

Time(a) =  1451.45 ms (benchmark - SimpleTerrainCircles_1000)  

The same benchmarks running with chipmunkjs in node 0.10.12 (v8: 3.14.5.9):

$ node bench.js 
SimpleTerrainCircles 1000  
Run 1: 22426  
Run 2: 21808  

(ie, 21 seconds, 15x baseline)

Using v8 head (v8: 3.19.18.5):

$ ../v8/out/native/d8 bench.js
SimpleTerrainCircles 1000  
Run 1: 11248  
Run 2: 12930  

(8x baseline)

Emscripten (-O2 -DNDEBUG), v8: 3.19.18.5 in Chrome Canary:

Time(a) =  3967.12 ms (benchmark - SimpleTerrainCircles_1000)  

(2.7x baseline)

In Firefox 22 (which has asmjs support) (Firefox nightly (25a) has about the same performance):

Time(a) =  2044.10 ms (benchmark - SimpleTerrainCircles_1000)  

(1.4x - only 40% slower than C!!!)

The V8 team is actively working on making asmjs code run faster in v8. They don't want to have a special 'asm.js mode' like firefox does - instead they're adding optimizations which can kick in for asmjs-style code (source: insiders on the Chrome team). I expect Chrome performance to catch up to firefox performance in the next ~6 months or so.

Notes:

  • I didn't make any changes to chipmunk (although I did bump chipmunkjs tests runs back up to 1000 to match chipmunk). My test code is here

  • I compiled the benchmark code from C using emscripten. If your game is written in javascript, performance will be worse than this.

  • These numbers are approximate. I didn't run the benchmarks multiple times and I have a million things open on my machine. I doubt they'll be off by more than ~10% though.

  • Downloaded filesize increases by nearly 3x. Chipmunk-js is 170k, or 17k minified & gzipped. With emscripten the output is 300k, minified & gzipped to 49k. This is way bigger.

  • We can expose most of chipmunk directly to javascript. Unfortunately, we can't share vectors from inside the emscripten environment and outside of it - emscripten (obviously) inlines vectors inside its own heap & stack. In javascript, the best we can do is use objects in the JS heap. Our options are either removing vectors (as much as possible) from the API (cpBodySetPos(v) -> cpBodySetPos(x, y)), writing some javascript wrappers around everything to bridge between a javascript vector type and a C vector type or putting vectors in the emscripten heap (which would be faster than a JS bridge, but require that you match cpv() calls with cpvFree() or something. All options are kind of nasty.

  • Emscripten doesn't use the GC, so you can now leak memory if you don't cpSpaceFree(), etc.

  • As well as running faster, its easier to port code like this. Keeping chipmunkjs updated with the latest version of chipmunk should mostly just require a rebuild.