diff --git a/packages/core/src/render3/PERF_NOTES.md b/packages/core/src/render3/PERF_NOTES.md index 625b37a2ac..d2015a4d24 100644 --- a/packages/core/src/render3/PERF_NOTES.md +++ b/packages/core/src/render3/PERF_NOTES.md @@ -22,6 +22,21 @@ Great reads: See benchmark [here](https://jsperf.com/mono-vs-megamorphic-property-access). +## Packed vs. holey Array + +V8 represents arrays internally in a different way depending on: +- type of elements in the array; +- presence of holes (indexes that were never assigned). + +Generally speaking packed arrays (a set of continuous, initialized indexes) perform better as compared to arrays with holes. To assure that arrays are packed follow those guidelines: +* create array literals with known values whenever possible (ex. `a = [0];` is better than `a = []; a.push[0];`; +* don't use `Array` constructor with the size value (ex. `new Array(5)`) - this will create a `HOLEY_ELEMENTS` array (even if this array is filled in later on!); +* don't delete elements from an array (ex. `delete a[0]`) - this will create a hole; +* don't write past the array length as this will create holes; + +Great reads: +- [Elements kinds in V8](https://v8.dev/blog/elements-kinds) + ## Exporting top level variables Exporting top level variables should be avoided where possible where performance diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index f0a39848e9..0aa8b3953f 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -642,11 +642,14 @@ export function createTView( } function createViewBlueprint(bindingStartIndex: number, initialViewLength: number): LView { - const blueprint = new (ngDevMode ? LViewBlueprint ! : Array)(initialViewLength) - .fill(null, 0, bindingStartIndex) - .fill(NO_CHANGE, bindingStartIndex) as LView; + const blueprint = ngDevMode ? new LViewBlueprint !() : []; + + for (let i = 0; i < initialViewLength; i++) { + blueprint.push(i < bindingStartIndex ? null : NO_CHANGE); + } blueprint[BINDING_INDEX] = bindingStartIndex; - return blueprint; + + return blueprint as LView; } export function createError(text: string, token: any) { @@ -1562,7 +1565,7 @@ export function componentRefresh(hostLView: LView, adjustedElementIndex: number) function syncViewWithBlueprint(componentView: LView) { const componentTView = componentView[TVIEW]; for (let i = componentView.length; i < componentTView.blueprint.length; i++) { - componentView[i] = componentTView.blueprint[i]; + componentView.push(componentTView.blueprint[i]); } }