Merge changes from topic "nn_sync_fence"

* changes:
  Add VTS tests for NNAPI IPreparedModel::executeFenced
  Support sync fence in NNAPI
This commit is contained in:
Miao Wang
2020-01-23 08:01:42 +00:00
committed by Android (Google) Code Review
8 changed files with 199 additions and 5 deletions

View File

@@ -655,7 +655,8 @@ a3eddd9bbdc87e8c22764070037dd1154f1cf006e6fba93364c4f85d4c134a19 android.hardwar
65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer
d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardware.neuralnetworks@1.3::IDevice
4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback
7d23020248194abbee8091cc624f39a5a6d7ccba338b172d5d2d3df0cceffbee android.hardware.neuralnetworks@1.3::IPreparedModel
29e26e83399b69c7998b787bd30426dd5baa2da350effca76bbee1ba877355c9 android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
384fd9fd6e4d43ea11d407e52ea81da5242c3c5f4b458b8707d8feb652a13e36 android.hardware.neuralnetworks@1.3::IPreparedModel
0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback
5f1a4e0c29fc686ed476f9f04eed35e4405d21288cb2746b978d6891de5cc37d android.hardware.neuralnetworks@1.3::types
3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi

View File

@@ -11,6 +11,7 @@ hidl_interface {
"IBuffer.hal",
"IDevice.hal",
"IExecutionCallback.hal",
"IFencedExecutionCallback.hal",
"IPreparedModel.hal",
"IPreparedModelCallback.hal",
],

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.neuralnetworks@1.3;
import @1.2::Timing;
import ErrorStatus;
/**
* IFencedExecutionCallback can be used to query the error status result
* and duration information from an IPreparedModel::executeFenced call.
*/
interface IFencedExecutionCallback {
/**
* The getExecutionInfo method is used by the clients to query error status
* result and duration information. The method must only be called after the actual
* evaluation has finished or resulted in an runtime error, as indicated by the status
* of the sync fence returned by the IPreparedModel::executeFenced call, otherwise
* GENERAL_FAILURE must be returned.
*
* @return status Error status returned from the asynchronously dispatched execution
* must be:
* - NONE if the asynchronous execution was successful
* - DEVICE_UNAVAILABLE if driver is offline or busy
* - GENERAL_FAILURE if the asynchronous task resulted in an
* unspecified error
* @return timing Duration of execution. Unless MeasureTiming::YES was passed when
* launching the execution and status is NONE, all times must
* be reported as UINT64_MAX. A driver may choose to report
* any time as UINT64_MAX, indicating that particular measurement is
* not available.
*/
getExecutionInfo() generates (ErrorStatus status, Timing timing);
};

View File

@@ -24,6 +24,7 @@ import ErrorStatus;
import OptionalTimePoint;
import Request;
import IExecutionCallback;
import IFencedExecutionCallback;
/**
* IPreparedModel describes a model that has been prepared for execution and
@@ -91,7 +92,8 @@ interface IPreparedModel extends @1.2::IPreparedModel {
* execution cannot be finished by the deadline, the
* execution must be aborted.
* @param callback A callback object used to return the error status of
* the execution. The callback object's notify function must
* the execution, shape information of model output operands, and
* duration of execution. The callback object's notify function must
* be called exactly once, even if the execution was
* unsuccessful.
* @return status Error status of the call, must be:
@@ -187,4 +189,57 @@ interface IPreparedModel extends @1.2::IPreparedModel {
OptionalTimePoint deadline)
generates (ErrorStatus status, vec<OutputShape> outputShapes,
Timing timing);
/**
* Launch a fenced asynchronous execution on a prepared model.
*
* The execution is performed asynchronously with respect to the caller.
* executeFenced must fully validate the request, and only accept one that is
* guaranteed to be completed, unless a hardware failure or kernel panic happens on the device.
* If there is an error during validation, executeFenced must immediately return with
* the corresponding ErrorStatus. If the request is valid and there is no error launching,
* executeFenced must dispatch an asynchronous task to perform the execution in the
* background, and immediately return with ErrorStatus::NONE, a sync_fence that will be
* signaled once the execution is completed, and a callback that can be used by the client
* to query the duration and runtime error status. If the task has finished
* before the call returns, empty handle may be returned for the sync fence. If the
* asynchronous task fails to launch, executeFenced must immediately return with
* ErrorStatus::GENERAL_FAILURE, and empty handle for the sync fence and nullptr
* for callback. The execution must wait for all the sync fences (if any) in wait_for to be
* signaled before starting the actual execution.
*
* If any of sync fences in wait_for changes to error status after the executeFenced
* call succeeds, the driver must immediately set the returned sync fence to error status.
*
* When the asynchronous task has finished its execution, it must
* immediately signal the sync_fence created when dispatching. After
* the sync_fence is signaled, the task must not modify the content of
* any data object referenced by 'request' (described by the
* {@link @1.0::DataLocation} of a {@link @1.0::RequestArgument}).
*
* Any number of calls to the executeFenced, execute* 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.
* @param waitFor A vector of sync fence file descriptors.
* Execution must not start until all sync fences have been signaled.
* @param measure Specifies whether or not to measure duration of the execution.
* The duration runs from the time the driver sees the call
* to the executeFenced function to the time sync_fence is triggered.
* @return status Error status of the call, must be:
* - NONE if 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, including
* fences in error states.
* @return syncFence The sync fence that will be triggered when the task is completed.
* The sync fence will be set to error if a critical error,
* e.g. hardware failure or kernel panic, occurs when doing execution.
* @return callback The IFencedExecutionCallback can be used to query information like duration
* and error status when the execution is completed.
*/
executeFenced(Request request, vec<handle> waitFor, MeasureTiming measure)
generates (ErrorStatus status, handle syncFence, IFencedExecutionCallback callback);
};

View File

@@ -65,6 +65,7 @@ cc_test {
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
"libneuralnetworks_utils",
"libsync",
],
whole_static_libs: [
"neuralnetworks_generated_V1_0_example",

View File

@@ -29,11 +29,13 @@
#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
#include <android/hardware/neuralnetworks/1.2/types.h>
#include <android/hardware/neuralnetworks/1.3/IDevice.h>
#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
#include <android/hardware/neuralnetworks/1.3/types.h>
#include <android/hidl/allocator/1.0/IAllocator.h>
#include <android/hidl/memory/1.0/IMemory.h>
#include <android/sync.h>
#include <gtest/gtest.h>
#include <hidlmemory/mapping.h>
@@ -70,7 +72,7 @@ using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_
namespace {
enum class Executor { ASYNC, SYNC, BURST };
enum class Executor { ASYNC, SYNC, BURST, FENCED };
enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT };
@@ -562,6 +564,43 @@ void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>&
break;
}
case Executor::FENCED: {
SCOPED_TRACE("fenced");
ErrorStatus result;
hidl_handle sync_fence_handle;
sp<IFencedExecutionCallback> fenced_callback;
Return<void> ret = preparedModel->executeFenced(
request, {}, testConfig.measureTiming,
[&result, &sync_fence_handle, &fenced_callback](
ErrorStatus error, const hidl_handle& handle,
const sp<IFencedExecutionCallback>& callback) {
result = error;
sync_fence_handle = handle;
fenced_callback = callback;
});
ASSERT_TRUE(ret.isOk());
if (result != ErrorStatus::NONE) {
ASSERT_EQ(sync_fence_handle.getNativeHandle(), nullptr);
ASSERT_EQ(fenced_callback, nullptr);
executionStatus = ErrorStatus::GENERAL_FAILURE;
} else if (sync_fence_handle.getNativeHandle()) {
constexpr int kInfiniteTimeout = -1;
int sync_fd = sync_fence_handle.getNativeHandle()->data[0];
ASSERT_GT(sync_fd, 0);
int r = sync_wait(sync_fd, kInfiniteTimeout);
ASSERT_GE(r, 0);
}
if (result == ErrorStatus::NONE) {
ASSERT_NE(fenced_callback, nullptr);
Return<void> ret = fenced_callback->getExecutionInfo(
[&executionStatus, &timing](ErrorStatus error, Timing t) {
executionStatus = error;
timing = t;
});
ASSERT_TRUE(ret.isOk());
}
break;
}
}
if (testConfig.outputType != OutputType::FULLY_SPECIFIED &&
@@ -635,7 +674,7 @@ void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>&
case TestKind::GENERAL: {
outputTypesList = {OutputType::FULLY_SPECIFIED};
measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST};
executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST, Executor::FENCED};
} break;
case TestKind::DYNAMIC_SHAPE: {
outputTypesList = {OutputType::UNSPECIFIED, OutputType::INSUFFICIENT};
@@ -671,7 +710,8 @@ void EvaluatePreparedCoupledModels(const sp<IDevice>& device,
const TestModel& coupledModel) {
const std::vector<OutputType> outputTypesList = {OutputType::FULLY_SPECIFIED};
const std::vector<MeasureTiming> measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
const std::vector<Executor> executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST};
const std::vector<Executor> executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST,
Executor::FENCED};
for (const OutputType outputType : outputTypesList) {
for (const MeasureTiming measureTiming : measureTimingList) {

View File

@@ -16,7 +16,9 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
#include <chrono>
#include "1.0/Utils.h"
#include "1.3/Callbacks.h"
#include "ExecutionBurstController.h"
@@ -136,6 +138,22 @@ static void validate(const sp<IPreparedModel>& preparedModel, const std::string&
burst->freeMemory(keys.front());
}
}
// dispatch
{
SCOPED_TRACE(message + " [executeFenced]");
Return<void> ret = preparedModel->executeFenced(
request, {}, MeasureTiming::NO,
[](ErrorStatus error, const hidl_handle& handle,
const sp<IFencedExecutionCallback>& callback) {
if (error != ErrorStatus::DEVICE_UNAVAILABLE) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
}
ASSERT_EQ(handle.getNativeHandle(), nullptr);
ASSERT_EQ(callback, nullptr);
});
ASSERT_TRUE(ret.isOk());
}
}
///////////////////////// REMOVE INPUT ////////////////////////////////////

View File

@@ -133,6 +133,35 @@ void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Reque
// Forward declaration from ValidateBurst.cpp
void validateBurst(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
// Validate sync_fence handles for dispatch with valid input
void validateExecuteFenced(const sp<IPreparedModel>& preparedModel, const Request& request) {
SCOPED_TRACE("Expecting request to fail [executeFenced]");
Return<void> ret_null =
preparedModel->executeFenced(request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO,
[](ErrorStatus error, const hidl_handle& handle,
const sp<IFencedExecutionCallback>& callback) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
ASSERT_EQ(handle.getNativeHandle(), nullptr);
ASSERT_EQ(callback, nullptr);
});
ASSERT_TRUE(ret_null.isOk());
native_handle_t* nativeHandle = native_handle_create(1, 0);
ASSERT_NE(nullptr, nativeHandle);
nativeHandle->data[0] = -1;
hidl_handle hidlHandle;
hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true);
Return<void> ret_invalid =
preparedModel->executeFenced(request, {hidlHandle}, V1_2::MeasureTiming::NO,
[](ErrorStatus error, const hidl_handle& handle,
const sp<IFencedExecutionCallback>& callback) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
ASSERT_EQ(handle.getNativeHandle(), nullptr);
ASSERT_EQ(callback, nullptr);
});
ASSERT_TRUE(ret_invalid.isOk());
}
void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request,
std::pair<bool, bool> supportsDeadlines) {
const auto [prepareModelDeadlineSupported, executionDeadlineSupported] = supportsDeadlines;
@@ -144,6 +173,7 @@ void validateEverything(const sp<IDevice>& device, const Model& model, const Req
if (preparedModel == nullptr) return;
validateRequest(preparedModel, request, executionDeadlineSupported);
validateExecuteFenced(preparedModel, request);
// TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
ASSERT_TRUE(nn::compliantWithV1_0(request));