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:
Pawel Kozlowski 2019-10-16 11:44:08 +02:00 committed by Matias Niemelä
parent 21c1e14385
commit aef7dca234
1 changed files with 21 additions and 31 deletions

View File

@ -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;
} }
////////////////////////// //////////////////////////