Skip to content

Commit a8728c4

Browse files
authored
[js/webgpu] following up for JSEP/WebGPU code cleanup (microsoft#15666)
### Description This PR resolves a part of non-critical comments from code review comments in microsoft#14579. - use `USE_JSEP` instead of `USE_JS` in build definition to make it less ambiguous - remove unused util functions from util.ts - fix transpose.h - other misc fixes
1 parent 069950d commit a8728c4

File tree

3 files changed

+6
-325
lines changed

3 files changed

+6
-325
lines changed

web/lib/wasm/jsep/backend-webgpu.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,10 @@ export class WebGpuBackend {
144144
}
145145

146146
dispose(): void {
147-
// TODO: uninitialization
148-
// this.glContext.dispose();
147+
// currently, we do not do anything in this function. In all known use cases, we don't have the requirement to
148+
// actually dispose the WebGpuBackend instance, because it's always used as a singleton.
149+
//
150+
// revisit this place if we get real requirement to dispose the instance.
149151
}
150152

151153
getCommandEncoder(): GPUCommandEncoder {

web/lib/wasm/jsep/init.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class TensorViewImpl implements TensorView {
2929
}
3030
}
3131

32-
class OpKernelContext implements ComputeContext {
32+
class ComputeContextImpl implements ComputeContext {
3333
readonly opKernelContext: number;
3434
readonly inputs: readonly TensorView[];
3535
get customData(): {[key: string]: unknown} {
@@ -142,7 +142,7 @@ export const init = async(module: OrtWasmModule): Promise<void> => {
142142
// jsepRun
143143
(kernel: number, contextDataOffset: number) => {
144144
LOG_DEBUG('verbose', () => `[WebGPU] jsepRun: kernel=${kernel}, contextDataOffset=${contextDataOffset}`);
145-
const context = new OpKernelContext(module, backend, contextDataOffset);
145+
const context = new ComputeContextImpl(module, backend, contextDataOffset);
146146
return backend.computeKernel(kernel, context);
147147
});
148148
}

web/lib/wasm/jsep/util.ts

Lines changed: 0 additions & 321 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,6 @@
44
/* eslint-disable no-param-reassign */
55

66
export class MatMulUtil {
7-
/**
8-
* Fix the input shapes for MatMul operation if they need fixing
9-
* @param dimsA The shape of tensor A. Should be an array of positive integers
10-
* @param dimsB The shape of tensor B. Should be an array of positive integers
11-
* @returns A tuple containing the preprocessed input shapes as required by ONNX specifications
12-
*/
13-
static preprocessInputShapes(dimsA: readonly number[], dimsB: readonly number[]):
14-
[readonly number[], readonly number[]] {
15-
// If the first argument is 1-D, it is promoted to a matrix by prepending
16-
// a 1 to its dimensions. After matrix multiplication the prepended 1 is
17-
// removed.
18-
const a = (dimsA.length === 1) ? [1, dimsA[0]] : dimsA;
19-
20-
// If the second argument is 1-D, it is promoted to a matrix by appending
21-
// a 1 to its dimensions. After matrix multiplication the appended 1 is
22-
// removed.
23-
const b = (dimsB.length === 1) ? [dimsB[0], 1] : dimsB;
24-
25-
return [a, b];
26-
}
27-
28-
/**
29-
* Fix the output shape computed for MatMul operation if it needs fixing
30-
* @param outputShape The computed outputShape. Should be an array (atleast of length 2) of positive integers.
31-
* This will be mutated.
32-
* @param aRank The rank of tensor A.
33-
* @param bRank The rank of tensor B.
34-
*/
35-
static postprocessOutputShape(outputShape: number[], aRank: number, bRank: number): void {
36-
// Remove prepended dimension if first input is 1d
37-
if (aRank === 1) {
38-
// outputShape = outputShape.slice(0, outputShape.length - 2).concat(outputShape.slice(outputShape.length - 1));
39-
outputShape.splice(outputShape.length - 2, 1);
40-
}
41-
// Remove appended dimension if second input is 1d
42-
if (bRank === 1) {
43-
outputShape.pop();
44-
}
45-
}
46-
477
/**
488
* Calculate the expected shape when matrix multiplication
499
* @param a The shape of tensor A. Should be a tuple of 2 positive integers
@@ -102,39 +62,6 @@ export class BroadcastUtil {
10262
return cdims;
10363
}
10464

105-
/**
106-
* Given the indices of a broadcasted tensor, calculate the original indices
107-
* @param broadcastedIndices The given indices of the broadcasted tensor.
108-
* @param originalShape The original shape of the tensor before broadcas
109-
* @returns The calculated indices that maps to the original tensor.
110-
*/
111-
static index(broadcastedIndices: readonly number[], originalShape: readonly number[]): number[] {
112-
// NOTE 1: we assume the parameter broadcastedIndices is valid. ie. it should have the same
113-
// length as the broadcasted shape, and for each dimension the index should
114-
// not be out of range.
115-
const originalIndices = new Array(originalShape.length);
116-
BroadcastUtil.fillIndex(broadcastedIndices, originalShape, originalIndices);
117-
return originalIndices;
118-
}
119-
120-
/**
121-
* Given the indices of a broadcasted tensor, calculate the original indices
122-
* @param broadcastedIndices The given indices of the broadcasted tensor.
123-
* @param originalShape The original shape of the tensor before broadcast
124-
* @param originalIndices The mapping of broadcastedIndices to the originalIndices (output parameter - will be
125-
* mutated).
126-
*/
127-
static fillIndex(broadcastedIndices: readonly number[], originalShape: readonly number[], originalIndices: number[]):
128-
void {
129-
// NOTE 1: we assume the parameter broadcastedIndices is valid. ie. it should have the same length as the
130-
// broadcasted shape, and for each dimension the index should not be out of range.
131-
// NOTE 2: we assume the parameter originalIndices has the same length as the originalShape
132-
const dimOffset = broadcastedIndices.length - originalShape.length;
133-
for (let i = 0; i < originalShape.length; i++) {
134-
originalIndices[i] = broadcastedIndices[dimOffset + i] % originalShape[i];
135-
}
136-
}
137-
13865
/**
13966
* Determine if a shape is unidirectional broadcastable to another shape
14067
* @param shape The input shape
@@ -154,27 +81,6 @@ export class BroadcastUtil {
15481
}
15582
return true;
15683
}
157-
158-
/**
159-
* Determine the broadcasted dims in input shape based on the given output shape.
160-
* Note that this function only returns the broadcasted dims.
161-
* @param inputShape The input shape
162-
* @param outputShape The output shape
163-
* @returns The broadcasted dims in input shape.
164-
*/
165-
static getBroadcastDims(inputShape: readonly number[], outputShape: readonly number[]): number[] {
166-
const inRank = inputShape.length;
167-
const dims: number[] = [];
168-
for (let i = 0; i < inRank; i++) {
169-
const dim = inRank - 1 - i;
170-
const a = inputShape[dim] || 1;
171-
const b = outputShape[outputShape.length - 1 - i] || 1;
172-
if (b > 1 && a === 1) {
173-
dims.unshift(dim);
174-
}
175-
}
176-
return dims;
177-
}
17884
}
17985

18086

@@ -240,38 +146,6 @@ export class ShapeUtil {
240146
return strides;
241147
}
242148

243-
static transpose(dims: readonly number[]): readonly number[] {
244-
const copy = dims.slice();
245-
return copy.reverse();
246-
}
247-
248-
static indicesToOffset(indices: readonly number[], strides: readonly number[], axis?: number): number {
249-
if (axis === undefined) {
250-
axis = indices.length;
251-
}
252-
let offset = 0;
253-
for (let i = 0; i < axis; ++i) {
254-
offset += strides[i] * indices[i];
255-
}
256-
return offset;
257-
}
258-
259-
static offsetToIndices(offset: number, strides: readonly number[]): readonly number[] {
260-
const rank = strides.length;
261-
if (rank === 0) {
262-
return [];
263-
} else if (rank === 1) {
264-
return [offset * strides[0]];
265-
}
266-
const indices: number[] = new Array(strides.length);
267-
for (let i = 0; i < indices.length - 1; ++i) {
268-
indices[i] = Math.floor(offset / strides[i]);
269-
offset -= indices[i] * strides[i];
270-
}
271-
indices[indices.length - 1] = offset;
272-
return indices;
273-
}
274-
275149
/**
276150
* normailze axis of range [-r, r) into [0, r).
277151
*/
@@ -286,98 +160,6 @@ export class ShapeUtil {
286160
return axes.map(x => this.normalizeAxis(x, tensorRank ?? axes.length));
287161
}
288162

289-
/**
290-
* Increment an index into a tensor (in lexicographic ordering), wrapping around the specified upper_bound.
291-
* @param index Given index to increment (Will be mutated)
292-
* @param dims The dimensions of the tensor for which the given index corresponds to
293-
* @param axisToIncrementOn The 1-indexed axis to increment on. If undefined, axisToIncrementOn == rank
294-
*/
295-
static incrementIndex(index: number[], dims: readonly number[], axisToIncrementOn?: number): void {
296-
if (dims.length === 0 || index.length === 0) {
297-
throw new Error('Index incrementing unsupported for scalar Tensor');
298-
}
299-
if (axisToIncrementOn === undefined) {
300-
axisToIncrementOn = dims.length;
301-
} else {
302-
if (axisToIncrementOn <= 0 || axisToIncrementOn > dims.length) {
303-
throw new Error('Incorrect axis to increment on');
304-
}
305-
}
306-
307-
for (let k = axisToIncrementOn - 1; k >= 0; --k) {
308-
index[k]++;
309-
if (index[k] < dims[k]) {
310-
break;
311-
}
312-
index[k] = 0;
313-
}
314-
}
315-
316-
/**
317-
* Produces a new dimensions array based on the values in the 'originalDimensions' and 'shape' array
318-
* Used in Reshape
319-
* @param originalDims Original Shape array
320-
* @param shapeHints array containing values to compute the new dimensions
321-
* For example:
322-
* originalDims = [2,2] and shapeHints = [0,-1] will return [2,2]
323-
* originalDims = [2,2] and shapeHints = [4] will return [4]
324-
* originalDims = [2,2] and shapeHints = [5] will throw an exception
325-
* https://github.com/onnx/onnx/blob/main/docs/Operators.md#Reshape
326-
*/
327-
328-
static calculateReshapedDims(originalDims: readonly number[], shapeHints: ArrayLike<number>): number[] {
329-
// reshape to a Scalar Tensor
330-
if (shapeHints.length === 0) {
331-
if (originalDims.length === 0 || ShapeUtil.size(originalDims) === 1) {
332-
return [];
333-
} else {
334-
throw new Error('cannot reshape to a scalar Tensor');
335-
}
336-
}
337-
338-
const nDims = shapeHints.length;
339-
const reshapedDims = new Array<number>(nDims);
340-
let unknownDimension = -1;
341-
let newTensorSize = 1;
342-
for (let i = 0; i < nDims; i++) {
343-
if (shapeHints[i] < -1) {
344-
throw new Error('a dimension in shape hints cannot be less than -1');
345-
}
346-
if (shapeHints[i] === -1) {
347-
if (unknownDimension !== -1) {
348-
throw new Error('at most one dimension in shape hints can be -1');
349-
}
350-
unknownDimension = i;
351-
} else {
352-
if (shapeHints[i] === 0) {
353-
if (i >= originalDims.length) {
354-
throw new Error('the dimension with value zero exceeds the dimension size of the input tensor');
355-
}
356-
reshapedDims[i] = originalDims[i];
357-
} else {
358-
reshapedDims[i] = shapeHints[i];
359-
}
360-
newTensorSize *= reshapedDims[i];
361-
}
362-
}
363-
364-
const oldTensorSize = ShapeUtil.size(originalDims);
365-
if (unknownDimension !== -1) {
366-
if (oldTensorSize % newTensorSize !== 0) {
367-
throw new Error(`the input tensor cannot be reshaped to the requested shape. Input shape: [${
368-
originalDims}] Output shape: [${shapeHints}]`);
369-
}
370-
reshapedDims[unknownDimension] = oldTensorSize / newTensorSize;
371-
}
372-
// validate sizes from originalDims and reshapedDims match
373-
else {
374-
if (newTensorSize !== oldTensorSize) {
375-
throw new Error('reshapedDims and originalDims don\'t have matching sizes');
376-
}
377-
}
378-
return reshapedDims;
379-
}
380-
381163
/**
382164
* Sorts a given array based on the indices in the Perm array
383165
* Used in Transpose
@@ -413,109 +195,6 @@ export class ShapeUtil {
413195
}
414196
return shape1.every((v, i) => v === shape2[i]);
415197
}
416-
417-
/**
418-
* Validates if the given `dims` or `shape` is valid in ONNX.js context and returns data size
419-
* @param dims - input `dims` that needs to be checked
420-
*/
421-
static validateDimsAndCalcSize(dims: readonly number[]): number {
422-
if (dims.length > 6) {
423-
throw new TypeError('Only rank 0 to 6 is supported for tensor shape.');
424-
}
425-
let size = 1;
426-
for (const n of dims) {
427-
if (!Number.isInteger(n)) {
428-
throw new TypeError(`Invalid shape: ${n} is not an integer`);
429-
}
430-
if (n < 0 || n > 2147483647) {
431-
throw new TypeError(`Invalid shape: length ${n} is not allowed`);
432-
}
433-
size *= n;
434-
}
435-
return size;
436-
}
437-
438-
/**
439-
* Determines the shape of output tensor y = flatten(x, axis)
440-
* @param dims - shape of input tensor
441-
* @param axis - flatten axis, in the range [-r, r]
442-
*/
443-
static flattenShape(dims: readonly number[], axis: number): readonly number[] {
444-
if (axis < 0) {
445-
axis += dims.length;
446-
}
447-
const total = dims.reduce((x, y) => x * y, 1);
448-
const right = dims.slice(axis).reduce((x, y) => x * y, 1);
449-
const outputDims = [total / right, right];
450-
451-
return outputDims;
452-
}
453-
454-
/**
455-
* Determines the shape of output tensor y = squeeze(x, axes)
456-
* @param dims - shape of input tensor
457-
* @param axes - squeeze axes
458-
*/
459-
static squeezeShape(dims: readonly number[], axes: readonly number[]): readonly number[] {
460-
const outputDims = new Array<number>();
461-
462-
// sanity check
463-
axes = ShapeUtil.normalizeAxes(axes, dims.length);
464-
465-
for (let i = 0; i < dims.length; i++) {
466-
const inSqueezeList = axes.indexOf(i) >= 0;
467-
if (inSqueezeList && dims[i] !== 1) {
468-
throw new Error('squeeze an axis of size different than 1');
469-
}
470-
471-
if ((axes.length === 0 && dims[i] > 1) || (axes.length > 0 && !inSqueezeList)) {
472-
outputDims.push(dims[i]);
473-
}
474-
}
475-
476-
return outputDims;
477-
}
478-
479-
/**
480-
* Determines the shape of output tensor y = unsqueeze(x, axes)
481-
* @param dims - shape of input tensor
482-
* @param axes - unsqueeze axes
483-
*/
484-
static unsqueezeShape(dims: readonly number[], axes: readonly number[]): readonly number[] {
485-
const outputDims = new Array<number>(dims.length + axes.length);
486-
487-
// initialize the array elements to 0
488-
outputDims.fill(0);
489-
490-
// set all axes indices to 1 in outputDims and check for duplicates
491-
for (let i = 0; i < axes.length; i++) {
492-
const axis = ShapeUtil.normalizeAxis(axes[i], outputDims.length);
493-
if (axis >= outputDims.length) {
494-
throw new Error('\'axes\' has an out of range axis');
495-
}
496-
if (outputDims[axis] !== 0) {
497-
throw new Error('\'axes\' has a duplicate axis');
498-
}
499-
500-
outputDims[axis] = 1;
501-
}
502-
503-
// fill in the zero entries of outputDims with the input tensor's shape
504-
let inputDimsIterator = 0;
505-
for (let i = 0; i < outputDims.length; i++) {
506-
if (outputDims[i] === 0) {
507-
outputDims[i] = dims[inputDimsIterator++];
508-
}
509-
}
510-
511-
// sanity check assertion. 'inputDimsIterator'
512-
// should be equal to the length of 'dims'
513-
if (inputDimsIterator !== dims.length) {
514-
throw new Error('the unsqueezed dimension could not be established');
515-
}
516-
517-
return outputDims;
518-
}
519198
}
520199

521200
export class PoolConvUtil {

0 commit comments

Comments
 (0)