Skip to content

Commit e203298

Browse files
committed
PR feedback
* Missing GPUBufferDataTypes to MLBufferDataTypes changes * Remove unused [jsep]GetBuffer method * Enable io-binding on test-runner-cli * Correctly use MLContext on test-runner * Hoist `shouldTransferToMLBuffer` closer to the start of DataTransfer::CopyTensor * Corrected indentation on pre-jsep.js
1 parent cd1b01a commit e203298

File tree

10 files changed

+55
-74
lines changed

10 files changed

+55
-74
lines changed

js/common/lib/tensor-factory-impl.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
import {OptionsDimensions, OptionsFormat, OptionsNormalizationParameters, OptionsTensorFormat, OptionsTensorLayout, TensorFromGpuBufferOptions, TensorFromImageBitmapOptions, TensorFromImageDataOptions, TensorFromImageElementOptions, TensorFromTextureOptions, TensorFromUrlOptions} from './tensor-factory.js';
4+
import {OptionsDimensions, OptionsFormat, OptionsNormalizationParameters, OptionsTensorFormat, OptionsTensorLayout, TensorFromGpuBufferOptions, TensorFromImageBitmapOptions, TensorFromImageDataOptions, TensorFromImageElementOptions, TensorFromMLBufferOptions, TensorFromTextureOptions, TensorFromUrlOptions} from './tensor-factory.js';
55
import {Tensor} from './tensor-impl.js';
66
import {Tensor as TensorInterface} from './tensor.js';
77

@@ -277,8 +277,8 @@ export const tensorFromGpuBuffer = <T extends TensorInterface.GpuBufferDataTypes
277277
/**
278278
* implementation of Tensor.fromMLBuffer().
279279
*/
280-
export const tensorFromMLBuffer = <T extends TensorInterface.GpuBufferDataTypes>(
281-
mlBuffer: TensorInterface.MLBufferType, options: TensorFromGpuBufferOptions<T>): Tensor => {
280+
export const tensorFromMLBuffer = <T extends TensorInterface.MLBufferDataTypes>(
281+
mlBuffer: TensorInterface.MLBufferType, options: TensorFromMLBufferOptions<T>): Tensor => {
282282
const {dataType, dims, download, dispose} = options;
283283
return new Tensor({location: 'ml-buffer', type: dataType ?? 'float32', mlBuffer, dims, download, dispose});
284284
};

js/common/lib/tensor-impl.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import {tensorToDataURL, tensorToImageData} from './tensor-conversion-impl.js';
55
import {TensorToDataUrlOptions, TensorToImageDataOptions} from './tensor-conversion.js';
66
import {tensorFromGpuBuffer, tensorFromImage, tensorFromMLBuffer, tensorFromPinnedBuffer, tensorFromTexture} from './tensor-factory-impl.js';
7-
import {CpuPinnedConstructorParameters, GpuBufferConstructorParameters, MLBufferConstructorParameters, TensorFromGpuBufferOptions, TensorFromImageBitmapOptions, TensorFromImageDataOptions, TensorFromImageElementOptions, TensorFromTextureOptions, TensorFromUrlOptions, TextureConstructorParameters} from './tensor-factory.js';
7+
import {CpuPinnedConstructorParameters, GpuBufferConstructorParameters, MLBufferConstructorParameters, TensorFromGpuBufferOptions, TensorFromImageBitmapOptions, TensorFromImageDataOptions, TensorFromImageElementOptions, TensorFromMLBufferOptions, TensorFromTextureOptions, TensorFromUrlOptions, TextureConstructorParameters} from './tensor-factory.js';
88
import {checkTypedArray, NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP, NUMERIC_TENSOR_TYPEDARRAY_TO_TYPE_MAP, SupportedTypedArray, SupportedTypedArrayConstructors} from './tensor-impl-type-mapping.js';
99
import {calculateSize, tensorReshape} from './tensor-utils-impl.js';
1010
import {Tensor as TensorInterface} from './tensor.js';
@@ -273,8 +273,8 @@ export class Tensor implements TensorInterface {
273273
return tensorFromGpuBuffer(gpuBuffer, options);
274274
}
275275

276-
static fromMLBuffer<T extends TensorInterface.GpuBufferDataTypes>(
277-
mlBuffer: TensorMLBufferType, options: TensorFromGpuBufferOptions<T>): TensorInterface {
276+
static fromMLBuffer<T extends TensorInterface.MLBufferDataTypes>(
277+
mlBuffer: TensorMLBufferType, options: TensorFromMLBufferOptions<T>): TensorInterface {
278278
return tensorFromMLBuffer(mlBuffer, options);
279279
}
280280

js/web/lib/wasm/jsep/backend-webnn.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,6 @@ export class WebNNBackend {
9999
this.bufferManager.releaseBufferId(bufferId);
100100
}
101101

102-
public getBuffer(bufferId: BufferId): MLBuffer {
103-
return this.bufferManager.getBuffer(bufferId);
104-
}
105-
106102
public ensureBuffer(bufferId: BufferId, onnxDataType: number|MLOperandDataType, dimensions: number[]): MLBuffer {
107103
let dataType: MLOperandDataType;
108104
if (typeof onnxDataType === 'number') {
@@ -129,7 +125,7 @@ export class WebNNBackend {
129125
return this.bufferManager.download(bufferId);
130126
}
131127

132-
public createMLBufferDownloader(bufferId: BufferId, type: Tensor.GpuBufferDataTypes): () => Promise<Tensor.DataType> {
128+
public createMLBufferDownloader(bufferId: BufferId, type: Tensor.MLBufferDataTypes): () => Promise<Tensor.DataType> {
133129
return async () => {
134130
const data = await this.bufferManager.download(bufferId);
135131
return createView(data, type);

js/web/lib/wasm/jsep/webnn/buffer-manager.ts

-14
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ export interface BufferManager {
2222
* Release a BufferId.
2323
*/
2424
releaseBufferId(bufferId: BufferId): void;
25-
/**
26-
* Get MLBuffer by BufferId.
27-
*/
28-
getBuffer(bufferId: BufferId): MLBuffer;
2925
/**
3026
* Ensure a MLBuffer is created for the BufferId.
3127
*/
@@ -155,16 +151,6 @@ class BufferManagerImpl implements BufferManager {
155151
}
156152
}
157153

158-
public getBuffer(bufferId: BufferId): MLBuffer {
159-
if (!this.buffersById.has(bufferId)) {
160-
throw new Error('BufferID not found.');
161-
}
162-
if (!this.buffersById.get(bufferId)!.buffer) {
163-
throw new Error('Buffer has not been created.');
164-
}
165-
return this.buffersById.get(bufferId)!.buffer!;
166-
}
167-
168154
public ensureBuffer(bufferId: BufferId, dataType: MLOperandDataType, dimensions: number[]): MLBuffer {
169155
const buffer = this.buffersById.get(bufferId);
170156
if (!buffer) {

js/web/lib/wasm/session-handler-inference.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const decodeTensorMetadata = (tensor: TensorMetadata): Tensor => {
3737
case 'ml-buffer': {
3838
const dataType = tensor[0];
3939
if (!isMLBufferSupportedType(dataType)) {
40-
throw new Error(`not supported data type: ${dataType} for deserializing GPU tensor`);
40+
throw new Error(`not supported data type: ${dataType} for deserializing MLBuffer tensor`);
4141
}
4242
const {mlBuffer, download, dispose} = tensor[2];
4343
return Tensor.fromMLBuffer(mlBuffer, {dataType, dims: tensor[1], download, dispose});

js/web/lib/wasm/wasm-types.ts

-8
Original file line numberDiff line numberDiff line change
@@ -160,14 +160,6 @@ export declare namespace JSEP {
160160
* @param bufferId - specify the MLBuffer ID.
161161
* @returns the MLBuffer.
162162
*/
163-
jsepGetMLBuffer: (bufferId: number) => MLBuffer;
164-
/**
165-
* [exported from pre-jsep.js] Ensure MLBuffer has been created with the correct type and dimensions.
166-
* @param bufferId - specify the MLBuffer ID.
167-
* @param dataType - specify the data type.
168-
* @param dimensions - specify the dimensions.
169-
* @returns the MLBuffer.
170-
*/
171163
jsepEnsureBuffer: (bufferId: number, dataType: number|MLOperandDataType, dimensions: number[]) => MLBuffer;
172164
/**
173165
* [exported from pre-jsep.js] Upload data to MLBuffer.

js/web/script/test-runner-cli.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ async function main() {
359359
}
360360

361361
let ioBinding: Test.IOBindingMode;
362-
if (backend !== 'webgpu' && args.ioBindingMode !== 'none') {
362+
if (!['webgpu', 'webnn'].includes(backend) && args.ioBindingMode !== 'none') {
363363
npmlog.warn(
364364
'TestRunnerCli.Init.Model', `Ignoring IO Binding Mode "${args.ioBindingMode}" for backend "${backend}".`);
365365
ioBinding = 'none';

js/web/test/test-runner.ts

+35-26
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ export class ModelTestContext {
204204
readonly perfData: ModelTestContext.ModelTestPerfData,
205205
readonly ioBinding: Test.IOBindingMode,
206206
private readonly profile: boolean,
207+
public readonly mlContext?: MLContext,
207208
) {}
208209

209210
/**
@@ -254,7 +255,25 @@ export class ModelTestContext {
254255

255256
const initStart = now();
256257
const executionProviderConfig =
257-
modelTest.backend === 'webnn' ? (testOptions?.webnnOptions || 'webnn') : modelTest.backend!;
258+
modelTest.backend === 'webnn' ? (testOptions?.webnnOptions || {name: 'webnn'}) : modelTest.backend!;
259+
let mlContext: MLContext|undefined;
260+
if (modelTest.ioBinding.includes('ml-tensor') || modelTest.ioBinding.includes('ml-location')) {
261+
262+
const webnnOptions = executionProviderConfig as ort.InferenceSession.WebNNExecutionProviderOption;
263+
const deviceType = (webnnOptions as ort.InferenceSession.WebNNContextOptions)?.deviceType;
264+
const numThreads = (webnnOptions as ort.InferenceSession.WebNNContextOptions)?.numThreads;
265+
const powerPreference = (webnnOptions as ort.InferenceSession.WebNNContextOptions)?.powerPreference;
266+
267+
mlContext = await navigator.ml.createContext({
268+
deviceType,
269+
numThreads,
270+
powerPreference,
271+
});
272+
(executionProviderConfig as ort.InferenceSession.WebNNExecutionProviderOption).context = mlContext;
273+
if (!deviceType) {
274+
(executionProviderConfig as ort.InferenceSession.WebNNContextOptions).deviceType = deviceType;
275+
}
276+
}
258277
const session = await initializeSession(
259278
modelTest.modelUrl, executionProviderConfig, modelTest.ioBinding, profile, modelTest.externalData,
260279
testOptions?.sessionOptions || {}, this.cache);
@@ -271,6 +290,7 @@ export class ModelTestContext {
271290
{init: initEnd - initStart, firstRun: -1, runs: [], count: 0},
272291
modelTest.ioBinding,
273292
profile,
293+
mlContext,
274294
);
275295
} finally {
276296
this.initializing = false;
@@ -565,46 +585,34 @@ function createGpuTensorForOutput(type: ort.Tensor.Type, dims: readonly number[]
565585
});
566586
}
567587

568-
const getContext = (() => {
569-
let context: MLContext|undefined;
570-
571-
return async(): Promise<MLContext> => {
572-
if (!context) {
573-
context = await navigator.ml.createContext();
574-
}
575-
return context;
576-
};
577-
})();
578588

579-
async function createMlTensorForOutput(type: ort.Tensor.Type, dims: readonly number[]) {
589+
async function createMLTensorForOutput(mlContext: MLContext, type: ort.Tensor.Type, dims: readonly number[]) {
580590
if (!isMLBufferSupportedType(type)) {
581-
throw new Error(`createMlTensorForOutput can not work with ${type} tensor`);
591+
throw new Error(`createMLTensorForOutput can not work with ${type} tensor`);
582592
}
583593

584594
const dataType = type === 'bool' ? 'uint8' : type;
585595

586-
const context = await getContext();
587-
const mlBuffer = context.createBuffer({dataType, dimensions: dims as number[]});
596+
const mlBuffer = mlContext.createBuffer({dataType, dimensions: dims as number[]});
588597

589598
return ort.Tensor.fromMLBuffer(mlBuffer, {
590599
dataType: type,
591600
dims,
592601
dispose: () => mlBuffer.destroy(),
593602
download: async () => {
594-
const arrayBuffer = await context.readBuffer(mlBuffer);
595-
return createView(arrayBuffer, type) as ort.Tensor.DataTypeMap[ort.Tensor.GpuBufferDataTypes];
603+
const arrayBuffer = await mlContext.readBuffer(mlBuffer);
604+
return createView(arrayBuffer, type) as ort.Tensor.DataTypeMap[ort.Tensor.MLBufferDataTypes];
596605
}
597606
});
598607
}
599608

600-
async function createMlTensorForInput(cpuTensor: ort.Tensor): Promise<ort.Tensor> {
609+
async function createMLTensorForInput(mlContext: MLContext, cpuTensor: ort.Tensor): Promise<ort.Tensor> {
601610
if (!isMLBufferSupportedType(cpuTensor.type) || Array.isArray(cpuTensor.data)) {
602-
throw new Error(`createMlTensorForInput can not work with ${cpuTensor.type} tensor`);
611+
throw new Error(`createMLTensorForInput can not work with ${cpuTensor.type} tensor`);
603612
}
604-
const context = await getContext();
605613
const dataType = cpuTensor.type === 'bool' ? 'uint8' : cpuTensor.type;
606-
const mlBuffer = context.createBuffer({dataType, dimensions: cpuTensor.dims as number[]});
607-
context.writeBuffer(mlBuffer, cpuTensor.data);
614+
const mlBuffer = mlContext.createBuffer({dataType, dimensions: cpuTensor.dims as number[]});
615+
mlContext.writeBuffer(mlBuffer, cpuTensor.data);
608616
return ort.Tensor.fromMLBuffer(
609617
mlBuffer, {dataType: cpuTensor.type, dims: cpuTensor.dims, dispose: () => mlBuffer.destroy()});
610618
}
@@ -613,6 +621,7 @@ export async function sessionRun(options: {
613621
session: ort.InferenceSession; feeds: Record<string, ort.Tensor>;
614622
outputsMetaInfo: Record<string, Pick<ort.Tensor, 'dims'|'type'>>;
615623
ioBinding: Test.IOBindingMode;
624+
mlContext?: MLContext;
616625
}): Promise<[number, number, ort.InferenceSession.OnnxValueMapType]> {
617626
const session = options.session;
618627
const feeds = options.feeds;
@@ -633,7 +642,7 @@ export async function sessionRun(options: {
633642
if (Object.hasOwnProperty.call(feeds, name)) {
634643
if (feeds[name].size > 0) {
635644
if (options.ioBinding === 'ml-location' || options.ioBinding === 'ml-tensor') {
636-
feeds[name] = await createMlTensorForInput(feeds[name]);
645+
feeds[name] = await createMLTensorForInput(options.mlContext!, feeds[name]);
637646
} else {
638647
feeds[name] = createGpuTensorForInput(feeds[name]);
639648
}
@@ -650,7 +659,7 @@ export async function sessionRun(options: {
650659
fetches[name] = new ort.Tensor(type, [], dims);
651660
} else {
652661
if (options.ioBinding === 'ml-tensor') {
653-
fetches[name] = await createMlTensorForOutput(type, dims);
662+
fetches[name] = await createMLTensorForOutput(options.mlContext!, type, dims);
654663
} else {
655664
fetches[name] = createGpuTensorForOutput(type, dims);
656665
}
@@ -701,8 +710,8 @@ export async function runModelTestSet(
701710
const outputsMetaInfo: Record<string, ort.Tensor> = {};
702711
testCase.inputs!.forEach((tensor) => feeds[tensor.name] = tensor);
703712
testCase.outputs!.forEach((tensor) => outputsMetaInfo[tensor.name] = tensor);
704-
const [start, end, outputs] =
705-
await sessionRun({session: context.session, feeds, outputsMetaInfo, ioBinding: context.ioBinding});
713+
const [start, end, outputs] = await sessionRun(
714+
{session: context.session, feeds, outputsMetaInfo, ioBinding: context.ioBinding, mlContext: context.mlContext});
706715
if (context.perfData.count === 0) {
707716
context.perfData.firstRun = end - start;
708717
} else {

onnxruntime/core/providers/webnn/data_transfer.cc

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ bool DataTransfer::CanCopy(const OrtDevice& src_device, const OrtDevice& dst_dev
1616
}
1717

1818
common::Status DataTransfer::CopyTensor(const Tensor& src, Tensor& dst) const {
19+
if (!emscripten::val::module_property("shouldTransferToMLBuffer").as<bool>()) {
20+
// We don't need to transfer the buffer to an MLBuffer, so we don't need to copy the buffer.
21+
return Status::OK();
22+
}
23+
1924
size_t bytes = src.SizeInBytes();
2025
if (bytes > 0) {
2126
const void* src_data = src.DataRaw();
2227
void* dst_data = dst.MutableDataRaw();
2328

2429
const auto& dst_device = dst.Location().device;
2530

26-
if (!emscripten::val::module_property("shouldTransferToMLBuffer").as<bool>()) {
27-
// We don't need to transfer the buffer to an MLBuffer, so we don't need to copy the buffer.
28-
return Status::OK();
29-
}
30-
3131
if (dst_device.Type() == OrtDevice::GPU) {
3232
EM_ASM({
3333
Module.jsepUploadBuffer($0, HEAPU8.subarray($1, $1 + $2));

onnxruntime/wasm/pre-jsep.js

+6-8
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,11 @@ Module['jsepInit'] = (name, params) => {
201201
} else if(name === 'webnn') {
202202
// Functions called from EM_ASM need to be assigned in a way that can be minified.
203203
[Module.jsepBackend,
204-
Module.jsepReserveBufferId,
205-
Module.jsepReleaseBufferId,
206-
Module.jsepEnsureBuffer,
207-
Module.jsepUploadBuffer,
208-
Module.jsepDownloadBuffer,
204+
Module.jsepReserveBufferId,
205+
Module.jsepReleaseBufferId,
206+
Module.jsepEnsureBuffer,
207+
Module.jsepUploadBuffer,
208+
Module.jsepDownloadBuffer,
209209
] = params;
210210

211211

@@ -225,12 +225,10 @@ Module['jsepInit'] = (name, params) => {
225225
Module['jsepOnReleaseSession'] = sessionId => {
226226
backend['onReleaseSession'](sessionId);
227227
};
228+
Module['jsepReleaseBufferId'] = Module.jsepReleaseBufferId;
228229
Module['jsepGetMLContext'] = sessionId => {
229230
return backend['getMLContext'](sessionId);
230231
};
231-
Module['jsepGetMLBuffer'] = (bufferId) => {
232-
return backend['getBuffer'](bufferId);
233-
}
234232
Module['jsepCreateMLBufferDownloader'] = (bufferId, type) => {
235233
return backend['createMLBufferDownloader'](bufferId, type);
236234
}

0 commit comments

Comments
 (0)