blob: 52cabd8b96983b93e26f16bc2ab8e1bbc3069f99 [file] [log] [blame]
Michael Butlerf6b2d1a2020-12-19 14:44:35 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "ExecutionBurstServer"
18
19#include "ExecutionBurstServer.h"
Michael Butler76e491f2020-12-19 01:55:32 -080020#include "Conversions.h"
21#include "ExecutionBurstUtils.h"
Michael Butlerf6b2d1a2020-12-19 14:44:35 -080022
23#include <android-base/logging.h>
Michael Butler76e491f2020-12-19 01:55:32 -080024#include <nnapi/IBurst.h>
25#include <nnapi/Result.h>
26#include <nnapi/TypeUtils.h>
27#include <nnapi/Types.h>
28#include <nnapi/Validation.h>
29#include <nnapi/hal/1.0/Conversions.h>
Michael Butlere8645c32021-10-15 18:42:32 -070030#include <nnapi/hal/1.0/ProtectCallback.h>
Michael Butler76e491f2020-12-19 01:55:32 -080031#include <nnapi/hal/HandleError.h>
Michael Butler76e491f2020-12-19 01:55:32 -080032#include <nnapi/hal/TransferValue.h>
Michael Butlerf6b2d1a2020-12-19 14:44:35 -080033
34#include <algorithm>
35#include <cstring>
36#include <limits>
37#include <map>
38#include <memory>
39#include <tuple>
40#include <utility>
41#include <vector>
42
Michael Butlerf6b2d1a2020-12-19 14:44:35 -080043#include "Tracing.h"
44
Michael Butler76e491f2020-12-19 01:55:32 -080045namespace android::hardware::neuralnetworks::V1_2::utils {
Michael Butlerf6b2d1a2020-12-19 14:44:35 -080046namespace {
47
Michael Butler76e491f2020-12-19 01:55:32 -080048constexpr V1_2::Timing kNoTiming = {std::numeric_limits<uint64_t>::max(),
49 std::numeric_limits<uint64_t>::max()};
50
51nn::GeneralResult<std::vector<nn::SharedMemory>> getMemoriesCallback(
52 V1_0::ErrorStatus status, const hidl_vec<hidl_memory>& memories) {
53 HANDLE_HAL_STATUS(status) << "getting burst memories failed with " << toString(status);
54 std::vector<nn::SharedMemory> canonicalMemories;
55 canonicalMemories.reserve(memories.size());
56 for (const auto& memory : memories) {
57 canonicalMemories.push_back(NN_TRY(nn::convert(memory)));
Michael Butlerf6b2d1a2020-12-19 14:44:35 -080058 }
Michael Butler76e491f2020-12-19 01:55:32 -080059 return canonicalMemories;
60}
Michael Butlerf6b2d1a2020-12-19 14:44:35 -080061
62} // anonymous namespace
63
Michael Butler76e491f2020-12-19 01:55:32 -080064ExecutionBurstServer::MemoryCache::MemoryCache(nn::SharedBurst burstExecutor,
65 sp<IBurstCallback> burstCallback)
66 : kBurstExecutor(std::move(burstExecutor)), kBurstCallback(std::move(burstCallback)) {
67 CHECK(kBurstExecutor != nullptr);
68 CHECK(kBurstCallback != nullptr);
69}
70
71nn::GeneralResult<std::vector<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>>
72ExecutionBurstServer::MemoryCache::getCacheEntries(const std::vector<int32_t>& slots) {
73 std::lock_guard guard(mMutex);
74 NN_TRY(ensureCacheEntriesArePresentLocked(slots));
75
76 std::vector<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>> results;
77 results.reserve(slots.size());
78 for (int32_t slot : slots) {
79 results.push_back(NN_TRY(getCacheEntryLocked(slot)));
80 }
81
82 return results;
83}
84
85nn::GeneralResult<void> ExecutionBurstServer::MemoryCache::ensureCacheEntriesArePresentLocked(
86 const std::vector<int32_t>& slots) {
87 const auto slotIsKnown = [this](int32_t slot)
88 REQUIRES(mMutex) { return mCache.count(slot) > 0; };
89
90 // find unique unknown slots
91 std::vector<int32_t> unknownSlots = slots;
92 std::sort(unknownSlots.begin(), unknownSlots.end());
93 auto unknownSlotsEnd = std::unique(unknownSlots.begin(), unknownSlots.end());
94 unknownSlotsEnd = std::remove_if(unknownSlots.begin(), unknownSlotsEnd, slotIsKnown);
95 unknownSlots.erase(unknownSlotsEnd, unknownSlots.end());
96
97 // quick-exit if all slots are known
98 if (unknownSlots.empty()) {
99 return {};
100 }
101
102 auto cb = neuralnetworks::utils::CallbackValue(getMemoriesCallback);
103
104 const auto ret = kBurstCallback->getMemories(unknownSlots, cb);
105 HANDLE_TRANSPORT_FAILURE(ret);
106
107 auto returnedMemories = NN_TRY(cb.take());
108
109 if (returnedMemories.size() != unknownSlots.size()) {
110 return NN_ERROR()
111 << "ExecutionBurstServer::MemoryCache::ensureCacheEntriesArePresentLocked: Error "
112 "retrieving memories -- count mismatch between requested memories ("
113 << unknownSlots.size() << ") and returned memories (" << returnedMemories.size()
114 << ")";
115 }
116
117 // add memories to unknown slots
118 for (size_t i = 0; i < unknownSlots.size(); ++i) {
119 addCacheEntryLocked(unknownSlots[i], std::move(returnedMemories[i]));
120 }
121
122 return {};
123}
124
125nn::GeneralResult<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>
126ExecutionBurstServer::MemoryCache::getCacheEntryLocked(int32_t slot) {
127 if (const auto iter = mCache.find(slot); iter != mCache.end()) {
128 return iter->second;
129 }
130 return NN_ERROR()
131 << "ExecutionBurstServer::MemoryCache::getCacheEntryLocked failed because slot " << slot
132 << " is not present in the cache";
133}
134
135void ExecutionBurstServer::MemoryCache::addCacheEntryLocked(int32_t slot, nn::SharedMemory memory) {
136 auto hold = kBurstExecutor->cacheMemory(memory);
137 mCache.emplace(slot, std::make_pair(std::move(memory), std::move(hold)));
138}
139
140void ExecutionBurstServer::MemoryCache::removeCacheEntry(int32_t slot) {
141 std::lock_guard guard(mMutex);
142 mCache.erase(slot);
143}
144
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800145// ExecutionBurstServer methods
146
Michael Butler76e491f2020-12-19 01:55:32 -0800147nn::GeneralResult<sp<ExecutionBurstServer>> ExecutionBurstServer::create(
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800148 const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
Michael Butler76e491f2020-12-19 01:55:32 -0800149 const MQDescriptorSync<FmqResultDatum>& resultChannel, nn::SharedBurst burstExecutor,
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800150 std::chrono::microseconds pollingTimeWindow) {
151 // check inputs
Michael Butler76e491f2020-12-19 01:55:32 -0800152 if (callback == nullptr || burstExecutor == nullptr) {
153 return NN_ERROR() << "ExecutionBurstServer::create passed a nullptr";
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800154 }
155
156 // create FMQ objects
Michael Butler76e491f2020-12-19 01:55:32 -0800157 auto requestChannelReceiver =
158 NN_TRY(RequestChannelReceiver::create(requestChannel, pollingTimeWindow));
159 auto resultChannelSender = NN_TRY(ResultChannelSender::create(resultChannel));
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800160
161 // check FMQ objects
Michael Butler76e491f2020-12-19 01:55:32 -0800162 CHECK(requestChannelReceiver != nullptr);
163 CHECK(resultChannelSender != nullptr);
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800164
165 // make and return context
Michael Butler76e491f2020-12-19 01:55:32 -0800166 return sp<ExecutionBurstServer>::make(PrivateConstructorTag{}, callback,
167 std::move(requestChannelReceiver),
168 std::move(resultChannelSender), std::move(burstExecutor));
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800169}
170
Michael Butler76e491f2020-12-19 01:55:32 -0800171ExecutionBurstServer::ExecutionBurstServer(PrivateConstructorTag /*tag*/,
172 const sp<IBurstCallback>& callback,
173 std::unique_ptr<RequestChannelReceiver> requestChannel,
174 std::unique_ptr<ResultChannelSender> resultChannel,
175 nn::SharedBurst burstExecutor)
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800176 : mCallback(callback),
177 mRequestChannelReceiver(std::move(requestChannel)),
178 mResultChannelSender(std::move(resultChannel)),
Michael Butler76e491f2020-12-19 01:55:32 -0800179 mBurstExecutor(std::move(burstExecutor)),
180 mMemoryCache(mBurstExecutor, mCallback) {
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800181 // TODO: highly document the threading behavior of this class
182 mWorker = std::thread([this] { task(); });
183}
184
185ExecutionBurstServer::~ExecutionBurstServer() {
186 // set teardown flag
187 mTeardown = true;
188 mRequestChannelReceiver->invalidate();
189
190 // wait for task thread to end
191 mWorker.join();
192}
193
Michael Butler76e491f2020-12-19 01:55:32 -0800194Return<void> ExecutionBurstServer::freeMemory(int32_t slot) {
195 mMemoryCache.removeCacheEntry(slot);
196 return Void();
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800197}
198
199void ExecutionBurstServer::task() {
200 // loop until the burst object is being destroyed
201 while (!mTeardown) {
202 // receive request
203 auto arguments = mRequestChannelReceiver->getBlocking();
204
Michael Butler76e491f2020-12-19 01:55:32 -0800205 // if the request packet was not properly received, return a generic error and skip the
206 // execution
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800207 //
Michael Butler76e491f2020-12-19 01:55:32 -0800208 // if the burst is being torn down, skip the execution so the "task" function can end
209 if (!arguments.has_value()) {
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800210 if (!mTeardown) {
211 mResultChannelSender->send(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
212 }
213 continue;
214 }
215
Michael Butler76e491f2020-12-19 01:55:32 -0800216 // unpack the arguments; types are Request, std::vector<int32_t>, and MeasureTiming,
217 // respectively
218 const auto [requestWithoutPools, slotsOfPools, measure] = std::move(arguments).value();
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800219
Michael Butler76e491f2020-12-19 01:55:32 -0800220 auto result = execute(requestWithoutPools, slotsOfPools, measure);
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800221
222 // return result
Michael Butler76e491f2020-12-19 01:55:32 -0800223 if (result.has_value()) {
224 const auto& [outputShapes, timing] = result.value();
225 mResultChannelSender->send(V1_0::ErrorStatus::NONE, outputShapes, timing);
226 } else {
227 const auto& [message, code, outputShapes] = result.error();
228 LOG(ERROR) << "IBurst::execute failed with " << code << ": " << message;
229 mResultChannelSender->send(convert(code).value(), convert(outputShapes).value(),
230 kNoTiming);
231 }
Michael Butlerf6b2d1a2020-12-19 14:44:35 -0800232 }
233}
234
Michael Butler76e491f2020-12-19 01:55:32 -0800235nn::ExecutionResult<std::pair<hidl_vec<OutputShape>, Timing>> ExecutionBurstServer::execute(
236 const V1_0::Request& requestWithoutPools, const std::vector<int32_t>& slotsOfPools,
237 MeasureTiming measure) {
238 NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
239 "ExecutionBurstServer getting memory, executing, and returning results");
240
241 // ensure executor with cache has required memory
Michael Butlerff9a5a52021-10-15 16:23:20 -0700242 const auto cacheEntries = NN_TRY(mMemoryCache.getCacheEntries(slotsOfPools));
Michael Butler76e491f2020-12-19 01:55:32 -0800243
244 // convert request, populating its pools
245 // This code performs an unvalidated convert because the request object without its pools is
246 // invalid because it is incomplete. Instead, the validation is performed after the memory pools
247 // have been added to the request.
Michael Butlerff9a5a52021-10-15 16:23:20 -0700248 auto canonicalRequest = NN_TRY(nn::unvalidatedConvert(requestWithoutPools));
Michael Butler76e491f2020-12-19 01:55:32 -0800249 CHECK(canonicalRequest.pools.empty());
250 std::transform(cacheEntries.begin(), cacheEntries.end(),
251 std::back_inserter(canonicalRequest.pools),
252 [](const auto& cacheEntry) { return cacheEntry.first; });
Michael Butlerff9a5a52021-10-15 16:23:20 -0700253 NN_TRY(validate(canonicalRequest));
Michael Butler76e491f2020-12-19 01:55:32 -0800254
Michael Butlerff9a5a52021-10-15 16:23:20 -0700255 nn::MeasureTiming canonicalMeasure = NN_TRY(nn::convert(measure));
Michael Butler76e491f2020-12-19 01:55:32 -0800256
257 const auto [outputShapes, timing] =
Michael Butler8414a6e2021-03-10 18:41:05 -0800258 NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure, {}, {}));
Michael Butler76e491f2020-12-19 01:55:32 -0800259
Michael Butlerff9a5a52021-10-15 16:23:20 -0700260 return std::make_pair(NN_TRY(convert(outputShapes)), NN_TRY(convert(timing)));
Michael Butler76e491f2020-12-19 01:55:32 -0800261}
262
263} // namespace android::hardware::neuralnetworks::V1_2::utils