diff options
author | Xusong Wang <xusongw@google.com> | 2021-03-03 16:20:37 -0800 |
---|---|---|
committer | Xusong Wang <xusongw@google.com> | 2021-05-06 13:56:32 -0700 |
commit | 727a7b2104b0962509fedffe720eec508b2ee6de (patch) | |
tree | db56f9f27626747458a78fe1a1c5454fc666794a /neuralnetworks/1.0/utils/test/PreparedModelTest.cpp | |
parent | 57c9114315650206adab4beaa97e07d27e3e4b10 (diff) |
Introduce reusable execution to canonical interface -- HAL.
This CL modifies the canonical interface for reusable executions:
- Add new interface: IExecution with compute and computeFenced methods
- Add new method IPreparedModel::createExecution
In NNAPI runtime, the new interface IExecution is used to
memoize request-specific execution resources (e.g. converted HAL
request). The expected usage is that, IPreparedModel::createExecution
will be invoked in the first computation of a reusable NDK ANNExecution
object, and IExecution::compute* will be invoked repeatedly.
The IPreparedModel::execute* methods are preserved to avoid redundant
object creation and memoization overhead for a single-time
(non-reusable) execution.
For a vendor implementing the canonical interfaces, only the
IPreparedModel::execute* methods will be called because there is
currently no reusable execution at HAL interface. A DefaultExecution
implementation is provided to reduce the work needed on the vendor side.
Bug: 184073769
Test: NNT_static
Test: neuralnetworks_utils_hal_1_0_test
Test: neuralnetworks_utils_hal_1_1_test
Test: neuralnetworks_utils_hal_1_2_test
Test: neuralnetworks_utils_hal_1_3_test
Test: neuralnetworks_utils_hal_common_test
Test: neuralnetworks_utils_hal_aidl_test
Change-Id: I91790bb5ccf5ae648687fe603f88ffda2c9fd2b2
Diffstat (limited to 'neuralnetworks/1.0/utils/test/PreparedModelTest.cpp')
-rw-r--r-- | neuralnetworks/1.0/utils/test/PreparedModelTest.cpp | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp index f19ed7756e..7820c06746 100644 --- a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp +++ b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp @@ -19,6 +19,7 @@ #include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h> #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <nnapi/IExecution.h> #include <nnapi/IPreparedModel.h> #include <nnapi/TypeUtils.h> #include <nnapi/Types.h> @@ -224,6 +225,150 @@ TEST(PreparedModelTest, executeFencedNotSupported) { EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE); } +TEST(PreparedModelTest, reusableExecute) { + // setup call + const uint32_t kNumberOfComputations = 2; + const auto mockPreparedModel = createMockPreparedModel(); + const auto preparedModel = PreparedModel::create(mockPreparedModel).value(); + EXPECT_CALL(*mockPreparedModel, execute(_, _)) + .Times(kNumberOfComputations) + .WillRepeatedly(Invoke(makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE))); + + // create execution + const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + ASSERT_TRUE(createResult.has_value()) + << "Failed with " << createResult.error().code << ": " << createResult.error().message; + ASSERT_NE(createResult.value(), nullptr); + + // invoke compute repeatedly + for (uint32_t i = 0; i < kNumberOfComputations; i++) { + const auto computeResult = createResult.value()->compute({}); + EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code + << ": " << computeResult.error().message; + } +} + +TEST(PreparedModelTest, reusableExecuteLaunchError) { + // setup test + const auto mockPreparedModel = createMockPreparedModel(); + const auto preparedModel = PreparedModel::create(mockPreparedModel).value(); + EXPECT_CALL(*mockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(Invoke(makeExecute(V1_0::ErrorStatus::GENERAL_FAILURE, + V1_0::ErrorStatus::GENERAL_FAILURE))); + + // create execution + const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + ASSERT_TRUE(createResult.has_value()) + << "Failed with " << createResult.error().code << ": " << createResult.error().message; + ASSERT_NE(createResult.value(), nullptr); + + // invoke compute + const auto computeResult = createResult.value()->compute({}); + ASSERT_FALSE(computeResult.has_value()); + EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST(PreparedModelTest, reusableExecuteReturnError) { + // setup test + const auto mockPreparedModel = createMockPreparedModel(); + const auto preparedModel = PreparedModel::create(mockPreparedModel).value(); + EXPECT_CALL(*mockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(Invoke( + makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE))); + + // create execution + const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + ASSERT_TRUE(createResult.has_value()) + << "Failed with " << createResult.error().code << ": " << createResult.error().message; + ASSERT_NE(createResult.value(), nullptr); + + // invoke compute + const auto computeResult = createResult.value()->compute({}); + ASSERT_FALSE(computeResult.has_value()); + EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST(PreparedModelTest, reusableExecuteTransportFailure) { + // setup test + const auto mockPreparedModel = createMockPreparedModel(); + const auto preparedModel = PreparedModel::create(mockPreparedModel).value(); + EXPECT_CALL(*mockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure)); + + // create execution + const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + ASSERT_TRUE(createResult.has_value()) + << "Failed with " << createResult.error().code << ": " << createResult.error().message; + ASSERT_NE(createResult.value(), nullptr); + + // invoke compute + const auto computeResult = createResult.value()->compute({}); + ASSERT_FALSE(computeResult.has_value()); + EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + +TEST(PreparedModelTest, reusableExecuteDeadObject) { + // setup test + const auto mockPreparedModel = createMockPreparedModel(); + const auto preparedModel = PreparedModel::create(mockPreparedModel).value(); + EXPECT_CALL(*mockPreparedModel, execute(_, _)) + .Times(1) + .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure)); + + // create execution + const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + ASSERT_TRUE(createResult.has_value()) + << "Failed with " << createResult.error().code << ": " << createResult.error().message; + ASSERT_NE(createResult.value(), nullptr); + + // invoke compute + const auto computeResult = createResult.value()->compute({}); + ASSERT_FALSE(computeResult.has_value()); + EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT); +} + +TEST(PreparedModelTest, reusableExecuteCrash) { + // setup test + const auto mockPreparedModel = createMockPreparedModel(); + const auto preparedModel = PreparedModel::create(mockPreparedModel).value(); + const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> { + mockPreparedModel->simulateCrash(); + return V1_0::ErrorStatus::NONE; + }; + EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(1).WillOnce(InvokeWithoutArgs(ret)); + + // create execution + const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + ASSERT_TRUE(createResult.has_value()) + << "Failed with " << createResult.error().code << ": " << createResult.error().message; + ASSERT_NE(createResult.value(), nullptr); + + // invoke compute + const auto computeResult = createResult.value()->compute({}); + ASSERT_FALSE(computeResult.has_value()); + EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT); +} + +TEST(PreparedModelTest, reusableExecuteFencedNotSupported) { + // setup test + const auto mockPreparedModel = createMockPreparedModel(); + const auto preparedModel = PreparedModel::create(mockPreparedModel).value(); + + // create execution + const auto createResult = preparedModel->createReusableExecution({}, {}, {}); + ASSERT_TRUE(createResult.has_value()) + << "Failed with " << createResult.error().code << ": " << createResult.error().message; + ASSERT_NE(createResult.value(), nullptr); + + // invoke compute + const auto computeResult = createResult.value()->computeFenced({}, {}, {}); + ASSERT_FALSE(computeResult.has_value()); + EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE); +} + TEST(PreparedModelTest, configureExecutionBurst) { // setup test const auto mockPreparedModel = MockPreparedModel::create(); |