Skip to content

Commit c76cac2

Browse files
AndrewKushnirdylhunn
authored andcommitted
test(core): add benchmark for defer runtime logic (angular#52222)
This commit adds a benchmark for `@defer` runtime logic and uses `@if` as a baseline. PR Close angular#52222
1 parent c2560d0 commit c76cac2

14 files changed

Lines changed: 580 additions & 0 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
load("//tools:defaults.bzl", "ng_module", "ts_library")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ng_module(
6+
name = "shared_lib",
7+
srcs = [
8+
"init.ts",
9+
"util.ts",
10+
],
11+
tsconfig = "//modules/benchmarks:tsconfig-build.json",
12+
deps = [
13+
"//modules/benchmarks/src:util_lib",
14+
"//packages/core",
15+
"//packages/platform-browser",
16+
],
17+
)
18+
19+
ts_library(
20+
name = "perf_tests_lib",
21+
testonly = 1,
22+
srcs = ["defer.perf-spec.ts"],
23+
tsconfig = "//modules/benchmarks:tsconfig-e2e.json",
24+
deps = [
25+
"@npm//@angular/build-tooling/bazel/benchmark/driver-utilities",
26+
"@npm//protractor",
27+
],
28+
)
29+
30+
ts_library(
31+
name = "e2e_tests_lib",
32+
testonly = 1,
33+
srcs = ["defer.e2e-spec.ts"],
34+
tsconfig = "//modules/benchmarks:tsconfig-e2e.json",
35+
deps = [
36+
"@npm//@angular/build-tooling/bazel/benchmark/driver-utilities",
37+
"@npm//protractor",
38+
],
39+
)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Defer benchmark
2+
3+
This folder contains defer benchmark that tests the process of `@defer` block creation.
4+
5+
There are 2 folders in this benchmark:
6+
7+
* `baseline` - renders a component using an `@if` condition, we use it as a baseline
8+
* `main` - the same code as the `baseline`, but instead of the `@if`, we use `@defer` to compare defer blocks against conditionals
9+
10+
The benchmarks are based on `largetable` benchmarks.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
load("//tools:defaults.bzl", "app_bundle", "http_server", "ng_module")
2+
load("@npm//@angular/build-tooling/bazel/benchmark/component_benchmark:benchmark_test.bzl", "benchmark_test")
3+
load("//modules/benchmarks:e2e_test.bzl", "e2e_test")
4+
5+
package(default_visibility = ["//modules/benchmarks:__subpackages__"])
6+
7+
ng_module(
8+
name = "main",
9+
srcs = glob(["*.ts"]),
10+
tsconfig = "//modules/benchmarks:tsconfig-build.json",
11+
deps = [
12+
"//modules/benchmarks/src:util_lib",
13+
"//modules/benchmarks/src/defer:shared_lib",
14+
"//packages/core",
15+
"//packages/platform-browser",
16+
],
17+
)
18+
19+
app_bundle(
20+
name = "bundle",
21+
entry_point = ":index.ts",
22+
deps = [
23+
":main",
24+
"@npm//rxjs",
25+
],
26+
)
27+
28+
# The script needs to be called `app_bundle` for easier syncing into g3.
29+
genrule(
30+
name = "app_bundle",
31+
srcs = [":bundle.debug.min.js"],
32+
outs = ["app_bundle.js"],
33+
cmd = "cp $< $@",
34+
)
35+
36+
http_server(
37+
name = "prodserver",
38+
srcs = ["index.html"],
39+
deps = [
40+
":app_bundle",
41+
"//packages/zone.js/bundles:zone.umd.js",
42+
],
43+
)
44+
45+
benchmark_test(
46+
name = "perf",
47+
server = ":prodserver",
48+
deps = ["//modules/benchmarks/src/defer:perf_tests_lib"],
49+
)
50+
51+
e2e_test(
52+
name = "e2e",
53+
server = ":prodserver",
54+
deps = ["//modules/benchmarks/src/defer:e2e_tests_lib"],
55+
)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Component, Input} from '@angular/core';
10+
import {DomSanitizer, SafeStyle} from '@angular/platform-browser';
11+
12+
import {TableCell} from '../util';
13+
14+
let trustedEmptyColor: SafeStyle;
15+
let trustedGreyColor: SafeStyle;
16+
17+
@Component({
18+
standalone: true,
19+
selector: 'app',
20+
template: `
21+
<table>
22+
<tbody>
23+
@for (row of data; track $index) {
24+
<tr>
25+
@for (cell of row; track $index) {
26+
<td [style.backgroundColor]="getColor(cell.row)">
27+
@if (condition) {
28+
<!--
29+
Use static text in cells to avoid the need
30+
to run a new change detection cycle.
31+
-->
32+
Cell
33+
}
34+
</td>
35+
}
36+
</tr>
37+
}
38+
</tbody>
39+
</table>
40+
`,
41+
})
42+
export class AppComponent {
43+
@Input() data: TableCell[][] = [];
44+
45+
condition = true;
46+
47+
constructor(sanitizer: DomSanitizer) {
48+
trustedEmptyColor = sanitizer.bypassSecurityTrustStyle('white');
49+
trustedGreyColor = sanitizer.bypassSecurityTrustStyle('grey');
50+
}
51+
52+
getColor(row: number) {
53+
return row % 2 ? trustedEmptyColor : trustedGreyColor;
54+
}
55+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<!-- Prevent the browser from requesting any favicon. -->
6+
<link rel="icon" href="data:," />
7+
</head>
8+
<body>
9+
<h2>Params</h2>
10+
<form>
11+
Cols:
12+
<input type="number" id="cols" name="cols" value="" />
13+
<br />
14+
Rows:
15+
<input type="number" id="rows" name="rows" value="" />
16+
<br />
17+
<button>Apply</button>
18+
</form>
19+
20+
<h2>Defer Benchmark (baseline)</h2>
21+
<p>
22+
<button id="destroyDom">destroyDom</button>
23+
<button id="createDom">createDom</button>
24+
<button id="createDomProfile">profile createDom</button>
25+
<button id="updateDomProfile">profile updateDom</button>
26+
</p>
27+
28+
<div>
29+
<app id="root"></app>
30+
</div>
31+
32+
<!-- BEGIN-EXTERNAL -->
33+
<script src="/angular/packages/zone.js/bundles/zone.umd.js"></script>
34+
<!-- END-EXTERNAL -->
35+
36+
<!-- Needs to be named `app_bundle` for sync into Google. -->
37+
<script src="/app_bundle.js"></script>
38+
</body>
39+
</html>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {bootstrapApplication, provideProtractorTestingSupport} from '@angular/platform-browser';
10+
11+
import {init, syncUrlParamsToForm} from '../init';
12+
13+
import {AppComponent} from './app.component';
14+
15+
syncUrlParamsToForm();
16+
17+
bootstrapApplication(AppComponent, {
18+
providers: [
19+
provideProtractorTestingSupport(),
20+
],
21+
}).then(init);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {openBrowser, verifyNoBrowserErrors} from '@angular/build-tooling/bazel/benchmark/driver-utilities';
10+
import {$} from 'protractor';
11+
12+
describe('defer benchmark', () => {
13+
afterEach(verifyNoBrowserErrors);
14+
15+
it(`should render the table`, async () => {
16+
openBrowser({
17+
url: '',
18+
ignoreBrowserSynchronization: true,
19+
params: [{name: 'cols', value: 5}, {name: 'rows', value: 5}],
20+
});
21+
await $('#createDom').click();
22+
expect($('#root').getText()).toContain('Cell');
23+
});
24+
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {runBenchmark, verifyNoBrowserErrors} from '@angular/build-tooling/bazel/benchmark/driver-utilities';
10+
import {$} from 'protractor';
11+
12+
interface Worker {
13+
id: string;
14+
prepare?(): void;
15+
work(): void;
16+
}
17+
18+
const CreateWorker: Worker = {
19+
id: 'create',
20+
prepare: () => $('#destroyDom').click(),
21+
work: () => $('#createDom').click()
22+
};
23+
24+
const UpdateWorker: Worker = {
25+
id: 'update',
26+
prepare: () => {
27+
$('#createDom').click();
28+
},
29+
work: () => $('#createDom').click()
30+
};
31+
32+
// In order to make sure that we don't change the ids of the benchmarks, we need to
33+
// determine the current test package name from the Bazel target. This is necessary
34+
// because previous to the Bazel conversion, the benchmark test ids contained the test
35+
// name. e.g. "largeTable.ng2_switch.createDestroy". We determine the name of the
36+
// Bazel package where this test runs from the current test target. The Bazel target
37+
// looks like: "//modules/benchmarks/src/largetable/{pkg_name}:{target_name}".
38+
const testPackageName = process.env['BAZEL_TARGET']!.split(':')[0].split('/').pop();
39+
40+
describe('defer benchmark perf', () => {
41+
afterEach(verifyNoBrowserErrors);
42+
43+
[CreateWorker, UpdateWorker].forEach((worker) => {
44+
describe(worker.id, () => {
45+
it(`should run benchmark for ${testPackageName}`, async () => {
46+
await runTableBenchmark({
47+
id: `defer.${testPackageName}.${worker.id}`,
48+
url: '/',
49+
ignoreBrowserSynchronization: true,
50+
worker,
51+
});
52+
});
53+
});
54+
});
55+
});
56+
57+
function runTableBenchmark(
58+
config: {id: string, url: string, ignoreBrowserSynchronization?: boolean, worker: Worker}) {
59+
return runBenchmark({
60+
id: config.id,
61+
url: config.url,
62+
ignoreBrowserSynchronization: config.ignoreBrowserSynchronization,
63+
params: [{name: 'cols', value: 40}, {name: 'rows', value: 200}],
64+
prepare: config.worker.prepare,
65+
work: config.worker.work
66+
});
67+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {ApplicationRef} from '@angular/core';
10+
11+
import {bindAction, profile} from '../util';
12+
13+
import {buildTable, emptyTable, initTableUtils} from './util';
14+
15+
const DEFAULT_COLS_COUNT = '40';
16+
const DEFAULT_ROWS_COUNT = '200';
17+
18+
function getUrlParamValue(name: string): string|null {
19+
const url = new URL(document.location.href);
20+
return url.searchParams.get(name);
21+
}
22+
23+
export function syncUrlParamsToForm(): {cols: string, rows: string} {
24+
let cols = getUrlParamValue('cols') ?? DEFAULT_COLS_COUNT;
25+
let rows = getUrlParamValue('rows') ?? DEFAULT_ROWS_COUNT;
26+
(document.getElementById('cols') as HTMLInputElement).value = cols;
27+
(document.getElementById('rows') as HTMLInputElement).value = rows;
28+
return {cols, rows};
29+
}
30+
31+
export function init(appRef: ApplicationRef) {
32+
const table = appRef.components[0].instance;
33+
34+
function destroyDom() {
35+
table.data = emptyTable;
36+
appRef.tick();
37+
}
38+
39+
function createDom() {
40+
table.data = buildTable();
41+
appRef.tick();
42+
}
43+
44+
function noop() {}
45+
46+
initTableUtils();
47+
48+
bindAction('#destroyDom', destroyDom);
49+
bindAction('#createDom', createDom);
50+
bindAction('#createDomProfile', profile(createDom, destroyDom, 'create'));
51+
bindAction('#updateDomProfile', profile(createDom, noop, 'update'));
52+
}

0 commit comments

Comments
 (0)