test(core): Add benchmark for transplanted views when insertion is dirty (#36722)
The current benchmark for transplanted views only exercises the path when the declaration location is dirty and the insertion is not. This test adds a benchmark for when both insertion and declaration are dirty. PR Close #36722
This commit is contained in:
parent
e30e1325f3
commit
35d61c11fd
|
@ -23,8 +23,14 @@ describe('change detection benchmark', () => {
|
||||||
expect(await $('#root').getText()).toEqual('');
|
expect(await $('#root').getText()).toEqual('');
|
||||||
await $('#createDom').click();
|
await $('#createDom').click();
|
||||||
expect($('#root').getText()).toContain('1');
|
expect($('#root').getText()).toContain('1');
|
||||||
|
|
||||||
|
await $('#markInsertionComponentForCheck').click();
|
||||||
await $('#detectChanges').click();
|
await $('#detectChanges').click();
|
||||||
expect($('#root').getText()).toContain('2');
|
// Ivy currently refreshes at *both* declaration and insertion while VE only refreshes at
|
||||||
|
// insertion. Simply assert that the view was updated at least once since the first update.
|
||||||
|
expect(Number(await $('#root').getText())).toBeGreaterThan(1);
|
||||||
|
|
||||||
|
// The button click causes change detection to trigger at the root
|
||||||
await $('#destroyDom').click();
|
await $('#destroyDom').click();
|
||||||
expect(await $('#root').getText()).toEqual('');
|
expect(await $('#root').getText()).toEqual('');
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,8 +15,9 @@ interface Worker {
|
||||||
work(): void;
|
work(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UpdateWorker: Worker = {
|
// Used to benchmark performance when insertion tree is not dirty.
|
||||||
id: 'createOnly',
|
const InsertionNotDirtyWorker: Worker = {
|
||||||
|
id: 'insertionNotDirty',
|
||||||
prepare: () => {
|
prepare: () => {
|
||||||
$('#destroyDom').click();
|
$('#destroyDom').click();
|
||||||
$('#createDom').click();
|
$('#createDom').click();
|
||||||
|
@ -24,6 +25,17 @@ const UpdateWorker: Worker = {
|
||||||
work: () => $('#detectChanges').click()
|
work: () => $('#detectChanges').click()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Used to benchmark performance when both declaration and insertion trees are dirty.
|
||||||
|
const AllComponentsDirtyWorker: Worker = {
|
||||||
|
id: 'allComponentsDirty',
|
||||||
|
prepare: () => {
|
||||||
|
$('#destroyDom').click();
|
||||||
|
$('#createDom').click();
|
||||||
|
$('#markInsertionComponentForCheck').click();
|
||||||
|
},
|
||||||
|
work: () => $('#detectChanges').click()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// In order to make sure that we don't change the ids of the benchmarks, we need to
|
// In order to make sure that we don't change the ids of the benchmarks, we need to
|
||||||
// determine the current test package name from the Bazel target. This is necessary
|
// determine the current test package name from the Bazel target. This is necessary
|
||||||
|
@ -36,7 +48,7 @@ const testPackageName = process.env['BAZEL_TARGET']!.split(':')[0].split('/').po
|
||||||
describe('change detection benchmark perf', () => {
|
describe('change detection benchmark perf', () => {
|
||||||
afterEach(verifyNoBrowserErrors);
|
afterEach(verifyNoBrowserErrors);
|
||||||
|
|
||||||
[UpdateWorker].forEach((worker) => {
|
[InsertionNotDirtyWorker, AllComponentsDirtyWorker].forEach((worker) => {
|
||||||
describe(worker.id, () => {
|
describe(worker.id, () => {
|
||||||
it(`should run benchmark for ${testPackageName}`, async () => {
|
it(`should run benchmark for ${testPackageName}`, async () => {
|
||||||
await runChangeDetectionBenchmark({
|
await runChangeDetectionBenchmark({
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
<p>
|
<p>
|
||||||
<button id="destroyDom">destroyDom</button>
|
<button id="destroyDom">destroyDom</button>
|
||||||
<button id="createDom">createDom</button>
|
<button id="createDom">createDom</button>
|
||||||
|
<button id="markInsertionComponentForCheck">markInsertionComponentForCheck</button>
|
||||||
<button id="detectChanges">detectChanges</button>
|
<button id="detectChanges">detectChanges</button>
|
||||||
<button id="detectChangesProfile">profile detectChanges</button>
|
<button id="detectChangesProfile">profile detectChanges</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -19,6 +19,7 @@ export function init(moduleRef: NgModuleRef<TransplantedViewsModule>) {
|
||||||
|
|
||||||
bindAction('#destroyDom', destroyDom);
|
bindAction('#destroyDom', destroyDom);
|
||||||
bindAction('#createDom', createDom);
|
bindAction('#createDom', createDom);
|
||||||
|
bindAction('#markInsertionComponentForCheck', markInsertionComponentForCheck);
|
||||||
bindAction('#detectChanges', detectChanges);
|
bindAction('#detectChanges', detectChanges);
|
||||||
bindAction('#detectChangesProfile', profile(detectChanges, noop, 'detectChanges'));
|
bindAction('#detectChangesProfile', profile(detectChanges, noop, 'detectChanges'));
|
||||||
|
|
||||||
|
@ -35,6 +36,10 @@ export function init(moduleRef: NgModuleRef<TransplantedViewsModule>) {
|
||||||
appRef.tick();
|
appRef.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markInsertionComponentForCheck() {
|
||||||
|
declaration.insertionComponent.changeDetector.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
function detectChanges() {
|
function detectChanges() {
|
||||||
appRef.tick();
|
appRef.tick();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,29 +6,11 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ChangeDetectionStrategy, Component, Input, NgModule, TemplateRef} from '@angular/core';
|
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, NgModule, TemplateRef, ViewChild} from '@angular/core';
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
|
|
||||||
import {newArray} from '../util';
|
import {newArray} from '../util';
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'declaration-component',
|
|
||||||
template: `
|
|
||||||
<ng-template #template>{{trackTemplateRefresh()}}</ng-template>
|
|
||||||
<insertion-component [template]="template" [viewCount]="viewCount"></insertion-component>
|
|
||||||
`,
|
|
||||||
})
|
|
||||||
export class DeclarationComponent {
|
|
||||||
@Input() viewCount = 1;
|
|
||||||
// Tracks number of times the template was executed to ensure it was updated during CD.
|
|
||||||
templateRefreshCount = 0;
|
|
||||||
|
|
||||||
trackTemplateRefresh() {
|
|
||||||
this.templateRefreshCount++;
|
|
||||||
return this.templateRefreshCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'insertion-component',
|
selector: 'insertion-component',
|
||||||
template: `
|
template: `
|
||||||
|
@ -44,12 +26,33 @@ export class InsertionComponent {
|
||||||
this.views = n > 0 ? newArray<any>(n) : [];
|
this.views = n > 0 ? newArray<any>(n) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor(readonly changeDetector: ChangeDetectorRef) {}
|
||||||
|
|
||||||
// use trackBy to ensure profile isn't affected by the cost to refresh ngFor.
|
// use trackBy to ensure profile isn't affected by the cost to refresh ngFor.
|
||||||
trackByIndex(index: number, item: any) {
|
trackByIndex(index: number, item: any) {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'declaration-component',
|
||||||
|
template: `
|
||||||
|
<ng-template #template>{{trackTemplateRefresh()}}</ng-template>
|
||||||
|
<insertion-component [template]="template" [viewCount]="viewCount"></insertion-component>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class DeclarationComponent {
|
||||||
|
@Input() viewCount = 1;
|
||||||
|
@ViewChild(InsertionComponent) insertionComponent!: InsertionComponent;
|
||||||
|
// Tracks number of times the template was executed to ensure it was updated during CD.
|
||||||
|
templateRefreshCount = 0;
|
||||||
|
|
||||||
|
trackTemplateRefresh() {
|
||||||
|
this.templateRefreshCount++;
|
||||||
|
return this.templateRefreshCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [DeclarationComponent, InsertionComponent],
|
declarations: [DeclarationComponent, InsertionComponent],
|
||||||
bootstrap: [DeclarationComponent],
|
bootstrap: [DeclarationComponent],
|
||||||
|
|
Loading…
Reference in New Issue