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