[js/webgpu] allow specify preferredLayout (#17756)

### Description
Allow WebGPU backend to specify `preferredLayout`. Default is NHWC.

```js
const options = {executionProviders: [{name:'webgpu', preferredLayout: 'NCHW'}]};
sess1 = await ort.InferenceSession.create('./mobilenetv2-12.onnx', options);
```

### Motivation and Context
- implement @qjia7's requirement for an easier way to do performance
comparison between NCHW vs NHWC.
- It's possible that NCHW does better on some models and NHWC on others.
So offer user the capability to switch.
This commit is contained in:
Yulong Wang 2023-10-02 21:25:12 -07:00 committed by GitHub
parent f158f394d6
commit 451c02543a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 40 additions and 5 deletions

View file

@ -192,6 +192,7 @@ export declare namespace InferenceSession {
wasm: WebAssemblyExecutionProviderOption;
webgl: WebGLExecutionProviderOption;
xnnpack: XnnpackExecutionProviderOption;
webgpu: WebGpuExecutionProviderOption;
webnn: WebNNExecutionProviderOption;
nnapi: NnapiExecutionProviderOption;
}
@ -233,6 +234,10 @@ export declare namespace InferenceSession {
export interface XnnpackExecutionProviderOption extends ExecutionProviderOption {
readonly name: 'xnnpack';
}
export interface WebGpuExecutionProviderOption extends ExecutionProviderOption {
readonly name: 'webgpu';
preferredLayout?: 'NCHW'|'NHWC';
}
export interface WebNNExecutionProviderOption extends ExecutionProviderOption {
readonly name: 'webnn';
deviceType?: 'cpu'|'gpu';

View file

@ -88,6 +88,21 @@ const setExecutionProviders =
break;
case 'webgpu':
epName = 'JS';
if (typeof ep !== 'string') {
const webgpuOptions = ep as InferenceSession.WebGpuExecutionProviderOption;
if (webgpuOptions?.preferredLayout) {
if (webgpuOptions.preferredLayout !== 'NCHW' && webgpuOptions.preferredLayout !== 'NHWC') {
throw new Error(`preferredLayout must be either 'NCHW' or 'NHWC': ${webgpuOptions.preferredLayout}`);
}
const keyDataOffset = allocWasmString('preferredLayout', allocs);
const valueDataOffset = allocWasmString(webgpuOptions.preferredLayout, allocs);
if (getInstance()._OrtAddSessionConfigEntry(sessionOptionsHandle, keyDataOffset, valueDataOffset) !==
0) {
checkLastError(
`Can't set a session config entry: 'preferredLayout' - ${webgpuOptions.preferredLayout}.`);
}
}
}
break;
case 'wasm':
case 'cpu':

View file

@ -624,7 +624,8 @@ std::unique_ptr<KernelRegistry> RegisterKernels() {
using namespace js;
JsExecutionProvider::JsExecutionProvider(const JsExecutionProviderInfo& info)
: IExecutionProvider{kJsExecutionProvider, OrtDevice(OrtDevice::GPU, OrtDevice::MemType::DEFAULT, 0), true} {
: IExecutionProvider{kJsExecutionProvider, OrtDevice(OrtDevice::GPU, OrtDevice::MemType::DEFAULT, 0), true},
preferred_data_layout_{info.data_layout} {
}
std::vector<AllocatorPtr> JsExecutionProvider::CreatePreferredAllocators() {

View file

@ -19,12 +19,21 @@ KernelCreateInfo BuildKernelCreateInfo();
} // namespace js
// placeholder for future use. no options currently
struct JsExecutionProviderInfo {
JsExecutionProviderInfo() = default;
JsExecutionProviderInfo(const ProviderOptions& po) {
auto it = po.find("preferred_layout");
if (it != po.end()) {
auto& value = it->second;
if (value == "NCHW") {
data_layout = DataLayout::NCHW;
} else if (value == "NHWC") {
data_layout = DataLayout::NHWC;
}
}
}
// JSEP default preferred layout is NHWC
DataLayout data_layout = DataLayout::NHWC;
};
class JsExecutionProvider : public IExecutionProvider {
@ -39,7 +48,7 @@ class JsExecutionProvider : public IExecutionProvider {
std::shared_ptr<KernelRegistry> GetKernelRegistry() const override;
std::unique_ptr<onnxruntime::IDataTransfer> GetDataTransfer() const override;
DataLayout GetPreferredLayout() const override { return DataLayout::NHWC; }
DataLayout GetPreferredLayout() const override { return preferred_data_layout_; }
FusionStyle GetFusionStyle() const override { return FusionStyle::FilteredGraphViewer; }
@ -48,6 +57,7 @@ class JsExecutionProvider : public IExecutionProvider {
bool ConcurrentRunSupported() const override { return false; }
std::vector<AllocatorPtr> CreatePreferredAllocators() override;
DataLayout preferred_data_layout_;
};
} // namespace onnxruntime

View file

@ -109,6 +109,10 @@ ORT_API_STATUS_IMPL(OrtApis::SessionOptionsAppendExecutionProvider,
#endif
} else if (strcmp(provider_name, "JS") == 0) {
#if defined(USE_JSEP)
std::string preferred_layout;
if (options->value.config_options.TryGetConfigEntry("preferredLayout", preferred_layout)) {
provider_options["preferred_layout"] = preferred_layout;
}
options->provider_factories.push_back(JsProviderFactoryCreator::Create(provider_options));
#else
status = create_not_supported_status();