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 {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 defs = tView.data;
|
||||
|
||||
const tNodeAttrs = tNode.attrs;
|
||||
const inputsFromAttrs: InitialInputData = ngDevMode ? new TNodeInitialInputs() : [];
|
||||
let inputsStore: PropertyAliases|null = null;
|
||||
let outputsStore: PropertyAliases|null = null;
|
||||
for (let i = start; i < end; i++) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -857,6 +862,7 @@ function initializeInputAndOutputAliases(tView: TView, tNode: TNode): void {
|
|||
}
|
||||
}
|
||||
|
||||
tNode.initialInputs = inputsFromAttrs;
|
||||
tNode.inputs = inputsStore;
|
||||
tNode.outputs = outputsStore;
|
||||
}
|
||||
|
@ -1046,7 +1052,7 @@ export function resolveDirectives(
|
|||
const directives: DirectiveDef<any>[]|null = findDirectiveMatches(tView, lView, tNode);
|
||||
const exportsMap: ({[key: string]: number} | null) = localRefs ? {'': -1} : null;
|
||||
|
||||
if (directives) {
|
||||
if (directives !== null) {
|
||||
initNodeFlags(tNode, tView.data.length, directives.length);
|
||||
// When the same token is provided by several directives on the same node, some rules apply in
|
||||
// the viewEngine:
|
||||
|
@ -1102,7 +1108,7 @@ function instantiateAllDirectives(tView: TView, lView: LView, tNode: TNode) {
|
|||
addComponentLogic(lView, tNode, def as ComponentDef<any>);
|
||||
}
|
||||
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>,
|
||||
directiveDefIdx: number): void {
|
||||
postProcessBaseDirective(lView, hostTNode, directive);
|
||||
if (hostTNode.attrs !== null) {
|
||||
if (hostTNode.initialInputs !== null) {
|
||||
setInputsFromAttrs(lView, directiveDefIdx, directive, def, hostTNode);
|
||||
}
|
||||
|
||||
|
@ -1362,12 +1368,8 @@ export function elementAttributeInternal(
|
|||
*/
|
||||
function setInputsFromAttrs<T>(
|
||||
lView: LView, directiveIndex: number, instance: T, def: DirectiveDef<T>, tNode: TNode): void {
|
||||
let initialInputData = tNode.initialInputs as InitialInputData | undefined;
|
||||
if (initialInputData === undefined || directiveIndex >= initialInputData.length) {
|
||||
initialInputData = generateInitialInputs(directiveIndex, def.inputs, tNode);
|
||||
}
|
||||
|
||||
const initialInputs: InitialInputs|null = initialInputData[directiveIndex];
|
||||
const initialInputData = tNode.initialInputs as InitialInputData;
|
||||
const initialInputs: InitialInputs|null = initialInputData ![directiveIndex];
|
||||
if (initialInputs !== null) {
|
||||
const setInput = def.setInput;
|
||||
for (let i = 0; i < initialInputs.length;) {
|
||||
|
@ -1398,20 +1400,12 @@ function setInputsFromAttrs<T>(
|
|||
*
|
||||
* <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 tNode The static data on this node
|
||||
* @param attrs The static attrs on this node
|
||||
*/
|
||||
function generateInitialInputs(
|
||||
directiveIndex: number, inputs: {[key: string]: string}, tNode: TNode): InitialInputData {
|
||||
const initialInputData: InitialInputData =
|
||||
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 !;
|
||||
function generateInitialInputs(inputs: {[key: string]: string}, attrs: TAttributes): InitialInputs|
|
||||
null {
|
||||
let inputsToStore: InitialInputs|null = null;
|
||||
let i = 0;
|
||||
while (i < attrs.length) {
|
||||
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 (typeof attrName === 'number') break;
|
||||
|
||||
const minifiedInputName = inputs[attrName as string];
|
||||
const attrValue = attrs[i + 1];
|
||||
|
||||
if (minifiedInputName !== undefined) {
|
||||
const inputsToStore: InitialInputs = initialInputData[directiveIndex] ||
|
||||
(initialInputData[directiveIndex] = ngDevMode ? new TNodeInitialData() : []);
|
||||
inputsToStore.push(attrName as string, minifiedInputName, attrValue as string);
|
||||
if (inputs.hasOwnProperty(attrName as string)) {
|
||||
if (inputsToStore === null) inputsToStore = [];
|
||||
inputsToStore.push(attrName as string, inputs[attrName as string], attrs[i + 1] as string);
|
||||
}
|
||||
|
||||
i += 2;
|
||||
}
|
||||
return initialInputData;
|
||||
return inputsToStore;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
|
Loading…
Reference in New Issue