diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp index ab524c2679..7c5b0bc0c4 100644 --- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp +++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp @@ -88,11 +88,22 @@ static Return ExecutePreparedModel(sp& prepar sp& callback) { return preparedModel->execute_1_2(request, callback); } +static Return ExecutePreparedModel(sp&, const Request&) { + ADD_FAILURE() << "asking for synchronous execution at V1_0"; + return ErrorStatus::GENERAL_FAILURE; +} +static Return ExecutePreparedModel(sp& preparedModel, + const Request& request) { + return preparedModel->executeSynchronously(request); +} +enum class Synchronously { NO, YES }; +const float kDefaultAtol = 1e-5f; +const float kDefaultRtol = 1e-5f; template void EvaluatePreparedModel(sp& preparedModel, std::function is_ignored, const std::vector& examples, - bool hasRelaxedFloat32Model = false, float fpAtol = 1e-5f, - float fpRtol = 1e-5f) { + bool hasRelaxedFloat32Model = false, float fpAtol = kDefaultAtol, + float fpRtol = kDefaultRtol, Synchronously sync = Synchronously::NO) { const uint32_t INPUT = 0; const uint32_t OUTPUT = 1; @@ -185,19 +196,31 @@ void EvaluatePreparedModel(sp& preparedModel, std::functioncommit(); outputMemory->commit(); - // launch execution - sp executionCallback = new ExecutionCallback(); - ASSERT_NE(nullptr, executionCallback.get()); - Return executionLaunchStatus = ExecutePreparedModel( - preparedModel, {.inputs = inputs_info, .outputs = outputs_info, .pools = pools}, - executionCallback); - ASSERT_TRUE(executionLaunchStatus.isOk()); - EXPECT_EQ(ErrorStatus::NONE, static_cast(executionLaunchStatus)); + if (sync == Synchronously::NO) { + SCOPED_TRACE("asynchronous"); - // retrieve execution status - executionCallback->wait(); - ErrorStatus executionReturnStatus = executionCallback->getStatus(); - EXPECT_EQ(ErrorStatus::NONE, executionReturnStatus); + // launch execution + sp executionCallback = new ExecutionCallback(); + ASSERT_NE(nullptr, executionCallback.get()); + Return executionLaunchStatus = ExecutePreparedModel( + preparedModel, {.inputs = inputs_info, .outputs = outputs_info, .pools = pools}, + executionCallback); + ASSERT_TRUE(executionLaunchStatus.isOk()); + EXPECT_EQ(ErrorStatus::NONE, static_cast(executionLaunchStatus)); + + // retrieve execution status + executionCallback->wait(); + ErrorStatus executionReturnStatus = executionCallback->getStatus(); + EXPECT_EQ(ErrorStatus::NONE, executionReturnStatus); + } else { + SCOPED_TRACE("synchronous"); + + // execute + Return executionStatus = ExecutePreparedModel( + preparedModel, {.inputs = inputs_info, .outputs = outputs_info, .pools = pools}); + ASSERT_TRUE(executionStatus.isOk()); + EXPECT_EQ(ErrorStatus::NONE, static_cast(executionStatus)); + } // validate results outputMemory->read(); @@ -215,6 +238,13 @@ void EvaluatePreparedModel(sp& preparedModel, std::function +void EvaluatePreparedModel(sp& preparedModel, std::function is_ignored, + const std::vector& examples, + bool hasRelaxedFloat32Model, Synchronously sync) { + EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model, kDefaultAtol, + kDefaultRtol, sync); +} static void getPreparedModel(sp callback, sp* preparedModel) { @@ -362,7 +392,9 @@ void Execute(const sp& device, std::function c ASSERT_NE(nullptr, preparedModel.get()); EvaluatePreparedModel(preparedModel, is_ignored, examples, - model.relaxComputationFloat32toFloat16); + model.relaxComputationFloat32toFloat16, Synchronously::NO); + EvaluatePreparedModel(preparedModel, is_ignored, examples, + model.relaxComputationFloat32toFloat16, Synchronously::YES); } } // namespace generated_tests diff --git a/neuralnetworks/1.2/IPreparedModel.hal b/neuralnetworks/1.2/IPreparedModel.hal index 5590487538..4e91c6754e 100644 --- a/neuralnetworks/1.2/IPreparedModel.hal +++ b/neuralnetworks/1.2/IPreparedModel.hal @@ -51,8 +51,9 @@ interface IPreparedModel extends @1.0::IPreparedModel { * and complete successfully (ErrorStatus::NONE). There must be * no failure unless the device itself is in a bad state. * - * Multiple threads can call the execute_1_2 function on the same IPreparedModel - * object concurrently with different requests. + * Any number of calls to the execute, execute_1_2, and executeSynchronously + * functions, in any combination, may be made concurrently, even on the same + * IPreparedModel object. * * @param request The input and output information on which the prepared * model is to be executed. @@ -71,4 +72,39 @@ interface IPreparedModel extends @1.0::IPreparedModel { */ execute_1_2(Request request, IExecutionCallback callback) generates (ErrorStatus status); + + /** + * Performs a synchronous execution on a prepared model. + * + * The execution is performed synchronously with respect to the caller. + * executeSynchronously must verify the inputs to the function are + * correct. If there is an error, executeSynchronously must immediately + * return with the appropriate ErrorStatus value. If the inputs to the + * function are valid and there is no error, executeSynchronously must + * perform the execution, and must not return until the execution is + * complete. + * + * If the prepared model was prepared from a model wherein all tensor + * operands have fully specified dimensions, and the inputs to the function + * are valid, then the execution should complete successfully + * (ErrorStatus::NONE). There must be no failure unless the device itself is + * in a bad state. + * + * Any number of calls to the execute, execute_1_2, and executeSynchronously + * functions, in any combination, may be made concurrently, even on the same + * IPreparedModel object. + * + * @param request The input and output information on which the prepared + * model is to be executed. + * @return status Error status of the execution, must be: + * - NONE if execution is performed successfully + * - DEVICE_UNAVAILABLE if driver is offline or busy + * - GENERAL_FAILURE if there is an unspecified error + * - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is + * not large enough to store the resultant values + * - INVALID_ARGUMENT if one of the input arguments is + * invalid + */ + executeSynchronously(Request request) + generates (ErrorStatus status); }; diff --git a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp index e2722aa7dc..d80fbcf689 100644 --- a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp +++ b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp @@ -97,18 +97,29 @@ static void createPreparedModel(const sp& device, const Model& model, static void validate(const sp& preparedModel, const std::string& message, Request request, const std::function& mutation) { mutation(&request); - SCOPED_TRACE(message + " [execute]"); - sp executionCallback = new ExecutionCallback(); - ASSERT_NE(nullptr, executionCallback.get()); - Return executeLaunchStatus = - preparedModel->execute_1_2(request, executionCallback); - ASSERT_TRUE(executeLaunchStatus.isOk()); - ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast(executeLaunchStatus)); + { + SCOPED_TRACE(message + " [execute_1_2]"); - executionCallback->wait(); - ErrorStatus executionReturnStatus = executionCallback->getStatus(); - ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, executionReturnStatus); + sp executionCallback = new ExecutionCallback(); + ASSERT_NE(nullptr, executionCallback.get()); + Return executeLaunchStatus = + preparedModel->execute_1_2(request, executionCallback); + ASSERT_TRUE(executeLaunchStatus.isOk()); + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast(executeLaunchStatus)); + + executionCallback->wait(); + ErrorStatus executionReturnStatus = executionCallback->getStatus(); + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, executionReturnStatus); + } + + { + SCOPED_TRACE(message + " [executeSynchronously]"); + + Return executeStatus = preparedModel->executeSynchronously(request); + ASSERT_TRUE(executeStatus.isOk()); + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast(executeStatus)); + } } // Delete element from hidl_vec. hidl_vec doesn't support a "remove" operation,