System: Linux (4.11.3-1-ARCH) 64bit on i5-3475S CPU @ 2.90GHz, 8205MB RAM
Software versions:
Benchmarks were cloned from https://github.com/def-/nim-benchmarksgame and modified to run and give useable time measurements in the browser (source not yet on GitHub, will follow). Nim used clang for the native target and emcc (the emscripten compiler) for the Webassembly (wasm) and Asm.js targets. Results are given in seconds with 30000000 cycles for each benchmark:
Ffox | Ffox | Ffox | Chrom | Chrom | Chrom | ||
benchmark | native | wasm | asmjs | nim js | wasm | asmjs | nim js |
---|---|---|---|---|---|---|---|
n-body problem (float64) | 3.43 | 4.06 | 3.94 | 13.10 | 5.56 | 4.87 | 5.46 |
fibonacci (iterative, int64) | 0.69 | 1.26 | 5.38 | 1.29 | 1.52 | 11.27 | 1.93 |
fibonacci (iterative, int32) | 0.90 | 0.99 | 1.07 | 1.35 | 1.06 | 1.04 | 1.94 |
fibonacci (tail recursive, int64) | 0.91 | 1.28 | 7.76 | 4.00 | 1.31 | 10.33 | 6.34 |
fibonacci (tail recursive, int32) | 0.65 | 0.98 | 0.99 | 4.05 | 1.02 | 1.12 | 6.35 |
WebAssembly is the winner for me, it shows solid and consistent performance pretty close to native code. Since wasm is a 32-bit compilation target for now, the performance drop with 64-bit calculations was to be expected. In the JavaScript realm, i.e. for the asmjs and nim js targets, things get more volatile. The Asm.js code has a massive problem with 64-bit integers. JavaScript code generated by Nim holds up remarkably well, probably because it looks a lot like Asm.js already and is probably treated as such where possible by browsers. I didn't expect tail recursion to help performance in Javascript code, but that it is such a problem baffles me.
The local NIM config file (nim.cfg) used was:
cc = clang
@if emscripten:
clang.exe = "emcc"
clang.linkerexe = "emcc"
clang.options.linker = ""
cpu = "i386"
@if wasm:
passC = "-s WASM=1 -s 'BINARYEN_METHOD=\"native-wasm\"' -Iemscripten"
passL = "-O3 -s WASM=1 -s 'BINARYEN_METHOD=\"native-wasm\"' -Lemscripten"
@elif asmjs:
passC = "-s ASM_JS=1 --separate-asm -Iemscripten"
passL = "-O3 -s ASM_JS=1 --separate-asm -Lemscripten"
@else:
# this should produce JavaScript, but actually compiles to asm.js with -O1 or higher
passC = "-Iemscripten"
passL = "-Lemscripten"
@end
@end
Compiler commands:
Thanks for performing these benchmarks.
I frankly find it to be pretty amazing that Nim's Javascript target produces such reasonable performance. I can also see why Chrome decided to move towards Firefox's implementation of asm.js as Firefox's performance in that arena was better under almost every test.
I'm really excited to see where asm.js takes the web in terms of fully featured and cross-platform applications. We may even see close to native performance levels for in-browser applications. Theoretically, the only real bottleneck is data latency with the server.
I hope Nim has a large part to play in this future. It would be really nice to write both my back-end and front-end code in the same language. I know you can currently do this with js and Node, but Javascript's type system is infuriating and Node is a huge runtime dependency.