perf: cache resolved instance functions to avoid repeated lookups in deep hierarchies#108
perf: cache resolved instance functions to avoid repeated lookups in deep hierarchies#108hectorhdzg wants to merge 2 commits into
Conversation
MSNev
left a comment
There was a problem hiding this comment.
No!
DynamicProto MUST work in ES5 and all of the WeakXXXX classes don't exist in ES5.
3d8a573 to
846ea21
Compare
| // instFuncTable is created with objCreate(null) during construction and is not frozen | ||
| // even when the instance itself is frozen (Object.freeze is shallow). | ||
| if (instFuncTable) { | ||
| instFuncTable[cacheKey] = instFunc; |
There was a problem hiding this comment.
So there is one thought around this PR and the class one, in that by caching the "lookups" this would break the previous capability (but not sure if we actual use this today) of dynamically adjusting to change on base classes.
Generally, we build "on-top" of the items (extending / changing features) for new base classes, so this caching should be fine as I can't currently thing of any specific issues...
But we should probably test locally before releasing an updated version.
There was a problem hiding this comment.
7 new re-registration tests
Re-calling dynamicProto on the same instance updates the resolved function (not stale)
Re-registration on one instance doesn't affect other instances
Child class with setInstFuncs: true — base proxy is early-bound (re-registration doesn't propagate)
Child class with setInstFuncs: false — base proxy is late-bound through the dynamic proxy (re-registration IS visible)
Multiple dynamicProto calls in a single constructor — last registration wins
846ea21 to
47efc2f
Compare
When the instance shortcut optimization cannot be applied (frozen/readonly targets, Proxy objects, or when the instance already owns the property), every dynamic method call previously re-entered _getInstFunc with repeated property lookups on every invocation. This change adds a per-instance resolved-function cache stored directly in the existing instFuncTable object (keyed by className + funcName). Since instFuncTable is created with objCreate(null) during construction and Object.freeze is shallow, it remains writable even when the instance itself is frozen. Subsequent calls to the same proxy for the same instance return the cached function directly, eliminating the _getInstFunc overhead entirely. Additionally, the prototype chain walk inside _getInstFunc is now guarded behind the canAddInst check, avoiding unnecessary visited-array allocation when the instance already owns the property. Fully ES5 compatible - no WeakMap or other ES6+ APIs required. Impact: Reduces per-call overhead from O(property lookups) to O(1) cache hit for all cases where the shortcut optimization cannot be applied, particularly benefiting deep inheritance hierarchies (10+ levels).
47efc2f to
2ac41b2
Compare
When the instance shortcut optimization cannot be applied (frozen/readonly targets, Proxy objects, or instances that already own the property), every dynamic method call previously re-entered _getInstFunc with repeated property lookups on every invocation.
This change adds a per-proxy WeakMap cache that stores the resolved instance function after first resolution. Subsequent calls to the same proxy for the same instance return the cached function directly, eliminating the _getInstFunc overhead entirely.
Additionally, the prototype chain walk inside _getInstFunc is now guarded behind the canAddInst check, avoiding unnecessary visited-array allocation when the instance already owns the property.
For environments without WeakMap (e.g. IE8), the behavior gracefully falls back to the existing code path with no regression.
Impact: Reduces per-call overhead from O(property lookups) to O(1) WeakMap.get for all cases where the shortcut optimization cannot be applied, particularly benefiting deep inheritance hierarchies (10+ levels).