mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-06-25 02:50:42 +00:00
[JS/Web] WebGL Profiling Tool (#7724)
This commit is contained in:
parent
43e2ee37f2
commit
e4a985ff17
16 changed files with 67 additions and 18 deletions
|
|
@ -32,6 +32,9 @@ export class WebGLInferenceHandler implements InferenceHandler {
|
|||
let artifact = this.session.programManager.getArtifact(op);
|
||||
if (!artifact) {
|
||||
const programInfo = op.createProgramInfo(this, inputs);
|
||||
if (!programInfo.name) {
|
||||
programInfo.name = op.constructor?.name;
|
||||
}
|
||||
artifact = this.session.programManager.build(programInfo);
|
||||
this.session.programManager.setArtifact(op, artifact);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@ export class WebGLUnpackedConv extends Conv {
|
|||
}
|
||||
`;
|
||||
return {
|
||||
name: 'Im2Col',
|
||||
inputLayouts: [inferenceHandler.createTextureLayoutFromShape(xshape)],
|
||||
outputLayout,
|
||||
samplers: ['X'],
|
||||
|
|
@ -361,6 +362,7 @@ export class WebGLUnpackedConv extends Conv {
|
|||
return value;
|
||||
}`;
|
||||
return {
|
||||
name: 'dotProduct',
|
||||
inputLayouts: inputs.length === 3 ? [im2colLayout, kLayout, bLayout!] : [im2colLayout, kLayout],
|
||||
outputLayout,
|
||||
shaderSource,
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ export class WebGLIm2ColPacked implements WebGLOperator {
|
|||
}
|
||||
`;
|
||||
return {
|
||||
name: 'WebGLIm2ColPacked',
|
||||
inputLayouts: [inferenceHandler.getOrCreateTextureLayout(inputs[0], 4, true, xshape, true)],
|
||||
outputLayout:
|
||||
inferenceHandler.createTextureLayoutFromShape(im2colShape, 4, im2colShape, {isPacked: true, reverseWH: true}),
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ export class WebGLInstanceNormalization extends InstanceNormalization {
|
|||
if (!this.artifacts) {
|
||||
this.artifacts = [];
|
||||
const programInfos = this.createProgramInfos(inferenceHandler, inputs);
|
||||
programInfos.forEach((pi) => {
|
||||
const artifact = inferenceHandler.session.programManager.build(pi);
|
||||
programInfos.forEach((programInfo) => {
|
||||
const artifact = inferenceHandler.session.programManager.build(programInfo);
|
||||
this.artifacts.push(artifact);
|
||||
});
|
||||
}
|
||||
|
|
@ -78,6 +78,7 @@ export class WebGLInstanceNormalization extends InstanceNormalization {
|
|||
outputLayout: inferenceHandler.createTextureLayoutFromShape(outputShape, 4, outputUnpackedShape),
|
||||
samplers: ['X'],
|
||||
shaderSource,
|
||||
name: 'MeanAndVariance',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +115,7 @@ export class WebGLInstanceNormalization extends InstanceNormalization {
|
|||
samplers: ['X', 'MeanAndVariance', 'Scale', 'B'],
|
||||
variables: [{name: 'epsilon', type: 'float'}],
|
||||
shaderSource,
|
||||
name: 'ComputOutput',
|
||||
};
|
||||
}
|
||||
createProgramInfos(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[]): ProgramInfo[] {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ export class WebGLMatMulPacked extends MatMul implements WebGLOperator {
|
|||
return value;
|
||||
}`;
|
||||
return {
|
||||
name: 'WebGLMatMulPacked',
|
||||
inputLayouts: inputs.map((t, i) => handler.getOrCreateTextureLayout(t, 4, true, inputs[i].dims, true)),
|
||||
outputLayout:
|
||||
handler.createTextureLayoutFromShape(outputShape, 4, outputShape, {isPacked: true, reverseWH: true}),
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ export class WebGLPack implements WebGLOperator {
|
|||
`;
|
||||
|
||||
return {
|
||||
name: 'WebGLPack',
|
||||
inputLayouts: [handler.getOrCreateTextureLayout(inputs[0], 1, false, [], true)],
|
||||
outputLayout,
|
||||
samplers: ['A'],
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ export class WebGLReshapePacked extends Reshape implements WebGLOperator {
|
|||
`;
|
||||
|
||||
return {
|
||||
name: 'WebGLReshapePacked',
|
||||
inputLayouts: [inputLayout],
|
||||
outputLayout: this.outputLayout,
|
||||
samplers: ['A'],
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ export class WebGLSoftmax extends Softmax {
|
|||
outputLayout: inferenceHandler.createTextureLayoutFromShape(outputShape),
|
||||
samplers: ['A', 'Max', 'Norm'],
|
||||
shaderSource,
|
||||
name: 'SoftMax',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +132,7 @@ export class WebGLSoftmax extends Softmax {
|
|||
outputLayout: inferenceHandler.createTextureLayoutFromShape(outputShape),
|
||||
samplers: ['A', 'Max'],
|
||||
shaderSource,
|
||||
name: 'ComputScale',
|
||||
};
|
||||
}
|
||||
/**
|
||||
|
|
@ -179,6 +181,7 @@ export class WebGLSoftmax extends Softmax {
|
|||
outputLayout: inferenceHandler.createTextureLayoutFromShape(outputShape),
|
||||
samplers: ['A'],
|
||||
shaderSource,
|
||||
name: 'ComputeMax',
|
||||
};
|
||||
}
|
||||
createProgramInfos(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[]): ProgramInfo[] {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export class WebGLSplit extends Split {
|
|||
return _A(indices);
|
||||
}`;
|
||||
return {
|
||||
name: 'WebGLSplit',
|
||||
inputLayouts: [inferenceHandler.getOrCreateTextureLayout(input)],
|
||||
outputLayout: inferenceHandler.createTextureLayoutFromShape(outputShape),
|
||||
samplers: ['A'],
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ export class WebGLUint8Encode {
|
|||
float value = ${glsl.texture2D}(X,TexCoords).r;
|
||||
${glsl.output} = encodeAsUint8(value);
|
||||
}`;
|
||||
const programInfo = {inputLayouts: [input], outputLayout, samplers: ['X'], shaderSource, hasMain: true};
|
||||
const programInfo =
|
||||
{name: 'Uint8Encode', inputLayouts: [input], outputLayout, samplers: ['X'], shaderSource, hasMain: true};
|
||||
const artifact = inferenceHandler.session.programManager.build(programInfo);
|
||||
|
||||
const encoder = inferenceHandler.session.backend.glContext.getEncoder('byte', 4);
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export class WebGLUnpack implements WebGLOperator {
|
|||
`;
|
||||
|
||||
return {
|
||||
name: 'WebGLUnpack',
|
||||
inputLayouts: [handler.getOrCreateTextureLayout(inputs[0], 4, true, inputs[0].dims, true)],
|
||||
outputLayout,
|
||||
samplers: ['A'],
|
||||
|
|
|
|||
|
|
@ -37,10 +37,7 @@ export class ProgramManager {
|
|||
this.repo.set(key, artifact);
|
||||
}
|
||||
run(buildArtifact: Artifact, runData: RunData): void {
|
||||
const inputInfo = runData.inputTextureDatas.map((d, i) => `input${i}:[${d.shape}]`).join(', ');
|
||||
const outputInfo = `output: [${runData.outputTextureData.shape}]`;
|
||||
|
||||
this.profiler.event('backend', `ProgramManager.run ${inputInfo} ; ${outputInfo}`, () => {
|
||||
this.profiler.event('op', `ProgramManager.run ${buildArtifact.programInfo.name ?? 'unknown kernel'}`, () => {
|
||||
const gl = this.glContext.gl;
|
||||
const program = buildArtifact.program;
|
||||
gl.useProgram(program);
|
||||
|
|
@ -57,7 +54,7 @@ export class ProgramManager {
|
|||
this.profiler.event('backend', 'GlContext.draw()', () => {
|
||||
this.doDraw(buildArtifact, runData);
|
||||
});
|
||||
});
|
||||
}, this.glContext);
|
||||
}
|
||||
dispose(): void {
|
||||
if (this.vertexShader) {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ export interface ProgramInfo {
|
|||
|
||||
expectPackedInputs?: boolean;
|
||||
expectPackedOutputs?: boolean;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface VariableInfo {
|
||||
|
|
|
|||
|
|
@ -520,6 +520,7 @@ export class WebGLContext {
|
|||
if (this.version === 2) {
|
||||
const gl2 = this.gl as WebGL2RenderingContext;
|
||||
timeElapsed = gl2.getQueryParameter(query, gl2.QUERY_RESULT);
|
||||
gl2.deleteQuery(query);
|
||||
} else {
|
||||
// TODO: add webgl 1 handling.
|
||||
throw new Error('WebGL1 profiling currently not supported');
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
import {SessionHandler} from './backend';
|
||||
import {WebGLBackend} from './backends/backend-webgl';
|
||||
import {WebGLContext} from './backends/webgl/webgl-context';
|
||||
import {Graph} from './graph';
|
||||
import {Logger, Profiler} from './instrument';
|
||||
import {Operator} from './operators';
|
||||
|
|
@ -53,12 +51,6 @@ export class ExecutionPlan {
|
|||
}
|
||||
|
||||
async execute(sessionHandler: SessionHandler, modelInputs: Tensor[]): Promise<Tensor[]> {
|
||||
const isWebGLBackend = sessionHandler.backend instanceof WebGLBackend;
|
||||
let glCtx: WebGLContext|undefined;
|
||||
if (isWebGLBackend) {
|
||||
glCtx = (sessionHandler.backend as WebGLBackend).glContext;
|
||||
}
|
||||
|
||||
return this.profiler.event('session', 'ExecutionPlan.execute', async () => {
|
||||
// reset mediem result
|
||||
this.reset();
|
||||
|
|
@ -114,8 +106,7 @@ export class ExecutionPlan {
|
|||
return result;
|
||||
};
|
||||
|
||||
const outputList = isWebGLBackend ? await this.profiler.event('node', thisOp.node.name, execNodeFn, glCtx) :
|
||||
await this.profiler.event('node', thisOp.node.name, execNodeFn);
|
||||
const outputList = await this.profiler.event('node', thisOp.node.name, execNodeFn);
|
||||
|
||||
// check output
|
||||
if (outputList.length !== thisOp.node.outputs.length) {
|
||||
|
|
|
|||
42
js/web/script/parse-profiler.ts
Normal file
42
js/web/script/parse-profiler.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||
|
||||
// parse-profiler
|
||||
//
|
||||
// this script is used to parse performance profiling result.
|
||||
// usage:
|
||||
// STEP.1 - profiling
|
||||
// > npm test -- model test/test-data/{path-to-my-model} --backend={cpu/webgl/wasm} --profile > profile.raw.log
|
||||
// STEP.2 - parse
|
||||
// > node script/parse-profiler < profile.raw.log > profile.parsed.log
|
||||
|
||||
|
||||
import * as readline from 'readline';
|
||||
const lines = readline.createInterface({input: process.stdin, output: process.stdout, terminal: false});
|
||||
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const matcher = /Profiler\.([^[\s\x1b]+)(\x1b\[0m)? (\d.+Z)\|([\d.]+)ms on event '([^']+)' at (\d*\.*\d*)/;
|
||||
|
||||
const allEvents: any[] = [];
|
||||
lines.on('line', input => {
|
||||
const matches = matcher.exec(input);
|
||||
if (matches) {
|
||||
// console.log(matches);
|
||||
const category = matches[1];
|
||||
const logTimeStamp = new Date(matches[3]);
|
||||
const ms = Number.parseFloat(matches[4]);
|
||||
const event = matches[5];
|
||||
const endTimeInNumber = matches[6];
|
||||
allEvents.push({event, ms, logTimeStamp, category, endTimeInNumber});
|
||||
}
|
||||
});
|
||||
|
||||
lines.on('close', () => {
|
||||
for (const i of allEvents) {
|
||||
console.log(`${(i.category + ' ').substring(0, 12)} ${((i.ms) + ' ').substring(0, 12)} ${
|
||||
(i.event + ' ').substring(0, 40)} ${i.endTimeInNumber}`);
|
||||
}
|
||||
});
|
||||
Loading…
Reference in a new issue