1 of 32

High Performance JS in V8

Peter Marshall - V8 Engineer

Chrome

2 of 32

  • Problem Space: Intro to JS Engines

  • How it was: Crankshaft

  • Introducing: Ignition + Turbofan

  • The Future of Fast JS

Chrome

3 of 32

Why do we care?

  • Users click away from slow sites
  • This translates directly into money
  • We can enable new use-cases
  • Low-end devices particularly benefit

Chrome

4 of 32

What is an Engine, Anyway?

Alice

Hello, Alice

greeter.exe

Chrome

5 of 32

What is an Engine, Anyway?

2 + 2

4

Chrome

6 of 32

What is an Engine, Anyway?

Chrome

7 of 32

Why is JS so tricky?

  • Really, really dynamic
  • Dynamic typing is the classic case
    • Easier for prototyping, getting started
  • Also dynamic inheritance model
  • Dynamic features like eval
  • Monkey-patching almost anything
    • Useful for libraries and frameworks

Chrome

8 of 32

“Speculative Optimization a.k.a Guessing”

function updateData(node, user) {� node.userName = user.first + user.last;�}

Chrome

9 of 32

“Speculative Optimization a.k.a Guessing”

  • Do the properties exist
  • How do we access the property in memory
  • What types are returned

  • What should Plus do?

function updateData(node, user) {� node.userName = user.first + user.last;�}

Property Load

Property Load

Property Store

Plus Operation

Chrome

10 of 32

Feedback and Deoptimization

  • Initially, we run some general-purpose code
  • Gather feedback on what types we see
  • Generate optimized code that only handles simple strings
  • Discard this faster code if we find a non-string, or go to a slower backup

function updateData(node, user) {� node.userName = user.first + user.last;�}

String

String

String

String Concat

Chrome

11 of 32

Chrome

12 of 32

How it was: Crankshaft

Chrome

13 of 32

The Old V8 Architecture

Parser

2 + 2

2

2

+

AST

Source�Code

Fullcode�Gen

Crankshaft

ASM Code

Addq 0x2,0x2

…�Movq rax, rbx

x64, ARM, etc. (9 total!)

Baseline Compiler

Optimizing Compiler

4

Output

C++ Runtime

Chrome

14 of 32

Problems

Crankshaft was starting to show its age:

  • Could not support new language features (ES6, etc.)
    • V8 supported them, but not directly in optimized code
  • Potential for a lot of deoptimization
  • Performance cliffs cause huge, unexpected slowdown (narrow paths)
  • No clear separation of ‘phases’ of compilation
  • A lot of handwritten code required for 9 architectures
  • FullcodeGen produced a lot of code, costing memory

Chrome

15 of 32

“Communication” Process

You’re holding it wrong

This is how we’re holding it

Chrome

16 of 32

Crankshaft Performance Advice

  • “Don’t use let/const”
  • “Don’t use try/catch or try/finally”
  • “Don’t use for-in”
  • “Don’t use generators or async functions”

  • CrankshaftScript™
  • Crankshaft guessed way too much, and had terrible performance on bailout

Chrome

17 of 32

CrankshaftScript 1

“/* This implementation of checkIsHttpToken() loops over the string instead of using a regular expression since the former is up to 180% faster with v8 4.9 */”

var validTokens = [0, 1, 0, … /*256 length */ , 0];

function checkIsHttpToken(val) {

if (!validTokens[val.charCodeAt(0)])

return false;

if (val.length < 2)

return true;

if (!validTokens[val.charCodeAt(1)])

return false;

if (val.length < 3)

return true;

if (!validTokens[val.charCodeAt(2)])

return false;

for (var i = 3; i < val.length; ++i) {

if (!validTokens[val.charCodeAt(i)])

return false;

}

return true;

}

Chrome

18 of 32

CrankshaftScript 1

  • Brittle binding between code and engine
  • Easily outdated
  • Much harder to read or maintain
  • We aren’t supporting real-world use-cases

const token = /^[a-zA-Z0-9_!#$%&'*+.^`|~-]+$/;

function checkIsHttpToken(val) {

return typeof val === 'string' && token.test(val);

}

Chrome

19 of 32

CrankshaftScript 2

function foo(x) {

try {

return bar(x);

} catch (e) {

return 0;

}

}

function bar(x) {

return x.thingThatMightThrow();

}

V8 5.1

V8 6.0

1.27x

1.00x

(19x)

Chrome

20 of 32

Chrome

21 of 32

Ignition + Turbofan

Chrome

22 of 32

The New V8 Architecture

Parser

2 + 2

2

2

+

AST

Source�Code

Bytecode�Generator

Turbofan

ASM Code

Addq 0x2,0x2

…�Movq rax, rbx

x64, ARM, etc. (9 total!)

Optimizing Compiler

4

Output

Ignition

C++ Runtime

Interpreter

Chrome

23 of 32

Advantages of the New World

  • Interpreter saves memory on low-end devices
  • No total “optimization killers”
  • Wide fast-paths that handle common use-cases
  • Supports all language features!
  • Much more predictable, stable performance profile

Chrome

24 of 32

Real World Performance Results

Chrome

25 of 32

Fast JS for Turbofan

What would “TurbofanScript” look like?

  • Regular, readable JavaScript
  • Uses ES6 features
  • Should work well on any engine

  • We just shipped I + TF to stable!

Chrome

26 of 32

Changing JS Ecosystem

Chrome

27 of 32

Transpilation and Frameworks

Performance:

  • Faster, because ES6 features can be slower than ES5 (decreased return over time)
  • Slower, because parsing cost probably increases

�Compatibility

  • Transpiled code not always equivalent to original code

Sanity

  • Easier to develop with nice modular abstractions
  • Harder to debug code you didn’t write��

Chrome

28 of 32

What About ES6 Features?

ES6:

Math.max(...[1, 2, 3]);

ES5:

Math.max.apply(Math, [1, 2, 3]);

19x

Chrome

29 of 32

420kB

Code shipped to the average website

Chrome

30 of 32

15-20%

Total V8 time taken in parsing

Chrome

31 of 32

Takeaways

  • No more CrankshaftScript�
  • ES6 Features are ready�
  • We want your use cases

Chrome

32 of 32

Thanks!

petermarshall@google.com

Chrome