How Polymorphic Inline Cache Speeds Up JavaScript
A Polymorphic Inline Cache (PIC) is an optimization strategy used by modern JavaScript engines like V8 and SpiderMonkey to accelerate property access and method calls on objects with varying shapes. JavaScript is dynamically typed, meaning property lookups typically require runtime inspection of an object’s structure and prototype chain. PIC speeds up repetitive calls by recording a small set of previously encountered object shapes and their resolved method locations directly at the call site, replacing expensive dynamic lookups with fast conditional checks.
The Problem: Dynamic Method Resolution
In JavaScript, objects can change shapes at runtime, and methods can
be added, overridden, or inherited. When a method call like
obj.render() executes, the engine cannot inherently know
where render resides in memory. By default, it must perform
a dynamic lookup:
- Inspect the object’s internal structure (hidden class or “Shape”).
- Search the object’s own properties for the method.
- Traverse the prototype chain if the property is not found locally.
- Resolve the method’s memory address and invoke it.
Repeating this full resolution on every iteration of a loop causes significant performance overhead.
From Monomorphic to Polymorphic Caching
To optimize method resolution, engines use Inline Caching (IC):
- Monomorphic Inline Cache: If a call site repeatedly encounters objects of the exact same shape, the engine caches that single shape and the direct pointer to the method. Subsequent executions only verify that the object matches the cached shape before jumping directly to the method.
- Polymorphic Inline Cache: In real-world code, a
single call site often processes objects of a few different types (e.g.,
passing both
CircleandSquareinstances to adraw()function). When a monomorphic cache encounters a second shape, it transitions into a Polymorphic Inline Cache instead of abandoning optimization entirely.
How PIC Operates Internally
A Polymorphic Inline Cache maintains a small, fixed-size lookup table (typically 2 to 4 entries) directly at the call site:
- Shape Comparison: When the method is invoked, the engine reads the hidden class identifier (Shape/Map) of the incoming object.
- Linear Search in Cache: The engine checks the
identifier against the cached shapes in the PIC list:
[Shape A -> Method Address A][Shape B -> Method Address B][Shape C -> Method Address C]
- Cache Hit: If a match is found, the engine retrieves the pre-resolved method address and executes it immediately, bypassing prototype traversal entirely.
- Cache Miss and Expansion: If the shape is not in
the cache and the cache has not reached its limit (usually 4 entries),
the engine resolves the method normally, adds the new
[Shape -> Method]pair to the cache, and continues.
The Megamorphic State
If the number of unique object shapes passing through a single call site exceeds the engine’s threshold, the PIC transitions to a megamorphic state. At this stage, expanding the inline cache list becomes inefficient because linear scanning slows down execution. The engine stops caching locally and falls back to a global lookup table or dynamic resolution.
Summary
Polymorphic Inline Caches balance the flexibility of dynamic typing with the speed of static dispatch. By storing a small list of shape-to-method mappings at the call site, the engine replaces expensive prototype chain walks with quick pointer comparisons, dramatically reducing execution time in hot code paths.