From 28042a9db039fc0115c5928ac69672982ef9b4ae Mon Sep 17 00:00:00 2001 From: Tobias Ortmayr Date: Mon, 9 Sep 2024 15:45:18 +0200 Subject: [PATCH] Add optional module for performance measurements - Introduce an optional performance module that add performance measurements to core GLSP concepts. - Performance is simply measured using console.time which also allows inspection of the tracked events in the dev tools trace viewer - The module is opt-in and disabled by default. To enable it the &performance=true query parameter has to be added to the url --- integration/standalone/src/app.ts | 4 +++- integration/standalone/src/di.config.ts | 4 +++- integration/viewer/src/app.ts | 4 +++- integration/viewer/src/di.config.ts | 3 +++ packages/editor/src/index.ts | 2 +- .../src/performance/perf-action-dispatcher.ts | 23 +++++++++++++++++++ .../src/performance/perf-diagram-loader.ts | 23 +++++++++++++++++++ .../editor/src/performance/perf-viewer.ts | 14 +++++++++++ .../src/performance/performance-module.ts | 18 +++++++++++++++ 9 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 packages/editor/src/performance/perf-action-dispatcher.ts create mode 100644 packages/editor/src/performance/perf-diagram-loader.ts create mode 100644 packages/editor/src/performance/perf-viewer.ts create mode 100644 packages/editor/src/performance/performance-module.ts diff --git a/integration/standalone/src/app.ts b/integration/standalone/src/app.ts index b6909c2d..4f456b3c 100644 --- a/integration/standalone/src/app.ts +++ b/integration/standalone/src/app.ts @@ -23,6 +23,7 @@ const sourceUri = parameters.get('file') ?? ''; const select = parameters.get('select'); const theme = (parameters.get('theme') as ThemeMode) ?? SwitchThemeActionHandler.prefsColorScheme(); const debug = parameters.has('debug', 'true'); +const measurePerformance = parameters.has('performance', 'true'); const id = 'ivy-glsp-process-editor'; const diagramType = 'ivy-glsp-process'; @@ -51,7 +52,8 @@ async function initialize(connectionProvider: MessageConnection, isReconnecting app, pmv, server: webSocketBase - } + }, + measurePerformance }); const diagramLoader = container.get(DiagramLoader); diff --git a/integration/standalone/src/di.config.ts b/integration/standalone/src/di.config.ts index c0ae0628..e668022d 100644 --- a/integration/standalone/src/di.config.ts +++ b/integration/standalone/src/di.config.ts @@ -1,4 +1,4 @@ -import { createIvyDiagramContainer, ivyThemeModule } from '@axonivy/process-editor'; +import { createIvyDiagramContainer, createPerformanceModule, ivyThemeModule } from '@axonivy/process-editor'; import { ivyInscriptionModule } from '@axonivy/process-editor-inscription'; import type { IDiagramOptions } from '@eclipse-glsp/client'; import { createDiagramOptionsModule, standaloneExportModule, standaloneSelectModule, undoRedoModule } from '@eclipse-glsp/client'; @@ -15,12 +15,14 @@ export interface IvyDiagramOptions extends IDiagramOptions { select: string | null; theme: ThemeMode; inscriptionContext: InscriptionContext & { server: string }; + measurePerformance?: boolean; } export default function createContainer(options: IvyDiagramOptions): Container { const container = createIvyDiagramContainer( 'sprotty', createDiagramOptionsModule(options), + createPerformanceModule(options.measurePerformance), // standalone modules standaloneSelectModule, standaloneExportModule, diff --git a/integration/viewer/src/app.ts b/integration/viewer/src/app.ts index ed174130..80d07056 100644 --- a/integration/viewer/src/app.ts +++ b/integration/viewer/src/app.ts @@ -23,6 +23,7 @@ const highlight = parameters.get('highlight') ?? ''; const select = parameters.get('select'); const zoom = parameters.get('zoom') ?? ''; const theme = (parameters.get('theme') as ThemeMode) ?? SwitchThemeActionHandler.prefsColorScheme(); +const measurePerformance = parameters.has('performance', 'true'); const id = 'ivy-glsp-process-viewer'; const diagramType = 'ivy-glsp-process'; @@ -47,7 +48,8 @@ async function initialize(connectionProvider: MessageConnection, isReconnecting highlight, select, zoom, - theme + theme, + measurePerformance }); const diagramLoader = container.get(DiagramLoader); diff --git a/integration/viewer/src/di.config.ts b/integration/viewer/src/di.config.ts index 89515092..f0e51970 100644 --- a/integration/viewer/src/di.config.ts +++ b/integration/viewer/src/di.config.ts @@ -1,5 +1,6 @@ import { createIvyDiagramContainer, + createPerformanceModule, ivyChangeBoundsToolModule, ivyConnectorModule, ivyKeyListenerModule, @@ -26,6 +27,7 @@ export interface IvyDiagramOptions extends IDiagramOptions { select: string | null; zoom: string; theme: ThemeMode; + measurePerformance?: boolean; } export default function createContainer(options: IvyDiagramOptions): Container { @@ -33,6 +35,7 @@ export default function createContainer(options: IvyDiagramOptions): Container { const container = createIvyDiagramContainer( 'sprotty', createDiagramOptionsModule(options), + createPerformanceModule(options.measurePerformance), ivyThemeModule, ivyNavigationModule, ivyStartupDiagramModule, diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index de13b466..911e3e2a 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -42,5 +42,5 @@ export * from './ui-tools/viewport/viewport-bar'; export * from './key-listener/jump-out'; export * from './key-listener/quick-actions'; export * from './start-action/actions'; - +export * from './performance/performance-module'; export * from './ivy-glsp-jsonrpc-client'; diff --git a/packages/editor/src/performance/perf-action-dispatcher.ts b/packages/editor/src/performance/perf-action-dispatcher.ts new file mode 100644 index 00000000..e9ba0658 --- /dev/null +++ b/packages/editor/src/performance/perf-action-dispatcher.ts @@ -0,0 +1,23 @@ +import { Action, GLSPActionDispatcher, RequestAction, ResponseAction } from '@eclipse-glsp/client'; +import { injectable } from 'inversify'; + +@injectable() +export class PerfActionDispatcher extends GLSPActionDispatcher { + protected counter = 0; + + override request(action: RequestAction): Promise { + const counter = ++this.counter; + console.time(`request-${action.kind}-${counter}`); + const result = super.request(action); + console.timeEnd(`request-${action.kind}-${counter}`); + this.counter++; + return result; + } + + override async dispatch(action: Action): Promise { + const counter = ++this.counter; + console.time(`dispatch-${action.kind}-${counter}`); + await super.dispatch(action); + console.timeEnd(`dispatch-${action.kind}-${counter}`); + } +} diff --git a/packages/editor/src/performance/perf-diagram-loader.ts b/packages/editor/src/performance/perf-diagram-loader.ts new file mode 100644 index 00000000..5d944efc --- /dev/null +++ b/packages/editor/src/performance/perf-diagram-loader.ts @@ -0,0 +1,23 @@ +import { DiagramLoader, IDiagramStartup, ResolvedDiagramLoadingOptions } from '@eclipse-glsp/client'; +import { injectable } from 'inversify'; + +@injectable() +export class PerfDiagramLoader extends DiagramLoader { + protected override async invokeStartupHook(hook: keyof Omit): Promise { + console.time('invokeStartupHook-' + hook); + await super.invokeStartupHook(hook); + console.timeEnd('invokeStartupHook-' + hook); + } + + protected override async initialize(options: ResolvedDiagramLoadingOptions): Promise { + console.time('DiagramLoader.initialize'); + await super.initialize(options); + console.timeEnd('DiagramLoader.initialize'); + } + + protected override async requestModel(options: ResolvedDiagramLoadingOptions): Promise { + console.time('DiagramLoader.requestModel'); + await super.requestModel(options); + console.timeEnd('DiagramLoader.requestModel'); + } +} diff --git a/packages/editor/src/performance/perf-viewer.ts b/packages/editor/src/performance/perf-viewer.ts new file mode 100644 index 00000000..a6e6163d --- /dev/null +++ b/packages/editor/src/performance/perf-viewer.ts @@ -0,0 +1,14 @@ +import { Action, GModelRoot, ModelViewer } from '@eclipse-glsp/client'; +import { injectable } from 'inversify'; + +@injectable() +export class PerfModelViewer extends ModelViewer { + protected counter = 0; + + update(model: Readonly, cause?: Action): void { + const counter = ++this.counter; + console.time('Viewer update ' + counter); + super.update(model, cause); + console.timeEnd('Viewer update ' + counter); + } +} diff --git a/packages/editor/src/performance/performance-module.ts b/packages/editor/src/performance/performance-module.ts new file mode 100644 index 00000000..d5eebe0b --- /dev/null +++ b/packages/editor/src/performance/performance-module.ts @@ -0,0 +1,18 @@ +import { DiagramLoader, FeatureModule, GLSPActionDispatcher, TYPES } from '@eclipse-glsp/client'; +import { PerfDiagramLoader } from './perf-diagram-loader'; +import { PerfActionDispatcher } from './perf-action-dispatcher'; +import { PerfModelViewer } from './perf-viewer'; + +export function createPerformanceModule(enabled?: boolean): FeatureModule { + return new FeatureModule( + (bind, unbind, isBound, rebind) => { + if (!enabled) { + return; + } + rebind(DiagramLoader).to(PerfDiagramLoader).inSingletonScope(); + rebind(GLSPActionDispatcher).to(PerfActionDispatcher).inSingletonScope(); + rebind(TYPES.ModelViewer).to(PerfModelViewer).inSingletonScope(); + }, + { featureId: Symbol('performance') } + ); +}