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 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 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 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 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 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 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 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 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 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 343 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