Understanding Javascript Engines

Post on 14-Jan-2015

4,789 views 0 download

Tags:

description

A quick introduction to how Javascript engi

transcript

JavaScript EnginesPop the hood

z = x + y

1. Read operation from memory2. Get location of x and y3. Read values for x and y 4. Unbox x and y.5. Choose meaning of “+”, perform

“+”6. Save z to memory7. Do garbage.

Executing z = x + y

1. String “z = x + y” is passed into tokenizer.– Webkit uses Flex (LEX)– Accommodate semi colon insertion, etc.

2. Tokenizer output fed to parser – Webkit uses Bison, bottom up shift

reduce parser– Gecko has top down parser

3. Statement now available as Abstract Syntax Tree (AST)

1. Read operation from memory…

• X & Y could be number, string, object, null, undefined, array, etc. – Offsets directly available for primitives

• Values also depend on context of execution– Closures (activation contexts)– Local Variables– Object properties– Scope modifiers – eval, with, etc.

2. Get locations of x and y

• If x is a actually array - obj[x]– Dense array have offsets– Created using 0..N or push– Gecko creates sparse array on N..0– Adding obj[“name”] fails optimization

2. Get values of x – Array

• If X is an object property (obj.x)– Looks up current object or up the prototype

chain– Inline Cache (IC) the value

• Objects have shape – {x:1} is different from {x:1,y:2}

• Webkit stores memory offsets in hidden classes• New shape created for every new property.• IC can read from prototype without walking tree• Closures only save path, still have to walk every

time.

– OpCodes generated for each shape• Obj.x ==> read shape1[member1]

2. Get values of x – Object

• JavaScript variable assignments are un-typed. • Assignments stored as boxed inputs– x could be (int32 | 100) – indicating type and

value

• Javascript numbers are IEEE-754 floating point.– Who cares, just use 32 bit to optimize. – Overflow to doubles.

• Ways to Box values (ref)– Tagging the LSBs (ref)– Nan Boxing (ref) – 51 bit of NaN space for non-

doubles (Webkit)– Nun Boxing (favor doubles in NAN – Mozilla pun)

3. Read boxed input …

From box, infer type and value, represent it in native type Int32 x = 100;

From NunBoxed Values 0x400c 0000 | 0x0000 0000 = not a nan, so

double (3.5) 0xFFFF0001 | 0x0000 0040 = Nan space, so

Int32 (0x0000 0040) From NanBoxed Values (0xFFFF80

00000040) Mask to get pointer, shift to get double X64 portability, fits in register, but harder to

decode

4. Unbox x and y

If (typeof x == int32 && typeof y == int32) result = x <operator> y If (result overflows), result = float If (result Nan), result = NaN.

If (typeof x == int32 && typeof y === float) result = CoarceToFloat(x) + y // Same sanity checks

….. If (typeof x === Object) ….

5. Perform “+” : Interpreter

If type of x and y are known, generate opcodes Fall to opcode when routine is hit. Save cost of boxing, unboxing.

Determined opcode Paths for each shape Multiple shapes mean multiple if-else

branches Still have to check for validity/dirtiness of

opcpdes Typical JagerMonkey JITing

5. Perform “+” : Basic JIT

Observe and identify hot code 70 iterations Crankshaft – according to profiler Other heuristics

Execute opcode routine for specific type Initial compilation cost OpCodes may be thrown away if types

change Fallback to non-optimized version

(Performance fault) Classic Tracemonkey

5. Perform “+” : Typed JIT

Static analysis of code to “prove” types

Reduce checks, ensure only one path taken at all times.

Type stable Javascript IonMonkey Internals. Use multicore processors

To box/unbox values To generate compiled opcodes (Chakra)

5. Perform “+” : Next Gen JIT

Used by WebGL and Canvas render computation routines

Shaders and alpha channel (no direct JS code)

Super fast floating point math Note that variables is strongly typed

as Blobs

5. Perform “+” : GPU Enabled

Strings Substring is O(1), Concat also optimized Concat fault on Opera, Chrome

Array Denser the better Named property is sparse in FF, IE Iterate using index, not for:in or

functional Functions

F() faster than f.call(), f.apply(). Using arguments slows down execution

5. Perform “+” : Datatypes

Objects Prototypes are better than closures

Exceptions Try is mostly free, catch is expensive May cause optimizer to stop

Eval and With Cause scope sudden change, prevent JIT

5. Perform “+” : Datatypes

Box “z” and put it on heap if required. Leave “z” in register if used

subsequently Create shape for “z” if object

6. Save Z

Mark and Sweep Causes GC to pause main routine Jittery animation, unresponsive UI thread

Incremental GC Mark and sweep only parts

Generational GC Separate objects in nursery and tenured

areas Promoting young to old is expensive

Combine Generational and Incremental Frequent young cleanup, occasional old

cleanup

7. Garbage Collection

Block Scope reduces variable lookups (let, var)

“Yeild” may need to save current context

Blob Data type – boxing/unboxing issues

Classes === Concrete Shapes ? Syntactic sugar, no impact

Assignment de-structuring Promises, modules, etc.

Changes due to ECMA.next

Don’t write any code Practice type safety Leave hot loops alone – don’t create

shapes in them Make arrays dense Avoid operations that require different

type coercion every time.

Conclusions

@nparashuramhttp://nparashuram.com