Skip to content

Commit

Permalink
perf(ivy): initialise inputs from static attrs on the first template …
Browse files Browse the repository at this point in the history
…pass only (angular#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 angular#33195
  • Loading branch information
pkozlowski-opensource authored and matsko committed Oct 16, 2019
1 parent 21c1e14 commit aef7dca
Showing 1 changed file with 21 additions and 31 deletions.
52 changes: 21 additions & 31 deletions packages/core/src/render3/instructions/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';



Expand Down Expand Up @@ -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);
}

Expand All @@ -857,6 +862,7 @@ function initializeInputAndOutputAliases(tView: TView, tNode: TNode): void {
}
}

tNode.initialInputs = inputsFromAttrs;
tNode.inputs = inputsStore;
tNode.outputs = outputsStore;
}
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;) {
Expand Down Expand Up @@ -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];
Expand All @@ -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;
}

//////////////////////////
Expand Down

0 comments on commit aef7dca

Please sign in to comment.