refactor(ivy): rewrite flatten function to be more memory efficient (#30468)

The `flatten` function used `concat` and `slice` which created a lot of intermediary
object allocations. Because `flatten` is used from query any benchmark which
used query would exhibit high minor GC counts.

PR Close #30468
This commit is contained in:
Miško Hevery 2019-05-14 15:55:46 -07:00 committed by Jason Aden
parent c9f5f3d802
commit f3c69e7f6b
4 changed files with 35 additions and 37 deletions

View File

@ -3,7 +3,7 @@
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime": 1497, "runtime": 1497,
"main": 166739, "main": 166799,
"polyfills": 43626 "polyfills": 43626
} }
} }

View File

@ -30,20 +30,16 @@ function getSymbolIterator() {
if ("undefined" === typeof ngI18nClosureMode) _global["ngI18nClosureMode"] = "undefined" !== typeof goog && "function" === typeof goog.getMsg; if ("undefined" === typeof ngI18nClosureMode) _global["ngI18nClosureMode"] = "undefined" !== typeof goog && "function" === typeof goog.getMsg;
function flatten(list, mapFn) { function flatten(list, dst) {
const result = []; if (void 0 === dst) dst = list;
let i = 0; for (let i = 0; i < list.length; i++) {
while (i < list.length) { let item = list[i];
const item = list[i]; if (Array.isArray(item)) {
if (Array.isArray(item)) if (item.length > 0) { if (dst === list) dst = list.slice(0, i);
list = item.concat(list.slice(i + 1)); flatten(item, dst);
i = 0; } else if (dst !== list) dst.push(item);
} else i++; else {
result.push(mapFn ? mapFn(item) : item);
i++;
} }
} return dst;
return result;
} }
class EventEmitter extends Subject { class EventEmitter extends Subject {

View File

@ -108,11 +108,13 @@ export function compileNgModuleDefs(moduleType: NgModuleType, ngModule: NgModule
ngModuleDef = getCompilerFacade().compileNgModule( ngModuleDef = getCompilerFacade().compileNgModule(
angularCoreEnv, `ng:///${moduleType.name}/ngModuleDef.js`, { angularCoreEnv, `ng:///${moduleType.name}/ngModuleDef.js`, {
type: moduleType, type: moduleType,
bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY, resolveForwardRef), bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY).map(resolveForwardRef),
declarations: declarations.map(resolveForwardRef), declarations: declarations.map(resolveForwardRef),
imports: flatten(ngModule.imports || EMPTY_ARRAY, resolveForwardRef) imports: flatten(ngModule.imports || EMPTY_ARRAY)
.map(resolveForwardRef)
.map(expandModuleWithProviders), .map(expandModuleWithProviders),
exports: flatten(ngModule.exports || EMPTY_ARRAY, resolveForwardRef) exports: flatten(ngModule.exports || EMPTY_ARRAY)
.map(resolveForwardRef)
.map(expandModuleWithProviders), .map(expandModuleWithProviders),
emitInline: true, emitInline: true,
schemas: ngModule.schemas ? flatten(ngModule.schemas) : null, schemas: ngModule.schemas ? flatten(ngModule.schemas) : null,
@ -156,12 +158,12 @@ function verifySemanticsOfNgModuleDef(moduleType: NgModuleType): void {
const errors: string[] = []; const errors: string[] = [];
const declarations = maybeUnwrapFn(ngModuleDef.declarations); const declarations = maybeUnwrapFn(ngModuleDef.declarations);
const imports = maybeUnwrapFn(ngModuleDef.imports); const imports = maybeUnwrapFn(ngModuleDef.imports);
flatten(imports, unwrapModuleWithProvidersImports).forEach(verifySemanticsOfNgModuleDef); flatten(imports).map(unwrapModuleWithProvidersImports).forEach(verifySemanticsOfNgModuleDef);
const exports = maybeUnwrapFn(ngModuleDef.exports); const exports = maybeUnwrapFn(ngModuleDef.exports);
declarations.forEach(verifyDeclarationsHaveDefinitions); declarations.forEach(verifyDeclarationsHaveDefinitions);
const combinedDeclarations: Type<any>[] = [ const combinedDeclarations: Type<any>[] = [
...declarations.map(resolveForwardRef), // ...declarations.map(resolveForwardRef), //
...flatten(imports.map(computeCombinedExports), resolveForwardRef), ...flatten(imports.map(computeCombinedExports)).map(resolveForwardRef),
]; ];
exports.forEach(verifyExportsAreDeclaredOrReExported); exports.forEach(verifyExportsAreDeclaredOrReExported);
declarations.forEach(verifyDeclarationIsUnique); declarations.forEach(verifyDeclarationIsUnique);
@ -170,7 +172,8 @@ function verifySemanticsOfNgModuleDef(moduleType: NgModuleType): void {
const ngModule = getAnnotation<NgModule>(moduleType, 'NgModule'); const ngModule = getAnnotation<NgModule>(moduleType, 'NgModule');
if (ngModule) { if (ngModule) {
ngModule.imports && ngModule.imports &&
flatten(ngModule.imports, unwrapModuleWithProvidersImports) flatten(ngModule.imports)
.map(unwrapModuleWithProvidersImports)
.forEach(verifySemanticsOfNgModuleDef); .forEach(verifySemanticsOfNgModuleDef);
ngModule.bootstrap && ngModule.bootstrap.forEach(verifyCorrectBootstrapType); ngModule.bootstrap && ngModule.bootstrap.forEach(verifyCorrectBootstrapType);
ngModule.bootstrap && ngModule.bootstrap.forEach(verifyComponentIsPartOfNgModule); ngModule.bootstrap && ngModule.bootstrap.forEach(verifyComponentIsPartOfNgModule);

View File

@ -19,24 +19,23 @@ export function addAllToArray(items: any[], arr: any[]) {
} }
/** /**
* Flattens an array in non-recursive way. Input arrays are not modified. * Flattens an array.
*/ */
export function flatten(list: any[], mapFn?: (value: any) => any): any[] { export function flatten(list: any[], dst?: any[]): any[] {
const result: any[] = []; if (dst === undefined) dst = list;
let i = 0; for (let i = 0; i < list.length; i++) {
while (i < list.length) { let item = list[i];
const item = list[i];
if (Array.isArray(item)) { if (Array.isArray(item)) {
if (item.length > 0) { // we need to inline it.
list = item.concat(list.slice(i + 1)); if (dst === list) {
i = 0; // Our assumption that the list was already flat was wrong and
} else { // we need to clone flat since we need to write to it.
i++; dst = list.slice(0, i);
} }
} else { flatten(item, dst);
result.push(mapFn ? mapFn(item) : item); } else if (dst !== list) {
i++; dst.push(item);
} }
} }
return result; return dst;
} }