blob: 25659728c381db29b66c3ec8de13025a9cc32482 [file] [log] [blame]
Michael Butlera685c3d2020-02-22 22:37:59 -08001/*
2 * Copyright (C) 2020 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#include "CommonUtils.h"
18
Michael Butler3670c382020-08-06 23:22:35 -070019#include "HandleError.h"
20
Michael Butlera685c3d2020-02-22 22:37:59 -080021#include <android-base/logging.h>
22#include <nnapi/Result.h>
23#include <nnapi/SharedMemory.h>
24#include <nnapi/TypeUtils.h>
25#include <nnapi/Types.h>
26#include <nnapi/Validation.h>
27
28#include <algorithm>
29#include <any>
Michael Butler3670c382020-08-06 23:22:35 -070030#include <functional>
Michael Butlera685c3d2020-02-22 22:37:59 -080031#include <optional>
32#include <variant>
33#include <vector>
34
35namespace android::hardware::neuralnetworks::utils {
36namespace {
37
38bool hasNoPointerData(const nn::Operand& operand);
39bool hasNoPointerData(const nn::Model::Subgraph& subgraph);
40bool hasNoPointerData(const nn::Request::Argument& argument);
41
42template <typename Type>
43bool hasNoPointerData(const std::vector<Type>& objects) {
44 return std::all_of(objects.begin(), objects.end(),
45 [](const auto& object) { return hasNoPointerData(object); });
46}
47
48bool hasNoPointerData(const nn::DataLocation& location) {
49 return std::visit([](auto ptr) { return ptr == nullptr; }, location.pointer);
50}
51
52bool hasNoPointerData(const nn::Operand& operand) {
53 return hasNoPointerData(operand.location);
54}
55
56bool hasNoPointerData(const nn::Model::Subgraph& subgraph) {
57 return hasNoPointerData(subgraph.operands);
58}
59
60bool hasNoPointerData(const nn::Request::Argument& argument) {
61 return hasNoPointerData(argument.location);
62}
63
64void copyPointersToSharedMemory(nn::Operand* operand, nn::ConstantMemoryBuilder* memoryBuilder) {
65 CHECK(operand != nullptr);
66 CHECK(memoryBuilder != nullptr);
67
68 if (operand->lifetime != nn::Operand::LifeTime::POINTER) {
69 return;
70 }
71
72 const void* data = std::visit([](auto ptr) { return static_cast<const void*>(ptr); },
73 operand->location.pointer);
74 CHECK(data != nullptr);
75 operand->lifetime = nn::Operand::LifeTime::CONSTANT_REFERENCE;
76 operand->location = memoryBuilder->append(data, operand->location.length);
77}
78
79void copyPointersToSharedMemory(nn::Model::Subgraph* subgraph,
80 nn::ConstantMemoryBuilder* memoryBuilder) {
81 CHECK(subgraph != nullptr);
82 std::for_each(subgraph->operands.begin(), subgraph->operands.end(),
83 [memoryBuilder](auto& operand) {
84 copyPointersToSharedMemory(&operand, memoryBuilder);
85 });
86}
87
88} // anonymous namespace
89
90nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWithP(
91 const nn::Capabilities::PerformanceInfo& float32Performance,
92 const nn::Capabilities::PerformanceInfo& quantized8Performance) {
93 // In Android P, most data types are treated as having the same performance as
94 // TENSOR_QUANT8_ASYMM. This collection must be in sorted order.
95 std::vector<nn::Capabilities::OperandPerformance> operandPerformances = {
96 {.type = nn::OperandType::FLOAT32, .info = float32Performance},
97 {.type = nn::OperandType::INT32, .info = quantized8Performance},
98 {.type = nn::OperandType::UINT32, .info = quantized8Performance},
99 {.type = nn::OperandType::TENSOR_FLOAT32, .info = float32Performance},
100 {.type = nn::OperandType::TENSOR_INT32, .info = quantized8Performance},
101 {.type = nn::OperandType::TENSOR_QUANT8_ASYMM, .info = quantized8Performance},
102 {.type = nn::OperandType::OEM, .info = quantized8Performance},
103 {.type = nn::OperandType::TENSOR_OEM_BYTE, .info = quantized8Performance},
104 };
105 return nn::Capabilities::OperandPerformanceTable::create(std::move(operandPerformances))
106 .value();
107}
108
109bool hasNoPointerData(const nn::Model& model) {
110 return hasNoPointerData(model.main) && hasNoPointerData(model.referenced);
111}
112
113bool hasNoPointerData(const nn::Request& request) {
114 return hasNoPointerData(request.inputs) && hasNoPointerData(request.outputs);
115}
116
Michael Butler3670c382020-08-06 23:22:35 -0700117nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
118 const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut) {
119 CHECK(model != nullptr);
120 CHECK(maybeModelInSharedOut != nullptr);
121
122 if (hasNoPointerData(*model)) {
123 return *model;
124 }
125
126 // Make a copy of the model in order to make modifications. The modified model is returned to
127 // the caller through `maybeModelInSharedOut` if the function succeeds.
128 nn::Model modelInShared = *model;
Michael Butlera685c3d2020-02-22 22:37:59 -0800129
130 nn::ConstantMemoryBuilder memoryBuilder(modelInShared.pools.size());
131 copyPointersToSharedMemory(&modelInShared.main, &memoryBuilder);
132 std::for_each(modelInShared.referenced.begin(), modelInShared.referenced.end(),
133 [&memoryBuilder](auto& subgraph) {
134 copyPointersToSharedMemory(&subgraph, &memoryBuilder);
135 });
136
137 if (!memoryBuilder.empty()) {
138 auto memory = NN_TRY(memoryBuilder.finish());
139 modelInShared.pools.push_back(std::move(memory));
140 }
141
Michael Butler3670c382020-08-06 23:22:35 -0700142 *maybeModelInSharedOut = modelInShared;
143 return **maybeModelInSharedOut;
Michael Butlera685c3d2020-02-22 22:37:59 -0800144}
145
Michael Butler3670c382020-08-06 23:22:35 -0700146nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointerToShared(
147 const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut) {
148 CHECK(request != nullptr);
149 CHECK(maybeRequestInSharedOut != nullptr);
150
151 if (hasNoPointerData(*request)) {
152 return *request;
153 }
154
155 // Make a copy of the request in order to make modifications. The modified request is returned
156 // to the caller through `maybeRequestInSharedOut` if the function succeeds.
157 nn::Request requestInShared = *request;
Michael Butlera685c3d2020-02-22 22:37:59 -0800158
159 // Change input pointers to shared memory.
160 nn::ConstantMemoryBuilder inputBuilder(requestInShared.pools.size());
161 for (auto& input : requestInShared.inputs) {
162 const auto& location = input.location;
163 if (input.lifetime != nn::Request::Argument::LifeTime::POINTER) {
164 continue;
165 }
166
167 input.lifetime = nn::Request::Argument::LifeTime::POOL;
168 const void* data = std::visit([](auto ptr) { return static_cast<const void*>(ptr); },
169 location.pointer);
170 CHECK(data != nullptr);
171 input.location = inputBuilder.append(data, location.length);
172 }
173
174 // Allocate input memory.
175 if (!inputBuilder.empty()) {
176 auto memory = NN_TRY(inputBuilder.finish());
177 requestInShared.pools.push_back(std::move(memory));
178 }
179
180 // Change output pointers to shared memory.
181 nn::MutableMemoryBuilder outputBuilder(requestInShared.pools.size());
182 for (auto& output : requestInShared.outputs) {
183 const auto& location = output.location;
184 if (output.lifetime != nn::Request::Argument::LifeTime::POINTER) {
185 continue;
186 }
187
188 output.lifetime = nn::Request::Argument::LifeTime::POOL;
189 output.location = outputBuilder.append(location.length);
190 }
191
192 // Allocate output memory.
193 if (!outputBuilder.empty()) {
194 auto memory = NN_TRY(outputBuilder.finish());
195 requestInShared.pools.push_back(std::move(memory));
196 }
197
Michael Butler3670c382020-08-06 23:22:35 -0700198 *maybeRequestInSharedOut = requestInShared;
199 return **maybeRequestInSharedOut;
Michael Butlera685c3d2020-02-22 22:37:59 -0800200}
201
Michael Butler3670c382020-08-06 23:22:35 -0700202nn::GeneralResult<void> unflushDataFromSharedToPointer(
203 const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared) {
204 if (!maybeRequestInShared.has_value() || maybeRequestInShared->pools.empty() ||
205 !std::holds_alternative<nn::Memory>(maybeRequestInShared->pools.back())) {
Michael Butlera685c3d2020-02-22 22:37:59 -0800206 return {};
207 }
Michael Butler3670c382020-08-06 23:22:35 -0700208 const auto& requestInShared = *maybeRequestInShared;
Michael Butlera685c3d2020-02-22 22:37:59 -0800209
210 // Map the memory.
211 const auto& outputMemory = std::get<nn::Memory>(requestInShared.pools.back());
212 const auto [pointer, size, context] = NN_TRY(map(outputMemory));
213 const uint8_t* constantPointer =
214 std::visit([](const auto& o) { return static_cast<const uint8_t*>(o); }, pointer);
215
216 // Flush each output pointer.
217 CHECK_EQ(request.outputs.size(), requestInShared.outputs.size());
218 for (size_t i = 0; i < request.outputs.size(); ++i) {
219 const auto& location = request.outputs[i].location;
220 const auto& locationInShared = requestInShared.outputs[i].location;
221 if (!std::holds_alternative<void*>(location.pointer)) {
222 continue;
223 }
224
225 // Get output pointer and size.
226 void* data = std::get<void*>(location.pointer);
227 CHECK(data != nullptr);
228 const size_t length = location.length;
229
230 // Get output pool location.
231 CHECK(requestInShared.outputs[i].lifetime == nn::Request::Argument::LifeTime::POOL);
232 const size_t index = locationInShared.poolIndex;
233 const size_t offset = locationInShared.offset;
234 const size_t outputPoolIndex = requestInShared.pools.size() - 1;
235 CHECK(locationInShared.length == length);
236 CHECK(index == outputPoolIndex);
237
238 // Flush memory.
239 std::memcpy(data, constantPointer + offset, length);
240 }
241
242 return {};
243}
244
245std::vector<uint32_t> countNumberOfConsumers(size_t numberOfOperands,
246 const std::vector<nn::Operation>& operations) {
247 return nn::countNumberOfConsumers(numberOfOperands, operations);
248}
249
250} // namespace android::hardware::neuralnetworks::utils