a performant scheme interpreter in asm.js
TRANSCRIPT
A Performant Scheme interpreter in asm.js
Noah Van Es, Jens Nicolay, Quentin Stievenart, Theo D’Hondt, Coen De Roover
6 April 2016, Symposium on Applied Computing, Pisa
[email protected] @noahvanes github.com/noahvanes
Context: JavaScript
‣ Language of the web
‣ “Universal virtual machine”
‣ Supports other languages:
‣ popular compilation target
‣ implementation language for interpreters
Context: JavaScript
‣ Language of the web
‣ “Universal virtual machine”
‣ Supports other languages:
‣ popular compilation target
‣ implementation language for interpretersProblem: performance is unacceptable!
‣ Low-level subset of JavaScript
‣ very limited language ✓ no garbage collection
✓ static typing
‣ backward compatible
asm.js
“An extraordinarily optimizable, low-level subset of JavaScript”
- asmjs.org
‣ Optimizable
‣ ‘use asm’ annotation
‣ static typing, unboxed values, …
‣ AOT-compilation
asm.js
“An extraordinarily optimizable, low-level subset of JavaScript”
- asmjs.org
Compiling from C to asm.js
‣ Used as a compilation target
‣ Emscripten toolchain
C program LLVM IR asm.js
Compiling from C to asm.js
‣ Used as a compilation target
‣ Emscripten toolchain
‣ Portable IR
‣ can be further compiled AOT …
‣ … or executed directly as JS
C program LLVM IR asm.js
Integrating asm.js by hand
‣ Integrate handwritten asm.js into existing application
‣ Key questions:
‣ is asm.js human-writable?
‣ can existing interpreters migrate to asm.js?
‣ balance between development effort and performance?
SLIP: Simple Language Implementation Platform
‣ Scheme-like language
‣ Omits non-idiomatic features
‣ http://soft.vub.ac.be/~tjdhondt/PLE
SLIP: Simple Language Implementation Platform
‣ Scheme-like language
‣ Omits non-idiomatic features
‣ http://soft.vub.ac.be/~tjdhondt/PLE
SLIP: Base implementations
‣ Reference C implementation: ~6000 LOC
‣ Plain JS implementation: ~4000 LOC
‣ Design characteristics:
✓ continuation-passing style
✓ register-machine architecture
✓ low-level memory model
✓ garbage collection
✓ trampoline
✓ unified abstract grammar
Integrating asm.js
‣ Refactor critical modules to asm.js
MEMORY MANAGEMENT
ABSTRACT GRAMMAR
COMPILER
EVALUATOR
NATIVES
PRINTERPARSER
POOL
REPL
plain JS asm.js
Integrating asm.js
‣ Refactor critical modules to asm.js
MEMORY MANAGEMENT
ABSTRACT GRAMMAR
COMPILER
EVALUATOR
NATIVES
PRINTERPARSER
POOL
REPL
plain JS asm.js
Integrating asm.js
‣ Refactor critical modules to asm.js
MEMORY MANAGEMENT
ABSTRACT GRAMMAR
COMPILER
EVALUATOR
NATIVES
PRINTERPARSER
POOL
REPL
plain JS asm.js
Integrating asm.js
‣ Refactor critical modules to asm.js
MEMORY MANAGEMENT
ABSTRACT GRAMMAR
COMPILER
EVALUATOR
NATIVES
PRINTERPARSER
POOL
REPL
plain JS asm.js
Integrating asm.js
‣ Iterative integration process
‣ Milestones
plain JS asm.js
100%24%
76%
7%
93% 81%
19%
asm0 asm1 asm2 asm3
Development effort
‣ Challenges in writing asm.js by hand
‣ limited type system
‣ explicit typecasts
‣ manual memory management
‣ poor abstraction mechanisms
‣ interface between JS and asm.js
Development effort
‣ Challenges in writing asm.js by hand
‣ limited type system
‣ explicit typecasts
‣ manual memory management
‣ poor abstraction mechanisms
‣ interface between JS and asm.js
Macros to the rescue
‣ Macros to facilitate writing asm.js by hand
‣ Using the sweet.js macro expander
‣ Macros help to improve:
‣ readability
‣ maintainability
‣ performance
Performance comparison with JavaScript
‣ Too much overhead in first iterations ‣ asm3: ~80% faster than original JS implementation
spee
dup
asm0 asm1 asm2 asm30
1
2
3
4
5
Performance comparison with Cm
illise
cond
s
0
2000
4000
6000
8000
tower-fib nqueens qsort hanoi tak cpstak ctak destruct array1 primes
asm4 native C Emscripten
Performance comparison with C
‣ asm4 is ~19% slower than native C
‣ asm4 is ~46% faster than Emscripten
Table 1
native slip.js (asm4) compiled
1,0 1,185 1,742
native asm4 compiled
tower-fib 3583 3518 4711 1,31481998325426
nqueens 1033 1296 1864 1,80445304937076
qsort 2749 3948 5003 1,81993452164423
hanoi 3890 4046 7204 1,85192802056555
tak 772 878 1483 1,92098445595855
cpstak 936 985 1517 1,6207264957265
ctak 3118 5222 4541 1,45638229634381
destruct 4216 4350 7801 1,8503320683112
array1 2710 3518 5900 2,17712177121771
primes 3461 3832 6105 1,76394105749783
tower-fib
nqueens
qsort
hanoi
tak
cpstak
ctak
destruct
array1
primes
0 2000 4000 6000 8000
native asm4 compiled
0,00,20,40,60,81,01,21,41,61,82,0
native slip.js (asm4) compiled
Table 1-1
asm0 asm1 asm2 asm3
1,0 0,47 0,34 0,28
asm0 asm1 asm2 asm3slo
wdo
wn
native C asm4 Emscripten
Conclusion
‣ Integrated asm.js into interpreter
‣ Development effort: considerable
‣ more components had to be ported
‣ maintainability & readability improved by macros
‣ Performance: huge improvements
‣ close to native speed on the web!
github.com/noahvanes