JavaScript
Virtual Machines
JITs
GCs
Disclaimer
I’m not an expert, I’m just a JS developer who loves to know about the technology that runs his favorite language
And so should you!
I’m trying to give links/citations where possible
What I’m talking about
- Architecture and staged jit compilation of V8
- … and History of SpiderMonkey
- (no other engines; there is hardly any public info about them)
- Inline Caches (ICs)
- (Generational) Garbage Collection
- Some optimization pitfalls/suggestions
Javascript
Is the worlds most compiled language
(even though you don’t compile it yourself)
The browser compiles it over and over again,
every single time you load a web site.
Compilation needs to be insanely fast!
Dynamic (staged) recompilation
- Stage 1: fast compiles, not so fast code
- Stage 2: slow compiles, extremely fast code
- Stage 1.5: in between, dynamic compiles, mostly fast code (ICs)
Stage 2 is very specialized, can “bail out” to stage 1
Not every code construct can be compiled in stage 2
V8
Stage 1: full-codegen
↓
Stage 2: Crankshaft
- good baseline with ICs
- insanely fast optimized Crankshaft
http://wingolog.org/archives/2013/04/18/inside-full-codegen-v8s-baseline-compiler
SpiderMonkey (FF 3.5)
Stage 0: Interpreter
↓
Stage 2: TraceMonkey
- Records (traces) loops, replays them
- extremely fast, but very fragile, bails out often
SpiderMonkey (FF4)
Stage 0: Interpreter
↓
Stage 1: JägerMonkey
↓
Stage 2: TraceMonkey
- JägerMonkey compiles whole functions, not just loops
- Bailouts still go to the Interpreter
SpiderMonkey (FF 9)
Stage 0: Interpreter
↓
Stage 1: JägerMonkey + Type Inference
↓
Stage 2: TraceMonkey
- TI creates type specialized code, reducing chance of Bailouts
SpiderMonkey (FF 18)
Stage 0: Interpreter
↓
Stage 1: JägerMonkey + Type Inference
↓
Stage 2: IonMonkey
- IM does advanced optimizations
- Bailouts still go to the Interpreter
SpiderMonkey (FF 24)
Stage 0: Interpreter
↓
Stage 1: Baseline
↓
Stage 2: IonMonkey
- Baseline uses ICs
- Bailouts go to Baseline
- Interpreter still useful for IIFE and similar
Inline Caches
Stage 1 generates native code, but most operations still fall back to generic C++ methods
ICs are type specialized snippets that are chained in front of the generic fallback
var a=3.14; -a;
bool
ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
masm.ensureDouble(R0, FloatReg0, &failure);
JS_ASSERT(op == JSOP_NEG);
masm.negateDouble(FloatReg0);
masm.boxDouble(FloatReg0, R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
http://mxr.mozilla.org/mozilla-central/source/js/src/jit/BaselineIC.cpp#3087
Garbage Collection
Something is rotten in the state of Denmark
and Hamlet is taking out the trash!
No, Srsly
You allocate *a lot* of objects and produce a lot of trash
“new”, object literals, …
The optimizing (stage 2) JITs can optimize most of it away
Stage 1 JITs will sometimes allocate for every single arithmatic
expression.
Taking out the trash costs, a lot.
Generational GC
- The cost of a GC is proportional to the number of live objects
- Most objects are temporary with a short lifetime
- How to combine those two principles?
- Create two heaps, for short-lived and long-lived objects
- Nursery collected frequently, most objects are dead, so collection is fast
- Tenured heap is collected less frequently, most objects will stay alive anyway
- Other advantage: memory compaction, less fragmentation
GGC in Firefox
- GGC moves objects from nursery to tenured
- GC runtime unpredictable
- pointers will need to be changed
- C++ code can have pointers to JS objects
- Past: conservative scanning, everything that looks like a pointer is treated as one
- false positives, but not harmful for GC
- but *rewriting* those may lead to serious bugs
GGC in Firefox
Optimization?
- Avoid type changes in performance critical code
- Avoid allocations in performance critical code
- Optimizing JITs cant deal (yet?) with: try-catch, with-stmt
- Type changes also apply to changes to object shapes (hidden classes)
- Use dense arrays of same type
http://research.scee.net/files/presentations/gcapaustralia09/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf
Optimization tips
- Move performance critical code to own function
- Handle multiple dispatch and errors in outer function
- Create all object properties in the constructor
- Profile! Use the V8/node tools, like `--prof` and `node-tick-processor`
- *Know* what you are doing :-)
JavaScript: VMs, JITs, GC
By Arpad Borsos
JavaScript: VMs, JITs, GC
- 2,813