summaryrefslogtreecommitdiff
path: root/nn
diff options
context:
space:
mode:
authorMichael Butler <butlermichael@google.com>2018-04-13 17:26:47 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2018-04-13 17:26:47 +0000
commit984b07d42ce0ed8629a6be2229862d0db4684890 (patch)
tree4452e26bcffe8933858f4b1612a6447699d13bb7 /nn
parent665fe302d261cce3261a7bb6fa41ef519312e3f2 (diff)
parent1e9666208595bc251a8958155b1e41eca90b69db (diff)
downloadml-984b07d42ce0ed8629a6be2229862d0db4684890.tar.gz
Merge changes from topic "nnapi-execution-preference" into pi-dev
* changes: NNAPI: Add execution preference to prepareModel (runtime) Upgrade NeuralNetworks v1.0 to v1.1 followup CL
Diffstat (limited to 'nn')
-rw-r--r--nn/common/CpuExecutor.cpp8
-rw-r--r--nn/common/ValidateHal.cpp6
-rw-r--r--nn/common/include/CpuExecutor.h5
-rw-r--r--nn/common/include/HalInterfaces.h1
-rw-r--r--nn/common/include/ValidateHal.h3
-rw-r--r--nn/driver/sample/SampleDriver.cpp9
-rw-r--r--nn/driver/sample/SampleDriver.h2
-rw-r--r--nn/runtime/ExecutionBuilder.cpp9
-rw-r--r--nn/runtime/ExecutionBuilder.h2
-rw-r--r--nn/runtime/ExecutionPlan.cpp35
-rw-r--r--nn/runtime/ExecutionPlan.h11
-rw-r--r--nn/runtime/VersionedIDevice.cpp12
-rw-r--r--nn/runtime/VersionedIDevice.h173
-rw-r--r--nn/runtime/test/TestExecution.cpp3
-rw-r--r--nn/runtime/test/TestPartitioning.cpp5
-rw-r--r--nn/runtime/test/TestPartitioningRandom.cpp4
16 files changed, 229 insertions, 59 deletions
diff --git a/nn/common/CpuExecutor.cpp b/nn/common/CpuExecutor.cpp
index 86e117d9a..333683acd 100644
--- a/nn/common/CpuExecutor.cpp
+++ b/nn/common/CpuExecutor.cpp
@@ -184,7 +184,13 @@ static bool setInfoAndAllocateIfNeeded(RunTimeOperandInfo* info, const Shape& sh
// Ignore the .pools entry in model and request. This will have been taken care of
// by the caller.
-int CpuExecutor::run(const Model& model, const Request& request,
+int CpuExecutor::run(const V1_0::Model& model, const Request& request,
+ const std::vector<RunTimePoolInfo>& modelPoolInfos,
+ const std::vector<RunTimePoolInfo>& requestPoolInfos) {
+ return run(convertToV1_1(model), request, modelPoolInfos, requestPoolInfos);
+}
+
+int CpuExecutor::run(const V1_1::Model& model, const Request& request,
const std::vector<RunTimePoolInfo>& modelPoolInfos,
const std::vector<RunTimePoolInfo>& requestPoolInfos) {
VLOG(CPUEXE) << "CpuExecutor::run()";
diff --git a/nn/common/ValidateHal.cpp b/nn/common/ValidateHal.cpp
index e981bb3fa..095517889 100644
--- a/nn/common/ValidateHal.cpp
+++ b/nn/common/ValidateHal.cpp
@@ -515,5 +515,11 @@ bool validateRequest(const Request& request, const V1_1::Model& model) {
return validateRequestVersioned(request, model);
}
+bool validateExecutionPreference(ExecutionPreference preference) {
+ return preference == ExecutionPreference::LOW_POWER ||
+ preference == ExecutionPreference::FAST_SINGLE_ANSWER ||
+ preference == ExecutionPreference::SUSTAINED_SPEED;
+}
+
} // namespace nn
} // namespace android
diff --git a/nn/common/include/CpuExecutor.h b/nn/common/include/CpuExecutor.h
index 23a25a9fe..c7a318bc3 100644
--- a/nn/common/include/CpuExecutor.h
+++ b/nn/common/include/CpuExecutor.h
@@ -106,7 +106,10 @@ public:
// specified in the constructor.
// The model must outlive the executor. We prevent it from being modified
// while this is executing.
- int run(const Model& model, const Request& request,
+ int run(const V1_0::Model& model, const Request& request,
+ const std::vector<RunTimePoolInfo>& modelPoolInfos,
+ const std::vector<RunTimePoolInfo>& requestPoolInfos);
+ int run(const V1_1::Model& model, const Request& request,
const std::vector<RunTimePoolInfo>& modelPoolInfos,
const std::vector<RunTimePoolInfo>& requestPoolInfos);
diff --git a/nn/common/include/HalInterfaces.h b/nn/common/include/HalInterfaces.h
index befede565..7467955ae 100644
--- a/nn/common/include/HalInterfaces.h
+++ b/nn/common/include/HalInterfaces.h
@@ -47,6 +47,7 @@ using ::android::hardware::neuralnetworks::V1_0::PerformanceInfo;
using ::android::hardware::neuralnetworks::V1_0::Request;
using ::android::hardware::neuralnetworks::V1_0::RequestArgument;
using ::android::hardware::neuralnetworks::V1_1::Capabilities;
+using ::android::hardware::neuralnetworks::V1_1::ExecutionPreference;
using ::android::hardware::neuralnetworks::V1_1::IDevice;
using ::android::hardware::neuralnetworks::V1_1::Model;
using ::android::hardware::neuralnetworks::V1_1::Operation;
diff --git a/nn/common/include/ValidateHal.h b/nn/common/include/ValidateHal.h
index c3c3c5a47..d07449b71 100644
--- a/nn/common/include/ValidateHal.h
+++ b/nn/common/include/ValidateHal.h
@@ -38,6 +38,9 @@ bool validateModel(const V1_1::Model& model);
bool validateRequest(const Request& request, const V1_0::Model& model);
bool validateRequest(const Request& request, const V1_1::Model& model);
+// Verfies that the execution preference is valid.
+bool validateExecutionPreference(ExecutionPreference preference);
+
} // namespace nn
} // namespace android
diff --git a/nn/driver/sample/SampleDriver.cpp b/nn/driver/sample/SampleDriver.cpp
index d537299df..9ebefe580 100644
--- a/nn/driver/sample/SampleDriver.cpp
+++ b/nn/driver/sample/SampleDriver.cpp
@@ -61,10 +61,12 @@ Return<ErrorStatus> SampleDriver::prepareModel(const V1_0::Model& model,
callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
return ErrorStatus::INVALID_ARGUMENT;
}
- return prepareModel_1_1(convertToV1_1(model), callback);
+ return prepareModel_1_1(convertToV1_1(model), ExecutionPreference::FAST_SINGLE_ANSWER,
+ callback);
}
Return<ErrorStatus> SampleDriver::prepareModel_1_1(const V1_1::Model& model,
+ ExecutionPreference preference,
const sp<IPreparedModelCallback>& callback) {
if (VLOG_IS_ON(DRIVER)) {
VLOG(DRIVER) << "prepareModel_1_1";
@@ -74,7 +76,7 @@ Return<ErrorStatus> SampleDriver::prepareModel_1_1(const V1_1::Model& model,
LOG(ERROR) << "invalid callback passed to prepareModel";
return ErrorStatus::INVALID_ARGUMENT;
}
- if (!validateModel(model)) {
+ if (!validateModel(model) || !validateExecutionPreference(preference)) {
callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
return ErrorStatus::INVALID_ARGUMENT;
}
@@ -118,8 +120,7 @@ void SamplePreparedModel::asyncExecute(const Request& request,
}
CpuExecutor executor;
- V1_1::Model model = convertToV1_1(mModel);
- int n = executor.run(model, request, mPoolInfos, requestPoolInfos);
+ int n = executor.run(mModel, request, mPoolInfos, requestPoolInfos);
VLOG(DRIVER) << "executor.run returned " << n;
ErrorStatus executionStatus =
n == ANEURALNETWORKS_NO_ERROR ? ErrorStatus::NONE : ErrorStatus::GENERAL_FAILURE;
diff --git a/nn/driver/sample/SampleDriver.h b/nn/driver/sample/SampleDriver.h
index 89423e262..15dd8b3ce 100644
--- a/nn/driver/sample/SampleDriver.h
+++ b/nn/driver/sample/SampleDriver.h
@@ -41,7 +41,7 @@ public:
getSupportedOperations_cb cb) override;
Return<ErrorStatus> prepareModel(const V1_0::Model& model,
const sp<IPreparedModelCallback>& callback) override;
- Return<ErrorStatus> prepareModel_1_1(const V1_1::Model& model,
+ Return<ErrorStatus> prepareModel_1_1(const V1_1::Model& model, ExecutionPreference preference,
const sp<IPreparedModelCallback>& callback) override;
Return<DeviceStatus> getStatus() override;
diff --git a/nn/runtime/ExecutionBuilder.cpp b/nn/runtime/ExecutionBuilder.cpp
index 02efd408b..63301b10a 100644
--- a/nn/runtime/ExecutionBuilder.cpp
+++ b/nn/runtime/ExecutionBuilder.cpp
@@ -552,7 +552,14 @@ int StepExecutor::startComputeOnDevice(sp<ExecutionCallback>* synchronizationCal
// TODO Dangerous! In async, the model will outlive it here. Safe for now
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
- ErrorStatus prepareLaunchStatus = mDriver->prepareModel(model, preparedModelCallback);
+ // TODO(butlermichael): Propagate user preference to this point instead of
+ // using default value of ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER, or
+ // remove this entire block of code since it is a stale path that is only
+ // encountered on an #if-removed code.
+ ExecutionPreference preference =
+ static_cast<ExecutionPreference>(ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER);
+ ErrorStatus prepareLaunchStatus = mDriver->prepareModel(model, preference,
+ preparedModelCallback);
if (prepareLaunchStatus != ErrorStatus::NONE) {
return convertErrorStatusToResultCode(prepareLaunchStatus);
}
diff --git a/nn/runtime/ExecutionBuilder.h b/nn/runtime/ExecutionBuilder.h
index 40b41e920..7c188d3de 100644
--- a/nn/runtime/ExecutionBuilder.h
+++ b/nn/runtime/ExecutionBuilder.h
@@ -22,7 +22,6 @@
#include "Memory.h"
#include "ModelBuilder.h"
#include "NeuralNetworks.h"
-#include "VersionedIDevice.h"
#include <unordered_map>
#include <vector>
@@ -38,6 +37,7 @@ class ExecutionPlan;
class Memory;
class ModelBuilder;
class StepExecutor;
+class VersionedIDevice;
// TODO move length out of DataLocation
struct ModelArgumentInfo {
diff --git a/nn/runtime/ExecutionPlan.cpp b/nn/runtime/ExecutionPlan.cpp
index 655d51266..10470ba39 100644
--- a/nn/runtime/ExecutionPlan.cpp
+++ b/nn/runtime/ExecutionPlan.cpp
@@ -38,17 +38,16 @@ using ::android::hardware::neuralnetworks::V1_0::implementation::PreparedModelCa
namespace android {
namespace nn {
-static int compile(std::shared_ptr<Device> device,
- const ModelBuilder* model,
- sp<IPreparedModel>* preparedModel) {
+static int compile(std::shared_ptr<Device> device, const ModelBuilder* model,
+ int32_t executionPreference, sp<IPreparedModel>* preparedModel) {
nnAssert(device != nullptr); // nullptr indicates CPU
// Compilation logic copied from ExecutionBuilder::startComputeOnDevice().
Model hidlModel;
model->setHidlModel(&hidlModel);
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
- Return<ErrorStatus> prepareLaunchStatus =
- device->getInterface()->prepareModel(hidlModel, preparedModelCallback);
+ Return<ErrorStatus> prepareLaunchStatus = device->getInterface()->prepareModel(
+ hidlModel, static_cast<ExecutionPreference>(executionPreference), preparedModelCallback);
if (!prepareLaunchStatus.isOk()) {
LOG(ERROR) << "ExecutionStep::finishSubModel compilation failed due to transport error: "
<< prepareLaunchStatus.description();
@@ -347,7 +346,8 @@ static void convertModelInputsOrOutputs(
}
}
-int ExecutionStep::finishSubModel(const ModelBuilder* fromModel, bool* hasOutputOfUnknownSize) {
+int ExecutionStep::finishSubModel(const ModelBuilder* fromModel, bool* hasOutputOfUnknownSize,
+ int32_t executionPreference) {
if (VLOG_IS_ON(COMPILATION)) {
logSubModel();
}
@@ -430,7 +430,7 @@ int ExecutionStep::finishSubModel(const ModelBuilder* fromModel, bool* hasOutput
}
VLOG(COMPILATION) << "ExecutionStep::finishSubModel, compilation";
- return compile(mDevice, &mSubModel, &mPreparedSubModel);
+ return compile(mDevice, &mSubModel, executionPreference, &mPreparedSubModel);
}
void ExecutionStep::dump() const {
@@ -443,10 +443,12 @@ void ExecutionStep::dump() const {
}
}
-int ExecutionPlan::CompoundBody::finish(const ModelBuilder* fromModel) {
+int ExecutionPlan::CompoundBody::finish(const ModelBuilder* fromModel,
+ int32_t executionPreference) {
findTempsAsSubModelOutputs();
for (const auto& step : mSteps) {
- int n = step->finishSubModel(fromModel, &mHasSubModelOutputOfUnknownSize);
+ int n = step->finishSubModel(fromModel, &mHasSubModelOutputOfUnknownSize,
+ executionPreference);
if (n != ANEURALNETWORKS_NO_ERROR) {
VLOG(COMPILATION) << "ExecutionPlan::CompoundBody::finish -- finishSubModel failed";
return n;
@@ -461,21 +463,22 @@ int ExecutionPlan::CompoundBody::finish(const ModelBuilder* fromModel) {
return ANEURALNETWORKS_NO_ERROR;
}
-int ExecutionPlan::SimpleBody::finish([[maybe_unused]] const ModelBuilder* fromModel) {
+int ExecutionPlan::SimpleBody::finish([[maybe_unused]] const ModelBuilder* fromModel,
+ int32_t executionPreference) {
if (mDevice == nullptr) {
mSuccessfulFinish = true;
return ANEURALNETWORKS_NO_ERROR;
}
VLOG(COMPILATION) << "ExecutionPlan::SimpleBody::finish, compilation";
- const int n = compile(mDevice, mModel, &mPreparedModel);
+ const int n = compile(mDevice, mModel, executionPreference, &mPreparedModel);
mSuccessfulFinish = (n == ANEURALNETWORKS_NO_ERROR);
return n;
}
-int ExecutionPlan::finish(const ModelBuilder* fromModel) {
+int ExecutionPlan::finish(const ModelBuilder* fromModel, int32_t executionPreference) {
nnAssert(mBody != nullptr);
- return mBody->finish(fromModel);
+ return mBody->finish(fromModel, executionPreference);
}
ExecutionPlan::Controller::Controller(
@@ -779,7 +782,7 @@ int ModelBuilder::partitionTheWork(const std::vector<std::shared_ptr<Device>>& d
}
}
plan->becomeSingleStep(nullptr /* CPU */, this);
- return plan->finish(this);
+ return plan->finish(this, preference);
}
// Figure out where each operation will best execute.
@@ -801,7 +804,7 @@ int ModelBuilder::partitionTheWork(const std::vector<std::shared_ptr<Device>>& d
<< bestDeviceIndex << " = "
<< (cpu ? "CPU" : devices[bestDeviceIndex]->getName());
plan->becomeSingleStep(cpu ? nullptr : devices[bestDeviceIndex], this);
- return plan->finish(this);
+ return plan->finish(this, preference);
}
// No easy solution, we need to split the work.
@@ -860,7 +863,7 @@ int ModelBuilder::partitionTheWork(const std::vector<std::shared_ptr<Device>>& d
}
}
- int n = plan->finish(this);
+ int n = plan->finish(this, preference);
if (VLOG_IS_ON(COMPILATION)) {
Model model;
setHidlModel(&model);
diff --git a/nn/runtime/ExecutionPlan.h b/nn/runtime/ExecutionPlan.h
index 29467575d..843447275 100644
--- a/nn/runtime/ExecutionPlan.h
+++ b/nn/runtime/ExecutionPlan.h
@@ -80,7 +80,8 @@ public:
// If this step has a submodel output of unknown size, sets
// *hasOutputOfUnknownSize to true; otherwise, leaves it
// unchanged.
- int finishSubModel(const ModelBuilder* fromModel, bool* hasOutputOfUnknownSize);
+ int finishSubModel(const ModelBuilder* fromModel, bool* hasOutputOfUnknownSize,
+ int32_t executionPreference);
const ModelBuilder* getSubModel() const { return &mSubModel; }
std::shared_ptr<Device> getDevice() const { return mDevice; }
@@ -203,7 +204,7 @@ public:
void becomeSingleStep(const std::shared_ptr<Device> device,
const ModelBuilder* model);
- int finish(const ModelBuilder* fromModel);
+ int finish(const ModelBuilder* fromModel, int32_t executionPreference);
void recordTemporaryDef(uint32_t fromModelIndex, uint32_t stepIndex) {
auto& temporaryToDefiningStep = compound()->mTemporaryToDefiningStep;
@@ -226,7 +227,7 @@ private:
struct Body {
virtual ~Body() {}
virtual void dump() const = 0;
- virtual int finish(const ModelBuilder* fromModel) = 0;
+ virtual int finish(const ModelBuilder* fromModel, int32_t executionPreference) = 0;
bool mSuccessfulFinish = false;
};
@@ -235,7 +236,7 @@ private:
mDevice(device), mModel(model) {}
void dump() const override;
- int finish(const ModelBuilder* fromModel) override;
+ int finish(const ModelBuilder* fromModel, int32_t executionPreference) override;
std::shared_ptr<Device> mDevice; // nullptr signifies CPU
const ModelBuilder* mModel;
@@ -244,7 +245,7 @@ private:
struct CompoundBody : Body {
void dump() const override;
- int finish(const ModelBuilder* fromModel) override;
+ int finish(const ModelBuilder* fromModel, int32_t executionPreference) override;
// TODO: Some of the data is working state information that
// shouldn't be needed after we've constructed but not
diff --git a/nn/runtime/VersionedIDevice.cpp b/nn/runtime/VersionedIDevice.cpp
index 5cd78424a..110eecc47 100644
--- a/nn/runtime/VersionedIDevice.cpp
+++ b/nn/runtime/VersionedIDevice.cpp
@@ -33,7 +33,7 @@ std::pair<ErrorStatus, Capabilities> VersionedIDevice::getCapabilities() {
if (mDeviceV1_1 != nullptr) {
Return<void> ret = mDeviceV1_1->getCapabilities_1_1(
- [&](ErrorStatus error, const Capabilities& capabilities) {
+ [&result](ErrorStatus error, const Capabilities& capabilities) {
result = std::make_pair(error, capabilities);
});
if (!ret.isOk()) {
@@ -42,7 +42,7 @@ std::pair<ErrorStatus, Capabilities> VersionedIDevice::getCapabilities() {
}
} else if (mDeviceV1_0 != nullptr) {
Return<void> ret = mDeviceV1_0->getCapabilities(
- [&](ErrorStatus error, const V1_0::Capabilities& capabilities) {
+ [&result](ErrorStatus error, const V1_0::Capabilities& capabilities) {
result = std::make_pair(error, convertToV1_1(capabilities));
});
if (!ret.isOk()) {
@@ -63,7 +63,7 @@ std::pair<ErrorStatus, hidl_vec<bool>> VersionedIDevice::getSupportedOperations(
if (mDeviceV1_1 != nullptr) {
Return<void> ret = mDeviceV1_1->getSupportedOperations_1_1(
- model, [&](ErrorStatus error, const hidl_vec<bool>& supported) {
+ model, [&result](ErrorStatus error, const hidl_vec<bool>& supported) {
result = std::make_pair(error, supported);
});
if (!ret.isOk()) {
@@ -72,7 +72,7 @@ std::pair<ErrorStatus, hidl_vec<bool>> VersionedIDevice::getSupportedOperations(
}
} else if (mDeviceV1_0 != nullptr && compliantWithV1_0(model)) {
Return<void> ret = mDeviceV1_0->getSupportedOperations(
- convertToV1_0(model), [&](ErrorStatus error, const hidl_vec<bool>& supported) {
+ convertToV1_0(model), [&result](ErrorStatus error, const hidl_vec<bool>& supported) {
result = std::make_pair(error, supported);
});
if (!ret.isOk()) {
@@ -89,10 +89,10 @@ std::pair<ErrorStatus, hidl_vec<bool>> VersionedIDevice::getSupportedOperations(
return result;
}
-ErrorStatus VersionedIDevice::prepareModel(const Model& model,
+ErrorStatus VersionedIDevice::prepareModel(const Model& model, ExecutionPreference preference,
const sp<IPreparedModelCallback>& callback) {
if (mDeviceV1_1 != nullptr) {
- Return<ErrorStatus> ret = mDeviceV1_1->prepareModel_1_1(model, callback);
+ Return<ErrorStatus> ret = mDeviceV1_1->prepareModel_1_1(model, preference, callback);
if (!ret.isOk()) {
LOG(ERROR) << "prepareModel_1_1 failure: " << ret.description();
return ErrorStatus::GENERAL_FAILURE;
diff --git a/nn/runtime/VersionedIDevice.h b/nn/runtime/VersionedIDevice.h
index f2b308818..561d780ce 100644
--- a/nn/runtime/VersionedIDevice.h
+++ b/nn/runtime/VersionedIDevice.h
@@ -26,39 +26,174 @@
namespace android {
namespace nn {
-// TODO(butlermichael): document VersionedIDevice class
+/**
+ * This object is either a V1_1::IDevice or V1_0::IDevice object. This class
+ * abstracts away version differences, allowing the remainder of the runtime to
+ * always use the most up-to-date version of all HIDL types. As such, any
+ * reference to a HIDL type in the rest of the runtime will--by default--be the
+ * latest HIDL version.
+ *
+ * This class will attempt to call the latest version of each interface method
+ * if possible. If the latest method is unavailable, the VersionedIDevice class
+ * will attempt to upcast the type (e.g., V1_1::Model to V1_0::Model), and
+ * invoke the latest interface method possible. If the VersionedIDevice class
+ * fails to find a matching applicable function, it will return an error.
+ */
class VersionedIDevice {
DISALLOW_IMPLICIT_CONSTRUCTORS(VersionedIDevice);
public:
+ /**
+ * Constructor for the VersionedIDevice object.
+ *
+ * VersionedIDevice is constructed with the V1_0::IDevice object, which
+ * represents a device that is at least v1.0 of the interface. The
+ * constructor downcasts to the latest version of the IDevice interface, and
+ * will default to using the latest version of all IDevice interface
+ * methods automatically.
+ *
+ * @param device A device object that is least version 1.0 of the IDevice
+ * interface.
+ */
VersionedIDevice(sp<V1_0::IDevice> device);
+ /**
+ * Gets the capabilities of a driver.
+ *
+ * @return status Error status of the call, must be:
+ * - NONE if successful
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if there is an unspecified error
+ * @return capabilities Capabilities of the driver.
+ */
std::pair<ErrorStatus, Capabilities> getCapabilities();
- ErrorStatus prepareModel(const Model& model, const sp<IPreparedModelCallback>& callback);
-
+ /**
+ * Gets the supported operations in a model.
+ *
+ * getSupportedSubgraph indicates which operations of a model are fully
+ * supported by the vendor driver. If an operation may not be supported for
+ * any reason, getSupportedOperations must return false for that operation.
+ *
+ * @param model A model whose operations--and their corresponding
+ * operands--are to be verified by the driver.
+ * @return status Error status of the call, must be:
+ * - NONE if successful
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if there is an unspecified error
+ * - INVALID_ARGUMENT if provided model is invalid
+ * @return supportedOperations A list of supported operations, where true
+ * indicates the operation is supported and
+ * false indicates the operation is not
+ * supported. The index of "supported"
+ * corresponds with the index of the operation
+ * it is describing.
+ */
std::pair<ErrorStatus, hidl_vec<bool>> getSupportedOperations(const Model& model);
+ /**
+ * Creates a prepared model for execution.
+ *
+ * prepareModel is used to make any necessary transformations or alternative
+ * representations to a model for execution, possiblly including
+ * transformations on the constant data, optimization on the model's graph,
+ * or compilation into the device's native binary format. The model itself
+ * is not changed.
+ *
+ * The model is prepared asynchronously with respect to the caller. The
+ * prepareModel function must verify the inputs to the prepareModel function
+ * are correct. If there is an error, prepareModel must immediately invoke
+ * the callback with the appropriate ErrorStatus value and nullptr for the
+ * IPreparedModel, then return with the same ErrorStatus. If the inputs to
+ * the prepareModel function are valid and there is no error, prepareModel
+ * must launch an asynchronous task to prepare the model in the background,
+ * and immediately return from prepareModel with ErrorStatus::NONE. If the
+ * asynchronous task fails to launch, prepareModel must immediately invoke
+ * the callback with ErrorStatus::GENERAL_FAILURE and nullptr for the
+ * IPreparedModel, then return with ErrorStatus::GENERAL_FAILURE.
+ *
+ * When the asynchronous task has finished preparing the model, it must
+ * immediately invoke the callback function provided as an input to
+ * prepareModel. If the model was prepared successfully, the callback object
+ * must be invoked with an error status of ErrorStatus::NONE and the
+ * produced IPreparedModel object. If an error occurred preparing the model,
+ * the callback object must be invoked with the appropriate ErrorStatus
+ * value and nullptr for the IPreparedModel.
+ *
+ * The only information that may be unknown to the model at this stage is
+ * the shape of the tensors, which may only be known at execution time. As
+ * such, some driver services may return partially prepared models, where
+ * the prepared model can only be finished when it is paired with a set of
+ * inputs to the model. Note that the same prepared model object can be
+ * used with different shapes of inputs on different (possibly concurrent)
+ * executions.
+ *
+ * Multiple threads can call prepareModel on the same model concurrently.
+ *
+ * @param model The model to be prepared for execution.
+ * @param callback A callback object used to return the error status of
+ * preparing the model for execution and the prepared model
+ * if successful, nullptr otherwise. The callback object's
+ * notify function must be called exactly once, even if the
+ * model could not be prepared.
+ * @return status Error status of launching a task which prepares the model
+ * in the background; must be:
+ * - NONE if preparation task is successfully launched
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if there is an unspecified error
+ * - INVALID_ARGUMENT if one of the input arguments is
+ * invalid
+ */
+ ErrorStatus prepareModel(const Model& model, ExecutionPreference preference,
+ const sp<IPreparedModelCallback>& callback);
+
+ /**
+ * Returns the current status of a driver.
+ *
+ * @return status Status of the driver, one of:
+ * - DeviceStatus::AVAILABLE
+ * - DeviceStatus::BUSY
+ * - DeviceStatus::OFFLINE
+ * - DeviceStatus::UNKNOWN
+ */
DeviceStatus getStatus();
- bool operator==(nullptr_t);
+ /**
+ * Returns whether this handle to an IDevice object is valid or not.
+ *
+ * @return bool true if V1_0::IDevice (which could be V1_1::IDevice) is
+ * valid, false otherwise.
+ */
bool operator!=(nullptr_t);
+ /**
+ * Returns whether this handle to an IDevice object is valid or not.
+ *
+ * @return bool true if V1_0::IDevice (which could be V1_1::IDevice) is
+ * invalid, false otherwise.
+ */
+ bool operator==(nullptr_t);
+
private:
- // Both versions of IDevice are necessary because the driver could v1.0,
- // v1.1, or a later version. These two pointers logically represent the same
- // object.
- //
- // The general strategy is: HIDL returns a V1_0 device object, which
- // (if not nullptr) could be v1.0, v1.1, or a greater version. The V1_0
- // object is then "dynamically cast" to a V1_1 object. If successful,
- // mDeviceV1_1 will point to the same object as mDeviceV1_0; otherwise,
- // mDeviceV1_1 will be nullptr.
- //
- // In general:
- // * If the device is truly v1.0, mDeviceV1_0 will point to a valid object
- // and mDeviceV1_1 will be nullptr.
- // * If the device is truly v1.1 or later, both mDeviceV1_0 and mDeviceV1_1
- // will point to the same valid object.
+ /**
+ * Both versions of IDevice are necessary because the driver could be v1.0,
+ * v1.1, or a later version. These two pointers logically represent the same
+ * object.
+ *
+ * The general strategy is: HIDL returns a V1_0 device object, which
+ * (if not nullptr) could be v1.0, v1.1, or a greater version. The V1_0
+ * object is then "dynamically cast" to a V1_1 object. If successful,
+ * mDeviceV1_1 will point to the same object as mDeviceV1_0; otherwise,
+ * mDeviceV1_1 will be nullptr.
+ *
+ * In general:
+ * * If the device is truly v1.0, mDeviceV1_0 will point to a valid object
+ * and mDeviceV1_1 will be nullptr.
+ * * If the device is truly v1.1 or later, both mDeviceV1_0 and mDeviceV1_1
+ * will point to the same valid object.
+ *
+ * Idiomatic usage: if mDeviceV1_1 is non-null, do V1_1 dispatch; otherwise,
+ * do V1_0 dispatch.
+ */
sp<V1_0::IDevice> mDeviceV1_0;
sp<V1_1::IDevice> mDeviceV1_1;
};
diff --git a/nn/runtime/test/TestExecution.cpp b/nn/runtime/test/TestExecution.cpp
index 26e1dc379..8df7464e6 100644
--- a/nn/runtime/test/TestExecution.cpp
+++ b/nn/runtime/test/TestExecution.cpp
@@ -109,11 +109,12 @@ public:
Return<ErrorStatus> prepareModel_1_1(
const HidlModel& model,
+ ExecutionPreference preference,
const sp<IPreparedModelCallback>& actualCallback) override {
sp<PreparedModelCallback> localCallback = new PreparedModelCallback;
Return<ErrorStatus> prepareModelReturn =
- SampleDriver::prepareModel_1_1(model, localCallback);
+ SampleDriver::prepareModel_1_1(model, preference, localCallback);
if (!prepareModelReturn.isOkUnchecked()) {
return prepareModelReturn;
}
diff --git a/nn/runtime/test/TestPartitioning.cpp b/nn/runtime/test/TestPartitioning.cpp
index 690fff872..9b989b716 100644
--- a/nn/runtime/test/TestPartitioning.cpp
+++ b/nn/runtime/test/TestPartitioning.cpp
@@ -242,11 +242,12 @@ public:
mOperationMask(operationMask), mOEM(oem) {}
~PartitioningDriver() override {}
- Return<ErrorStatus> prepareModel_1_1(const Model&,
+ Return<ErrorStatus> prepareModel_1_1(const Model&, ExecutionPreference,
const sp<IPreparedModelCallback>& cb) override {
cb->notify(ErrorStatus::NONE, new PartitioningPreparedModel);
return ErrorStatus::NONE;
}
+
Return<DeviceStatus> getStatus() override {
return DeviceStatus::AVAILABLE;
}
@@ -255,6 +256,7 @@ public:
cb(ErrorStatus::NONE, mCapabilities);
return Void();
}
+
Return<void> getSupportedOperations_1_1(const Model& model,
getSupportedOperations_cb cb) override {
if (!android::nn::validateModel(model)) {
@@ -278,6 +280,7 @@ public:
cb(ErrorStatus::NONE, supported);
return Void();
}
+
private:
Capabilities mCapabilities;
uint32_t mOperationMask;
diff --git a/nn/runtime/test/TestPartitioningRandom.cpp b/nn/runtime/test/TestPartitioningRandom.cpp
index a72935dad..c302b133f 100644
--- a/nn/runtime/test/TestPartitioningRandom.cpp
+++ b/nn/runtime/test/TestPartitioningRandom.cpp
@@ -510,7 +510,7 @@ public:
return Void();
}
- Return<ErrorStatus> prepareModel_1_1(const HidlModel& model,
+ Return<ErrorStatus> prepareModel_1_1(const HidlModel& model, ExecutionPreference preference,
const sp<IPreparedModelCallback>& callback) override {
// NOTE: We verify that all operations in the model are supported.
ErrorStatus outStatus = ErrorStatus::INVALID_ARGUMENT;
@@ -525,7 +525,7 @@ public:
}
});
if (ret.isOk() && (outStatus == ErrorStatus::NONE)) {
- return SampleDriver::prepareModel_1_1(model, callback);
+ return SampleDriver::prepareModel_1_1(model, preference, callback);
} else {
callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
return ErrorStatus::INVALID_ARGUMENT;