Hypothesis
Node.init() (node.js) uses for-in with hasOwnProperty to iterate over defaults object properties. cloneNode() (node.js) also uses for-in + hasOwnProperty for deep cloning. V8 optimizes for-in poorly compared to Object.keys() + indexed for loop because for-in must check the prototype chain and handle non-enumerable properties.
Replace for-in patterns with:
let keys = Object.keys(defaults)
for (let i = 0; i < keys.length; i++) {
let key = keys[i]
// ... assignment
}
This gives V8 a tight indexed loop it can JIT-optimize aggressively. Object.keys() returns only own enumerable properties, making the hasOwnProperty check unnecessary.
Also in cloneNode, replace the recursive for-in deep copy with a structured approach using Object.keys.
Editable surface
- lib/node.js — replace for-in with Object.keys in init() and cloneNode()
What's different from prior work
Expected impact
METRIC_A improvement of 1-3ms (thousands of node creations). METRIC_B improvement of 2-4ms. Combined 2-4ms.
Hypothesis
Node.init() (node.js) uses for-in with hasOwnProperty to iterate over defaults object properties. cloneNode() (node.js) also uses for-in + hasOwnProperty for deep cloning. V8 optimizes for-in poorly compared to Object.keys() + indexed for loop because for-in must check the prototype chain and handle non-enumerable properties.
Replace for-in patterns with:
This gives V8 a tight indexed loop it can JIT-optimize aggressively. Object.keys() returns only own enumerable properties, making the hasOwnProperty check unnecessary.
Also in cloneNode, replace the recursive for-in deep copy with a structured approach using Object.keys.
Editable surface
What's different from prior work
Expected impact
METRIC_A improvement of 1-3ms (thousands of node creations). METRIC_B improvement of 2-4ms. Combined 2-4ms.