Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Architectural Changes In V8 And How They Will Improve Server Performance

Tamar Twena-Stern
September 30, 2023
36

Architectural Changes In V8 And How They Will Improve Server Performance

Tamar Twena-Stern

September 30, 2023
Tweet

Transcript

  1. Compiler - Translates High level code to machine code in

    compilation process Interpreter - directly executes source code written in a high-level programming, translate to machine code line by line Dynamic Typed Languages - checks variable types at runtime Static Typed Languages - checks variable types at compile time
  2. Dynamic Type Languages Have To Do Those Techniques At Runtime

    • Runtime Type Checking • Dynamic Dispatch
  3. Compiler, static typed language == Interpreter, dynamic type language ==

    == Interpreter, dynamic type language So In Most Cases
  4. megamorohic(myNumber : number) : void; megamorohic(myString: String) : void; megamorohic(myPerson

    : Person) : void; megamorohic(myFirstAray: Array<String>) : void; megamorohic(mySecondArray: Array<number>) : void;
  5. De-Optimization - Example class Person { constructor(name, age) { this.name

    = name; this.age = age; } } function getAge(person) { return person.age; }
  6. De-Optimization - Example for (let i = 0 ; i

    < 1000000 ; i++ ) { // After X iterations the Turbofan binary will be created const p = new Person("Tamar", 30); getAge(p); }
  7. De-Optimization - Example const differentPerson = new Person("Tamar", 30);; //

    Adding dynamic property, hidden class changed differentPerson.salary = 3000; //binary released getAge(differentPerson);
  8. • Assumption for turbofan optimizations - checking the Shape as

    a prerequisite • The “maps check” checks what hidden class the object is pointing to and makes sure it is the same as the compiler expects. • If it matches, then the check succeeds; if not, then it fails and the code is deoptimized due to “wrong map.” De-Optimization - Wrong “Hidden Class”
  9. Optimized Code function concat_str(arg) { return arg + arg; }

    // Start for (let i = 0 ; i< 100000; i++) { concat_str("a"); } // End
  10. Code With DeOptimization function concat_str(arg) { return arg + arg;

    } // Start for (let i = 0 ; i< 100000; i++) { if (i == 10231) { concat_str(1); } else { concat_str("a"); } } // End
  11. First Phase - De-optimizing After Transfering Different Type [marking 0x070b4aa60601

    <JSFunction (sfi = 0x3dc4dce32af9)> for optimization to TURBOFAN, ConcurrencyMode::kConcurrent, reason: hot and stable] [compiling method 0x070b4aa60601 <JSFunction (sfi = 0x3dc4dce32af9)> (target TURBOFAN) using Turbofan OSR] [optimizing 0x070b4aa60601 <JSFunction (sfi = 0x3dc4dce32af9)> (target TURBOFAN) - took 0.000, 0.625, 0.000 ms] [bailout (kind: deopt-eager, reason: Insufficient type feedback for call): begin. deoptimizing 0x070b4aa60601 <JSFunction (sfi = 0x3dc4dce32af9)>, opt id 0, bytecode offset 30, deopt exit 4, FP to SP delta 96, caller SP 0x00016bc4a310, pc 0x000128853144]
  12. Phase 2 - Optimizing Again [compiling method 0x070b4aa61039 <JSFunction concat_str

    (sfi = 0x3dc4dce32bb1)> (target TURBOFAN) using Turbofan] [marking 0x070b4aa60601 <JSFunction (sfi = 0x3dc4dce32af9)> for optimization to TURBOFAN, ConcurrencyMode::kConcurrent, reason: hot and stable] [optimizing 0x151588782981 <JSFunction concat_str (sfi = 0x3dc4dce32bb1)> (target TURBOFAN) - took 0.000, 0.458, 0.000 ms] [completed optimizing 0x151588782981 <JSFunction concat_str (sfi = 0x3dc4dce32bb1)> (target TURBOFAN)] [compiling method 0x1515887829c1 <JSFunction (sfi = 0x3dc4dce32af9)> (target TURBOFAN) using Turbofan OSR] [optimizing 0x1515887829c1 <JSFunction (sfi = 0x3dc4dce32af9)> (target TURBOFAN) - took 0.000, 0.584, 0.000 ms] [bailout (kind: deopt-eager, reason: Insufficient type feedback for generic named access): begin. deoptimizing 0x1515887829c1 <JSFunction (sfi = 0x3dc4dce32af9)>, opt id 2, bytecode offset 74, deopt exit 7, FP to SP delta 112, caller SP 0x00016bc4a310, pc 0x000128853888]
  13. const a = 1; const b = doStuff1(a); const c

    = doStuff2(a); for (let i=0; i<100000; i++) { doStuff1(a);//After X runs,run Machine Code (TurboFan) } Before Adding SparkPlug
  14. const a = 1; const b = doStuff1(a); // run

    Machine Code (SparkPlug) const c = doStuff2(a); // run Machine Code (SparkPlug) for (let i=0; i<100000; i++) { doStuff1(a); // After X runs, run Machine Code (TurboFan) } After Adding SparkPlug
  15. Ignition Does The Hard Work • variable resolution • parentheses

    or arrow functions • desugaring destructuring statements
  16. getX({ x: 3 }) // Cache size = 1 getX({

    x: 3, a: 1 }) // polymorphic getX({ x: 3, b: 1 }) // polymorphic getX({ x: 3, c: 1 }) // polymorphic, several cache entries getX({ x: 3, d: 1 }) // megamorphic - Entries are overwritten on collisions
  17. Until recent V8 versions - Inline Cache was not implemented

    for class fields and private methods
  18. const person = {}; person.age = 42; class Person {

    age; constructor(age) { this.age = age; } getAge() { return this.age; } }
  19. Benchmark Setup class Person { age; constructor(age) { this.age =

    age; } function getAge(person) { return person.age; } }
  20. Benchmark Phase 1 arr = []; // Start for (let

    i = 0 ; i < 1000000 ; i++ ) { const p = new Person(30); arr[i] = p; } // End
  21. Benchmark Phase 2 // Start for (let i = 0

    ; i < 1000000 ; i++ ) { arr[i].age; } // End
  22. What’s In It For Me ? class Person { age;

    constructor(age) { this.age = age; } getAge() { return this.age; } }