Skip to content
Permalink
Browse files

perf: don't create holey arrays (#32155)

Don't use `Array` constructor with the size value (ex. `new Array(5)`) - this will create a `HOLEY_ELEMENTS` array (even if this array is filled in later on!);

https://v8.dev/blog/elements-kinds
https://stackoverflow.com/questions/32054170/how-to-resize-an-array

PR Close #32155
  • Loading branch information...
mhevery authored and AndrewKushnir committed Aug 15, 2019
1 parent c957dfc commit 64770571b2a02cf6614672468e23c7dc8196fb18
Showing with 137 additions and 83 deletions.
  1. +2 −2 modules/benchmarks/src/largetable/baseline/table.ts
  2. +1 −1 modules/benchmarks/src/old/compiler/compiler_benchmark.ts
  3. +1 −1 modules/benchmarks/src/old/costs/index.ts
  4. +2 −2 modules/benchmarks/src/tree/baseline/tree.ts
  5. +10 −0 modules/benchmarks/src/tree/util.ts
  6. +11 −1 packages/bazel/src/ng_package/packager.ts
  7. +2 −3 packages/compiler/src/aot/compiler.ts
  8. +2 −2 packages/compiler/src/expression_parser/ast.ts
  9. +5 −4 packages/compiler/src/i18n/digest.ts
  10. +2 −2 packages/compiler/src/template_parser/template_parser.ts
  11. +10 −0 packages/compiler/src/util.ts
  12. +2 −1 packages/compiler/test/aot/test_util.ts
  13. +2 −1 packages/compiler/test/output/js_emitter_spec.ts
  14. +3 −2 packages/compiler/test/output/output_jit_spec.ts
  15. +3 −1 packages/compiler/test/output/ts_emitter_spec.ts
  16. +2 −2 packages/compiler/test/template_parser/template_parser_spec.ts
  17. +2 −3 packages/core/src/di/r3_injector.ts
  18. +4 −4 packages/core/src/di/reflective_injector.ts
  19. +4 −4 packages/core/src/reflection/reflection_capabilities.ts
  20. +2 −1 packages/core/src/render3/instructions/projection.ts
  21. +5 −6 packages/core/src/render3/query.ts
  22. +10 −0 packages/core/src/util/array_utils.ts
  23. +2 −2 packages/core/src/view/element.ts
  24. +4 −3 packages/core/src/view/ng_module.ts
  25. +4 −4 packages/core/src/view/provider.ts
  26. +20 −18 packages/core/src/view/pure_expression.ts
  27. +1 −1 packages/core/src/view/text.ts
  28. +3 −0 packages/core/test/bundling/injection/bundle.golden_symbols.json
  29. +1 −1 packages/platform-browser/src/browser/browser_adapter.ts
  30. +1 −1 packages/platform-webworker/src/web_workers/shared/service_message_broker.ts
  31. +7 −5 packages/service-worker/cli/sha1.ts
  32. +7 −5 packages/service-worker/worker/src/sha1.ts
@@ -50,12 +50,12 @@ export class TableComponent {
this._rootEl.appendChild(table);
const tbody = document.createElement('tbody');
table.appendChild(tbody);
this._renderCells = new Array(data.length);
this._renderCells = [];
for (let r = 0; r < data.length; r++) {
const dataRow = data[r];
const tr = document.createElement('tr');
tbody.appendChild(tr);
const renderRow = new Array(dataRow.length);
const renderRow = [];
this._renderCells[r] = renderRow;
for (let c = 0; c < dataRow.length; c++) {
const dataCell = dataRow[c];
@@ -75,7 +75,7 @@ class MultiplyDirectiveResolver extends DirectiveResolver {

private _fillCache(component: Type) {
const view = super.resolve(component);
const multipliedTemplates = new Array(this._multiplyBy);
const multipliedTemplates = [];
for (let i = 0; i < this._multiplyBy; ++i) {
multipliedTemplates[i] = view.template;
}
@@ -18,7 +18,7 @@ let testList = null;

export function main() {
const size = getIntParameter('size');
testList = new Array(size);
testList = [];

platformBrowserDynamic().bootstrapModule(AppModule).then((ref) => {
const injector = ref.injector;
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {TreeNode} from '../util';
import {TreeNode, newArray} from '../util';

export class TreeComponent {
private _renderNodes: any[];
@@ -25,7 +25,7 @@ export class TreeComponent {

private _create(parentNode: any, dataNode: TreeNode, index: number) {
if (!this._renderNodes) {
this._renderNodes = new Array(dataNode.transitiveChildCount);
this._renderNodes = newArray(dataNode.transitiveChildCount);
}

const span = document.createElement('span');
@@ -68,3 +68,13 @@ export function flattenTree(node: TreeNode, target: TreeNode[] = []): TreeNode[]
}
return target;
}

export function newArray<T = any>(size: number): T[];
export function newArray<T>(size: number, value: T): T[];
export function newArray<T>(size: number, value?: T): T[] {
const list: T[] = [];
for (let i = 0; i < size; i++) {
list.push(value !);
}
return list;
}
@@ -335,7 +335,7 @@ function main(args: string[]): number {
const parts = packageName.split('/');
// Remove the scoped package part, like @angular if present
const nameParts = packageName.startsWith('@') ? parts.splice(1) : parts;
const relativePath = Array(nameParts.length - 1).fill('..').join('/') || '.';
const relativePath = newArray(nameParts.length - 1, '..').join('/') || '.';
let basename: string;
if (dir === 'bundles') {
basename = nameParts.join('-') + '.umd';
@@ -435,3 +435,13 @@ export * from '${srcDirRelative(inputPath, typingsFile.replace(/\.d\.tsx?$/, '')
if (require.main === module) {
process.exitCode = main(process.argv.slice(2));
}

export function newArray<T = any>(size: number): T[];
export function newArray<T>(size: number, value: T): T[];
export function newArray<T>(size: number, value?: T): T[] {
const list: T[] = [];
for (let i = 0; i < size; i++) {
list.push(value !);
}
return list;
}
@@ -14,7 +14,6 @@ import {MessageBundle} from '../i18n/message_bundle';
import {Identifiers, createTokenForExternalReference} from '../identifiers';
import {InjectableCompiler} from '../injectable_compiler';
import {CompileMetadataResolver} from '../metadata_resolver';
import * as html from '../ml_parser/ast';
import {HtmlParser} from '../ml_parser/html_parser';
import {removeWhitespaces} from '../ml_parser/html_whitespaces';
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
@@ -32,7 +31,7 @@ import {SummaryResolver} from '../summary_resolver';
import {BindingParser} from '../template_parser/binding_parser';
import {TemplateAst} from '../template_parser/template_ast';
import {TemplateParser} from '../template_parser/template_parser';
import {OutputContext, ValueVisitor, error, syntaxError, visitValue} from '../util';
import {OutputContext, ValueVisitor, error, newArray, syntaxError, visitValue} from '../util';
import {TypeCheckCompiler} from '../view_compiler/type_check_compiler';
import {ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';

@@ -690,7 +689,7 @@ export class AotCompiler {
const suppliedTypeParams = typeParams || [];
const missingTypeParamsCount = arity - suppliedTypeParams.length;
const allTypeParams =
suppliedTypeParams.concat(new Array(missingTypeParamsCount).fill(o.DYNAMIC_TYPE));
suppliedTypeParams.concat(newArray(missingTypeParamsCount, o.DYNAMIC_TYPE));
return members.reduce(
(expr, memberName) => expr.prop(memberName),
<o.Expression>o.importExpr(
@@ -436,7 +436,7 @@ export class AstTransformer implements AstVisitor {
}

visitAll(asts: any[]): any[] {
const res = new Array(asts.length);
const res = [];
for (let i = 0; i < asts.length; ++i) {
res[i] = asts[i].visit(this);
}
@@ -598,7 +598,7 @@ export class AstMemoryEfficientTransformer implements AstVisitor {
}

visitAll(asts: any[]): any[] {
const res = new Array(asts.length);
const res = [];
let modified = false;
for (let i = 0; i < asts.length; ++i) {
const original = asts[i];
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {utf8Encode} from '../util';
import {newArray, utf8Encode} from '../util';

import * as i18n from './i18n_ast';

@@ -93,7 +93,7 @@ export function sha1(str: string): string {
const words32 = stringToWords32(utf8, Endian.Big);
const len = utf8.length * 8;

const w = new Array(80);
const w = newArray(80);
let [a, b, c, d, e]: number[] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];

words32[len >> 5] |= 0x80 << (24 - len % 32);
@@ -247,9 +247,10 @@ function rol64([hi, lo]: [number, number], count: number): [number, number] {
}

function stringToWords32(str: string, endian: Endian): number[] {
const words32 = Array((str.length + 3) >>> 2);
const size = (str.length + 3) >>> 2;
const words32 = [];

for (let i = 0; i < words32.length; i++) {
for (let i = 0; i < size; i++) {
words32[i] = wordAt(str, i * 4, endian);
}

@@ -24,7 +24,7 @@ import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer'
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {CssSelector, SelectorMatcher} from '../selector';
import {isStyleUrlResolvable} from '../style_url_resolver';
import {Console, syntaxError} from '../util';
import {Console, newArray, syntaxError} from '../util';

import {BindingParser} from './binding_parser';
import * as t from './template_ast';
@@ -528,7 +528,7 @@ class TemplateParseVisitor implements html.Visitor {
// Need to sort the directives so that we get consistent results throughout,
// as selectorMatcher uses Maps inside.
// Also deduplicate directives as they might match more than one time!
const directives = new Array(this.directivesIndex.size);
const directives = newArray(this.directivesIndex.size);
// Whether any directive selector matches on the element name
let matchElement = false;

@@ -253,3 +253,13 @@ const __global = typeof global !== 'undefined' && global;
// should be __global in that case.
const _global: {[name: string]: any} = __global || __window || __self;
export {_global as global};

export function newArray<T = any>(size: number): T[];
export function newArray<T>(size: number, value: T): T[];
export function newArray<T>(size: number, value?: T): T[] {
const list: T[] = [];
for (let i = 0; i < size; i++) {
list.push(value !);
}
return list;
}
@@ -10,6 +10,7 @@ import {AotCompilerHost, AotCompilerOptions, GeneratedFile, createAotCompiler, t
import {MetadataBundlerHost} from '@angular/compiler-cli/src/metadata/bundler';
import {MetadataCollector} from '@angular/compiler-cli/src/metadata/collector';
import {ModuleMetadata} from '@angular/compiler-cli/src/metadata/index';
import {newArray} from '@angular/compiler/src/util';
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
@@ -686,7 +687,7 @@ export function expectNoDiagnostics(program: ts.Program) {
return '';
}

function chars(len: number, ch: string): string { return new Array(len).fill(ch).join(''); }
function chars(len: number, ch: string): string { return newArray(len, ch).join(''); }

function lineNoOf(offset: number, text: string): number {
let result = 1;
@@ -9,6 +9,7 @@
import {StaticSymbol} from '@angular/compiler/src/aot/static_symbol';
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import * as o from '@angular/compiler/src/output/output_ast';
import {newArray} from '@angular/compiler/src/util';

import {stripSourceMapAndNewLine} from './abstract_emitter_spec';

@@ -108,7 +109,7 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some
});

it('should break expressions into multiple lines if they are too long', () => {
const values: o.Expression[] = new Array(100);
const values: o.Expression[] = newArray(100);
values.fill(o.literal(1));
values.splice(50, 0, o.fn([], [new o.ReturnStatement(o.literal(1))]));
expect(emitStmt(o.variable('fn').callFn(values).toStmt())).toEqual([
@@ -10,6 +10,7 @@ import {EmitterVisitorContext} from '@angular/compiler/src/output/abstract_emitt
import * as o from '@angular/compiler/src/output/output_ast';
import {JitEmitterVisitor, JitEvaluator} from '@angular/compiler/src/output/output_jit';
import {R3JitReflector} from '@angular/compiler/src/render3/r3_jit';
import {newArray} from '@angular/compiler/src/util';
import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector';

const anotherModuleUrl = 'somePackage/someOtherPath';
@@ -18,10 +19,10 @@ const anotherModuleUrl = 'somePackage/someOtherPath';
describe('Output JIT', () => {
describe('regression', () => {
it('should generate unique argument names', () => {
const externalIds = new Array(10).fill(1).map(
const externalIds = newArray(10, 1).map(
(_, index) =>
new o.ExternalReference(anotherModuleUrl, `id_${index}_`, {name: `id_${index}_`}));
const externalIds1 = new Array(10).fill(1).map(
const externalIds1 = newArray(10, 1).map(
(_, index) => new o.ExternalReference(
anotherModuleUrl, `id_${index}_1`, {name: `id_${index}_1`}));
const ctx = EmitterVisitorContext.createRoot();
@@ -10,6 +10,8 @@ import {StaticSymbol} from '@angular/compiler/src/aot/static_symbol';
import * as o from '@angular/compiler/src/output/output_ast';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '@angular/compiler/src/parse_util';
import {newArray} from '@angular/compiler/src/util';

import {stripSourceMapAndNewLine} from './abstract_emitter_spec';

const someGenFilePath = 'somePackage/someGenFile';
@@ -160,7 +162,7 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some
});

it('should break expressions into multiple lines if they are too long', () => {
const values: o.Expression[] = new Array(100);
const values: o.Expression[] = newArray(100);
values.fill(o.literal(1));
values.splice(50, 0, o.fn([], [new o.ReturnStatement(o.literal(1))]));
expect(emitStmt(o.variable('fn').callFn(values).toStmt())).toEqual([
@@ -19,7 +19,7 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle
import {CompileEntryComponentMetadata, CompileStylesheetMetadata} from '../../src/compile_metadata';
import {Identifiers, createTokenForExternalReference, createTokenForReference} from '../../src/identifiers';
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config';
import {noUndefined} from '../../src/util';
import {newArray, noUndefined} from '../../src/util';
import {MockSchemaRegistry} from '../../testing';
import {unparse} from '../expression_parser/utils/unparser';
import {TEST_COMPILER_PROVIDERS} from '../test_bindings';
@@ -481,7 +481,7 @@ class ArrayConsole implements Console {
new BoundDirectivePropertyAst('foo', 'bar', null !, null !)
];
const result = templateVisitAll(visitor, nodes, null);
expect(result).toEqual(new Array(nodes.length).fill(true));
expect(result).toEqual(newArray(nodes.length).fill(true));
});
});

@@ -11,9 +11,8 @@ import '../util/ng_dev_mode';
import {OnDestroy} from '../interface/lifecycle_hooks';
import {Type} from '../interface/type';
import {throwCyclicDependencyError, throwInvalidProviderError, throwMixedMultiProviderError} from '../render3/errors';
import {deepForEach} from '../util/array_utils';
import {deepForEach, newArray} from '../util/array_utils';
import {stringify} from '../util/stringify';

import {resolveForwardRef} from './forward_ref';
import {InjectionToken} from './injection_token';
import {Injector} from './injector';
@@ -428,7 +427,7 @@ function getUndecoratedInjectableFactory(token: Function) {
// If the token has parameters then it has dependencies that we cannot resolve implicitly.
const paramLength = token.length;
if (paramLength > 0) {
const args: string[] = new Array(paramLength).fill('?');
const args: string[] = newArray(paramLength, '?');
throw new Error(`Can't resolve all parameters for ${stringify(token)}: (${args.join(', ')}).`);
}

@@ -288,8 +288,8 @@ export class ReflectiveInjector_ implements ReflectiveInjector {

const len = _providers.length;

this.keyIds = new Array(len);
this.objs = new Array(len);
this.keyIds = [];
this.objs = [];

for (let i = 0; i < len; i++) {
this.keyIds[i] = _providers[i].key.id;
@@ -339,7 +339,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {

private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
if (provider.multiProvider) {
const res = new Array(provider.resolvedFactories.length);
const res = [];
for (let i = 0; i < provider.resolvedFactories.length; ++i) {
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
}
@@ -455,7 +455,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
}

function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] {
const res: any[] = new Array(injector._providers.length);
const res: any[] = [];
for (let i = 0; i < injector._providers.length; ++i) {
res[i] = fn(injector.getProviderAtIndex(i));
}
@@ -7,10 +7,10 @@
*/

import {Type, isType} from '../interface/type';
import {newArray} from '../util/array_utils';
import {ANNOTATIONS, PARAMETERS, PROP_METADATA} from '../util/decorators';
import {global} from '../util/global';
import {stringify} from '../util/stringify';

import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
import {GetterFn, MethodFn, SetterFn} from './types';

@@ -53,9 +53,9 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
let result: any[][];

if (typeof paramTypes === 'undefined') {
result = new Array(paramAnnotations.length);
result = newArray(paramAnnotations.length);
} else {
result = new Array(paramTypes.length);
result = newArray(paramTypes.length);
}

for (let i = 0; i < result.length; i++) {
@@ -120,7 +120,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
// based on function.length.
// Note: We know that this is a real constructor as we checked
// the content of the constructor above.
return new Array((<any>type.length)).fill(undefined);
return newArray<any[]>(type.length);
}

parameters(type: Type<any>): any[][] {
@@ -5,6 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {newArray} from '../../util/array_utils';
import {TAttributes, TElementNode, TNode, TNodeType} from '../interfaces/node';
import {ProjectionSlots} from '../interfaces/projection';
import {TVIEW, T_HOST} from '../interfaces/view';
@@ -81,7 +82,7 @@ export function ɵɵprojectionDef(projectionSlots?: ProjectionSlots): void {
// projection slot with the wildcard selector.
const numProjectionSlots = projectionSlots ? projectionSlots.length : 1;
const projectionHeads: (TNode | null)[] = componentNode.projection =
new Array(numProjectionSlots).fill(null);
newArray(numProjectionSlots, null !as TNode);
const tails: (TNode | null)[] = projectionHeads.slice();

let componentChild: TNode|null = componentNode.child;

0 comments on commit 6477057

Please sign in to comment.
You can’t perform that action at this time.