ExecutionPlan.h revision b26049114bc4c64e6bea3a5d5d129fcaec8e69b6
191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet/* 291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * Copyright (C) 2017 The Android Open Source Project 391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * 491e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * Licensed under the Apache License, Version 2.0 (the "License"); 591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * you may not use this file except in compliance with the License. 691e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * You may obtain a copy of the License at 791e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * 891e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * http://www.apache.org/licenses/LICENSE-2.0 991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * 1091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * Unless required by applicable law or agreed to in writing, software 1191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * distributed under the License is distributed on an "AS IS" BASIS, 1291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * See the License for the specific language governing permissions and 1491e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet * limitations under the License. 1591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet */ 1691e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet 1791e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet// Classes used to plan how to execute a model across multiple devices. 1891e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet 1991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#ifndef ANDROID_ML_NN_RUNTIME_EXECUTION_PLAN_H 2091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#define ANDROID_ML_NN_RUNTIME_EXECUTION_PLAN_H 2191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet 2291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#include "HalInterfaces.h" 2391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#include "Memory.h" 2491e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#include "NeuralNetworks.h" 2591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#include "Utils.h" 2691e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet 278913ae3283de7752aed108c1b26aef1adacb049fDavid Gross#include <set> 288913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 2991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletnamespace android { 3091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletnamespace nn { 3191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet 3291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass CompilationBuilder; 3391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass Device; 34b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Grossclass ExecutionBuilder; 358913ae3283de7752aed108c1b26aef1adacb049fDavid Grossclass ExecutionPlan; 3691e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass Memory; 3791e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass ModelBuilder; 38b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Grossclass StepExecutor; 3991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet 4091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass ExecutionStep { 418913ae3283de7752aed108c1b26aef1adacb049fDavid Grossprivate: 428913ae3283de7752aed108c1b26aef1adacb049fDavid Gross typedef std::vector<std::pair<uint32_t, uint32_t>> RemapVectorType; 43b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross typedef std::set<std::pair<uint32_t, uint32_t>> SubModelOutputSetType; 448913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 4591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletpublic: 46f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross enum OperandKind { INPUT, OUTPUT }; 47f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross 488913ae3283de7752aed108c1b26aef1adacb049fDavid Gross ExecutionStep(ExecutionPlan* plan, 498913ae3283de7752aed108c1b26aef1adacb049fDavid Gross uint32_t stepIndex, 508913ae3283de7752aed108c1b26aef1adacb049fDavid Gross std::shared_ptr<ModelBuilder> model, 518913ae3283de7752aed108c1b26aef1adacb049fDavid Gross std::shared_ptr<Device> device); 5291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet int addOperation(int operationIndex, const ModelBuilder& fromModel); 5391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet int addOperand(uint32_t fromOperandIndex, uint32_t* toOperandIndex, 54f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross const ModelBuilder& fromModel, OperandKind kind); 5591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet 568913ae3283de7752aed108c1b26aef1adacb049fDavid Gross // Each vector entry is of the form (fromModel index, subModel index) 578913ae3283de7752aed108c1b26aef1adacb049fDavid Gross const RemapVectorType& getSubModelInputs() const { 588913ae3283de7752aed108c1b26aef1adacb049fDavid Gross return mSubModelInputs; 598913ae3283de7752aed108c1b26aef1adacb049fDavid Gross } 608913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 61b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross size_t countSubModelOutputs() const; 62b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross 638913ae3283de7752aed108c1b26aef1adacb049fDavid Gross void recordSubModelOutput(uint32_t fromModelIndex) { 648913ae3283de7752aed108c1b26aef1adacb049fDavid Gross const auto it = mOperandMap.find(fromModelIndex); 658913ae3283de7752aed108c1b26aef1adacb049fDavid Gross nnAssert(it != mOperandMap.end()); 668913ae3283de7752aed108c1b26aef1adacb049fDavid Gross mSubModelOutputs.insert(std::make_pair(fromModelIndex, it->second)); 678913ae3283de7752aed108c1b26aef1adacb049fDavid Gross } 688913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 69b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // If this step has a submodel output of unknown size, sets 70b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // *hasOutputOfUnknownSize to true; otherwise, leaves it 71b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // unchanged. 72b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross int finishSubModel(bool* hasOutputOfUnknownSize); 738913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 748913ae3283de7752aed108c1b26aef1adacb049fDavid Gross void dump() const; 7591e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletprivate: 76f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross // TODO: Some of the data is working state information that 77f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross // shouldn't be needed after we've constructed but not executed 78f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross // the step. 79f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross 808913ae3283de7752aed108c1b26aef1adacb049fDavid Gross ExecutionPlan* mPlan; 818913ae3283de7752aed108c1b26aef1adacb049fDavid Gross uint32_t mIndex; // index of step within plan 8291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet std::shared_ptr<ModelBuilder> mSubModel; 838913ae3283de7752aed108c1b26aef1adacb049fDavid Gross std::shared_ptr<Device> mDevice; // nullptr signifies CPU 841f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross sp<IPreparedModel> mPreparedSubModel; // not used for CPU 85f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross 86f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross // Inputs of original model that are also inputs of this submodel: 87f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross // (fromModel index, subModel index) 888913ae3283de7752aed108c1b26aef1adacb049fDavid Gross RemapVectorType mModelInputs; 89f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross // Outputs of original model that are also outputs of this submodel: 90f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross // (fromModel index, subModel index) 918913ae3283de7752aed108c1b26aef1adacb049fDavid Gross RemapVectorType mModelOutputs; 92f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross // Temporaries of original model that are inputs of this submodel: 93f4e1c640547a44c7a37209e81ee5f3831b7d0fdcDavid Gross // (fromModel index, subModel index) 948913ae3283de7752aed108c1b26aef1adacb049fDavid Gross RemapVectorType mSubModelInputs; 958913ae3283de7752aed108c1b26aef1adacb049fDavid Gross // Temporaries of original model that are outputs of this submodel: 968913ae3283de7752aed108c1b26aef1adacb049fDavid Gross // (fromModel index, subModel index) 97b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross SubModelOutputSetType mSubModelOutputs; 9891e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet // Converts operand indexes from the main model to the submodel. 9991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet std::unordered_map<uint32_t, uint32_t> mOperandMap; 10091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet}; 10191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet 10291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouilletclass ExecutionPlan { 1038913ae3283de7752aed108c1b26aef1adacb049fDavid Grosspublic: 1041f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross ExecutionPlan(const ExecutionPlan&) = delete; 1051f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross ExecutionPlan& operator=(const ExecutionPlan&) = delete; 1068913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 1071f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross ExecutionPlan() { } 1081f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross ~ExecutionPlan() { delete mBody; } 1091f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 110b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // Controller is part of the interface to a mechanism for 111b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // performing an execution in N steps. 112b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // 113b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // Usage pattern: 114b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // - Instantiate Controller with ExecutionPlan::makeController(). 115b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // - Call ExecutionPlan::next() on Controller N+1 times. The first N times, 116b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // *executor is set to point to a new StepExecutor corresponding 117b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // to that step. The N+1st time, *executor is set to nullptr, 118b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // signifying there are no more steps. 119b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // - If ExecutionPlan::next() returns anything other than ANEURALNETWORKS_NO_ERROR, 120b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // a problem has occurred. 121b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross class Controller { 122b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross friend class ExecutionPlan; 123b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross private: 124b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross static const size_t kBadStepIndex = ~size_t(0); 125b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross 126b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross Controller(const ExecutionPlan* plan, const ExecutionBuilder* executionBuilder) : 127b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross mPlan(plan), mExecutionBuilder(executionBuilder), mNextStepIndex(0) {} 128b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross Controller() {} // used for error state 129b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross 130b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross const ExecutionPlan* mPlan = nullptr; 131b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross const ExecutionBuilder* mExecutionBuilder = nullptr; 132b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross size_t mNextStepIndex = kBadStepIndex; 133b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross }; 134b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross 135b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross Controller makeController(const ExecutionBuilder* executionBuilder) const; 136b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross 137b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross int next(Controller* controller, std::shared_ptr<StepExecutor>* executor) const; 138b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross 1391f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross std::shared_ptr<ExecutionStep> createNewStep(const std::shared_ptr<Device> device); 1401f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 1411f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross void becomeSingleStep(const std::shared_ptr<Device> device, 1421f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross const ModelBuilder* model); 1431f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 1441f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross int finish(); 1458913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 1468913ae3283de7752aed108c1b26aef1adacb049fDavid Gross void recordTemporaryDef(uint32_t fromModelIndex, uint32_t stepIndex) { 1471f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross auto& temporaryToDefiningStep = compound()->mTemporaryToDefiningStep; 1481f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross nnAssert(temporaryToDefiningStep.count(fromModelIndex) == 0); 1491f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross temporaryToDefiningStep.insert(std::make_pair(fromModelIndex, stepIndex)); 15091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet } 1518913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 1528913ae3283de7752aed108c1b26aef1adacb049fDavid Gross void dump() const; 1538913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 1541f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross // TODO: This member function is only temporary, until we finish 1551f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross // fully integrating ExecutionPlan with the compilation and 156b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // execution phases of the NN API. 1571f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross // 158b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // Returns true if the plan is "in scope for execution" -- i.e., 159b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // the structure of the plan is such that the 160b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // currently-implemented execution system ought to be able to 161b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // handle it. May return true even if something went wrong with 162b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // the partitioning and compilation process. 1631f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross // 164b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // true - single partition (even if compilation failed) 165b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // false - multiple partitions 166b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross bool shouldBeExecutable() const; 1671f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 1688913ae3283de7752aed108c1b26aef1adacb049fDavid Grossprivate: 1698913ae3283de7752aed108c1b26aef1adacb049fDavid Gross void findSubModelOutputs(); 1708913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 1711f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross struct Body { 1721f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross virtual ~Body() {} 1731f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross virtual void dump() const = 0; 1741f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross virtual int finish() = 0; 1751f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross }; 1761f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 1771f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross struct SimpleBody : Body { 1781f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross SimpleBody(std::shared_ptr<Device> device, const ModelBuilder* model) : 1791f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross mDevice(device), mModel(model) {} 1808913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 1811f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross void dump() const override; 1821f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross int finish() override; 1838913ae3283de7752aed108c1b26aef1adacb049fDavid Gross 1841f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross std::shared_ptr<Device> mDevice; // nullptr signifies CPU 1851f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross const ModelBuilder* mModel; 1861f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross sp<IPreparedModel> mPreparedModel; // not used for CPU 1871f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross bool mSuccessfulFinish = false; 1881f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross }; 1891f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 1901f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross struct CompoundBody : Body { 1911f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross void dump() const override; 1921f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross int finish() override; 1931f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 1941f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross // TODO: Some of the data is working state information that 1951f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross // shouldn't be needed after we've constructed but not 1961f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross // executed the plan. 1971f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 1981f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross std::vector<std::shared_ptr<ExecutionStep>> mSteps; 1991f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 2001f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross // Map from original operand index to defining step index. 2011f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross // Used for all (and only) TEMPORARY_VARIABLEs. 2021f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross std::unordered_map<uint32_t, uint32_t> mTemporaryToDefiningStep; 2031f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 204b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross // Total number of submodel outputs across all steps. 205b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross size_t mSubModelOutputCount = 0; 206b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross 207b26049114bc4c64e6bea3a5d5d129fcaec8e69b6David Gross bool mHasSubModelOutputOfUnknownSize = true; 2081f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross private: 2091f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross void findSubModelOutputs(); 2101f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross }; 2111f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross 2121f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross enum { EMPTY, SIMPLE, COMPOUND } mState = EMPTY; 2131f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross Body* mBody = nullptr; 2141f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross CompoundBody* compound() { 2151f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross nnAssert(mState == COMPOUND); 2161f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross return static_cast<CompoundBody*>(mBody); 2171f4381539b7e89c42336ee7cd1addb9a4c317b34David Gross } 21891e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet}; 21991e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet 22091e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet} // namespace nn 22191e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet} // namespace android 22291e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet 22391e8417c4c395e3922d12abfd956b93b71121976Jean-Luc Brouillet#endif // ANDROID_ML_NN_RUNTIME_EXECUTION_PLAN_H 224