Skip to content

Commit

Permalink
Separate profiler out of the user interface (#46)
Browse files Browse the repository at this point in the history
* WIP: separate user interface into a new plugin

* Narrow types, change `.result` to `.outcome` to avoid confusion

* Clean up empty import
  • Loading branch information
krassowski committed Apr 16, 2023
1 parent 5821702 commit 3c4d2c8
Show file tree
Hide file tree
Showing 10 changed files with 416 additions and 279 deletions.
102 changes: 7 additions & 95 deletions src/benchmark.ts
Original file line number Diff line number Diff line change
@@ -1,105 +1,17 @@
import { JSONSchema7 } from 'json-schema';
import type { Signal, ISignal } from '@lumino/signaling';
import { Statistic } from './statistics';
import { reportTagCounts } from './utils';
import { layoutReady } from './dramaturg';
import benchmarkExecutionOptionsSchema from './schema/benchmark-execution.json';
import type { ExecutionTimeBenchmarkOptions } from './types/_benchmark-execution';
import { renderTimings } from './ui';
import { IScenario } from './tokens';

export interface IProgress {
percentage: number;
interrupted?: boolean;
errored?: boolean;
}

export interface IBenchmark<T extends IOutcomeBase = IOutcomeBase> {
/**
* Unique identifier.
*/
id: string;
/**
* User-facing name of the benchmark.
*/
name: string;
/**
* Function excuting the benchmark for given scenario.
* @param scenario - the scenario to execute.
* @param options - the JSON data from rjsf form generated from `configSchema`.
*/
run: (
scenario: IScenario,
options: any,
progress?: Signal<any, IProgress>,
stopSignal?: ISignal<any, void>
) => Promise<T>;
/**
* Configuration schema for rendering by rjsf.
*/
configSchema: JSONSchema7;
/**
* Custom renderer for results.
*/
render?: (props: { outcome: T }) => JSX.Element;
/**
* Checks whether the benchmark can be executed on runing browser.
* If not defined, the benchmark is assumed to be available.
*/
isAvailable?: () => boolean;
/**
* Column to sort results by when presenting in a table.
*/
sortColumn?: string;
/**
* Brief (one-two sentences) explanation how to interpret the results.
*/
interpretation?: string | JSX.Element;
}

interface IMeasurement {
errors?: any[];
// allow assignments
[index: string]: any;
}

export interface ITimeMeasurement extends IMeasurement {
times: number[];
}

export interface IProfileMeasurement extends IMeasurement {
traces: ProfilerTrace[];
/**
* Average actual sampling interval.
*/
averageSampleInterval: number;
/**
* Sampling interval reported by profiler.
*/
samplingInterval: number;
}

export interface IOutcomeBase<T extends IMeasurement = IMeasurement> {
results: T[];
tags: Record<string, number>;
totalTime: number;
type: string;
interrupted: boolean;
}

export interface ITimingOutcome<T extends ITimeMeasurement = ITimeMeasurement>
extends IOutcomeBase<T> {
reference: number[];
type: 'time';
}

export interface IProfilingOutcome<
T extends IProfileMeasurement = IProfileMeasurement
> extends IOutcomeBase<T> {
type: 'profile';
}

export type IOutcome = ITimingOutcome | IProfilingOutcome;
import {
IBenchmark,
IScenario,
IProfileMeasurement,
ITimingOutcome,
ITimeMeasurement
} from './tokens';

export async function profile(
scenario: IScenario,
Expand Down
File renamed without changes.
78 changes: 31 additions & 47 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,22 @@ import {
JupyterFrontEndPlugin
} from '@jupyterlab/application';
import { MainAreaWidget, WidgetTracker } from '@jupyterlab/apputils';
import { PageConfig } from '@jupyterlab/coreutils';
import { IDocumentManager } from '@jupyterlab/docmanager';
import { FileBrowserModel } from '@jupyterlab/filebrowser';
import { ILauncher } from '@jupyterlab/launcher';
import { nullTranslator } from '@jupyterlab/translation';
import { offlineBoltIcon } from '@jupyterlab/ui-components';
import type { DockPanel } from '@lumino/widgets';
import { UIProfiler } from './ui';
import { UIProfilerWidget, ConstrainedUIProfiler } from './ui';
import {
styleSheetsBenchmark,
styleRuleBenchmark,
styleRuleGroupBenchmark,
styleRuleUsageBenchmark
} from './styleBenchmarks';
import { selfProfileBenchmark } from './jsBenchmarks';
import {
executionTimeBenchmark,
IBenchmark,
ITimingOutcome,
IProfilingOutcome
} from './benchmark';
import { IJupyterState } from './utils';
import { IScenario, IUIProfiler } from './tokens';
import { executionTimeBenchmark } from './benchmark';
import { IBenchmark, IUIProfiler } from './tokens';
import { UIProfiler } from './profiler';
import { plugin as scenariosPlugin } from './scenarios';

namespace CommandIDs {
Expand All @@ -40,50 +33,50 @@ namespace CommandIDs {
const plugin: JupyterFrontEndPlugin<IUIProfiler> = {
id: '@jupyterlab/ui-profiler:plugin',
autoStart: true,
requires: [IDocumentManager],
optional: [ILauncher, ILayoutRestorer],
provides: IUIProfiler,
activate: (app: JupyterFrontEnd) => {
return new UIProfiler({
app,
benchmarks: [
executionTimeBenchmark,
styleSheetsBenchmark,
styleRuleBenchmark,
styleRuleGroupBenchmark,
styleRuleUsageBenchmark,
selfProfileBenchmark
] as IBenchmark<any>[]
});
}
};

const interfacePlugin: JupyterFrontEndPlugin<void> = {
id: '@jupyterlab/ui-profiler:user-interface',
autoStart: true,
requires: [IUIProfiler, IDocumentManager],
optional: [ILauncher, ILayoutRestorer],
activate: (
app: JupyterFrontEnd,
profiler: IUIProfiler,
docManager: IDocumentManager,
launcher: ILauncher | null,
restorer: ILayoutRestorer | null
) => {
const fileBrowserModel = new FileBrowserModel({
manager: docManager
});
const scenarios: IScenario[] = [];
const options = {
benchmarks: [
executionTimeBenchmark,
styleSheetsBenchmark,
styleRuleBenchmark,
styleRuleGroupBenchmark,
styleRuleUsageBenchmark,
selfProfileBenchmark
] as (IBenchmark<ITimingOutcome<any>> | IBenchmark<IProfilingOutcome>)[],
scenarios: scenarios,
translator: nullTranslator,
profiler: profiler as unknown as ConstrainedUIProfiler,
upload: (file: File) => {
// https://github.com/jupyterlab/jupyterlab/issues/11416
return fileBrowserModel.upload(file);
},
getJupyterState: () => {
const state: IJupyterState = {
client: app.name,
version: app.version,
devMode:
(PageConfig.getOption('devMode') || '').toLowerCase() === 'true',
mode: PageConfig.getOption('mode') as DockPanel.Mode
};
return state;
},
resultLocation: '/ui-profiler-results/'
};
let lastWidget: MainAreaWidget<UIProfiler> | null = null;
let lastWidget: MainAreaWidget<UIProfilerWidget> | null = null;

const createWidget = () => {
const content = new UIProfiler(options);
const content = new UIProfilerWidget(options);
const widget = new MainAreaWidget({ content });
widget.id = 'ui-profiler-centre';
widget.title.label = 'UI Profiler';
Expand All @@ -92,13 +85,13 @@ const plugin: JupyterFrontEndPlugin<IUIProfiler> = {
return widget;
};

const tracker = new WidgetTracker<MainAreaWidget<UIProfiler>>({
const tracker = new WidgetTracker<MainAreaWidget<UIProfilerWidget>>({
namespace: 'ui-profiler'
});

app.commands.addCommand(CommandIDs.openProfiler, {
execute: async () => {
let widget: MainAreaWidget<UIProfiler>;
let widget: MainAreaWidget<UIProfilerWidget>;
if (!lastWidget || lastWidget.isDisposed) {
widget = createWidget();
} else {
Expand Down Expand Up @@ -141,18 +134,9 @@ const plugin: JupyterFrontEndPlugin<IUIProfiler> = {
rank: 1
});
}

return {
addScenario: (scenario: IScenario) => {
scenarios.push(scenario);
if (lastWidget) {
lastWidget.content.addScenario(scenario);
}
}
};
}
};

export * from './tokens';

export default [plugin, scenariosPlugin];
export default [plugin, scenariosPlugin, interfacePlugin];
10 changes: 3 additions & 7 deletions src/jsBenchmarks.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { JSONSchema7 } from 'json-schema';

import {
IProfilingOutcome,
IBenchmark,
IProfileMeasurement,
profile
} from './benchmark';
import { profile } from './benchmark';
import { IProfilingOutcome, IProfileMeasurement } from './tokens';
import { reportTagCounts } from './utils';
import { layoutReady } from './dramaturg';
import { renderProfile } from './ui';
import { IScenario } from './tokens';
import { IBenchmark, IScenario } from './tokens';

import benchmarkProfileOptionsSchema from './schema/benchmark-profile.json';
import type { ProfileBenchmarkOptions } from './types/_benchmark-profile';
Expand Down

0 comments on commit 3c4d2c8

Please sign in to comment.