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 "android.hardware.neuralnetworks (at) 1.0-impl-hvx" 18 19 #include "HexagonModel.h" 20 #include <numeric> 21 #include <unordered_set> 22 #include "HexagonOperations.h" 23 24 namespace android { 25 namespace hardware { 26 namespace neuralnetworks { 27 namespace V1_0 { 28 namespace implementation { 29 namespace hexagon { 30 31 static std::vector<OperandInfo> getOperandsInfo(const NeuralnetworksModel& model, 32 const std::vector<RunTimePoolInfo>& pools) { 33 std::vector<OperandInfo> info(model.operands.size()); 34 for (size_t i = 0; i < model.operands.size(); ++i) { 35 const Operand& operand = model.operands[i]; 36 info[i] = { 37 .type = operand.type, 38 .dimensions = operand.dimensions, 39 .scale = operand.scale, 40 .zeroPoint = operand.zeroPoint, 41 .lifetime = operand.lifetime, 42 .buffer = const_cast<uint8_t*>(getData(operand, model.operandValues, pools)), 43 .length = operand.location.length, 44 }; 45 } 46 return info; 47 } 48 49 Model::Model(const NeuralnetworksModel& model) : mGraphId(0), mNodeCount(0), mCompiled(false) { 50 mPools = mapPools(model.pools); 51 mOperands = getOperandsInfo(model, mPools); 52 std::for_each(mPools.begin(), mPools.end(), [](RunTimePoolInfo& mem) { mem.update(); }); 53 54 mOperations = model.operations; 55 mInputs = model.inputIndexes; 56 mOutputs = model.outputIndexes; 57 } 58 59 Model::Model(Model&& other) { 60 *this = std::move(other); 61 } 62 63 Model& Model::operator=(Model&& other) { 64 if (this != &other) { 65 mNodeCount = other.mNodeCount; 66 mGraphId = other.mGraphId; 67 mCompiled = other.mCompiled; 68 mOperands = std::move(other.mOperands); 69 mOperations = std::move(other.mOperations); 70 mInputs = std::move(other.mInputs); 71 mOutputs = std::move(other.mOutputs); 72 mPools = std::move(other.mPools); 73 other.mNodeCount = 0; 74 other.mGraphId = {}; 75 other.mCompiled = false; 76 } 77 return *this; 78 } 79 80 Model::~Model() { 81 clearModel(); 82 } 83 84 std::string Model::getLog() { 85 char buffer[16 * 1024]; 86 int err = hexagon::Controller::getInstance().getlog( 87 mGraphId, reinterpret_cast<uint8_t*>(buffer), sizeof(buffer)); 88 HEXAGON_SOFT_ASSERT_EQ(0, err, "failed getLog"); 89 return buffer; 90 } 91 92 std::string Model::getGraph() { 93 char buffer[16 * 1024]; 94 int err = hexagon::Controller::getInstance().snpprint( 95 mGraphId, reinterpret_cast<uint8_t*>(buffer), sizeof(buffer)); 96 HEXAGON_SOFT_ASSERT_EQ(0, err, "failed getGraph"); 97 return buffer; 98 } 99 100 uint32_t Model::getNextNode() { 101 return ++mNodeCount; 102 } 103 104 const int32_t* Model::getPointer(uint32_t operand) { 105 return reinterpret_cast<const int32_t*>(mOperands[operand].buffer); 106 } 107 108 Shape Model::getShape(uint32_t operand) { 109 return { 110 .type = mOperands[operand].type, 111 .dimensions = mOperands[operand].dimensions, 112 .scale = mOperands[operand].scale, 113 .offset = mOperands[operand].zeroPoint, 114 }; 115 } 116 117 bool Model::setShape(uint32_t operand, const Shape& shape) { 118 const hexagon_nn_output& output = mOperands[operand].hexagon_output; 119 HEXAGON_SOFT_ASSERT_EQ(output, hexagon_nn_output{}, "Output has already been set"); 120 // mOperands[operand].type = shape.type; 121 mOperands[operand].dimensions = shape.dimensions; 122 // mOperands[operand].scale = shape.scale; 123 // mOperands[operand].zeroPoint = shape.offset; 124 return true; 125 } 126 127 bool Model::isConstant(uint32_t operand) { 128 OperandLifeTime lifetime = mOperands[operand].lifetime; 129 return lifetime == OperandLifeTime::CONSTANT_COPY || 130 lifetime == OperandLifeTime::CONSTANT_REFERENCE; 131 } 132 133 hexagon_nn_input Model::createTensorInternal(uint32_t B, uint32_t H, uint32_t W, uint32_t D, 134 const uint8_t* ptr, size_t size) { 135 uint32_t node = getNextNode(); 136 bool success = hexagon::Controller::getInstance().append_const_node(mGraphId, node, B, H, W, D, 137 ptr, size) == 0; 138 HEXAGON_SOFT_ASSERT(success, "Failed to create tensor"); 139 return {.src_id = node, .output_idx = 0}; 140 } 141 142 hexagon_nn_input Model::createShape(uint32_t B, uint32_t H, uint32_t W, uint32_t D) { 143 uint32_t dump = 0; 144 return createTensorInternal(B, H, W, D, reinterpret_cast<uint8_t*>(&dump), sizeof(dump)); 145 } 146 147 hexagon_nn_input Model::addOperand(uint32_t operandIndex) { 148 const OperandInfo& operand = mOperands[operandIndex]; 149 std::vector<uint32_t> dims = getAlignedDimensions(operand.dimensions, 4); 150 HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Rank must be at most 4"); 151 hexagon_nn_input result = 152 createTensorInternal(dims[0], dims[1], dims[2], dims[3], operand.buffer, operand.length); 153 HEXAGON_SOFT_ASSERT_NE(hexagon_nn_input{}, result, "Failed to add operand"); 154 return result; 155 } 156 157 const hexagon_nn_input& Model::getTensor(uint32_t operand) { 158 hexagon_nn_input& tensor = mOperands[operand].hexagon_input; 159 if (tensor == hexagon_nn_input{}) { 160 tensor = addOperand(operand); 161 } 162 return tensor; 163 } 164 165 const hexagon_nn_input& Model::getQuantizationMin(uint32_t operand) { 166 OperandInfo& operandInfo = mOperands[operand]; 167 if (operandInfo.hexagon_input_min == hexagon_nn_input{}) { 168 float real_value = 169 operandInfo.type == OperandType::TENSOR_QUANT8_ASYMM 170 ? (std::numeric_limits<uint8_t>::min() - operandInfo.zeroPoint) * operandInfo.scale 171 : std::numeric_limits<uint32_t>::min() * operandInfo.scale; 172 operandInfo.hexagon_input_min = createValues<float>({real_value}); 173 } 174 return operandInfo.hexagon_input_min; 175 } 176 177 const hexagon_nn_input& Model::getQuantizationMax(uint32_t operand) { 178 OperandInfo& operandInfo = mOperands[operand]; 179 if (operandInfo.hexagon_input_max == hexagon_nn_input{}) { 180 float real_value = 181 operandInfo.type == OperandType::TENSOR_QUANT8_ASYMM 182 ? (std::numeric_limits<uint8_t>::max() - operandInfo.zeroPoint) * operandInfo.scale 183 : std::numeric_limits<uint32_t>::max() * operandInfo.scale; 184 operandInfo.hexagon_input_max = createValues<float>({real_value}); 185 } 186 return operandInfo.hexagon_input_max; 187 } 188 189 hexagon_nn_padding_type Model::getPadding(uint32_t operand) { 190 const int32_t padding = getScalar<int32_t>(operand); 191 return hexagon::getPadding(padding); 192 } 193 194 hexagon_nn_input Model::createQuantizationValue(uint32_t operand, int32_t quant_value) { 195 OperandInfo& operandInfo = mOperands[operand]; 196 float real_value = (quant_value - operandInfo.zeroPoint) * operandInfo.scale; 197 return createValues<float>({real_value}); 198 } 199 200 hexagon_nn_input Model::createConvFilterTensor(uint32_t operand) { 201 OperandInfo& operandInfo = mOperands[operand]; 202 std::vector<uint32_t> dims = getAlignedDimensions(mOperands[operand].dimensions, 4); 203 HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Need at most 4 dimensions"); 204 // NHWC --> HWCN 205 if (getShape(operand).type == OperandType::TENSOR_FLOAT32) { 206 std::vector<float> transposed = 207 transpose<float>(dims[0], dims[1] * dims[2] * dims[3], 208 reinterpret_cast<const float*>(operandInfo.buffer)); 209 return createTensorInternal(dims[1], dims[2], dims[3], dims[0], 210 reinterpret_cast<const uint8_t*>(transposed.data()), 211 operandInfo.length); 212 } else { 213 std::vector<uint8_t> transposed = 214 transpose<uint8_t>(dims[0], dims[1] * dims[2] * dims[3], 215 reinterpret_cast<const uint8_t*>(operandInfo.buffer)); 216 return createTensorInternal(dims[1], dims[2], dims[3], dims[0], 217 reinterpret_cast<const uint8_t*>(transposed.data()), 218 operandInfo.length); 219 } 220 } 221 222 hexagon_nn_input Model::createDepthwiseFilterTensor(uint32_t operand, int32_t depth_multiplier) { 223 OperandInfo& operandInfo = mOperands[operand]; 224 std::vector<uint32_t> dims = getAlignedDimensions(mOperands[operand].dimensions, 4); 225 HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Need at most 4 dimensions"); 226 // NHWC --> HWCN 227 return createTensorInternal(dims[1], dims[2], dims[3] / depth_multiplier, 228 dims[0] * depth_multiplier, operandInfo.buffer, operandInfo.length); 229 } 230 231 hexagon_nn_input Model::createFullyConnectedWeightTensor(uint32_t operand) { 232 OperandInfo& operandInfo = mOperands[operand]; 233 std::vector<uint32_t> dims = getAlignedDimensions(mOperands[operand].dimensions, 4); 234 HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Need at most 4 dimensions"); 235 // WC --> CW 236 uint32_t num_units = dims[0] * dims[1] * dims[2]; 237 uint32_t input_size = dims[3]; 238 if (getShape(operand).type == OperandType::TENSOR_FLOAT32) { 239 std::vector<float> transposed = transpose<float>( 240 num_units, input_size, reinterpret_cast<const float*>(operandInfo.buffer)); 241 return createTensorInternal(1, 1, input_size, num_units, 242 reinterpret_cast<const uint8_t*>(transposed.data()), 243 operandInfo.length); 244 } else { 245 std::vector<uint8_t> transposed = transpose<uint8_t>( 246 num_units, input_size, reinterpret_cast<const uint8_t*>(operandInfo.buffer)); 247 return createTensorInternal(1, 1, input_size, num_units, 248 reinterpret_cast<const uint8_t*>(transposed.data()), 249 operandInfo.length); 250 } 251 } 252 253 op_type Model::getFloatActivation(uint32_t operand) { 254 return getFloatActivationFunction(getScalar<FusedActivationFunc>(operand)); 255 } 256 257 op_type Model::getQuantizedActivation(uint32_t operand) { 258 return getQuantizedActivationFunction(getScalar<FusedActivationFunc>(operand)); 259 } 260 261 static bool verifyOperationInputs(const std::vector<hexagon_nn_input>& inputs) { 262 for (const hexagon_nn_input& input : inputs) { 263 if (input == hexagon_nn_input{}) { 264 return false; 265 } 266 } 267 return true; 268 } 269 270 static bool verifyOperationOutputs(const std::vector<hexagon_nn_output>& outputs) { 271 for (const hexagon_nn_output& output : outputs) { 272 if (output == hexagon_nn_output{}) { 273 return false; 274 } 275 } 276 return true; 277 } 278 279 uint32_t Model::addOperationInternal(op_type op, hexagon_nn_padding_type pad, 280 const std::vector<hexagon_nn_input>& inputs, 281 const std::vector<hexagon_nn_output>& outputs) { 282 HEXAGON_SOFT_ASSERT(verifyOperationInputs(inputs), 283 "error adding operation: one or more inputs is invalid"); 284 HEXAGON_SOFT_ASSERT(verifyOperationOutputs(outputs), 285 "error adding operation: one or more outputs is invalid"); 286 uint32_t node = getNextNode(); 287 return hexagon::Controller::getInstance().append_node(mGraphId, node, op, pad, inputs.data(), 288 inputs.size(), outputs.data(), 289 outputs.size()) == 0 290 ? node 291 : 0; 292 } 293 294 std::vector<hexagon_nn_output> Model::getHexagonOutputs(const std::vector<uint32_t>& operands) { 295 std::vector<hexagon_nn_output> outputs; 296 for (uint32_t index : operands) { 297 const OperandInfo& operand = mOperands[index]; 298 outputs.push_back(make_hexagon_nn_output(operand.dimensions, getSize(operand.type))); 299 if (operand.type == OperandType::TENSOR_QUANT8_ASYMM) { 300 outputs.push_back(make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float))); 301 outputs.push_back(make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float))); 302 } 303 } 304 return outputs; 305 } 306 307 bool Model::registerHexagonInputs(const std::vector<uint32_t>& operands, uint32_t node) { 308 uint32_t idx = 0; 309 for (uint32_t i = 0; i < static_cast<uint32_t>(operands.size()); ++i) { 310 OperandInfo& operand = mOperands[operands[i]]; 311 HEXAGON_SOFT_ASSERT_EQ(operand.hexagon_input, hexagon_nn_input{}, 312 "Error: operation output has already been registered"); 313 operand.hexagon_input = {.src_id = node, .output_idx = idx++}; 314 if (operand.type == OperandType::TENSOR_QUANT8_ASYMM) { 315 operand.hexagon_input_min = {.src_id = node, .output_idx = idx++}; 316 operand.hexagon_input_max = {.src_id = node, .output_idx = idx++}; 317 } 318 } 319 return true; 320 } 321 322 bool Model::addBasicOperation(op_type op, hexagon_nn_padding_type pad, 323 const std::vector<hexagon_nn_input>& inputs, 324 const std::vector<uint32_t>& outputs) { 325 std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs); 326 uint32_t node = addOperationInternal(op, pad, inputs, outs); 327 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation"); 328 return registerHexagonInputs(outputs, node); 329 } 330 331 std::vector<hexagon_nn_input> Model::setupActivationArgs(op_type op) { 332 switch (op) { 333 case OP_Nop: 334 return {}; 335 case OP_Relu_f: 336 FALLTHROUGH_INTENDED; 337 case OP_QuantizedRelu_8: 338 return {}; 339 case OP_ReluX_f: 340 FALLTHROUGH_INTENDED; 341 case OP_QuantizedReluX_8: 342 return {createValues<float>({6.0f})}; 343 case OP_Clamp_f: 344 FALLTHROUGH_INTENDED; 345 case OP_QuantizedClamp_8: 346 return {createValues<float>({-1.0f}), createValues<float>({1.0f})}; 347 default: 348 HEXAGON_SOFT_ASSERT(false, "Unknown activation symbol " << op); 349 } 350 } 351 352 bool Model::addFloatOperationWithActivation(op_type op, hexagon_nn_padding_type pad, 353 op_type activation, 354 const std::vector<hexagon_nn_input>& inputs, 355 const std::vector<uint32_t>& outputs) { 356 std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs); 357 std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation); 358 359 uint32_t node = addOperationInternal(op, pad, inputs, outs); 360 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation"); 361 362 std::vector<hexagon_nn_input> buffer_in = {{.src_id = node, .output_idx = 0}}; 363 buffer_in.insert(buffer_in.end(), actArgs.begin(), actArgs.end()); 364 node = addOperationInternal(activation, NN_PAD_NA, buffer_in, outs); 365 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation"); 366 367 return registerHexagonInputs(outputs, node); 368 } 369 370 bool Model::addQuant8OperationWithActivation(op_type op, hexagon_nn_padding_type pad, 371 op_type activation, 372 const std::vector<hexagon_nn_input>& inputs, 373 const std::vector<uint32_t>& outputs) { 374 std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs); 375 std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation); 376 377 uint32_t node = addOperationInternal(op, pad, inputs, outs); 378 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation"); 379 380 std::vector<hexagon_nn_input> buffer_in = {{.src_id = node, .output_idx = 0}, 381 {.src_id = node, .output_idx = 1}, 382 {.src_id = node, .output_idx = 2}}; 383 buffer_in.insert(buffer_in.end(), actArgs.begin(), actArgs.end()); 384 node = addOperationInternal(activation, NN_PAD_NA, buffer_in, outs); 385 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation"); 386 387 return registerHexagonInputs(outputs, node); 388 } 389 390 bool Model::addFusedFloatOperation(op_type op, hexagon_nn_padding_type pad, 391 const hexagon_nn_input& bias, op_type activation, 392 const std::vector<hexagon_nn_input>& inputs, 393 const std::vector<uint32_t>& outputs) { 394 HEXAGON_SOFT_ASSERT_EQ(1, outputs.size(), "addFusedFloatOperation requires 1 output"); 395 std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs); 396 std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation); 397 uint32_t node; 398 399 node = addOperationInternal(op, pad, inputs, outs); 400 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation"); 401 402 if (bias != hexagon_nn_input{}) { 403 const hexagon_nn_input buffer1_in = {.src_id = node, .output_idx = 0}; 404 node = addOperationInternal(OP_BiasAdd_f, NN_PAD_NA, {buffer1_in, bias}, outs); 405 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding bias operation"); 406 } 407 408 if (activation != OP_Nop) { 409 std::vector<hexagon_nn_input> buffer2_in = {{.src_id = node, .output_idx = 0}}; 410 buffer2_in.insert(buffer2_in.end(), actArgs.begin(), actArgs.end()); 411 node = addOperationInternal(activation, NN_PAD_NA, buffer2_in, outs); 412 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation"); 413 } 414 415 return registerHexagonInputs(outputs, node); 416 } 417 418 bool Model::addFusedQuant8Operation(op_type op, hexagon_nn_padding_type pad, 419 const std::vector<hexagon_nn_input>& bias, op_type activation, 420 const std::vector<hexagon_nn_input>& inputs, 421 const std::vector<uint32_t>& outputs) { 422 HEXAGON_SOFT_ASSERT_EQ(1, outputs.size(), "addFusedQuant8Operation requires 1 output"); 423 std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation); 424 const hexagon_nn_input& new_min = getQuantizationMin(outputs[0]); 425 const hexagon_nn_input& new_max = getQuantizationMax(outputs[0]); 426 uint32_t node; 427 428 hexagon_nn_output tensor_out8 = 429 make_hexagon_nn_output(mOperands[outputs[0]].dimensions, sizeof(uint8_t)); 430 hexagon_nn_output tensor_out32 = 431 make_hexagon_nn_output(mOperands[outputs[0]].dimensions, sizeof(int32_t)); 432 hexagon_nn_output scalar_out32 = make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float)); 433 434 std::vector<hexagon_nn_output> out8 = {tensor_out8, scalar_out32, scalar_out32}; 435 std::vector<hexagon_nn_output> out32 = {tensor_out32, scalar_out32, scalar_out32}; 436 437 // base operation 438 node = addOperationInternal(op, pad, inputs, out32); 439 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation"); 440 hexagon_nn_input previous = {.src_id = node, .output_idx = 0}; 441 hexagon_nn_input previous_min = {.src_id = node, .output_idx = 1}; 442 hexagon_nn_input previous_max = {.src_id = node, .output_idx = 2}; 443 444 // add bias 445 if (bias.size() == 3) { 446 node = addOperationInternal( 447 OP_QuantizedBiasAdd_32p32to32, NN_PAD_NA, 448 {previous, bias[0], previous_min, previous_max, bias[1], bias[2]}, out32); 449 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding bias operation"); 450 previous.src_id = node; 451 previous_min.src_id = node; 452 previous_max.src_id = node; 453 } 454 455 // requantize 456 node = addOperationInternal(OP_Requantize_32to8, NN_PAD_NA, 457 {previous, previous_min, previous_max, new_min, new_max}, out8); 458 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding requantize operation"); 459 previous.src_id = node; 460 previous_min.src_id = node; 461 previous_max.src_id = node; 462 463 // activation 464 if (activation != OP_Nop) { 465 std::vector<hexagon_nn_input> buffer = {previous, previous_min, previous_max}; 466 buffer.insert(buffer.end(), actArgs.begin(), actArgs.end()); 467 node = addOperationInternal(activation, NN_PAD_NA, buffer, out8); 468 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation"); 469 } 470 471 return registerHexagonInputs(outputs, node); 472 } 473 474 bool Model::verifyOperations() { 475 std::vector<bool> supported = supportedOperations(); 476 return std::all_of(supported.begin(), supported.end(), [](bool valid) { return valid; }); 477 } 478 479 bool Model::verifyOperands() { 480 for (const OperandInfo& operand : mOperands) { 481 HEXAGON_SOFT_ASSERT_GE(4ul, operand.dimensions.size(), 482 "Operand must have at most 4 dimensions"); 483 for (uint32_t dim : operand.dimensions) { 484 HEXAGON_SOFT_ASSERT_NE(0, dim, "At least one operand with unknown dimension"); 485 } 486 } 487 return true; 488 } 489 490 bool Model::addInputs() { 491 // prepare OP_INPUT's outputs 492 std::vector<hexagon_nn_output> outs; 493 for (size_t i = 0; i < mInputs.size(); ++i) { 494 OperandInfo& operand = mOperands[mInputs[i]]; 495 outs.push_back(make_hexagon_nn_output(operand.dimensions, getSize(operand.type))); 496 } 497 498 // add single input node for entire graph 499 uint32_t node = addOperationInternal(OP_INPUT, NN_PAD_NA, {}, outs); 500 HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding input operation"); 501 502 // update operand information 503 for (size_t i = 0; i < mInputs.size(); ++i) { 504 OperandInfo& operand = mOperands[mInputs[i]]; 505 operand.hexagon_input = {.src_id = node, .output_idx = static_cast<uint32_t>(i)}; 506 } 507 508 return true; 509 } 510 511 bool Model::addOperations() { 512 for (const Operation& operation : mOperations) { 513 OperationType operationType = operation.type; 514 515 // For now, the operation type is always the same as its first operand 516 // parameter. If this changes in the future, this line of code will need 517 // to be updated. 518 OperandType operandType = mOperands[operation.inputs[0]].type; 519 520 OperationTuple opTuple = std::make_pair(operationType, operandType); 521 HEXAGON_SOFT_ASSERT( 522 getOperationPrepareTable().find(opTuple) != getOperationPrepareTable().end(), 523 "Operation not found"); 524 bool success = 525 getOperationPrepareTable()[opTuple](operation.inputs, operation.outputs, this); 526 HEXAGON_SOFT_ASSERT(success, "error adding operation"); 527 } 528 return true; 529 } 530 531 bool Model::addOutputs() { 532 // prepare OP_OUTPUT's inputs 533 std::vector<hexagon_nn_input> ins; 534 for (size_t out : mOutputs) { 535 OperandInfo& operand = mOperands[out]; 536 HEXAGON_SOFT_ASSERT_NE(operand.hexagon_input, hexagon_nn_input{}, 537 "output operand has not been registered"); 538 539 if (operand.type == OperandType::TENSOR_QUANT8_ASYMM) { 540 // Adjust quantized range of outputs 541 uint32_t dequant = addOperationInternal( 542 OP_Dequantize, NN_PAD_NA, 543 {operand.hexagon_input, operand.hexagon_input_min, operand.hexagon_input_max}, 544 {make_hexagon_nn_output(operand.dimensions, sizeof(float))}); 545 uint32_t quant = 546 addOperationInternal(OP_Quantize, NN_PAD_NA, 547 {{.src_id = dequant, .output_idx = 0}, 548 createQuantizationValue(out, 0), 549 createQuantizationValue(out, 255)}, 550 {make_hexagon_nn_output(operand.dimensions, sizeof(uint8_t)), 551 make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float)), 552 make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float))}); 553 ins.push_back({.src_id = quant, .output_idx = 0}); 554 } else { 555 ins.push_back(operand.hexagon_input); 556 } 557 } 558 559 // add single output node for entire graph 560 bool success = addBasicOperation(OP_OUTPUT, NN_PAD_NA, ins, {}); 561 HEXAGON_SOFT_ASSERT(success, "Error adding output operation"); 562 563 return true; 564 } 565 566 void Model::clearModel() { 567 mCompiled = false; 568 for (OperandInfo& operand : mOperands) { 569 operand.hexagon_input = {}; 570 operand.hexagon_input_min = {}; 571 operand.hexagon_input_max = {}; 572 operand.hexagon_output = {}; 573 } 574 if (mGraphId != hexagon_nn_nn_id{}) { 575 hexagon::Controller::getInstance().teardown(mGraphId); 576 } 577 } 578 579 std::vector<bool> Model::supportedOperations() { 580 std::vector<bool> supported(mOperations.size()); 581 for (size_t i = 0; i < supported.size(); ++i) { 582 const Operation& operation = mOperations[i]; 583 OperationType operationType = operation.type; 584 585 // For now, the operation type is always the same as its first operand 586 // parameter. If this changes in the future, this line of code will need 587 // to be updated. 588 OperandType operandType = mOperands[operation.inputs[0]].type; 589 590 OperationTuple opTuple = std::make_pair(operationType, operandType); 591 592 auto entry = getOperationCheckTable().find(opTuple); 593 if (entry != getOperationCheckTable().end()) { 594 supported[i] = entry->second(operation.inputs, operation.outputs, this); 595 } else { 596 supported[i] = false; 597 } 598 } 599 return supported; 600 } 601 602 bool Model::prepare() { 603 if (!verifyOperations() || !verifyOperands()) { 604 return false; 605 } 606 607 int err = hexagon::Controller::getInstance().init(&mGraphId); 608 HEXAGON_SOFT_ASSERT_EQ(0, err, "Hexagon could not allocate new graph"); 609 HEXAGON_SOFT_ASSERT_NE(0, mGraphId, "Hexagon could not allocate new graph"); 610 hexagon::Controller::getInstance().set_debug_level(mGraphId, 0); 611 612 if (!addInputs() || !addOperations() || !addOutputs()) { 613 clearModel(); 614 LOG(ERROR) << "Something went wrong. Clearing the model and aborting."; 615 return false; 616 } 617 618 err = hexagon::Controller::getInstance().prepare(mGraphId); 619 620 LOG(INFO) << "PrepareModel was " << (err == 0 ? "SUCCESSFUL" : "UNSUCCESSFUL"); 621 622 return err == 0; 623 } 624 625 static hexagon_nn_tensordef convertToTensordef(const OperandInfo& operand) { 626 std::vector<uint32_t> dimensions = getAlignedDimensions(operand.dimensions, 4); 627 return { 628 .batches = dimensions[0], 629 .height = dimensions[1], 630 .width = dimensions[2], 631 .depth = dimensions[3], 632 .data = operand.buffer, 633 .dataLen = static_cast<int32_t>(operand.length), 634 .data_valid_len = operand.length, // unused? 635 .unused = 0, 636 }; 637 } 638 639 static uint32_t getSize(const OperandInfo& operand) { 640 return std::accumulate(operand.dimensions.begin(), operand.dimensions.end(), 641 getSize(operand.type), std::multiplies<>{}); 642 } 643 644 static OperandInfo getUpdatedOperand(const RequestArgument& inputOutput, 645 const std::vector<RunTimePoolInfo>& pools, 646 const OperandInfo& oldInfo) { 647 OperandInfo newInfo = oldInfo; 648 649 const RunTimePoolInfo& pool = pools[inputOutput.location.poolIndex]; 650 uint32_t offset = inputOutput.location.offset; 651 652 if (inputOutput.dimensions.size() > 0) { 653 newInfo.dimensions = inputOutput.dimensions; 654 } 655 656 newInfo.buffer = pool.getBuffer() + offset; 657 newInfo.length = getSize(newInfo); 658 659 return newInfo; 660 } 661 662 bool Model::execute(const Request& request) { 663 std::vector<RunTimePoolInfo> pools = mapPools(request.pools); 664 665 // prepare inputs 666 std::vector<hexagon_nn_tensordef> inputs; 667 for (size_t i = 0; i < request.inputs.size(); ++i) { 668 const OperandInfo& oldInfo = mOperands[mInputs[i]]; 669 OperandInfo newInfo = getUpdatedOperand(request.inputs[i], pools, oldInfo); 670 inputs.push_back(convertToTensordef(newInfo)); 671 } 672 673 // prepare outputs 674 std::vector<hexagon_nn_tensordef> outputs; 675 for (size_t i = 0; i < request.outputs.size(); ++i) { 676 const OperandInfo& oldInfo = mOperands[mOutputs[i]]; 677 OperandInfo newInfo = getUpdatedOperand(request.outputs[i], pools, oldInfo); 678 outputs.push_back(convertToTensordef(newInfo)); 679 } 680 681 // execute model 682 int err = hexagon::Controller::getInstance().execute_new(mGraphId, inputs.data(), inputs.size(), 683 outputs.data(), outputs.size()); 684 685 std::for_each(pools.begin(), pools.end(), [](RunTimePoolInfo& pool) { pool.update(); }); 686 687 LOG(INFO) << "EXECUTION WAS " << (err == 0 ? "SUCCESSFUL" : "UNSUCCESSFUL"); 688 689 return err == 0; 690 } 691 692 } // namespace hexagon 693 } // namespace implementation 694 } // namespace V1_0 695 } // namespace neuralnetworks 696 } // namespace hardware 697 } // namespace android 698