Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional module for performance measurements #612

Draft
wants to merge 4 commits into
base: release/12.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion integration/standalone/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -51,7 +52,8 @@ async function initialize(connectionProvider: MessageConnection, isReconnecting
app,
pmv,
server: webSocketBase
}
},
measurePerformance
});

const diagramLoader = container.get(DiagramLoader);
Expand Down
4 changes: 3 additions & 1 deletion integration/standalone/src/di.config.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion integration/viewer/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -47,7 +48,8 @@ async function initialize(connectionProvider: MessageConnection, isReconnecting
highlight,
select,
zoom,
theme
theme,
measurePerformance
});

const diagramLoader = container.get(DiagramLoader);
Expand Down
3 changes: 3 additions & 0 deletions integration/viewer/src/di.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
createIvyDiagramContainer,
createPerformanceModule,
ivyChangeBoundsToolModule,
ivyConnectorModule,
ivyKeyListenerModule,
Expand All @@ -26,13 +27,15 @@ export interface IvyDiagramOptions extends IDiagramOptions {
select: string | null;
zoom: string;
theme: ThemeMode;
measurePerformance?: boolean;
}

export default function createContainer(options: IvyDiagramOptions): Container {
// ivyNavigationModule is a replacement for navigationModule but it is already removed in the default IvyDiagramContainer
const container = createIvyDiagramContainer(
'sprotty',
createDiagramOptionsModule(options),
createPerformanceModule(options.measurePerformance),
ivyThemeModule,
ivyNavigationModule,
ivyStartupDiagramModule,
Expand Down
3 changes: 1 addition & 2 deletions packages/editor/src/di.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import ivyDiagramModule from './diagram/di.config';
import { LaneNode } from './diagram/model';
import { ivyLabelEditModule, ivyLabelEditUiModule } from './edit-label/di.config';
import ivyExecutionModule from './execution/di.config';
import { IvyGLSPCommandStack } from './ivy-command-stack';
import ivyJumpModule from './jump/di.config';
import ivyKeyListenerModule from './key-listener/di.config';
import ivyLaneModule from './lanes/di.config';
Expand Down Expand Up @@ -93,7 +92,7 @@ export default function createContainer(widgetId: string, ...containerConfigurat

bindOrRebind(container, MarqueeUtil).to(IvyMarqueeUtil).inSingletonScope();
bindOrRebind(container, TYPES.IMarqueeBehavior).toConstantValue({ entireEdge: true, entireElement: true });
bindOrRebind(container, TYPES.ICommandStack).to(IvyGLSPCommandStack).inSingletonScope();
// bindOrRebind(container, TYPES.ICommandStack).to(IvyGLSPCommandStack).inSingletonScope();
bindOrRebind(container, TYPES.ILogger).to(ConsoleLogger).inSingletonScope();
bindOrRebind(container, TYPES.LogLevel).toConstantValue(LogLevel.warn);
bindOrRebind(container, TYPES.ISnapper).to(GLSPCenterGridSnapper);
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
25 changes: 25 additions & 0 deletions packages/editor/src/performance/perf-action-dispatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Action, GLSPActionDispatcher, RequestAction, ResponseAction } from '@eclipse-glsp/client';
import { injectable } from 'inversify';

@injectable()
export class PerfActionDispatcher extends GLSPActionDispatcher {
protected counter = 0;

override request<Res extends ResponseAction>(action: RequestAction<Res>): Promise<Res> {
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;
}

protected override async handleAction(action: Action): Promise<void> {
const counter = ++this.counter;
console.time(`handleAction-${action.kind}-${counter}`);
console.log(`handleAction-${action.kind}-${counter}`, action);
const result = await super.handleAction(action);
console.timeEnd(`handleAction-${action.kind}-${counter}`);
return result;
}
}
19 changes: 19 additions & 0 deletions packages/editor/src/performance/perf-command-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { GModelRoot, ICommand } from '@eclipse-glsp/client';
import { injectable } from 'inversify';
import { IvyGLSPCommandStack } from '../ivy-command-stack';

@injectable()
export class PerfCommandStack extends IvyGLSPCommandStack {
protected counter = 0;

override async execute(command: ICommand): Promise<GModelRoot> {
const counter = ++this.counter;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const kind = (command as any).action?.kind;
const perfmessage = kind ? `executeCommand-${kind}-${counter}` : `executeCommand-${counter}`;
console.time(perfmessage);
const result = await super.execute(command);
console.timeEnd(perfmessage);
return result;
}
}
54 changes: 54 additions & 0 deletions packages/editor/src/performance/perf-diagram-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { DiagramLoader, DiagramLoadingOptions, IDiagramStartup, ResolvedDiagramLoadingOptions, StatusAction } from '@eclipse-glsp/client';
import { injectable } from 'inversify';

@injectable()
export class PerfDiagramLoader extends DiagramLoader {
protected override async invokeStartupHook(hook: keyof Omit<IDiagramStartup, 'rank'>): Promise<void> {
console.time('invokeStartupHook-' + hook);
await super.invokeStartupHook(hook);
console.timeEnd('invokeStartupHook-' + hook);
}

protected override async initialize(options: ResolvedDiagramLoadingOptions): Promise<void> {
console.time('DiagramLoader.initialize (DI)');
if (options.enableNotifications) {
console.time('dispatchStatus - (DI)');
await this.actionDispatcher.dispatch(StatusAction.create('Initializing...', { severity: 'INFO' }));
console.timeEnd('dispatchStatus - (DI)');
}

console.time('getClient - (DI)');
const glspClient = await this.options.glspClientProvider();
console.timeEnd('getClient - (DI)');
console.time('startClient - (DI)');
await glspClient.start();
console.timeEnd('startClient - (DI)');
if (!glspClient.initializeResult) {
console.time('initializeServer - (DI)');
await glspClient.initializeServer(options.initializeParameters);
console.timeEnd('initializeServer - (DI)');
}
console.time('configureModelSource - (DI)');
this.modelSource.configure(glspClient);
console.timeEnd('configureModelSource - (DI)');

if (options.enableNotifications) {
console.time('clearStatus - (DI)');
this.actionDispatcher.dispatch(StatusAction.create('', { severity: 'NONE' }));
console.timeEnd('clearStatus - (DI)');
}
console.timeEnd('DiagramLoader.initialize (DI)');
}

protected override async requestModel(options: ResolvedDiagramLoadingOptions): Promise<void> {
console.time('DiagramLoader.requestModel');
await super.requestModel(options);
console.timeEnd('DiagramLoader.requestModel');
}

override async load(options: DiagramLoadingOptions = {}): Promise<void> {
console.time('DiagramLoader.load');
await super.load(options);
console.timeEnd('DiagramLoader.load');
}
}
32 changes: 32 additions & 0 deletions packages/editor/src/performance/perf-model-source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Disposable, GLSPClient, GLSPModelSource } from '@eclipse-glsp/client';
import { injectable } from 'inversify';

@injectable()
export class PerfGLSPModelSource extends GLSPModelSource {
async configure(glspClient: GLSPClient): Promise<void> {
console.time('GLSPModelSource.configure (MS)');
this.glspClient = glspClient;
if (!glspClient.initializeResult) {
throw new Error('Could not configure model source. The GLSP client is not initialized yet!');
}

console.time('createInitializeClientSessionParameters (MS)');
const initializeParams = this.createInitializeClientSessionParameters(glspClient.initializeResult);
console.timeEnd('createInitializeClientSessionParameters (MS)');

console.time('configureServerActions (MS)');
this.configureServeActions(glspClient.initializeResult);
console.timeEnd('configureServerActions (MS)');

this.toDispose.push(
glspClient.onActionMessage(message => this.messageReceived(message), this.clientId),
Disposable.create(() => glspClient.disposeClientSession(this.createDisposeClientSessionParameters()))
);

console.time('initializeClientSession (MS)');
const result = await glspClient!.initializeClientSession(initializeParams);
console.timeEnd('initializeClientSession (MS)');
console.timeEnd('GLSPModelSource.configure (MS)');
return result;
}
}
45 changes: 45 additions & 0 deletions packages/editor/src/performance/perf-viewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/** @jsx html */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Action, GModelRoot, ModelViewer, copyClassesFromElement, copyClassesFromVNode, html, setClass } from '@eclipse-glsp/client';
import { injectable } from 'inversify';

@injectable()
export class PerfModelViewer extends ModelViewer {
protected counter = 0;
update(model: Readonly<GModelRoot>, cause?: Action): void {
const counter = ++this.counter;
console.time(`Viewer update (vu) (${counter})`);
this.logger.log(this, 'rendering', model);
const newVDOM = <div id={this.options.baseDiv}>{this.renderer.renderElement(model)}</div>;
if (this.lastVDOM !== undefined) {
const hadFocus = this.hasFocus();
copyClassesFromVNode(this.lastVDOM, newVDOM);
this.lastVDOM = this.patcher.call(this, this.lastVDOM, newVDOM);
this.restoreFocus(hadFocus);
} else if (typeof document !== 'undefined') {
let placeholder = null;
if (this.options.shadowRoot) {
const shadowRoot = document.getElementById(this.options.shadowRoot)?.shadowRoot;
if (shadowRoot) {
placeholder = shadowRoot.getElementById(this.options.baseDiv);
}
} else {
placeholder = document.getElementById(this.options.baseDiv);
}
if (placeholder !== null) {
if (typeof window !== 'undefined') {
window.addEventListener('resize', () => {
this.onWindowResize(newVDOM);
});
}
copyClassesFromElement(placeholder, newVDOM);
setClass(newVDOM, this.options.baseClass, true);
this.lastVDOM = this.patcher.call(this, placeholder, newVDOM);
} else {
this.logger.error(this, 'element not in DOM:', this.options.baseDiv);
}
}
this.renderer.postUpdate(cause);
console.timeEnd(`Viewer update (vu) (${counter})`);
}
}
22 changes: 22 additions & 0 deletions packages/editor/src/performance/performance-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { DiagramLoader, FeatureModule, GLSPActionDispatcher, GLSPModelSource, ModelViewer, TYPES } from '@eclipse-glsp/client';
import { PerfActionDispatcher } from './perf-action-dispatcher';
import { PerfCommandStack } from './perf-command-stack';
import { PerfDiagramLoader } from './perf-diagram-loader';
import { PerfModelViewer } from './perf-viewer';
import { PerfGLSPModelSource } from './perf-model-source';

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(ModelViewer).to(PerfModelViewer).inSingletonScope();
rebind(TYPES.ICommandStack).to(PerfCommandStack).inSingletonScope();
rebind(GLSPModelSource).to(PerfGLSPModelSource).inSingletonScope();
},
{ featureId: Symbol('performance') }
);
}