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 // Classes used to plan how to execute a model across multiple devices. 18 19 #ifndef ANDROID_FRAMEWORKS_ML_NN_RUNTIME_EXECUTION_PLAN_H 20 #define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_EXECUTION_PLAN_H 21 22 #include <LegacyUtils.h> 23 #include <TokenHasher.h> 24 #include <android-base/logging.h> 25 #include <nnapi/IBurst.h> 26 #include <nnapi/Types.h> 27 28 #include <algorithm> 29 #include <chrono> 30 #include <functional> 31 #include <map> 32 #include <memory> 33 #include <ostream> 34 #include <set> 35 #include <string> 36 #include <tuple> 37 #include <unordered_map> 38 #include <utility> 39 #include <variant> 40 #include <vector> 41 42 #include "Memory.h" 43 #include "ModelArgumentInfo.h" 44 #include "ModelBuilder.h" 45 #include "NeuralNetworks.h" 46 47 namespace android { 48 namespace nn { 49 50 class BurstBuilder; 51 class CompilationBuilder; 52 class Device; 53 class ExecutionBuilder; 54 class ExecutionPlan; 55 class RuntimeMemory; 56 class RuntimePreparedModel; 57 class StepExecutor; 58 59 struct ConstantReferenceLocation; 60 struct CacheInfo; 61 62 // NNAPI Control Flow allows referring to an NNAPI model inside another NNAPI 63 // model using OperandType::SUBGRAPH. For example, an IF operation within a 64 // model mey refer to two other models corresponding to then and else branches. 65 // 66 // The partitioning process transforms this nested representation into a list 67 // of LogicalSteps. 68 // 69 // The following terms are used: 70 // - The main model is the top-level model being compiled (not referenced by any 71 // OperandType::SUBGRAPH operand within the compilation). 72 // - A referenced model is a non-top-level model being compiled (referenced by 73 // at least one OperandType::SUBGRAPH operand within the set of models being 74 // compiled). 75 // - A source model is either the main model or a referenced model. 76 // - A step model is a model excerpted from a source model during the 77 // partitioning process. 78 // - A partition is a LogicalStep representing at least one operation of a 79 // source model. In particular, ExecutionStep represents a step model, IfStep 80 // represents an IF operation, WhileStep represents a WHILE operation. 81 // A GotoStep is not a partition. 82 // - A partition boundary operand is a source model operand that is an input or 83 // output of a partition. For ExecutionStep, the inputs and outputs of the 84 // step model are boundary operands; for IfStep and WhileStep, the inputs and 85 // outputs of the corresponding operation are boundary operands. 86 // - A partition boundary static temporary is a partition boundary 87 // operand which is of lifetime TEMPORARY_VARIABLE in the source model and 88 // whose dimensions are fully specified. 89 // - A partition boundary dynamic temporary is a partition boundary 90 // operand which is of lifetime TEMPORARY_VARIABLE in the source model and 91 // whose dimensions are not fully specified. 92 // - A main execution is the execution of a main model. 93 // 94 // Referenced models can be sources of partition boundary operands. For example, 95 // this happens when a referenced model is partitioned into one or more 96 // LogicalSteps. 97 // 98 // (model index, operand index within model) 99 typedef std::pair<uint32_t, uint32_t> SourceOperandIndex; 100 101 // A collection of source models. 102 class SourceModels { 103 public: addModel(const ModelBuilder * model)104 uint32_t addModel(const ModelBuilder* model) { 105 uint32_t modelIndex = mModels.size(); 106 mModels.push_back(model); 107 return modelIndex; 108 } 109 getModel(uint32_t index)110 const ModelBuilder* getModel(uint32_t index) const { return mModels[index]; } 111 size()112 uint32_t size() const { return mModels.size(); } 113 114 private: 115 std::vector<const ModelBuilder*> mModels; 116 }; 117 118 // Represents all partition boundary dynamic temporaries for a particular main 119 // execution. 120 // 121 // Usage pattern: 122 // - declare() every partition boundary dynamic temporary. 123 // - endDeclarations(). After this point, lookup() is permitted. 124 // - Before executing an ExecutionStep, call allocate(). 125 // - After executing an ExecutionStep, call redeclare() for every partition 126 // boundary dynamic temporary for which we've learned or guessed more about 127 // the dimensions or length. 128 // 129 // Each partition boundary temporary has a location assigned by allocate() for 130 // its defining step (see declare() and allocate()). That location remains 131 // valid until redeclare() increases the length of some temporary in its defining 132 // step or allocate() is called again for its defining step. 133 class DynamicTemporaries { 134 DISALLOW_COPY_AND_ASSIGN(DynamicTemporaries); 135 136 public: 137 DynamicTemporaries() = default; 138 DynamicTemporaries(DynamicTemporaries&&) = default; 139 DynamicTemporaries& operator=(DynamicTemporaries&&) = default; 140 141 // Declare a dynamic temporary. stepIndex is the step that defines the 142 // temporary (i.e., in which the temporary appears as an operation output 143 // operand). initialDimensions and initialLength indicate what we know or 144 // (in the case of length) guess about those properties. 145 void declare(SourceOperandIndex sourceOperandIndex, uint32_t stepIndex, 146 const Dimensions& initialDimensions, uint32_t initialLength, uint32_t alignment, 147 uint32_t padding); 148 149 // Indicate that we've finished declaring all dynamic temporaries. endDeclarations()150 void endDeclarations() { 151 CHECK(!mDeclared); 152 mDeclared = true; 153 } 154 155 // Redeclare a dynamic temporary, indicating what we've learned about it. 156 // This may invalidate the location of temporaries defined by its step. 157 // Returns true if dimensions or length changed, false otherwise. 158 bool redeclare(SourceOperandIndex sourceOperandIndex, const Dimensions& newDimensions, 159 uint32_t newLength); 160 161 // Ensure that all dynamic temporaries defined by the specified step have 162 // locations. The return value is a ResultCode (e.g., 163 // ANEURALNETWORKS_NO_ERROR). 164 // 165 // Even if dynamic temporaries have already been allocated for this step, 166 // this call may reallocate them. A reallocation is not guaranteed to 167 // preserve location (LocationAndShape.memory, LocationAndShape.offset) or 168 // contents of temporaries. 169 int allocate(uint32_t stepIndex); 170 171 // Do the dynamic temporaries defined by this step have valid allocations? 172 // (Will be true if there are no dynamic temporaries defined by this step.) 173 bool allocated(uint32_t stepIndex) const; 174 175 // Dump information to VLOG(EXECUTION). 176 void vlogDump(const char* context = nullptr) const; 177 178 // If the specified operand is a dynamic temporary, return location and 179 // shape information; otherwise, return std::nullopt. 180 // 181 // If temporary exists but does not have a valid allocation, then: 182 // - If mustBeAllocated == true, then trigger a failed CHECK(). 183 // - If mustBeAllocated == false, then memory == nullptr and offset == ~0. 184 struct LocationAndShape { 185 const RuntimeMemory* memory; 186 uint32_t offset; 187 const Dimensions* dimensions; 188 uint32_t paddedLength; 189 }; 190 std::optional<LocationAndShape> lookup(SourceOperandIndex sourceOperandIndex, 191 bool mustBeAllocated = true) const; 192 193 // Have any dynamic temporaries been declared? empty()194 bool empty() const { return mSourceOperandToTemporary.empty(); } 195 196 private: 197 // The same as LocationAndShape, except that: 198 // - the base of the location is represented not by memory but by defining stepIndex 199 // - it additionally contains information about the preferred alignment and padding 200 struct InternalLocationAndShape { 201 uint32_t stepIndex; 202 uint32_t offset; 203 Dimensions dimensions; 204 uint32_t paddedLength; 205 uint32_t alignment; 206 uint32_t padding; 207 }; 208 std::map<SourceOperandIndex, InternalLocationAndShape> mSourceOperandToTemporary; 209 210 // Every dynamic temporary defined at a given stepIndex. 211 std::map<uint32_t, std::vector<SourceOperandIndex>> mStepIndexToSourceOperandIndexes; 212 213 std::map<uint32_t, std::unique_ptr<MemoryAshmem>> mStepIndexToMemory; 214 215 // For a given defining stepIndex, we consider either all its dynamic 216 // temporaries to be allocated (have valid locations) or none of them to be. 217 std::set<uint32_t> mAllocatedStepIndexes; 218 219 // Has endDeclarations() been called? 220 bool mDeclared = false; 221 }; 222 223 // The location of a static temporary. 224 struct StaticTemporaryLocation { 225 // The offset relative to ExecutionPlan::Controller::mTemporaries during execution. 226 uint32_t offset; 227 uint32_t paddedLength; 228 }; 229 230 // An excerpt of a source model to be run by a specific device. 231 class ExecutionStep { 232 public: 233 typedef std::vector<std::pair<uint32_t, uint32_t>> RemapVectorType; 234 typedef std::set<std::pair<uint32_t, uint32_t>> StepModelOutputSetType; 235 236 enum OperandKind { INPUT, OUTPUT }; 237 238 ExecutionStep(ExecutionPlan* plan, uint32_t stepIndex, uint32_t sourceModelIndex, 239 std::shared_ptr<Device> device); 240 241 int addOperation(int operationIndex); 242 int addOperand(uint32_t sourceOperandIndex, uint32_t* stepOperandIndex, OperandKind kind); 243 244 // Each container entry is of the form (source model operand index, step model operand index) getStepModelInputs()245 const RemapVectorType& getStepModelInputs() const { return mStepModelInputs; } getStepModelOutputs()246 const RemapVectorType& getStepModelOutputs() const { return mStepModelOutputs; } getModelInputs()247 const RemapVectorType& getModelInputs() const { return mModelInputs; } getModelOutputs()248 const RemapVectorType& getModelOutputs() const { return mModelOutputs; } getTempsAsStepModelInputs()249 const RemapVectorType& getTempsAsStepModelInputs() const { return mTempsAsStepModelInputs; } getTempsAsStepModelOutputs()250 const StepModelOutputSetType& getTempsAsStepModelOutputs() const { 251 return mTempsAsStepModelOutputs; 252 } getOutputsAsStepModelInputs()253 const RemapVectorType& getOutputsAsStepModelInputs() const { return mOutputsAsStepModelInputs; } getInputIndexStepModelToMainModel()254 const std::vector<uint32_t>& getInputIndexStepModelToMainModel() const { 255 return mInputIndexStepModelToMainModel; 256 } getOutputIndexStepModelToMainModel()257 const std::vector<uint32_t>& getOutputIndexStepModelToMainModel() const { 258 return mOutputIndexStepModelToMainModel; 259 } getOutputsAsStepModelInputsIndexToMainModel()260 const std::vector<uint32_t>& getOutputsAsStepModelInputsIndexToMainModel() const { 261 return mOutputsAsStepModelInputsIndexToMainModel; 262 } 263 getModelOutputsThatAreDownstreamInputs()264 const std::set<uint32_t>& getModelOutputsThatAreDownstreamInputs() const { 265 return mModelOutputsThatAreDownstreamInputs; 266 } 267 getIndex()268 uint32_t getIndex() const { return mIndex; } getSourceModelIndex()269 uint32_t getSourceModelIndex() const { return mSourceModelIndex; } 270 271 void declareModelOutputIsDownstreamInput(uint32_t mainModelOutputIndex); 272 void recordTempAsStepModelOutput(uint32_t stepOperandIndex); 273 274 // If this step has a step model output of unknown size, sets 275 // *hasOutputOfUnknownSize to true; otherwise, leaves it 276 // unchanged. 277 int finishStepModel(const ModelBuilder* mainModel, bool* hasOutputOfUnknownSize, 278 int32_t executionPreference, int32_t priority); 279 getStepModel()280 const ModelBuilder* getStepModel() const { return &mStepModel; } getDevice()281 std::shared_ptr<Device> getDevice() const { return mDevice; } 282 283 // only available after calling finishStepModel() getPreparedStepModel()284 std::shared_ptr<RuntimePreparedModel> getPreparedStepModel() const { 285 return mPreparedStepModel; 286 } 287 288 // Map inputs and outputs from ExecutionBuilder to StepExecutor. 289 // 290 // This method only reads map entries for which the first element of 291 // SourceOperandIndex is mSourceModelIndex. 292 // 293 // mainModelOutputShapes may be nullptr if the only main model outputs that are 294 // inputs of this step are of fully specified shape. 295 void mapInputsAndOutputs( 296 std::shared_ptr<StepExecutor> stepExecutor, 297 const std::vector<OutputShape>* mainModelOutputShapes, 298 const RuntimeMemory* temporaryMemory, // for static temporaries 299 const std::map<SourceOperandIndex, StaticTemporaryLocation>& 300 sourceOperandToLocationOfTemporary, // for static temporaries 301 const DynamicTemporaries& dynamicTemporaries, 302 const std::map<SourceOperandIndex, uint32_t>& sourceOperandToInputIndex, 303 const std::map<SourceOperandIndex, uint32_t>& sourceOperandToOutputIndex, 304 const std::map<SourceOperandIndex, ConstantReferenceLocation>& 305 sourceOperandToConstantReference) const; 306 hasNoInputsOrNoOutputs()307 bool hasNoInputsOrNoOutputs() const { 308 return mStepModelInputs.empty() || mStepModelOutputs.empty(); 309 } 310 311 void dump() const; 312 313 // For test only, get the transformed cache token. forTest_getCacheToken()314 const uint8_t* forTest_getCacheToken() const { return mToken.getCacheToken(); } 315 316 private: 317 void logStepModel() const; 318 const ModelBuilder* getSourceModel() const; 319 320 // TODO: Some of the data is working state information that 321 // shouldn't be needed after we've constructed but not executed 322 // the step. 323 324 ExecutionPlan* mPlan; 325 uint32_t mIndex; // index of step within plan 326 uint32_t mSourceModelIndex; 327 ModelBuilder mStepModel; // An excerpt of a source model to be run by one device. 328 std::shared_ptr<Device> mDevice; 329 std::shared_ptr<RuntimePreparedModel> mPreparedStepModel; 330 331 // All inputs of this step model: 332 // (source model operand index, step model operand index) 333 // 334 // Depending on whether the source operand is an input or output of the main 335 // model, the memory should be mapped using 336 // ExecutionPlan::CompoundBody::mSourceOperandToInputIndex, 337 // ExecutionPlan::Controller::mSourceOperandToLocationOfTemporary, or 338 // ExecutionPlan::Controller::mDynamicTemporaries, or 339 // ExecutionPlan::CompoundBody::mSourceOperandToOutputIndex. 340 RemapVectorType mStepModelInputs; 341 // All outputs of this step model: 342 // (source model operand index, step model operand index) 343 // 344 // Depending on whether the source operand is an output of the main model, 345 // the memory should be mapped using 346 // ExecutionPlan::CompoundBody::mSourceOperandToOutputIndex, 347 // ExecutionPlan::Controller::mSourceOperandToLocationOfTemporary, or 348 // ExecutionPlan::Controller::mDynamicTemporaries. 349 // 350 // mOutputIndexStepModelToMainModel and declareModelOutputIsDownstreamInput() 351 // rely on mModelOutputs being a prefix of mStepModelOutputs. 352 RemapVectorType mStepModelOutputs; 353 // Inputs of main model that are also inputs of this step model: 354 // (main model operand index, step model operand index) 355 RemapVectorType mModelInputs; 356 // Outputs of main model that are also outputs of this step model: 357 // (main model operand index, step model operand index) 358 RemapVectorType mModelOutputs; 359 // Temporaries of source model that are inputs of this step model: 360 // (source model operand index, step model operand index) 361 RemapVectorType mTempsAsStepModelInputs; 362 // Temporaries of source model that are outputs of this step model: 363 // (source model operand index, step model operand index) 364 StepModelOutputSetType mTempsAsStepModelOutputs; 365 // Outputs of main model that are inputs of this step model: 366 // (main model operand index, step model operand index) 367 RemapVectorType mOutputsAsStepModelInputs; 368 // Converts operand indexes from the source model to the step model. 369 std::unordered_map<uint32_t, uint32_t> mOperandMap; 370 // Converts input indexes from the step model to the main model 371 // (these are input indexes, not operand indexes). This vector 372 // only describes inputs of the step model that are also inputs of 373 // the main model -- that is, mModelInputs but not mTempsAsStepModelInputs. 374 std::vector<uint32_t> mInputIndexStepModelToMainModel; 375 // Converts output indexes from the step model to the main model 376 // (these are output indexes, not operand indexes). This vector 377 // only describes outputs of the step model that are also outputs of 378 // the main model -- that is, mModelOutputs but not 379 // mTempsAsStepModelOutputs. 380 std::vector<uint32_t> mOutputIndexStepModelToMainModel; 381 // Converts indexes into mOutputsAsStepModelInputs to indexes into 382 // main model outputs (these are input and output indexes, not 383 // operand indexes). To be specific, if the main model outputs 384 // are mainModelOutputs, 385 // 386 // mOutputsAsStepModelInputsIndexToMainModel.size() == 387 // mOutputsAsStepModelInputs.size() 388 // 389 // and when (0 <= i < mOutputsAsStepModelInputs.size()), 390 // 391 // mainModelOutputs[mOutputsAsStepModelInputsIndexToMainModel[i]] == 392 // mOutputsAsStepModelInputs[i].first 393 std::vector<uint32_t> mOutputsAsStepModelInputsIndexToMainModel; 394 395 // Step model output indexes (not operand indexes) that are outputs of the 396 // main model used as inputs to some other partition. 397 std::set<uint32_t> mModelOutputsThatAreDownstreamInputs; 398 399 // The compilation caching token. 400 TokenHasher mToken; 401 }; 402 403 // An IF operation to be run on the ExecutionPlan::next() interpreter. The 404 // branch models might run on devices. See LogicalStep. 405 // 406 // Execution plan structure: 407 // Index Step 408 // i if then=(i + 1) else=(j + 1) 409 // ... (then model steps) 410 // j goto k 411 // ... (else model steps) 412 // k (steps after the IF) 413 struct IfStep { 414 // The index of this step. 415 size_t index = ~size_t(0); 416 // The index of the first step of the "then" branch. 417 size_t thenStepIndex = ~size_t(0); 418 // The index of the first step of the "else" branch. 419 size_t elseStepIndex = ~size_t(0); 420 // The boolean condition input of the IF operation. The value of this 421 // operand determines the branch of the IF operation to be executed. 422 SourceOperandIndex conditionOperandIndex = {~uint32_t(0), ~uint32_t(0)}; 423 // Input operands of the IF operation to be passed to a branch model. 424 std::vector<SourceOperandIndex> outerInputOperands; 425 // Output operands of the IF operation. 426 std::vector<SourceOperandIndex> outerOutputOperands; 427 // Input operands of the "then" branch model. 428 std::vector<SourceOperandIndex> thenBranchInputOperands; 429 // Output operands of the "then" branch model. 430 std::vector<SourceOperandIndex> thenBranchOutputOperands; 431 // Input operands of the "else" branch model. 432 std::vector<SourceOperandIndex> elseBranchInputOperands; 433 // Output operands of the "else" branch model. 434 std::vector<SourceOperandIndex> elseBranchOutputOperands; 435 }; 436 437 // A WHILE operation to be run on the ExecutionPlan::next() interpreter. The 438 // condition and body models might run other devices. See LogicalStep. 439 // 440 // Execution plan structure: 441 // Index Step 442 // i while cond=(i + 1) body=(j + 1) exit=(k + 1) 443 // ... (cond model steps) 444 // j goto i 445 // ... (body model steps) 446 // k goto i 447 // ... (steps after the WHILE) 448 // 449 // Note that WhileStep has WhileState associated with it. 450 struct WhileStep { 451 // The index of this step. 452 size_t index = ~size_t(0); 453 // The index of the first step of the condition model. 454 size_t condStepIndex = ~size_t(0); 455 // The index of the first step of the body model. 456 size_t bodyStepIndex = ~size_t(0); 457 // The index of the first step after the loop. 458 size_t exitStepIndex = ~size_t(0); 459 // Input operands of the WHILE operation to be passed to the condition and 460 // body models. 461 std::vector<SourceOperandIndex> outerInputOperands; 462 // Output operands of the WHILE operation. 463 std::vector<SourceOperandIndex> outerOutputOperands; 464 // Input operands of the condition model. 465 std::vector<SourceOperandIndex> condInputOperands; 466 // Output operand of the condition model. The value of this operand 467 // determines whether to continue execution or exit the loop. 468 SourceOperandIndex condOutputOperand = {~uint32_t(0), ~uint32_t(0)}; 469 // Input operands of the body model. 470 std::vector<SourceOperandIndex> bodyInputOperands; 471 // Output operands of the body model. 472 std::vector<SourceOperandIndex> bodyOutputOperands; 473 }; 474 475 // A helper step. See LogicalStep. 476 struct GotoStep { 477 // The index of this step. 478 size_t index = ~size_t(0); 479 // The index of the step to go to. 480 size_t gotoStepIndex = ~size_t(0); 481 }; 482 483 // One of ExecutionStep, IfStep, WhileStep, or GotoStep. 484 // 485 // When ExecutionPlan::next() is called, it interprets logical steps until it 486 // encounters an ExecutionStep ("interpreted execution"). 487 // - For an IfStep, it decides which branch to take and proceeds to the 488 // corresponding step. 489 // - For a WhileStep, it decides whether to execute the condition or body (based 490 // on WhileState), or exit the loop (based on the condition model output), and 491 // proceeds to the corresponding step. 492 // - For a GotoStep, it proceeds to the indicated step unconditionally. 493 class LogicalStep { 494 public: 495 template <typename... Args> LogicalStep(Args &&...args)496 explicit LogicalStep(Args&&... args) : mStep(std::forward<Args>(args)...) {} 497 isExecution()498 bool isExecution() const { return std::holds_alternative<ExecutionStep>(mStep); } isIf()499 bool isIf() const { return std::holds_alternative<IfStep>(mStep); } isWhile()500 bool isWhile() const { return std::holds_alternative<WhileStep>(mStep); } isGoto()501 bool isGoto() const { return std::holds_alternative<GotoStep>(mStep); } 502 503 // Returns a non-null pointer or crashes. executionStep()504 ExecutionStep* executionStep() { return &std::get<ExecutionStep>(mStep); } ifStep()505 IfStep* ifStep() { return &std::get<IfStep>(mStep); } whileStep()506 WhileStep* whileStep() { return &std::get<WhileStep>(mStep); } gotoStep()507 GotoStep* gotoStep() { return &std::get<GotoStep>(mStep); } 508 509 // Returns a non-null pointer or crashes. executionStep()510 const ExecutionStep* executionStep() const { return &std::get<ExecutionStep>(mStep); } ifStep()511 const IfStep* ifStep() const { return &std::get<IfStep>(mStep); } whileStep()512 const WhileStep* whileStep() const { return &std::get<WhileStep>(mStep); } gotoStep()513 const GotoStep* gotoStep() const { return &std::get<GotoStep>(mStep); } 514 515 // May return nullptr. tryExecutionStep()516 ExecutionStep* tryExecutionStep() { return std::get_if<ExecutionStep>(&mStep); } tryIfStep()517 IfStep* tryIfStep() { return std::get_if<IfStep>(&mStep); } tryWhileStep()518 WhileStep* tryWhileStep() { return std::get_if<WhileStep>(&mStep); } tryGotoStep()519 GotoStep* tryGotoStep() { return std::get_if<GotoStep>(&mStep); } 520 521 // May return nullptr. tryExecutionStep()522 const ExecutionStep* tryExecutionStep() const { return std::get_if<ExecutionStep>(&mStep); } tryIfStep()523 const IfStep* tryIfStep() const { return std::get_if<IfStep>(&mStep); } tryWhileStep()524 const WhileStep* tryWhileStep() const { return std::get_if<WhileStep>(&mStep); } tryGotoStep()525 const GotoStep* tryGotoStep() const { return std::get_if<GotoStep>(&mStep); } 526 527 void dump() const; 528 529 private: 530 std::variant<ExecutionStep, IfStep, WhileStep, GotoStep> mStep; 531 }; 532 533 std::ostream& operator<<(std::ostream& os, const IfStep& step); 534 std::ostream& operator<<(std::ostream& os, const WhileStep& step); 535 std::ostream& operator<<(std::ostream& os, const GotoStep& step); 536 537 // Describes the state of WhileStep. 538 struct WhileState { 539 // A pseudo iteration number indicating the loop is not being executed. 540 static constexpr uint64_t kOutsideLoop = ~uint64_t(0); 541 // Whether we need to evaluate the condition or body next. 542 enum Stage { EVALUATE_CONDITION, EVALUATE_BODY } stage = EVALUATE_CONDITION; 543 // Current iteration number. Must be set to kOutsideLoop when exiting the 544 // loop. 545 uint64_t iteration = kOutsideLoop; 546 // Time point when the loop started executing. 547 TimePoint startTime; 548 }; 549 550 struct ConstantCopyLocation { 551 const uint8_t* buffer; 552 uint32_t length; 553 }; 554 555 struct ConstantReferenceLocation { 556 const RuntimeMemory* memory; 557 uint32_t offset; 558 uint32_t length; 559 }; 560 561 // A tuple of {execution_step_index, io_type, io_index} specifying an input/output role of an 562 // ExecutionStep. 563 using StepRole = std::tuple<uint32_t, IOType, uint32_t>; 564 565 // A callback function that takes the prepared_model, io_type, and io_index of a step role. 566 using StepRoleCallback = std::function<void(const RuntimePreparedModel*, IOType, uint32_t)>; 567 568 class ExecutionPlan { 569 public: 570 ExecutionPlan(const ExecutionPlan&) = delete; 571 ExecutionPlan& operator=(const ExecutionPlan&) = delete; 572 ExecutionPlan()573 ExecutionPlan() {} ~ExecutionPlan()574 ~ExecutionPlan() { delete mBody; } 575 576 // Controller is part of the interface to a mechanism for performing a 577 // main execution in N steps. 578 // 579 // The value of N may not be known beforehand if the model contains WHILE 580 // loops. See LogicalStep. 581 // 582 // Usage pattern: 583 // - Instantiate Controller with ExecutionPlan::makeController(). 584 // - Call ExecutionPlan::next() on Controller N+1 times. The first N times, 585 // *executor is set to point to a new StepExecutor corresponding 586 // to that step. The N+1st time, *executor is set to nullptr, 587 // signifying there are no more steps. 588 // - If ExecutionPlan::next() returns anything other than ANEURALNETWORKS_NO_ERROR, 589 // a problem has occurred. 590 class Controller { 591 friend class ExecutionPlan; 592 593 private: 594 Controller(const Controller&) = delete; 595 Controller& operator=(const Controller&) = delete; 596 597 static const size_t kBadStepIndex = ~size_t(0); 598 599 // A constructor for mState == COMPOUND. 600 Controller(const ExecutionPlan* plan, ExecutionBuilder* executionBuilder, 601 const BurstBuilder* burstBuilder, 602 603 // static temporaries 604 uint32_t totalSizeOfTemporaries, 605 std::map<SourceOperandIndex, StaticTemporaryLocation> 606 sourceOperandToLocationOfTemporary, 607 std::map<SourceOperandIndex, StaticTemporaryLocation> 608 sourceOperandToLocationOfTemporary2, 609 610 std::map<SourceOperandIndex, uint32_t> sourceOperandToInputIndex, 611 std::map<SourceOperandIndex, uint32_t> sourceOperandToOutputIndex, 612 const std::map<SourceOperandIndex, ConstantCopyLocation>& 613 sourceOperandToConstantCopy, 614 std::map<SourceOperandIndex, ConstantReferenceLocation> 615 sourceOperandToConstantReference, 616 DynamicTemporaries dynamicTemporaries); 617 618 // Sets the location of innerOperand to be the same as the location of outerOperand. 619 void setInput(const SourceOperandIndex& outerOperand, 620 const SourceOperandIndex& innerOperand); 621 void setOutput(const SourceOperandIndex& outerOperand, 622 const SourceOperandIndex& innerOperand); 623 624 // Wait for mLastStepSyncFd to signal. 625 // No-op if mLastStepSyncFd is -1 which the mLastStepSyncFd is initialized to. 626 // mLastStepSyncFd will also be set to -1 when the most recently processed step 627 // does not generate a sync fence. 628 int waitForLastStepSyncFence() const; 629 630 [[maybe_unused]] const ExecutionPlan* mPlan; 631 ExecutionBuilder* mExecutionBuilder; 632 const BurstBuilder* mBurstBuilder; 633 // Map from source operand index to an offset into mTemporaries used 634 // to represent that operand as an inter-partition input or output. 635 // 636 // The four maps 637 // - mSourceOperandToLocationOfTemporary 638 // - mSourceOperandToInputIndex 639 // - mSourceOperandToOutputIndex 640 // - mSourceOperandToConstantReference 641 // are initialized from similarly named fields of ExecutionPlan::CompoundBody. 642 // 643 // A particular key appears in at most one map at any given time. This 644 // restriction does not apply to mSourceOperandToLocationOfTemporary2. 645 // 646 // The maps are modified during the execution of IfStep and WhileStep. 647 // See ExecutionPlan::nextCompound(). 648 std::map<SourceOperandIndex, StaticTemporaryLocation> mSourceOperandToLocationOfTemporary; 649 // Map from source operand index to an additional offset into 650 // mTemporaries used for double buffering of WHILE loop output operands. 651 std::map<SourceOperandIndex, StaticTemporaryLocation> mSourceOperandToLocationOfTemporary2; 652 // Map from source operand index to an input index of the main model. 653 std::map<SourceOperandIndex, uint32_t> mSourceOperandToInputIndex; 654 // Map from source operand index to an output index of the main model. 655 std::map<SourceOperandIndex, uint32_t> mSourceOperandToOutputIndex; 656 // Map from source operand index to a constant reference location. 657 // Used for WHILE loop operand initializers that are constant references. 658 std::map<SourceOperandIndex, ConstantReferenceLocation> mSourceOperandToConstantReference; 659 660 // static temporaries 661 std::unique_ptr<MemoryAshmem> mTemporaries; 662 663 DynamicTemporaries mDynamicTemporaries; 664 665 // Index of the next step to be processed by ExecutionPlan::next(). 666 size_t mNextStepIndex; 667 // The value to reset mNextStepIndex to for partial CPU fallback. 668 size_t mFallbackNextStepIndex; 669 // Map from WhileStep index to the associated WhileState. 670 std::unordered_map<size_t, WhileState> mWhileState; 671 // The sync fence fd of the last step. 672 int mLastStepSyncFd; 673 }; 674 675 std::vector<SharedBurst> makeBursts() const; 676 677 // Only legal to call when mState == COMPOUND. 678 std::shared_ptr<Controller> makeController(ExecutionBuilder* executionBuilder, 679 const BurstBuilder* burstBuilder) const; 680 681 // Sets up a new StepExecutor and burstController (if applicable) if there 682 // is a step to execute. See ExecutionPlan::Controller. 683 // Handles control flow. See LogicalStep. 684 // burstController is nullptr if we are not to do burst execution. 685 // mainModelOutputShapes may be nullptr if the only main model outputs that are step model 686 // inputs are of fully specified shape. 687 // syncFdOfLastStep is the sync fence fd generated by the most recently processed step. 688 // Only legal to call when mState == COMPOUND. 689 int next(std::shared_ptr<Controller> controller, std::shared_ptr<StepExecutor>* executor, 690 SharedBurst* burstController, const std::vector<OutputShape>* mainModelOutputShapes, 691 int syncFdOfLastStep = -1) const; 692 693 // Create the same executor as the last one created by next(). 694 int fallback(std::shared_ptr<Controller> controller, std::shared_ptr<StepExecutor>* executor, 695 SharedBurst* burstController, 696 const std::vector<OutputShape>* mainModelOutputShapes) const; 697 698 // Only legal to call when mState == SIMPLE. 699 // See the constructor of StepExecutor for the semantics of "reusable". 700 std::shared_ptr<StepExecutor> makeStepExecutor(bool reusable, 701 ExecutionBuilder* executionBuilder) const; 702 703 ExecutionStep* createNewExecutionStep(uint32_t sourceModelIndex, 704 const std::shared_ptr<Device> device); 705 IfStep* createNewIfStep(); 706 WhileStep* createNewWhileStep(); 707 GotoStep* createNewGotoStep(); 708 709 // Only legal to call when mState == COMPOUND. getNextStepIndex()710 size_t getNextStepIndex() const { return compound()->mSteps.size(); } 711 712 void becomeSingleStep(const std::shared_ptr<Device> device, const ModelBuilder* model); 713 714 // simulateFailureResultCode == ANEURALNETWORKS_NO_ERROR means behave normally. 715 int finish(int32_t executionPreference, int32_t priority, const OptionalTimePoint& deadline, 716 int simulateFailureResultCode); 717 718 void recordOutputDef(SourceOperandIndex sourceOperandIndex, uint32_t stepIndex); 719 void recordTemporaryDef(SourceOperandIndex sourceOperandIndex, uint32_t stepIndex); 720 721 void dump() const; 722 723 void reset(); 724 isValid()725 bool isValid() const { return mState != EMPTY && mBody != nullptr && mBody->mSuccessfulFinish; } isSimple()726 bool isSimple() const { return mState == SIMPLE; } isCompound()727 bool isCompound() const { return mState == COMPOUND; } 728 bool isSimpleCpu() const; 729 setCaching(const CacheInfo * cacheInfo,const uint8_t * token)730 void setCaching(const CacheInfo* cacheInfo, const uint8_t* token) { 731 mCacheInfo = cacheInfo; 732 mToken = token; 733 } getCacheInfo()734 const CacheInfo* getCacheInfo() const { return mCacheInfo; } getCacheToken()735 const uint8_t* getCacheToken() const { return mToken; } 736 737 // The caller is responsible for making sure the index is within range. forEachStepRoleOfInput(uint32_t index,const StepRoleCallback & callback)738 void forEachStepRoleOfInput(uint32_t index, const StepRoleCallback& callback) const { 739 CHECK(mBody != nullptr); 740 mBody->forEachStepRoleOfInput(index, callback); 741 } forEachStepRoleOfOutput(uint32_t index,const StepRoleCallback & callback)742 void forEachStepRoleOfOutput(uint32_t index, const StepRoleCallback& callback) const { 743 CHECK(mBody != nullptr); 744 mBody->forEachStepRoleOfOutput(index, callback); 745 } 746 747 // "type" specifies input or output, and "index" is the main model input or output index. 748 // The caller is responsible for making sure the index is within range. 749 MemoryPreference getMemoryPreference(IOType type, uint32_t index) const; 750 getSourceModels()751 SourceModels& getSourceModels() { return mSourceModels; } getSourceModels()752 const SourceModels& getSourceModels() const { return mSourceModels; } 753 754 // "index" is the main model input or output index. 755 // The caller is responsible for making sure the index is within range. 756 SourceOperandIndex getInputSourceOperand(uint32_t index) const; 757 SourceOperandIndex getOutputSourceOperand(uint32_t index) const; 758 759 bool hasDynamicTemporaries() const; 760 761 // These functions are solely intended for use by unit tests of 762 // the partitioning algorithm. 763 enum class Kind { 764 ERROR, 765 EMPTY, 766 SIMPLE, 767 COMPOUND 768 }; // See operator<< defined outside this class 769 Kind forTest_getKind() const; 770 std::shared_ptr<const Device> forTest_simpleGetDevice() const; 771 const std::vector<std::shared_ptr<LogicalStep>>& forTest_compoundGetSteps() const; forTest_compoundForEachStepRoleOfSourceOperand(SourceOperandIndex index,const StepRoleCallback & callback)772 void forTest_compoundForEachStepRoleOfSourceOperand(SourceOperandIndex index, 773 const StepRoleCallback& callback) const { 774 compound()->forEachStepRoleOfSourceOperand(index, callback); 775 } 776 // The "flat" in the name signifies that this method requires that the 777 // model not contain any control flow operations. 778 std::set<uint32_t> forTest_flatGetDynamicTemporaries() const; 779 const uint8_t* forTest_simpleGetCacheToken() const; 780 bool forTest_hasStepModelWithNoInputsOrNoOutputs() const; 781 782 private: 783 // Becomes a new COMPOUND step if mState == EMPTY, otherwise does nothing. 784 // Illegal to call for when mState == SIMPLE. 785 void becomeCompoundIfEmpty(); 786 getSourceOperand(const std::pair<uint32_t,uint32_t> & sourceOperandIndex)787 const Operand& getSourceOperand(const std::pair<uint32_t, uint32_t>& sourceOperandIndex) const { 788 return getSourceModels() 789 .getModel(sourceOperandIndex.first) 790 ->getOperand(sourceOperandIndex.second); 791 } 792 793 class Buffer { 794 public: 795 Buffer(void* pointer, uint32_t size); 796 Buffer(RunTimePoolInfo info, uint32_t offset); 797 void* getPointer() const; 798 uint32_t getSize() const; 799 void flush() const; 800 801 private: 802 RunTimePoolInfo mInfo; 803 uint32_t mOffset; 804 }; 805 806 // Returns the buffer associated with a partition boundary operand. 807 std::optional<Buffer> getBuffer(std::shared_ptr<Controller> controller, 808 SourceOperandIndex operandIndex) const; 809 std::optional<Buffer> getBufferFromModelArgumentInfo( 810 const ModelArgumentInfo& info, const ExecutionBuilder* executionBuilder) const; 811 // Reads the value of a partition boundary boolean condition operand. 812 int readConditionValue(std::shared_ptr<Controller> controller, SourceOperandIndex operandIndex, 813 bool* value) const; 814 815 // Handles control flow. See LogicalStep. 816 int nextCompound(std::shared_ptr<Controller> controller, 817 std::shared_ptr<StepExecutor>* executor, SharedBurst* burstController, 818 const std::vector<OutputShape>* mainModelOutputShapes) const; 819 int nextCompound(const ExecutionStep* step, std::shared_ptr<Controller> controller, 820 std::shared_ptr<StepExecutor>* executor, SharedBurst* burstController, 821 const std::vector<OutputShape>* mainModelOutputShapes) const; 822 int nextCompound(const IfStep* step, std::shared_ptr<Controller> controller, 823 std::shared_ptr<StepExecutor>* executor, SharedBurst* burstController, 824 const std::vector<OutputShape>* mainModelOutputShapes) const; 825 int nextCompound(const WhileStep* step, std::shared_ptr<Controller> controller, 826 std::shared_ptr<StepExecutor>* executor, SharedBurst* burstController, 827 const std::vector<OutputShape>* mainModelOutputShapes) const; 828 int nextCompound(const GotoStep* step, std::shared_ptr<Controller> controller, 829 std::shared_ptr<StepExecutor>* executor, SharedBurst* burstController, 830 const std::vector<OutputShape>* mainModelOutputShapes) const; 831 832 struct Body { ~BodyBody833 virtual ~Body() {} 834 virtual void dump() const = 0; 835 virtual int finish(const SourceModels* sourceModels, int32_t executionPreference, 836 int32_t priority, const OptionalTimePoint& deadline, 837 int simulateFailureResultCode) = 0; 838 virtual bool hasDynamicTemporaries() const = 0; 839 virtual bool hasStepModelWithNoInputsOrNoOutputs() const = 0; 840 virtual void forEachStepRoleOfInput(uint32_t index, 841 const StepRoleCallback& callback) const = 0; 842 virtual void forEachStepRoleOfOutput(uint32_t index, 843 const StepRoleCallback& callback) const = 0; 844 bool mSuccessfulFinish = false; 845 }; 846 847 struct SimpleBody : Body { SimpleBodySimpleBody848 SimpleBody(std::shared_ptr<Device> device, const ModelBuilder* model, 849 const CacheInfo* cacheInfo, const uint8_t* token) 850 : mDevice(device), mModel(model), mCacheInfo(cacheInfo), mToken(token) {} 851 852 void dump() const override; 853 int finish(const SourceModels* sourceModels, int32_t executionPreference, int32_t priority, 854 const OptionalTimePoint& deadline, int simulateFailureResultCode) override; hasDynamicTemporariesSimpleBody855 bool hasDynamicTemporaries() const override { return false; } hasStepModelWithNoInputsOrNoOutputsSimpleBody856 bool hasStepModelWithNoInputsOrNoOutputs() const override { return false; } 857 void forEachStepRoleOfInput(uint32_t index, 858 const StepRoleCallback& callback) const override; 859 void forEachStepRoleOfOutput(uint32_t index, 860 const StepRoleCallback& callback) const override; 861 862 std::shared_ptr<Device> mDevice; 863 const ModelBuilder* mModel; 864 std::shared_ptr<RuntimePreparedModel> mPreparedModel; 865 866 const CacheInfo* mCacheInfo; 867 TokenHasher mToken; 868 }; 869 870 struct CompoundBody : Body { CompoundBodyCompoundBody871 CompoundBody(const ExecutionPlan* plan) : mPlan(plan) { CHECK(plan != nullptr); } 872 873 void dump() const override; 874 int finish(const SourceModels* sourceModels, int32_t executionPreference, int32_t priority, 875 const OptionalTimePoint& deadline, int simulateFailureResultCode) override; hasDynamicTemporariesCompoundBody876 bool hasDynamicTemporaries() const override { return mHasDynamicTemporaries; } 877 bool hasStepModelWithNoInputsOrNoOutputs() const override; 878 void forEachStepRoleOfInput(uint32_t index, 879 const StepRoleCallback& callback) const override; 880 void forEachStepRoleOfOutput(uint32_t index, 881 const StepRoleCallback& callback) const override; 882 // Supported for any legal source operand index. For a source operand that doesn't have a 883 // step role, the callback will not be invoked at all. 884 void forEachStepRoleOfSourceOperand(const SourceOperandIndex& index, 885 const StepRoleCallback& callback) const; 886 // Supported for any legal source operand index. 887 MemoryPreference getMemoryPreferenceOfSourceOperand(const SourceOperandIndex& index) const; 888 889 // TODO: Some of the data is working state information that 890 // shouldn't be needed after we've constructed but not 891 // executed the plan. 892 893 std::vector<std::shared_ptr<LogicalStep>> mSteps; 894 895 // Map from source operand index to defining ExecutionStep index. 896 // Used for all (and only) SUBGRAPH_OUTPUTs that are defined by 897 // ExecutionSteps. Those defined by IfSteps and WhileSteps are not in 898 // the map. 899 std::map<SourceOperandIndex, uint32_t> mOutputToDefiningExecutionStep; 900 901 // Map from source operand index to defining ExecutionStep index. 902 // Used for all (and only) TEMPORARY_VARIABLEs that are defined by 903 // ExecutionSteps. Those defined by IfSteps and WhileSteps are not in 904 // the map. 905 std::map<SourceOperandIndex, uint32_t> mTemporaryToDefiningExecutionStep; 906 907 // Map from source operand index to input index of the main model. 908 // This map only contains SUBGRAPH_INPUTs of the main model and is used 909 // to initialize ExecutionPlan::Controller::mSourceOperandToInputIndex; 910 std::map<SourceOperandIndex, uint32_t> mSourceOperandToInputIndex; 911 912 // Map from source operand index to output index of the main model. 913 // This map only contains SUBGRAPH_OUTPUTs of the main model and is used 914 // to initialize ExecutionPlan::Controller::mSourceOperandToOutputIndex; 915 std::map<SourceOperandIndex, uint32_t> mSourceOperandToOutputIndex; 916 917 // Map from source operand index to location of a CONSTANT_COPY or 918 // POINTER operand. 919 // This map only contains constant partition boundary IF and WHILE 920 // operands and is used to create a ExecutionPlan::Controller. 921 std::map<SourceOperandIndex, ConstantCopyLocation> mSourceOperandToBoundaryConstantCopy; 922 923 // Map from source operand index to location of a CONSTANT_REFERENCE 924 // operand. This map only contains constant partition boundary IF and 925 // WHILE operands and is used to initialize 926 // ExecutionPlan::Controller::mSourceOperandToConstantReference. 927 std::map<SourceOperandIndex, ConstantReferenceLocation> 928 mSourceOperandToBoundaryConstantReference; 929 930 // Map from source operand index of a boundary operand to the step roles that its memory 931 // may be used for. 932 // This map only contains partition boundary operands that have ExecutionStep roles, that 933 // is, SUBGRAPH_INPUTs, SUBGRAPH_OUTPUTs, and partition boundary static and dynamic 934 // temporaries. If a partition boundary operand is not found in the map, then the operand 935 // does not have any ExecutionStep role (this may happen with interpreted control flow). 936 std::map<SourceOperandIndex, std::set<StepRole>> mSourceOperandToStepRoles; 937 938 bool mHasDynamicTemporaries = false; 939 940 private: 941 void findTempsAsStepModelOutputs(); 942 943 void findModelOutputsThatAreDownstreamInputs(); 944 945 // Constant values that are inputs to IF and WHILE operations and lie on 946 // a partition boundary ("control flow boundary constants") require 947 // special treatment. We need to be able to dynamically associate those 948 // values with the corresponding SUBGRAPH_INPUT operands in a referenced 949 // model. 950 // 951 // For CONSTANT_COPY and POINTER boundary operands, we copy those to 952 // temporary memory and treat them similarly to TEMPORARY_VARIABLE 953 // operands in Controller. 954 // 955 // For CONSTANT_REFERENCE boundary operands, we keep track of them in 956 // ExecutionPlan::Controller::mSourceOperandToConstantReference. 957 // 958 // Note that for IF inputs and input-only WHILE inputs that are boundary 959 // constants, we could embed those inside the referenced model, but we 960 // currently don't do so. See b/148216514. 961 void findControlFlowBoundaryConstants(const SourceModels* sourceModels); 962 963 // This method will set mSourceOperandToStepRoles. 964 void findMemoryStepRoles(); 965 966 const ExecutionPlan* mPlan; 967 }; 968 969 enum { EMPTY, SIMPLE, COMPOUND } mState = EMPTY; 970 Body* mBody = nullptr; simple()971 SimpleBody* simple() { 972 CHECK(mState == SIMPLE); 973 CHECK(mBody != nullptr); 974 return static_cast<SimpleBody*>(mBody); 975 } simple()976 const SimpleBody* simple() const { 977 CHECK(mState == SIMPLE); 978 CHECK(mBody != nullptr); 979 return static_cast<const SimpleBody*>(mBody); 980 } compound()981 CompoundBody* compound() { 982 CHECK(mState == COMPOUND); 983 CHECK(mBody != nullptr); 984 return static_cast<CompoundBody*>(mBody); 985 } compound()986 const CompoundBody* compound() const { 987 CHECK(mState == COMPOUND); 988 CHECK(mBody != nullptr); 989 return static_cast<const CompoundBody*>(mBody); 990 } 991 992 void forEachDynamicTemporary(const std::function<void(SourceOperandIndex, const Operand&, 993 uint32_t definingStepIndex)>&) const; 994 995 // Pointers to compilation caching information in CompilationBuilder. 996 const CacheInfo* mCacheInfo = nullptr; 997 const uint8_t* mToken = nullptr; 998 999 SourceModels mSourceModels; 1000 }; 1001 1002 inline std::ostream& operator<<(std::ostream& out, ExecutionPlan::Kind kind) { 1003 const int intKind = static_cast<int>(kind); 1004 if (kind < ExecutionPlan::Kind::ERROR || kind > ExecutionPlan::Kind::COMPOUND) { 1005 return out << "<UNK(" << intKind << ")>"; 1006 } 1007 static const char* name[] = {"ERROR", "EMPTY", "SIMPLE", "COMPOUND"}; 1008 return out << name[intKind]; 1009 } 1010 1011 } // namespace nn 1012 } // namespace android 1013 1014 #endif // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_EXECUTION_PLAN_H 1015