-
Notifications
You must be signed in to change notification settings - Fork 25.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(platform-server): Add an ssr benchmark setup. (#57647)
In order to investigate the performances of SSR, this commit introduces a benchmark suite which will measure several step of the rendering. PR Close #57647
- Loading branch information
1 parent
58bfb4a
commit 84b6896
Showing
48 changed files
with
7,262 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
load("//integration:index.bzl", "ng_integration_test") | ||
|
||
ng_integration_test( | ||
name = "run", | ||
commands = [ | ||
"yarn install --cache-folder ./.yarn_local_cache", | ||
"yarn ng build", | ||
"node ./dist/ssr-benchmarks/server/server.mjs", | ||
], | ||
) | ||
|
||
## This target is intended to run the benchmark in a browser | ||
## For this the benchmark script is loaded by an html page serve the http-server script | ||
## Note: This target doesn't end until the http-server is stopped. It should not run as part of our testset. | ||
ng_integration_test( | ||
name = "run_browser", | ||
commands = [ | ||
"yarn install --cache-folder ./.yarn_local_cache", | ||
|
||
# The patch only applies for the browser target (to remove the DOM Emulation polyfills and browser incompatible code) | ||
"yarn patch", | ||
# | ||
"NG_BUILD_MANGLE=0 yarn ng build --configuration production,browser", | ||
"yarn http-server ./dist", | ||
], | ||
) | ||
|
||
# This target is mostly intended for investigating via the devTools using the flamechart | ||
ng_integration_test( | ||
name = "run_browser_emulated_dom", | ||
commands = [ | ||
"yarn install --cache-folder ./.yarn_local_cache", | ||
# We keep the emulated dom in this target | ||
# But still need to drop the node import which doesn't work in browsers. | ||
"git apply patches/@angular-devkit+build-angular++@angular+build+19.0.0-next.6+require.patch", | ||
|
||
# We keep the symbols with the NG_BUILD_MANGLE flag | ||
"NG_BUILD_MANGLE=0 yarn ng build", | ||
"yarn http-server ./dist", | ||
], | ||
) | ||
|
||
# This is a target to investigate with deopt explorer (https://github.com/microsoft/deoptexplorer-vscode) | ||
# The v8 log file will be generated in the test directory | ||
ng_integration_test( | ||
name = "run_deopt", | ||
commands = [ | ||
"yarn install --cache-folder ./.yarn_local_cache", | ||
"NG_BUILD_MANGLE=0 yarn ng build", | ||
"yarn node --prof \ | ||
--log-deopt \ | ||
--log-ic \ | ||
--log-maps \ | ||
--log-maps-details \ | ||
--log-internal-timer-events \ | ||
--log-code \ | ||
--log-source-code \ | ||
--detailed-line-info \ | ||
./dist/ssr-benchmarks/server/server.mjs narrowRun", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
## Intro | ||
|
||
This small benchmark suite is dedicated to mesure & describe how compute time is spent when rendering an application like in SSR. | ||
|
||
## Struture | ||
|
||
* `./main.ts` is the entry point to run the benchmark | ||
* `./src` contains a sample app that exports a `render` function. | ||
* This app render a table of variable size, which depends on data (`initData()`) | ||
* This app is then rendered X numbers of times | ||
|
||
* Individual function calls are measured with `startMeasuring()`/`stopMeasuring()` from the core package. | ||
* If you add a new measure, make sure to add it also to the `levels` map for it to be represented correctly in the result | ||
|
||
## Build & run | ||
|
||
`yarn bazel run //modules/ssr-benchmarks:run` | ||
|
||
|
||
### Running the benchmark in a browser environment | ||
|
||
`yarn bazel run //modules/ssr-benchmarks:run_browser` | ||
|
||
This bazel target will build the benchmark, start a http-server with a html that will load the benckmark script. | ||
The benchmark script with this target will have DOM Emulation disabled. | ||
The result will be visible in the devtools console. | ||
|
||
Note: Due to the CLI adding some polyfills, @angular/build is patched to disable DOM emulation and running server code inside a browser: | ||
1. removing an import from `node:module` in `polyfills.server.mjs` (with `tail ...`) | ||
2. removing the import of `platform-server/init`. | ||
|
||
To run create a usable flame chart, prepare a narrowed run (like `benchmarkRun(10000, 20);`). | ||
Then in the performance tab of the devtools, trigger "Record & Reload" to generate a profile. | ||
|
||
### Deopt Explorer | ||
|
||
A target is dedicated to generate a v8 log that can be fed to the [Deopt Explorer extension](https://github.com/microsoft/deoptexplorer-vscode). | ||
|
||
1. Run `yarn bazel run //modules/ssr-benchmarks:run_deopt`, | ||
2. open the project generated at the path after `Successfully ran all commands in test directory:`, | ||
3. open the logfile in the extension | ||
|
||
## Result example | ||
|
||
=== table with 10000 rows, with 1000 renders === | ||
┌─────────┬──────────────────────────────────────┬──────────┬──────────┬────────────┬───────────┐ | ||
│ (index) │ name │ min │ average │ percentage │ max │ | ||
├─────────┼──────────────────────────────────────┼──────────┼──────────┼────────────┼───────────┤ | ||
│ 0 │ ' renderApplication ' │ '77.0ms' │ '86.4ms' │ '100.0%' │ '259.2ms' │ | ||
│ 1 │ ' └ createServerPlatform ' │ '0.0ms' │ '0.1ms' │ '0.1%' │ '3.7ms' │ | ||
│ 2 │ ' └ bootstrap ' │ '35.9ms' │ '42.6ms' │ '49.3%' │ '138.4ms' │ | ||
│ 3 │ ' └ _render ' │ '39.7ms' │ '43.8ms' │ '50.7%' │ '124.9ms' │ | ||
│ 4 │ ' └ whenStable ' │ '0.0ms' │ '0.0ms' │ '0.0%' │ '0.0ms' │ | ||
│ 5 │ ' └ prepareForHydration ' │ '13.1ms' │ '14.8ms' │ '17.1%' │ '53.4ms' │ | ||
│ 6 │ ' └ insertEventRecordScript ' │ '0.0ms' │ '0.0ms' │ '0.0%' │ '0.0ms' │ | ||
│ 7 │ ' └ serializeTransferStateFactory' │ '0.0ms' │ '0.0ms' │ '0.0%' │ '0.1ms' │ | ||
│ 8 │ ' └ renderToString ' │ '7.3ms' │ '8.9ms' │ '10.3%' │ '41.8ms' │ | ||
└─────────┴──────────────────────────────────────┴──────────┴──────────┴────────────┴───────────┘ | ||
|
||
Note: The max measure is often an outlier of the first few measures, probably before the JIT optimisation happens |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
{ | ||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", | ||
"version": 1, | ||
"newProjectRoot": "projects", | ||
"projects": { | ||
"ssr-benchmarks": { | ||
"projectType": "application", | ||
"schematics": {}, | ||
"root": "", | ||
"sourceRoot": "src", | ||
"prefix": "app", | ||
"architect": { | ||
"build": { | ||
"builder": "@angular-devkit/build-angular:application", | ||
"options": { | ||
"outputPath": "dist/ssr-benchmarks", | ||
"index": "src/index.html", | ||
"browser": "src/main.ts", | ||
"polyfills": [], | ||
"tsConfig": "tsconfig.app.json", | ||
"assets": [ | ||
{ | ||
"glob": "**/*", | ||
"input": "public" | ||
} | ||
], | ||
"styles": [], | ||
"scripts": [], | ||
"server": "src/main.server.ts", | ||
"prerender": false, | ||
"ssr": { | ||
"entry": "run-benchmark.ts" | ||
} | ||
}, | ||
"configurations": { | ||
"production": { | ||
"outputHashing": "none", | ||
"define": { | ||
"DISABLE_DOM_EMULATION": "false" | ||
} | ||
}, | ||
"development": { | ||
"optimization": false, | ||
"extractLicenses": false, | ||
"sourceMap": true | ||
}, | ||
"browser": { | ||
"define": { | ||
"DISABLE_DOM_EMULATION": "true" | ||
} | ||
} | ||
}, | ||
"defaultConfiguration": "production" | ||
}, | ||
"serve": { | ||
"builder": "@angular-devkit/build-angular:dev-server", | ||
"configurations": { | ||
"production": { | ||
"buildTarget": "ssr-new:build:production" | ||
}, | ||
"development": { | ||
"buildTarget": "ssr-new:build:development" | ||
} | ||
}, | ||
"defaultConfiguration": "development" | ||
} | ||
} | ||
} | ||
}, | ||
"cli": { | ||
"analytics": "8b89251b-0b82-4190-a832-e19ddedba643", | ||
"cache": { | ||
"enabled": false | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<html> | ||
<script type="module" src="./ssr-benchmarks/server/server.mjs"></script> | ||
<body> | ||
Check the console. | ||
<app-root></app-root> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "ssr-benchmark", | ||
"version": "0.0.0", | ||
"scripts": { | ||
"patch": "yarn patch-package", | ||
"ng": "ng", | ||
"http-server": "http-server" | ||
}, | ||
"license": "MIT", | ||
"dependencies": { | ||
"@angular-devkit/build-angular": "file:../../node_modules/@angular-devkit/build-angular", | ||
"@angular/cli": "file:../../node_modules/@angular/cli", | ||
"@angular/animations": "file:../../dist/packages-dist/animations", | ||
"@angular/common": "file:../../dist/packages-dist/common", | ||
"@angular/compiler": "file:../../dist/packages-dist/compiler", | ||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", | ||
"@angular/core": "file:../../dist/packages-dist/core", | ||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser", | ||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic", | ||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server", | ||
"@angular/router": "file:../../dist/packages-dist/router", | ||
"@angular/ssr": "file:../../dist/node_modules/@angular/ssr", | ||
"rxjs": "file:../../node_modules/rxjs", | ||
"typescript": "file:../../node_modules/typescript", | ||
"http-server": "file:../../node_modules/http-server", | ||
"patch-package": "file:../../node_modules/patch-package" | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
...chmarks/patches/@angular-devkit+build-angular++@angular+build+19.0.0-next.6+require.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
diff --git a/node_modules/@angular-devkit/build-angular/node_modules/@angular/build/src/tools/esbuild/application-code-bundle.js b/node_modules/@angular-devkit/build-angular/node_modules/@angular/build/src/tools/esbuild/application-code-bundle.js | ||
index f6fb661..5e5aabb 100755 | ||
--- a/node_modules/@angular-devkit/build-angular/node_modules/@angular/build/src/tools/esbuild/application-code-bundle.js | ||
+++ b/node_modules/@angular-devkit/build-angular/node_modules/@angular/build/src/tools/esbuild/application-code-bundle.js | ||
@@ -144,8 +144,8 @@ function createServerPolyfillBundleOptions(options, target, sourceFileCache) { | ||
js: [ | ||
// Note: Needed as esbuild does not provide require shims / proxy from ESModules. | ||
// See: https://github.com/evanw/esbuild/issues/1921. | ||
- `import { createRequire } from 'node:module';`, | ||
- `globalThis['require'] ??= createRequire(import.meta.url);`, | ||
+ // `import { createRequire } from 'node:module';`, | ||
+ // `globalThis['require'] ??= createRequire(import.meta.url);`, | ||
].join('\n'), | ||
}, | ||
target, | ||
@@ -197,15 +197,17 @@ function createServerMainCodeBundleOptions(options, target, sourceFileCache) { | ||
} | ||
// Mark manifest and polyfills file as external as these are generated by a different bundle step. | ||
(buildOptions.external ??= []).push(...utils_1.SERVER_GENERATED_EXTERNALS); | ||
- buildOptions.plugins.push((0, virtual_module_plugin_1.createVirtualModulePlugin)({ | ||
- namespace: mainServerInjectPolyfillsNamespace, | ||
- cache: sourceFileCache?.loadResultCache, | ||
- loadContent: () => ({ | ||
- contents: `import './polyfills.server.mjs';`, | ||
- loader: 'js', | ||
- resolveDir: workspaceRoot, | ||
- }), | ||
- }), (0, virtual_module_plugin_1.createVirtualModulePlugin)({ | ||
+ buildOptions.plugins.push( | ||
+ // (0, virtual_module_plugin_1.createVirtualModulePlugin)({ | ||
+ // namespace: mainServerInjectPolyfillsNamespace, | ||
+ // cache: sourceFileCache?.loadResultCache, | ||
+ // loadContent: () => ({ | ||
+ // contents: `import './polyfills.server.mjs';`, | ||
+ // loader: 'js', | ||
+ // resolveDir: workspaceRoot, | ||
+ // }), | ||
+ // }), | ||
+ (0, virtual_module_plugin_1.createVirtualModulePlugin)({ | ||
namespace: mainServerInjectManifestNamespace, | ||
cache: sourceFileCache?.loadResultCache, | ||
loadContent: async () => { |
51 changes: 51 additions & 0 deletions
51
.../ssr-benchmarks/patches/@angular-devkit+build-angular++@angular+build+19.0.0-next.6.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
diff --git a/node_modules/@angular-devkit/build-angular/node_modules/@angular/build/src/tools/esbuild/application-code-bundle.js b/node_modules/@angular-devkit/build-angular/node_modules/@angular/build/src/tools/esbuild/application-code-bundle.js | ||
index f6fb661..5e5aabb 100755 | ||
--- a/node_modules/@angular-devkit/build-angular/node_modules/@angular/build/src/tools/esbuild/application-code-bundle.js | ||
+++ b/node_modules/@angular-devkit/build-angular/node_modules/@angular/build/src/tools/esbuild/application-code-bundle.js | ||
@@ -121,7 +121,7 @@ function createServerPolyfillBundleOptions(options, target, sourceFileCache) { | ||
polyfillsFromConfig.has('@angular/localize/init')) { | ||
serverPolyfills.push('@angular/localize/init'); | ||
} | ||
- serverPolyfills.push('@angular/platform-server/init'); | ||
+ // serverPolyfills.push('@angular/platform-server/init'); | ||
const namespace = 'angular:polyfills-server'; | ||
const polyfillBundleOptions = getEsBuildCommonPolyfillsOptions({ | ||
...options, | ||
@@ -144,8 +144,8 @@ function createServerPolyfillBundleOptions(options, target, sourceFileCache) { | ||
js: [ | ||
// Note: Needed as esbuild does not provide require shims / proxy from ESModules. | ||
// See: https://github.com/evanw/esbuild/issues/1921. | ||
- `import { createRequire } from 'node:module';`, | ||
- `globalThis['require'] ??= createRequire(import.meta.url);`, | ||
+ // `import { createRequire } from 'node:module';`, | ||
+ // `globalThis['require'] ??= createRequire(import.meta.url);`, | ||
].join('\n'), | ||
}, | ||
target, | ||
@@ -197,15 +197,17 @@ function createServerMainCodeBundleOptions(options, target, sourceFileCache) { | ||
} | ||
// Mark manifest and polyfills file as external as these are generated by a different bundle step. | ||
(buildOptions.external ??= []).push(...utils_1.SERVER_GENERATED_EXTERNALS); | ||
- buildOptions.plugins.push((0, virtual_module_plugin_1.createVirtualModulePlugin)({ | ||
- namespace: mainServerInjectPolyfillsNamespace, | ||
- cache: sourceFileCache?.loadResultCache, | ||
- loadContent: () => ({ | ||
- contents: `import './polyfills.server.mjs';`, | ||
- loader: 'js', | ||
- resolveDir: workspaceRoot, | ||
- }), | ||
- }), (0, virtual_module_plugin_1.createVirtualModulePlugin)({ | ||
+ buildOptions.plugins.push( | ||
+ // (0, virtual_module_plugin_1.createVirtualModulePlugin)({ | ||
+ // namespace: mainServerInjectPolyfillsNamespace, | ||
+ // cache: sourceFileCache?.loadResultCache, | ||
+ // loadContent: () => ({ | ||
+ // contents: `import './polyfills.server.mjs';`, | ||
+ // loader: 'js', | ||
+ // resolveDir: workspaceRoot, | ||
+ // }), | ||
+ // }), | ||
+ (0, virtual_module_plugin_1.createVirtualModulePlugin)({ | ||
namespace: mainServerInjectManifestNamespace, | ||
cache: sourceFileCache?.loadResultCache, | ||
loadContent: async () => { |
Oops, something went wrong.