[js/rn] Add executionProviders support (#16233)

### Description
<!-- Describe your changes. -->

This PR adds support for `executionProviders` option for react-native
package, support:

- Android: cpu / xnnpack / nnapi
- iOS: cpu / xnnpack /  coreml

### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->

In my case I want to enable Core ML / NNAPI EP for react-native project.
This commit is contained in:
Jhen-Jie Hong 2023-06-16 17:38:41 +08:00 committed by GitHub
parent ea1a5cf920
commit 685816bb0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 0 deletions

View file

@ -167,6 +167,7 @@ export declare namespace InferenceSession {
// Backend Node.js binding: supports 'cpu' and 'cuda'.
// Backend WebAssembly: supports 'cpu', 'wasm', 'xnnpack' and 'webnn'.
// Backend ONNX.js: supports 'webgl'.
// Backend React Native: supports 'cpu', 'xnnpack', 'coreml' (iOS), 'nnapi' (Android).
interface ExecutionProviderOptionMap {
cpu: CpuExecutionProviderOption;
cuda: CudaExecutionProviderOption;
@ -174,6 +175,8 @@ export declare namespace InferenceSession {
webgl: WebGLExecutionProviderOption;
xnnpack: XnnpackExecutionProviderOption;
webnn: WebNNExecutionProviderOption;
coreml: CoreMLExecutionProviderOption;
nnapi: NnapiExecutionProviderOption;
}
type ExecutionProviderName = keyof ExecutionProviderOptionMap;
@ -206,6 +209,19 @@ export declare namespace InferenceSession {
deviceType?: 'cpu'|'gpu';
powerPreference?: 'default'|'low-power'|'high-performance';
}
export interface CoreMLExecutionProviderOption extends ExecutionProviderOption {
readonly name: 'coreml';
useCPUOnly?: boolean;
enableOnSubgraph?: boolean;
onlyEnableDeviceWithANE?: boolean;
}
export interface NnapiExecutionProviderOption extends ExecutionProviderOption {
readonly name: 'nnapi';
useFP16?: boolean;
useNCHW?: boolean;
cpuDisabled?: boolean;
cpuOnly?: boolean;
}
// #endregion
// #endregion

View file

@ -11,6 +11,7 @@ import ai.onnxruntime.OrtSession;
import ai.onnxruntime.OrtSession.Result;
import ai.onnxruntime.OrtSession.RunOptions;
import ai.onnxruntime.OrtSession.SessionOptions;
import ai.onnxruntime.providers.NNAPIFlags;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
@ -34,6 +35,8 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.math.BigInteger;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -367,6 +370,44 @@ public class OnnxruntimeModule extends ReactContextBaseJavaModule implements Lif
}
}
if (options.hasKey("executionProviders")) {
ReadableArray executionProviders = options.getArray("executionProviders");
for (int i = 0; i < executionProviders.size(); ++i) {
String epName = null;
ReadableMap epOptions = null;
if (executionProviders.getType(i) == ReadableType.String) {
epName = executionProviders.getString(i);
} else {
epOptions = executionProviders.getMap(i);
epName = epOptions.getString("name");
}
if (epName.equals("nnapi")) {
EnumSet<NNAPIFlags> flags = EnumSet.noneOf(NNAPIFlags.class);
if (epOptions != null) {
if (epOptions.hasKey("useFP16") && epOptions.getBoolean("useFP16")) {
flags.add(NNAPIFlags.USE_FP16);
}
if (epOptions.hasKey("useNCHW") && epOptions.getBoolean("useNCHW")) {
flags.add(NNAPIFlags.USE_NCHW);
}
if (epOptions.hasKey("cpuDisabled") && epOptions.getBoolean("cpuDisabled")) {
flags.add(NNAPIFlags.CPU_DISABLED);
}
if (epOptions.hasKey("cpuOnly") && epOptions.getBoolean("cpuOnly")) {
flags.add(NNAPIFlags.CPU_ONLY);
}
}
sessionOptions.addNnapi(flags);
} else if (epName.equals("xnnpack")) {
sessionOptions.addXnnpack(Collections.emptyMap());
} else if (epName.equals("cpu")) {
continue;
} else {
throw new OrtException("Unsupported execution provider: " + epName);
}
}
}
if (options.hasKey("logId")) {
String logId = options.getString("logId");
sessionOptions.setLoggerId(logId);

View file

@ -17,9 +17,11 @@
// https://google.github.io/styleguide/objcguide.html#import-and-include
// https://microsoft.github.io/objc-guide/Headers/ImportAndInclude.html
#ifdef ORT_ENABLE_EXTENSIONS
#include "coreml_provider_factory.h"
#include "onnxruntime_cxx_api.h"
#include "onnxruntime_extensions.h"
#else
#include "onnxruntime/coreml_provider_factory.h"
#include "onnxruntime/onnxruntime_cxx_api.h"
#endif
@ -371,6 +373,44 @@ static NSDictionary *executionModeTable = @{@"sequential" : @(ORT_SEQUENTIAL), @
}
}
if ([options objectForKey:@"executionProviders"]) {
NSArray *executionProviders = [options objectForKey:@"executionProviders"];
for (auto *executionProvider in executionProviders) {
NSString *epName = nil;
bool useOptions = false;
if ([executionProvider isKindOfClass:[NSString class]]) {
epName = (NSString *)executionProvider;
} else {
epName = [executionProvider objectForKey:@"name"];
useOptions = true;
}
if ([epName isEqualToString:@"coreml"]) {
uint32_t coreml_flags = 0;
if (useOptions) {
if ([[executionProvider objectForKey:@"useCPUOnly"] boolValue]) {
coreml_flags |= COREML_FLAG_USE_CPU_ONLY;
}
if ([[executionProvider objectForKey:@"enableOnSubgraph"] boolValue]) {
coreml_flags |= COREML_FLAG_ENABLE_ON_SUBGRAPH;
}
if ([[executionProvider objectForKey:@"onlyEnableDeviceWithANE"] boolValue]) {
coreml_flags |= COREML_FLAG_ONLY_ENABLE_DEVICE_WITH_ANE;
}
}
Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CoreML(sessionOptions, coreml_flags));
} else if ([epName isEqualToString:@"xnnpack"]) {
sessionOptions.AppendExecutionProvider("XNNPACK", {});
} else if ([epName isEqualToString:@"cpu"]) {
continue;
} else {
NSException *exception = [NSException exceptionWithName:@"onnxruntime"
reason:@"unsupported execution provider"
userInfo:nil];
@throw exception;
}
}
}
if ([options objectForKey:@"logId"]) {
NSString *logId = [[options objectForKey:@"logId"] stringValue];
sessionOptions.SetLogId([logId UTF8String]);

View file

@ -3,9 +3,16 @@
export * from 'onnxruntime-common';
import {registerBackend, env} from 'onnxruntime-common';
import {Platform} from 'react-native';
import {onnxruntimeBackend} from './backend';
import {version} from './version';
registerBackend('cpu', onnxruntimeBackend, 1);
registerBackend('xnnpack', onnxruntimeBackend, 1);
if (Platform.OS === 'android') {
registerBackend('nnapi', onnxruntimeBackend, 1);
} else if (Platform.OS === 'ios') {
registerBackend('coreml', onnxruntimeBackend, 1);
}
env.versions['react-native'] = version;