perf(ivy): initialise inputs from static attrs on the first template pass only (#33195)
This change assures that data structures related to initial inputs (ones set from static attributes) are created only once (during the first template pass) and no additional runtime checks are done for subsequent passes. Additionally this commit changes the data structure used by initial inputs on TNode - previously initial inputs for a directive were stored at the directive index in LView. This meant that an array holding initial inputs was relativelly big and had many null elements (as placeholders for elements, directives, injector etc.). After the change we only create an array of a size equal to a number of directives matched on a given TNode. For the `directive_instantiate` benchmark it boils to allocating a 1-element array vs. 100-element array previously. PR Close #33195
This commit is contained in:
parent
21c1e14385
commit
aef7dca234
|
@ -39,7 +39,7 @@ import {getLViewParent} from '../util/view_traversal_utils';
|
||||||
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils';
|
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils';
|
||||||
|
|
||||||
import {selectIndexInternal} from './advance';
|
import {selectIndexInternal} from './advance';
|
||||||
import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeConstructor, TNodeInitialData, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor, attachLContainerDebug, attachLViewDebug, cloneToLView, cloneToTViewData} from './lview_debug';
|
import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeConstructor, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor, attachLContainerDebug, attachLViewDebug, cloneToLView, cloneToTViewData} from './lview_debug';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -840,11 +840,16 @@ function initializeInputAndOutputAliases(tView: TView, tNode: TNode): void {
|
||||||
const end = tNode.directiveEnd;
|
const end = tNode.directiveEnd;
|
||||||
const defs = tView.data;
|
const defs = tView.data;
|
||||||
|
|
||||||
|
const tNodeAttrs = tNode.attrs;
|
||||||
|
const inputsFromAttrs: InitialInputData = ngDevMode ? new TNodeInitialInputs() : [];
|
||||||
let inputsStore: PropertyAliases|null = null;
|
let inputsStore: PropertyAliases|null = null;
|
||||||
let outputsStore: PropertyAliases|null = null;
|
let outputsStore: PropertyAliases|null = null;
|
||||||
for (let i = start; i < end; i++) {
|
for (let i = start; i < end; i++) {
|
||||||
const directiveDef = defs[i] as DirectiveDef<any>;
|
const directiveDef = defs[i] as DirectiveDef<any>;
|
||||||
inputsStore = generatePropertyAliases(directiveDef.inputs, i, inputsStore);
|
const directiveInputs = directiveDef.inputs;
|
||||||
|
inputsFromAttrs.push(
|
||||||
|
tNodeAttrs !== null ? generateInitialInputs(directiveInputs, tNodeAttrs) : null);
|
||||||
|
inputsStore = generatePropertyAliases(directiveInputs, i, inputsStore);
|
||||||
outputsStore = generatePropertyAliases(directiveDef.outputs, i, outputsStore);
|
outputsStore = generatePropertyAliases(directiveDef.outputs, i, outputsStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -857,6 +862,7 @@ function initializeInputAndOutputAliases(tView: TView, tNode: TNode): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tNode.initialInputs = inputsFromAttrs;
|
||||||
tNode.inputs = inputsStore;
|
tNode.inputs = inputsStore;
|
||||||
tNode.outputs = outputsStore;
|
tNode.outputs = outputsStore;
|
||||||
}
|
}
|
||||||
|
@ -1046,7 +1052,7 @@ export function resolveDirectives(
|
||||||
const directives: DirectiveDef<any>[]|null = findDirectiveMatches(tView, lView, tNode);
|
const directives: DirectiveDef<any>[]|null = findDirectiveMatches(tView, lView, tNode);
|
||||||
const exportsMap: ({[key: string]: number} | null) = localRefs ? {'': -1} : null;
|
const exportsMap: ({[key: string]: number} | null) = localRefs ? {'': -1} : null;
|
||||||
|
|
||||||
if (directives) {
|
if (directives !== null) {
|
||||||
initNodeFlags(tNode, tView.data.length, directives.length);
|
initNodeFlags(tNode, tView.data.length, directives.length);
|
||||||
// When the same token is provided by several directives on the same node, some rules apply in
|
// When the same token is provided by several directives on the same node, some rules apply in
|
||||||
// the viewEngine:
|
// the viewEngine:
|
||||||
|
@ -1102,7 +1108,7 @@ function instantiateAllDirectives(tView: TView, lView: LView, tNode: TNode) {
|
||||||
addComponentLogic(lView, tNode, def as ComponentDef<any>);
|
addComponentLogic(lView, tNode, def as ComponentDef<any>);
|
||||||
}
|
}
|
||||||
const directive = getNodeInjectable(tView.data, lView !, i, tNode as TElementNode);
|
const directive = getNodeInjectable(tView.data, lView !, i, tNode as TElementNode);
|
||||||
postProcessDirective(lView, tNode, directive, def, i);
|
postProcessDirective(lView, tNode, directive, def, i - start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1176,7 +1182,7 @@ function postProcessDirective<T>(
|
||||||
lView: LView, hostTNode: TNode, directive: T, def: DirectiveDef<T>,
|
lView: LView, hostTNode: TNode, directive: T, def: DirectiveDef<T>,
|
||||||
directiveDefIdx: number): void {
|
directiveDefIdx: number): void {
|
||||||
postProcessBaseDirective(lView, hostTNode, directive);
|
postProcessBaseDirective(lView, hostTNode, directive);
|
||||||
if (hostTNode.attrs !== null) {
|
if (hostTNode.initialInputs !== null) {
|
||||||
setInputsFromAttrs(lView, directiveDefIdx, directive, def, hostTNode);
|
setInputsFromAttrs(lView, directiveDefIdx, directive, def, hostTNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1362,12 +1368,8 @@ export function elementAttributeInternal(
|
||||||
*/
|
*/
|
||||||
function setInputsFromAttrs<T>(
|
function setInputsFromAttrs<T>(
|
||||||
lView: LView, directiveIndex: number, instance: T, def: DirectiveDef<T>, tNode: TNode): void {
|
lView: LView, directiveIndex: number, instance: T, def: DirectiveDef<T>, tNode: TNode): void {
|
||||||
let initialInputData = tNode.initialInputs as InitialInputData | undefined;
|
const initialInputData = tNode.initialInputs as InitialInputData;
|
||||||
if (initialInputData === undefined || directiveIndex >= initialInputData.length) {
|
const initialInputs: InitialInputs|null = initialInputData ![directiveIndex];
|
||||||
initialInputData = generateInitialInputs(directiveIndex, def.inputs, tNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialInputs: InitialInputs|null = initialInputData[directiveIndex];
|
|
||||||
if (initialInputs !== null) {
|
if (initialInputs !== null) {
|
||||||
const setInput = def.setInput;
|
const setInput = def.setInput;
|
||||||
for (let i = 0; i < initialInputs.length;) {
|
for (let i = 0; i < initialInputs.length;) {
|
||||||
|
@ -1398,20 +1400,12 @@ function setInputsFromAttrs<T>(
|
||||||
*
|
*
|
||||||
* <my-component name="Bess"></my-component>
|
* <my-component name="Bess"></my-component>
|
||||||
*
|
*
|
||||||
* @param directiveIndex Index to store the initial input data
|
|
||||||
* @param inputs The list of inputs from the directive def
|
* @param inputs The list of inputs from the directive def
|
||||||
* @param tNode The static data on this node
|
* @param attrs The static attrs on this node
|
||||||
*/
|
*/
|
||||||
function generateInitialInputs(
|
function generateInitialInputs(inputs: {[key: string]: string}, attrs: TAttributes): InitialInputs|
|
||||||
directiveIndex: number, inputs: {[key: string]: string}, tNode: TNode): InitialInputData {
|
null {
|
||||||
const initialInputData: InitialInputData =
|
let inputsToStore: InitialInputs|null = null;
|
||||||
tNode.initialInputs || (tNode.initialInputs = ngDevMode ? new TNodeInitialInputs() : []);
|
|
||||||
// Ensure that we don't create sparse arrays
|
|
||||||
for (let i = initialInputData.length; i <= directiveIndex; i++) {
|
|
||||||
initialInputData.push(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const attrs = tNode.attrs !;
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < attrs.length) {
|
while (i < attrs.length) {
|
||||||
const attrName = attrs[i];
|
const attrName = attrs[i];
|
||||||
|
@ -1428,18 +1422,14 @@ function generateInitialInputs(
|
||||||
// If we hit any other attribute markers, we're done anyway. None of those are valid inputs.
|
// If we hit any other attribute markers, we're done anyway. None of those are valid inputs.
|
||||||
if (typeof attrName === 'number') break;
|
if (typeof attrName === 'number') break;
|
||||||
|
|
||||||
const minifiedInputName = inputs[attrName as string];
|
if (inputs.hasOwnProperty(attrName as string)) {
|
||||||
const attrValue = attrs[i + 1];
|
if (inputsToStore === null) inputsToStore = [];
|
||||||
|
inputsToStore.push(attrName as string, inputs[attrName as string], attrs[i + 1] as string);
|
||||||
if (minifiedInputName !== undefined) {
|
|
||||||
const inputsToStore: InitialInputs = initialInputData[directiveIndex] ||
|
|
||||||
(initialInputData[directiveIndex] = ngDevMode ? new TNodeInitialData() : []);
|
|
||||||
inputsToStore.push(attrName as string, minifiedInputName, attrValue as string);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
return initialInputData;
|
return inputsToStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
Loading…
Reference in New Issue