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

Optimizing With Static Hermes (Chain React 2024)

Optimizing With Static Hermes (Chain React 2024)

Tzvetan Mikov

July 26, 2024
Tweet

More Decks by Tzvetan Mikov

Other Decks in Programming

Transcript

  1. What is Static Hermes? • Optional sound type annotations to

    improve performance • Allow ahead of time compilation to compact bytecode or fast native binaries • Tight native integration through zero-cost calls to C functions 3
  2. Benefits 4 • Improve performance of existing Flow/TypeScript with minimal

    changes • Avoid needing to switch from JS to C++ for native interop
  3. Are we Static Hermes yet? • Fully open source, in

    the static_h branch of the facebook/hermes repo • Not ready for general use • Many typed features implemented (e.g. unions, classes, generics) • Major improvements to untyped code spec compliance and performance in the works 5
  4. Sound Types • Type annotations in Flow and TypeScript are

    unsound, and do not guarantee that types are correct at runtime • Static Hermes modifies some JS semantics to allow efficient sound typing • New soundly typed code can co-exist and interop with unmodified code 8
  5. Unsound Types Example function addXY(x: number, y: number): number {

    return x + y; } let a: number[] = [1, 2, 3]; addXY(a[10], a[11]); 9
  6. Unsound Types Example function addXY(x: number, y: number): number {

    return x + y; } let a: number[] = [1, 2, 3]; // Adding undefined + undefined! addXY(a[10], a[11]); 1 0
  7. Sound Type Enforcement function addXY(x: number, y: number): number {

    return x + y; } let a: number[] = [1, 2, 3]; // RangeError thrown at runtime! addXY(a[10], a[11]); 1 1
  8. Class Fields 1 2 class Vector { constructor(x, y, z)

    { this.x = x; this.y = y; this.z = z; } } class Vector { x: number; y: number; z: number; constructor(x: number, y: number, z: number) { this.x = x; this.y = y; this.z = z; } } Untyped Typed print(myVec.z);
  9. Reading myVec.z 1. Check that myVec is an object 2.

    Get the cache entry for the read 3. Compare the object with the cache 4. On match, load from cached offset 5. On fail, full dictionary lookup 1. Load from offset 2 1 3 Untyped Typed
  10. Array Access 1 4 function getIdx( myArr: number[], i: number

    ): number { return myArr[i]; } Untyped Typed function getIdx( myArr, i ) { return myArr[i]; }
  11. Reading myArr[i] 1. Check that myArr is an object 2.

    Check if i is index-like 3. If it is, attempt to do an indexed load 4. Check if the value is available 5. If any checks fail, full dictionary lookup 1. Check that i is an integer within bounds 2. Load from the array at i 1 5 Untyped Typed
  12. myVec.z + myArr[i] 1. Slow property load from myVec 2.

    Slow array load from myArr 3. Check if both are numbers 4. If they are, add them 5. If not, fall through to general case (strings, objects, BigInt, etc) 1. Fast load from myVec 2. Fast load from myArr 3. Add the numbers 1 6 Untyped Typed
  13. Inlining 1 9 1 9 “He will win who knows

    when to inline and when not to inline.” soft e
  14. Motivation • Code tends to have a lot of small

    functions • Calls have significant overhead • Most optimizations cannot operate across calls 2 0
  15. Inlining Example 2 1 function normal(pos: Vector): Vector { return

    Vector_norm(Vector_minus(pos, this.center)); } function Vector_minus(v1: Vector, v2: Vector): Vector { return new Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); } function Vector_norm(v: Vector): Vector { var mag = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); var div = mag === 0 ? Infinity : 1.0 / mag; return new Vector(div * v.x, div * v.y, div * v.z); }
  16. Inlining Example 2 2 function normal(pos: Vector): Vector { var

    v = new Vector( pos.x – this.center.x, pos.y – this.center.y, pos.z – this.center.z ); var mag = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); var div = mag === 0 ? Infinity : 1.0 / mag; return new Vector(div * v.x, div * v.y, div * v.z); } *Artist’s impression Vector_minus Vector_norm
  17. Inlining Example 2 3 function normal(pos: Vector): Vector { var

    v = { x: pos.x – this.center.x, y: pos.y – this.center.y, z: pos.z – this.center.z }; var mag = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); var div = mag === 0 ? Infinity : 1.0 / mag; return {x: div * v.x, y: div * v.y, z: div * v.z}; } *Artist’s impression
  18. Call Analysis function foo(){ let op = (a, b) =>

    a + b; function bar(a, b){ return op(a, b); } return bar; } 2 4
  19. Call Analysis function foo(){ let op = Math.random() > 0.5

    ? (a, b) => a + b : (a, b) => a / b; function bar(a, b){ return op(a, b); } return bar; } 2 5
  20. Inlining Methods 2 6 class Base { getID(): number {

    return 0; } }; function getIDPlusOne(base: Base): number { return base.getID() + 1; } class Derived extends Base { getID(): number { return 42; } };
  21. Inlining Methods 2 7 class Base { getID(): number {

    return 0; } }; function getIDPlusOne(base: Base): number { return base.getID() + 1; } class Derived extends Base { getID(): number { return 42; } };
  22. Inlining Methods 2 8 class Base { getID(): number {

    return 0; } }; function getIDPlusOne(derived: Derived): number { return derived.getID() + 1; } class Derived extends Base { getID(): number { return 42; } };
  23. Inlining Methods 2 9 class Base { getID(): number {

    return 0; } }; function getIDPlusOne(derived: Derived): number { return 43; } class Derived extends Base { getID(): number { return 42; } };
  24. Types Enable Inlining 3 0 class Base { getID() {

    return 0; } }; function getIDPlusOne(derived) { return derived.getID() + 1; } class Derived extends Base { getID() { return 42; } };
  25. Inlining Recap • Eliminate calls by copying a function’s contents

    to where it is called • Depends on the compiler’s ability to statically identify what function is being called, types make this much easier • Enables many other optimizations 3 1
  26. Motivation • JavaScript tends to create many small objects •

    Object allocations are expensive • Analysing the values of object fields is difficult 3 4
  27. Object Elision Example 3 5 function normal(pos: Vector): Vector {

    var v = { x: pos.x – this.center.x, y: pos.y – this.center.y, z: pos.z – this.center.z }; var mag = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); var div = mag === 0 ? Infinity : 1.0 / mag; return {x: div * v.x, y: div * v.y, z: div * v.z}; }
  28. Object Elision Example 3 6 function normal(pos: Vector): Vector {

    var v = { x: pos.x – this.center.x, y: pos.y – this.center.y, z: pos.z – this.center.z }; var mag = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); var div = mag === 0 ? Infinity : 1.0 / mag; return {x: div * v.x, y: div * v.y, z: div * v.z}; }
  29. Object Elision Example 3 7 function normal(pos: Vector): Vector {

    var vx = pos.x – this.center.x; var vy = pos.y – this.center.y; var vz = pos.z – this.center.z; var mag = Math.sqrt(vx * vx + vy * vy + vz * vz); var div = mag === 0 ? Infinity : 1.0 / mag; return {x: div * vx, y: div * vy, z: div * vz}; }
  30. Elision Inlining class Foo { bar: () => number; constructor(bar:

    () => number) { this.bar = bar; } }; 3 8 function myFun(){ let fbar = () => 42; return fbar(); } function myFun(){ let f = new Foo(() => 42); let fbar = f.bar; return fbar(); } function myFun(){ return 42; } function myFun(){ let f = { bar: () => 42 }; let fbar = f.bar; return fbar(); } Inlining Object elision Inlining
  31. Recap • Static Hermes enforces soundness of declared types •

    We use type information to speed up basic operations • Types unlock additional ahead-of-time optimizations like inlining and object elision • These dramatically improve the performance of typed code 4 0
  32. Thank You! • For questions or suggestions, reach out to

    us on GitHub! o https://github.com/facebook/hermes/discussions/categories/static-hermes • For an introduction to Static Hermes, watch Tzvetan's talk at RN EU 2023 o https://youtu.be/q-xKYA0EO-c • For the raytracer benchmark, check out the static_h branch on GitHub o https://github.com/facebook/hermes/tree/static_h/benchmarks/raytracer 4 1