From ea86754aa61910f93786aef8f388b4cc4220dc9f Mon Sep 17 00:00:00 2001 From: Matthieu Riegler Date: Mon, 3 Jun 2024 21:16:33 +0200 Subject: [PATCH] refactor(platform-server): Add private profiler. (#56274) The commit adds a private profiler to investigate SSR performance. PR Close #56274 --- .../platform-server/src/private_export.ts | 4 ++ packages/platform-server/src/profiler.ts | 64 +++++++++++++++++++ packages/platform-server/src/utils.ts | 14 ++-- 3 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 packages/platform-server/src/profiler.ts diff --git a/packages/platform-server/src/private_export.ts b/packages/platform-server/src/private_export.ts index b3749f03276..d778507975d 100644 --- a/packages/platform-server/src/private_export.ts +++ b/packages/platform-server/src/private_export.ts @@ -11,3 +11,7 @@ export { SERVER_RENDER_PROVIDERS as ɵSERVER_RENDER_PROVIDERS, } from './server'; export {SERVER_CONTEXT as ɵSERVER_CONTEXT} from './utils'; +export { + enableSsrProfiling as ɵenableSsrProfiling, + disableSsrProfiling as ɵdisableSsrProfiling, +} from './profiler'; diff --git a/packages/platform-server/src/profiler.ts b/packages/platform-server/src/profiler.ts new file mode 100644 index 00000000000..1ee2a8f423c --- /dev/null +++ b/packages/platform-server/src/profiler.ts @@ -0,0 +1,64 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * 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 + */ + +const PERFORMANCE_MARK_PREFIX = '🅰️'; + +let enablePerfLogging = false; + +export function runAndMeasurePerf(label: string, method: () => T): T { + if (!enablePerfLogging) { + return method(); + } + + const labelName = `${PERFORMANCE_MARK_PREFIX}:${label}`; + const startLabel = `start:${labelName}`; + const endLabel = `end:${labelName}`; + + const end = () => { + /* tslint:disable:ban */ + performance.mark(endLabel); + performance.measure(labelName, startLabel, endLabel); + performance.clearMarks(startLabel); + performance.clearMarks(endLabel); + /* tslint:enable:ban */ + }; + + /* tslint:disable:ban */ + performance.mark(startLabel); + /* tslint:enable:ban */ + + const returnValue = method(); + if (returnValue instanceof Promise) { + return returnValue.finally(() => end()) as T; + } else { + end(); + return returnValue; + } +} + +let warningLogged = false; +/** + * This enables an internal performance profiler for SSR apps + * + * It should not be imported in application code + */ +export function enableSsrProfiling() { + if ( + !warningLogged && + (typeof performance === 'undefined' || !performance.mark || !performance.measure) + ) { + warningLogged = true; + console.warn('Performance API is not supported on this platform'); + return; + } + + enablePerfLogging = true; +} +export function disableSsrProfiling() { + enablePerfLogging = false; +} diff --git a/packages/platform-server/src/utils.ts b/packages/platform-server/src/utils.ts index a52be9f50c5..b7a2e3c09ff 100644 --- a/packages/platform-server/src/utils.ts +++ b/packages/platform-server/src/utils.ts @@ -26,6 +26,7 @@ import {PlatformState} from './platform_state'; import {platformServer} from './server'; import {BEFORE_APP_SERIALIZED, INITIAL_CONFIG} from './tokens'; import {createScript} from './transfer_state'; +import {runAndMeasurePerf} from './profiler'; /** * Event dispatch (JSAction) script is inlined into the HTML by the build @@ -145,7 +146,9 @@ function insertEventRecordScript( // This is defined in packages/core/primitives/event-dispatch/contract_binary.ts const replayScriptContents = `window.__jsaction_bootstrap('ngContracts', document.body, ${JSON.stringify( appId, - )}, ${JSON.stringify(Array.from(regular))}${capture.size ? ',' + JSON.stringify(Array.from(capture)) : ''});`; + )}, ${JSON.stringify(Array.from(regular))}${ + capture.size ? ',' + JSON.stringify(Array.from(capture)) : '' + });`; const replayScript = createScript(doc, replayScriptContents, nonce); @@ -272,7 +275,10 @@ export async function renderApplication( bootstrap: () => Promise, options: {document?: string | Document; url?: string; platformProviders?: Provider[]}, ): Promise { - const platformRef = createServerPlatform(options); - const applicationRef = await bootstrap(); - return _render(platformRef, applicationRef); + return runAndMeasurePerf('renderApplication', async () => { + const platformRef = createServerPlatform(options); + + const applicationRef = await bootstrap(); + return _render(platformRef, applicationRef); + }); }