These are some notes on performance engineering related to generating insights into Node.js (v8) performance.
All material here is specific to the v8 engine found in Node.js, Chrome, and Opera.
Speculative optimization with optimization and deoptimization
With speculative optimization we have the bytecode interpreter look at how a function is used (e.g. what types of values are passed as arguments) to see if it can delegate to the optimizing compiler for specialization or related optimization strategies after observing call usages for patterns.
If the observations that the interpreter uses to delegate to the optimizing compiler gets nullified after delegating to the optimizer then that unit of code (e.g. function) gets deoptimized and a request to the interpreter to generate general bytecode with no optimizations is made incurring more work and slowing the process down.
To understand what gets optimized by the turbofan optimizer and what gets delegated back for deoptimization from the turbofan to bytecode generator we can use:
The following terms may be used in different contexts to mean similar things but the following are defined in the bounded context of speculative optimization.
This is where a function handles a specific type only and all call sites only pass values of that type.
Using TypeScript we can ensure consistency of our call sites with appropriate type annotations:
const add = (x: number, y: number): number => x + y;
This is where a function handles a range of types effectively making an optimized switch statement for the cases. The more cases the more the optimizer loses its effectiveness and gives up where it gets "demoted" to megamorphic.
Anything goes! This breaks the optimizer, big time. Avoid this at all costs.
Node.js command-line options related to function inlining are: