Monomorphism

Blake Newman / @blakenewman

Monomorphism

vs

Polymorphism

vs

Lotsamorphism

Polymorphism

  1. Object-oriented based polymorphism usually means subtyping
     
  2. In Haskell there is parametric polymorphism
     
  3. In Javascript there is call site polymorphism
     
  4. Lots more Polymophism...

Dynamic Lookup

  • Applies to any dynamically bound operation 
    - Property lookup
    - Arithmetic operations

 

  • VM's want to avoid costly generic property lookups
    - Thus Inline Caching
    - Cache based object property lookups
const f = o => o.x

f({ x: 1 })
f({ x: 2 })

Inline Cache Lookup

  • Fastest path to discover the property shape
     
  • The cache is the path to property with the object’s shape as a key
     
  • As with most cache systems there is 
    - Size (number of currently cached entries)
    - Capacity  (maximum number of cached entries).

Stages of call site polymorphism

  • { x: 1 }   { x: 2 } have the same shape (Monomorphism)
  • { x: 1 }   { x: 2, y: 3 } have a different shape (Polymorphism)
    { x: 1 }   { x: 'string' } have a different shape (Polymorphism)
     
  • After a certain stage the function will become deoptimized as we pass different shapes to its parameters
    - This is known in call site polymorphism as Megamorphism
     
  • For each shape used this is known as the degree of polymorphism
     

Degrees of polymorphism

f({ x: 4 })       // monomorphic, degree 1
f({ x: 4, y: 1 }) // polymorphic, degree 2
f({ x: 5, z: 1 }) // polymorphic, degree 3
f({ x: 6, a: 1 }) // polymorphic, degree 4
f({ x: 7, b: 1 }) // megamorphic
  • VM's have different degrees of polymorphism before becoming megamorphic
    - V8 has 4 stages
     
  • At this point the VM starts to dislike your code
    - Depending on the VM it may still do some caching techniques

Different shapes?

class Point {
  constructor(x, y) {
    this.x = x
    this.y = y
  } 
}

class Point2 {
  constructor(x, y) {
    this.x = x
    this.y = y
  } 
}

const a = new Point(1, 2)
const b = new Point2(1, 2)
const c = { x: 1, y: 2 }
const d = { x: 1, y: 2, z: 3}

delete d.z

Different shapes?

class Point {
  constructor(x, y) {
    this.x = x
    this.y = y
  }
}

const a = Point(1, 2)
const b = Point(1, 3) // Same shape

a.z = 2 // Different shapes

Performance Implications

  • Monomorphic is the fastest possible
     
  • Polymorphic state performs linear search among cached entries
     
  • megamorphic state probe global hash table
     - hitting global cache is still better than complete miss

     
  • A miss has expensive costs
    - transitioning to runtime
    - generic operation

Performance Gains

Not the full story

  • Inline Cache acts on its own, knowing nothing about its neighbours
     
  • Individual Inline Cache can ultimately fallback to runtime
const f = o => o.x * o.x - o.y / o.y

f({ x: 1, y: 1 })
  • 7 states of inline monomorphic cache

    .x .x * .y .y / +
     

  • Arithmetic operations in JavaScript are inherently typed, with no guarantees
    - ahead-of-time optimizing compiler for JavaScript is extremely difficult

Why

  • JavaScript does not contain enough inherent type information for full static typing and AOT compilation


How

  • execute without any optimizations
  • compiled with a baseline non-optimizing compiler
  • Hot functions are later recompiled by an optimizing compiler

Benefits

  • Decreases startup latency
  • It gives inline caches a chance to collect type feedback.

Code warm up

The benchmarks

Why? When? How?

  • Obvious performance improvements
     
  • Simpler object shapes
     
  • Improved application performance
     
  • Leads to further under the hood optimizations
     

Why?

  • Hot functions
    - functions that are used very often
     
  • Libraries
    - Code in libraries can be very hot
     
  • Only when it makes sense
    - Only worry about it when you have extremely high profile number crunching
    - Don't over optimise (but be considerate when defining object shapes)

When?

  • Typescript / Flow
    - Adding static types to portions of you application can give you the safe guards needed to enforce monomorphic code
     
  • Resouces
    - https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html
    - http://mrale.ph/blog/2012/06/03/explaining-js-vms-in-js-inline-caches.html

How?

20% off

MEETUP-VJS-20

Competition Time

2 Free tickets

Q&A

Made with Slides.com