1 /*
2 * Copyright (C) 2017 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 "ModelBuilder"
18
19 #include "ModelBuilder.h"
20
21 #include "CompilationBuilder.h"
22 #include "Utils.h"
23
24 #include <map>
25 #include <utility>
26
27 namespace android {
28 namespace nn {
29
30 // The maximum number of operands and operations that a model may have.
31 const uint32_t MAX_NUMBER_OF_OPERANDS = 0xFFFFFFFE;
32 const uint32_t MAX_NUMBER_OF_OPERATIONS = 0xFFFFFFFE;
33
addOperand(const ANeuralNetworksOperandType & type)34 int ModelBuilder::addOperand(const ANeuralNetworksOperandType& type) {
35 if (mCompletedModel) {
36 LOG(ERROR) << "ANeuralNetworksModel_addOperand can't modify after model finished";
37 return ANEURALNETWORKS_BAD_DATA;
38 }
39 int n = validateOperandType(type, "ANeuralNetworksModel_addOperand", true);
40 if (n != ANEURALNETWORKS_NO_ERROR) {
41 return n;
42 }
43 size_t idx = mOperands.size();
44 if (idx >= MAX_NUMBER_OF_OPERANDS) {
45 LOG(ERROR) << "ANeuralNetworksModel_addOperand exceed max operands";
46 return ANEURALNETWORKS_BAD_DATA;
47 }
48 mOperands.resize(idx + 1);
49 auto& operand = mOperands[idx];
50 operand.type = static_cast<OperandType>(type.type);
51 setFromIntList(&operand.dimensions, type.dimensionCount, type.dimensions);
52 operand.numberOfConsumers = 0;
53 operand.scale = type.scale;
54 operand.zeroPoint = type.zeroPoint;
55 operand.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
56 operand.location = {.poolIndex = 0, .offset = 0, .length = 0};
57 return ANEURALNETWORKS_NO_ERROR;
58 }
59
setOperandValue(uint32_t index,const void * buffer,size_t length)60 int ModelBuilder::setOperandValue(uint32_t index, const void* buffer, size_t length) {
61 VLOG(MODEL) << __func__ << " for operand " << index << " size " << length;
62 if (index >= operandCount()) {
63 LOG(ERROR) << "ANeuralNetworksModel_setOperandValue setting operand " << index << " of "
64 << operandCount();
65 return ANEURALNETWORKS_BAD_DATA;
66 }
67 Operand& operand = mOperands[index];
68 if (buffer == nullptr) {
69 if (length) {
70 LOG(ERROR) << "ANeuralNetworksModel_setOperandValue buffer is nullptr but length is "
71 "not 0";
72 return ANEURALNETWORKS_BAD_DATA;
73 }
74 operand.lifetime = OperandLifeTime::NO_VALUE;
75 // The location is unused and is set to zeros.
76 operand.location = {.poolIndex = 0,
77 .offset = 0,
78 .length = 0};
79 } else {
80 if (length > 0xFFFFFFFF) {
81 LOG(ERROR) << "ANeuralNetworksModel_setOperandValue value length of " << length
82 << " exceeds max size";
83 return ANEURALNETWORKS_BAD_DATA;
84 }
85 uint32_t valueLength = static_cast<uint32_t>(length);
86 uint32_t neededLength = sizeOfData(operand.type, operand.dimensions);
87 if (neededLength != valueLength) {
88 LOG(ERROR) << "ANeuralNetworksModel_setOperandValue setting " << valueLength
89 << " bytes when needing " << neededLength;
90 return ANEURALNETWORKS_BAD_DATA;
91 }
92 if (valueLength <= ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES) {
93 uint32_t existingSize = static_cast<uint32_t>(mSmallOperandValues.size());
94 uint32_t extraBytes = alignBytesNeeded(existingSize, valueLength);
95 mSmallOperandValues.resize(existingSize + extraBytes + valueLength);
96 operand.lifetime = OperandLifeTime::CONSTANT_COPY;
97 operand.location = {
98 .poolIndex = 0, .offset = existingSize + extraBytes, .length = neededLength};
99 memcpy(&mSmallOperandValues[operand.location.offset], buffer, valueLength);
100 VLOG(MODEL) << "Copied small value to offset " << operand.location.offset;
101 } else {
102 VLOG(MODEL) << "Saving large value";
103 operand.lifetime = OperandLifeTime::CONSTANT_REFERENCE;
104 // The values for poolIndex and offset will be set when the model is finished.
105 operand.location = {.poolIndex = 0, .offset = 0, .length = valueLength};
106 // We keep track of the buffers. We'll allocate the shared memory only
107 // once we know the total size, to avoid needless copies.
108 mLargeOperandValues.push_back(LargeValue{.operandIndex = index, .buffer = buffer});
109 }
110 }
111 return ANEURALNETWORKS_NO_ERROR;
112 }
113
copyLargeValuesToSharedMemory()114 int ModelBuilder::copyLargeValuesToSharedMemory() {
115 VLOG(MODEL) << __func__ << " has " << mLargeOperandValues.size() << " values.";
116 if (!mLargeOperandValues.empty()) {
117 // Calculate the size of the shared memory needed for all the large values.
118 // Also sets the offset for each value within the memory.
119 size_t poolSize = 0;
120 for (LargeValue& l: mLargeOperandValues) {
121 Operand& operand = mOperands[l.operandIndex];
122 nnAssert(operand.lifetime == OperandLifeTime::CONSTANT_REFERENCE);
123 poolSize += alignBytesNeeded(poolSize, operand.location.length);
124 operand.location.offset = poolSize;
125 poolSize += operand.location.length;
126 }
127
128 // Allocated the shared memory.
129 int n = mLargeValueMemory.create(poolSize);
130 if (n != ANEURALNETWORKS_NO_ERROR) {
131 return n;
132 }
133 uint8_t* memoryPointer = nullptr;
134 n = mLargeValueMemory.getPointer(&memoryPointer);
135 if (n != ANEURALNETWORKS_NO_ERROR) {
136 return n;
137 }
138 uint32_t poolIndex = mMemories.add(&mLargeValueMemory);
139 VLOG(MODEL) << "Allocated large value pool of size " << poolSize << " at index "
140 << poolIndex;
141
142 // Copy the values to this memory.
143 for (LargeValue& l: mLargeOperandValues) {
144 Operand& operand = mOperands[l.operandIndex];
145 operand.location.poolIndex = poolIndex;
146 memcpy(memoryPointer + operand.location.offset, l.buffer, operand.location.length);
147 }
148 }
149 return ANEURALNETWORKS_NO_ERROR;
150 }
151
setOperandValueFromMemory(uint32_t index,const Memory * memory,uint32_t offset,size_t length)152 int ModelBuilder::setOperandValueFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
153 size_t length) {
154 VLOG(MODEL) << __func__ << " for operand " << index << " offset " << offset << " size " << length;
155 if (index >= operandCount()) {
156 LOG(ERROR) << "ANeuralNetworksModel_setOperandValueFromMemory setting operand " << index
157 << " of " << operandCount();
158 return ANEURALNETWORKS_BAD_DATA;
159 }
160 Operand& operand = mOperands[index];
161 uint32_t neededLength = sizeOfData(operand.type, operand.dimensions);
162 if (neededLength != length) {
163 LOG(ERROR) << "ANeuralNetworksModel_setOperandValueFromMemory setting " << length
164 << " bytes when needing " << neededLength;
165 return ANEURALNETWORKS_BAD_DATA;
166 }
167 // TODO validate does not exceed length of memory
168 operand.lifetime = OperandLifeTime::CONSTANT_REFERENCE;
169 operand.location = {
170 .poolIndex = mMemories.add(memory), .offset = offset, .length = neededLength};
171 return ANEURALNETWORKS_NO_ERROR;
172 }
173
addOperation(ANeuralNetworksOperationType type,uint32_t inputCount,const uint32_t * inputs,uint32_t outputCount,const uint32_t * outputs)174 int ModelBuilder::addOperation(ANeuralNetworksOperationType type, uint32_t inputCount,
175 const uint32_t* inputs, uint32_t outputCount,
176 const uint32_t* outputs) {
177 if (mCompletedModel) {
178 LOG(ERROR) << "ANeuralNetworksModel_addOperation can't modify after model finished";
179 return ANEURALNETWORKS_BAD_DATA;
180 }
181 if (!validCode(kNumberOfOperationTypes, kNumberOfOperationTypesOEM, type)) {
182 LOG(ERROR) << "ANeuralNetworksModel_addOperation invalid operations type " << type;
183 return ANEURALNETWORKS_BAD_DATA;
184 }
185 int n = validateOperandList(inputCount, inputs, operandCount(),
186 "ANeuralNetworksModel_addOperation inputs");
187 if (n != ANEURALNETWORKS_NO_ERROR) {
188 return n;
189 }
190 n = validateOperandList(outputCount, outputs, operandCount(),
191 "ANeuralNetworksModel_addOperation outputs");
192 if (n != ANEURALNETWORKS_NO_ERROR) {
193 return n;
194 }
195
196 uint32_t operationIndex = operationCount();
197 if (operationIndex >= MAX_NUMBER_OF_OPERATIONS) {
198 LOG(ERROR) << "ANeuralNetworksModel_addOperation exceed max operations";
199 return ANEURALNETWORKS_BAD_DATA;
200 }
201 mOperations.resize(operationIndex + 1);
202 auto& entry = mOperations[operationIndex];
203 entry.type = static_cast<OperationType>(type);
204
205 setFromIntList(&entry.inputs, inputCount, inputs);
206 setFromIntList(&entry.outputs, outputCount, outputs);
207 for (uint32_t i : entry.inputs) {
208 mOperands[i].numberOfConsumers++;
209 // TODO mOperands[i].consumers.push_back(operationIndex);
210 }
211 return ANEURALNETWORKS_NO_ERROR;
212 }
213
identifyInputsAndOutputs(uint32_t inputCount,const uint32_t * inputs,uint32_t outputCount,const uint32_t * outputs)214 int ModelBuilder::identifyInputsAndOutputs(uint32_t inputCount, const uint32_t* inputs,
215 uint32_t outputCount, const uint32_t* outputs) {
216 if (mCompletedModel) {
217 LOG(ERROR) << "ANeuralNetworksModel_identifyInputsAndOutputs can't modify after model finished";
218 return ANEURALNETWORKS_BAD_DATA;
219 }
220 int n = validateOperandList(inputCount, inputs, operandCount(),
221 "ANeuralNetworksModel_identifyInputsAndOutputs inputs");
222 if (n != ANEURALNETWORKS_NO_ERROR) {
223 return n;
224 }
225 n = validateOperandList(outputCount, outputs, operandCount(),
226 "ANeuralNetworksModel_identifyInputsAndOutputs outputs");
227 if (n != ANEURALNETWORKS_NO_ERROR) {
228 return n;
229 }
230
231 // Makes a copy of the index list, validates the arguments, and changes
232 // the lifetime info of the corresponding operand.
233 auto setArguments = [&](std::vector<uint32_t>* indexVector, uint32_t indexCount,
234 const uint32_t* indexList, OperandLifeTime lifetime) -> bool {
235 indexVector->resize(indexCount);
236 for (uint32_t i = 0; i < indexCount; i++) {
237 const uint32_t operandIndex = indexList[i];
238 if (operandIndex >= mOperands.size()) {
239 LOG(ERROR) << "ANeuralNetworksModel_identifyInputsAndOutputs Can't set input or output "
240 "to be "
241 << operandIndex << " as this exceeds the numbe of operands "
242 << mOperands.size();
243 return false;
244 }
245 (*indexVector)[i] = operandIndex;
246 Operand& operand = mOperands[operandIndex];
247 if (operand.lifetime != OperandLifeTime::TEMPORARY_VARIABLE) {
248 LOG(ERROR) << "ANeuralNetworksModel_identifyInputsAndOutputs Can't set operand "
249 << operandIndex
250 << " to be an input or output. Check that it's not a constant or "
251 "already an input or output";
252 return false;
253 }
254 operand.lifetime = lifetime;
255 }
256 return true;
257 };
258
259 if (!setArguments(&mInputIndexes, inputCount, inputs, OperandLifeTime::MODEL_INPUT) ||
260 !setArguments(&mOutputIndexes, outputCount, outputs, OperandLifeTime::MODEL_OUTPUT)) {
261 return ANEURALNETWORKS_BAD_DATA;
262 }
263
264 return ANEURALNETWORKS_NO_ERROR;
265 }
266
createCompilation(CompilationBuilder ** compilation)267 int ModelBuilder::createCompilation(CompilationBuilder** compilation) {
268 if (!mCompletedModel) {
269 LOG(ERROR) << "ANeuralNetworksCompilation_create passed an unfinished model";
270 *compilation = nullptr;
271 return ANEURALNETWORKS_BAD_STATE;
272 }
273 *compilation = new CompilationBuilder(this);
274 return (*compilation ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY);
275 }
276
finish()277 int ModelBuilder::finish() {
278 if (mCompletedModel) {
279 LOG(ERROR) << "ANeuralNetworksModel_finish called more than once";
280 return ANEURALNETWORKS_BAD_STATE;
281 }
282
283 int n = copyLargeValuesToSharedMemory();
284 if (n != ANEURALNETWORKS_NO_ERROR) {
285 return n;
286 }
287
288 // We sort the operations so that they will be in the appropriate
289 // order for a single-threaded, op at a time execution.
290 // TODO: we don't need this if we always run the partitioner.
291 sortIntoRunOrder();
292 mCompletedModel = true;
293 return ANEURALNETWORKS_NO_ERROR;
294 }
295
sortIntoRunOrder()296 void ModelBuilder::sortIntoRunOrder() {
297 // Tracks the operations that can be executed.
298 std::vector<uint32_t> opsReadyToRun;
299 std::vector<Operation> runOrder;
300
301 // Tracks how many inputs are needed for each operation to be ready to run.
302 std::multimap<uint32_t, uint32_t> operandToOperations;
303 std::vector<uint32_t> unknownInputCount(operationCount());
304 for (uint32_t operationIndex = 0; operationIndex < operationCount(); operationIndex++) {
305 uint32_t& count = unknownInputCount[operationIndex];
306 count = 0;
307 for (uint32_t operandIndex : mOperations[operationIndex].inputs) {
308 auto lifetime = mOperands[operandIndex].lifetime;
309 if (lifetime == OperandLifeTime::TEMPORARY_VARIABLE ||
310 lifetime == OperandLifeTime::MODEL_OUTPUT) {
311 count++;
312 operandToOperations.insert(
313 std::pair<uint32_t, uint32_t>(operandIndex, operationIndex));
314 }
315 }
316 if (count == 0) {
317 opsReadyToRun.push_back(operationIndex);
318 }
319 }
320
321 while (opsReadyToRun.size() > 0) {
322 // Execute the next op
323 int opIndex = opsReadyToRun.back();
324 opsReadyToRun.pop_back();
325 const Operation& operation = mOperations[opIndex];
326
327 runOrder.push_back(mOperations[opIndex]);
328
329 // Mark all its outputs as known.
330 for (uint32_t operandIndex : operation.outputs) {
331 auto range = operandToOperations.equal_range(operandIndex);
332 for (auto i = range.first; i != range.second; i++) {
333 uint32_t& count = unknownInputCount[i->second];
334 if (--count == 0) {
335 opsReadyToRun.push_back(i->second);
336 }
337 }
338 }
339 }
340 mOperations = runOrder;
341 }
342
setHidlModel(Model * model) const343 void ModelBuilder::setHidlModel(Model* model) const {
344 model->operands = mOperands;
345 model->operations = mOperations;
346 model->inputIndexes = mInputIndexes;
347 model->outputIndexes = mOutputIndexes;
348 model->operandValues = mSmallOperandValues;
349
350 uint32_t count = mMemories.size();
351 model->pools.resize(count);
352 for (uint32_t i = 0; i < count; i++) {
353 model->pools[i] = mMemories[i]->getHidlMemory();
354 }
355 }
356
357 } // namespace nn
358 } // namespace android
359