// // PipelineModule.cpp // MNN // // Created by MNN on 2020/01/09. // Copyright © 2018, Alibaba Group Holding Limited // #include "PipelineModule.hpp" #include #include #include "StaticModule.hpp" #include "IfModule.hpp" #include "WhileModule.hpp" #include "NMSModule.hpp" #include "MoEModule.hpp" #include "Utils.hpp" #include "core/Backend.hpp" #include "core/WrapExecution.hpp" #include "core/FileLoader.hpp" #include "core/OpCommonUtils.hpp" #include "utils/InitNet.hpp" #include "RuntimeAttr.hpp" #include "geometry/GeometryComputer.hpp" #include "geometry/GeometryComputerUtils.hpp" #include using namespace MNN::Express; namespace MNN { namespace Express { //#define DYNAMIC //#define MNN_PIPELINE_MODULE_DEBUG ExprModule::ExprModule(EXPRP expr) { mExpr = expr; setName(expr->name()); mInputs = expr->inputs(); auto op = mExpr->get(); if (op) { auto typeName = EnumNameOpType(op->type()); setType(typeName); } for (int i = 0; i < mInputs.size(); ++i) { auto inputExpr = mInputs[i]->expr().first; if (inputExpr->get() != nullptr) { mInputs[i] = nullptr; mInputIndexes.emplace_back(i); continue; } switch (inputExpr->inputType()) { case VARP::INPUT: mInputs[i] = nullptr; mInputIndexes.emplace_back(i); break; case VARP::CONSTANT: break; case VARP::TRAINABLE: addParameter(mInputs[i]); break; default: break; } } // TODO: Optimize the logic if (!mExpr->mCanDecompose) { ExecutorScope::Current()->setLazyComputeMode(Executor::LAZY_CONTENT); } } std::vector ExprModule::onForward(const std::vector& inputs) { MNN_ASSERT(mInputIndexes.size() == inputs.size()); if (nullptr == mExpr->get()) { return {Variable::create(mExpr)}; } std::vector tempInputs = mInputs; for (int i = 0; i < inputs.size(); ++i) { tempInputs[mInputIndexes[i]] = inputs[i]; } std::vector outputVars; auto newExpr = Expr::create(mExpr->extra(), std::move(tempInputs), mExpr->outputSize()); newExpr->setName(mExpr->name()); if (!mExpr->mCanDecompose) { // Set tensor shape from net newExpr->mCanDecompose = false; for (int index = 0; index < mExpr->outputSize(); ++index) { TensorUtils::copyShape(mExpr->inside()->mOutputTensors[index], newExpr->inside()->mOutputTensors[index], true, true); Utils::copyTensorToInfo(newExpr->inside()->mOutputInfos.data() + index, newExpr->inside()->mOutputTensors[index]); } } for (int i = 0; i < mExpr->outputSize(); ++i) { outputVars.emplace_back(Variable::create(newExpr, i)); } return outputVars; } Module* ExprModule::clone(CloneContext* ctx) const { MNN_ERROR("Don't support clone for Expr Module\n"); return nullptr; } PipelineModule::PipelineModule(std::vector inputs, std::vector outputs, const Transformer& transformFunction) { setType(PIPELINE_MODULE); std::map inputExpr; for (int i=0; iexpr().first; inputExpr.insert(std::make_pair(expr, i)); } std::vector executeOrder = Variable::getExecuteOrder(outputs); // Set Indexes std::map indexes; mInputSize = inputs.size(); int currentIndexes = inputs.size(); for (auto expr : executeOrder) { if (inputExpr.find(expr) != inputExpr.end()) { indexes[expr] = inputExpr[expr]; continue; } indexes[expr] = currentIndexes; currentIndexes += expr->outputSize(); } std::set inputSets; mStackSize = currentIndexes; for (auto v : inputs) { auto inputExpr = v->expr(); inputSets.insert(inputExpr.first); } mOutputIndex.clear(); for (auto output : outputs) { auto outputExpr = output->expr(); mOutputIndex.emplace_back(indexes[outputExpr.first] + outputExpr.second); } // Create All SubModule for (auto expr : executeOrder) { if (inputSets.find(expr) != inputSets.end()) { continue; } std::pair, std::shared_ptr > moduleResult; bool extracted = false; if (!transformFunction) { moduleResult = std::make_pair(std::vector{}, std::shared_ptr(nullptr)); } else { moduleResult = transformFunction(expr); } if (moduleResult.second == nullptr) { std::shared_ptr module(new ExprModule(expr)); moduleResult.first = ((ExprModule*)module.get())->inputIndexes(); moduleResult.second = module; } else { extracted = true; } auto subInputs = expr->inputs(); auto& exprInputIndexes = moduleResult.first; std::vector inputIndexes; if (exprInputIndexes.empty() && extracted) { inputIndexes.resize(subInputs.size()); for (int i = 0; i < inputIndexes.size(); ++i) { auto inputExpr = subInputs[i]->expr(); inputIndexes[i] = indexes[inputExpr.first] + inputExpr.second; } } else { inputIndexes.resize(exprInputIndexes.size()); for (int i = 0; i < inputIndexes.size(); ++i) { auto inputExpr = subInputs[exprInputIndexes[i]]->expr(); inputIndexes[i] = indexes[inputExpr.first] + inputExpr.second; } } std::vector outputIndexes(expr->outputSize()); for (int i = 0; i < outputIndexes.size(); ++i) { outputIndexes[i] = indexes[expr] + i; } mSubModules.emplace_back(std::make_tuple(moduleResult.second, inputIndexes, outputIndexes)); registerModel({moduleResult.second}); } } std::vector PipelineModule::countOutputReference(std::vector outputIndices) { MNN_ASSERT(outputIndices.size() > 0); std::vector countResult(outputIndices.size(), 0); for (int i = 0; i < mSubModules.size(); i++) { auto &m = mSubModules[i]; auto& theModule = std::get<0>(m); auto name = theModule->name(); auto &inputIndices = std::get<1>(m); for (int j = 0; j < inputIndices.size(); j++) { int index = inputIndices[j]; for (int k = 0; k < countResult.size(); k++) { if (index == outputIndices[k]) { countResult[k]++; } } } } return countResult; } int PipelineModule::onOptimize(Interpreter::SessionMode stage) { if (stage == Interpreter::Module_Forward_Separate) { if (mSubModules.size() == 1 && std::get<0>(mSubModules[0])->type() == "StaticModule") { mSeperate = true; return 0; } return NOT_SUPPORT; } else if (stage == Interpreter::Module_Forward_Combine) { mSeperate = false; } return 0; } std::vector PipelineModule::onForward(const std::vector& inputs) { if (mSeperate && inputs.empty()) { for (int index = 0; index < mSubModules.size(); ++index) { auto& m = mSubModules[index]; std::get<0>(m)->onForward(inputs); } return {}; } std::vector mStack(mStackSize); for (int i = 0; i < mInitVars.size(); ++i) { mStack[i + mInputSize] = mInitVars[i]; } MNN_ASSERT(mInputSize == inputs.size()); for (int i = 0; i < mInputSize; ++i) { mStack[i] = inputs[i]; } for (int index = 0; index < mSubModules.size(); ++index) { auto& m = mSubModules[index]; std::vector tempInputs(std::get<1>(m).size()); for (int i = 0; i < tempInputs.size(); ++i) { auto stackInput = std::get<1>(m)[i]; tempInputs[i] = mStack[stackInput]; MNN_ASSERT(nullptr != tempInputs[i]); } std::vector tempOutputs = std::get<0>(m)->onForward(tempInputs); if(tempOutputs.size() != std::get<2>(m).size()) { // Execute has error return {}; } for (int i = 0; i < tempOutputs.size(); ++i) { mStack[std::get<2>(m)[i]] = tempOutputs[i]; MNN_ASSERT(nullptr != tempOutputs[i]); } } std::vector outputs(mOutputIndex.size()); for (int i = 0; i < mOutputIndex.size(); ++i) { outputs[i] = mStack[mOutputIndex[i]]; } return outputs; } void PipelineModule::onClearCache() { // Do nothing } void PipelineModule::_createSubGraph(const MNN::Net* net, std::shared_ptr rtMgr, const Module::Config* config, std::map& subGraphMap) { auto subGraphs = net->subgraphs(); if (nullptr == subGraphs) { return; } for (int i=0; isize(); ++i) { auto graph = subGraphs->GetAs(i); std::vector subInputs; std::vector subOutputs; if (nullptr != graph->inputs()) { for (int v=0; vinputs()->size(); ++v) { auto index = graph->inputs()->data()[v]; subInputs.emplace_back(graph->tensors()->GetAsString(index)->str()); } } for (int v=0; voutputs()->size(); ++v) { auto index = graph->outputs()->data()[v]; subOutputs.emplace_back(graph->tensors()->GetAsString(index)->str()); } #ifdef MNN_PIPELINE_MODULE_DEBUG for (auto& s : subOutputs) { FUNC_PRINT_ALL(s.c_str(), s); } FUNC_PRINT_ALL(graph->name()->c_str(), s); #endif // Pack to Net for loading std::shared_ptr submodule; { std::unique_ptr _tempInfo(graph->UnPack()); std::unique_ptr _tempNet(new NetT); _tempNet->oplists = std::move(_tempInfo->nodes); _tempNet->tensorName = std::move(_tempInfo->tensors); _tempNet->extraTensorDescribe = std::move(_tempInfo->extraTensorDescribe); flatbuffers::FlatBufferBuilder builder(1024); auto offset = Net::Pack(builder, _tempNet.get()); builder.Finish(offset); std::shared_ptr bufferStorage(new BufferStorage); bufferStorage->storage = builder.ReleaseRaw(bufferStorage->allocated_size, bufferStorage->offset); submodule.reset(PipelineModule::load(subInputs, subOutputs, bufferStorage, rtMgr, config, subGraphMap)); if (graph->name() != nullptr) { submodule->setName(graph->name()->str()); } } auto key = graph->name()->str(); SubGraph subgraph; subgraph.inputs = std::move(subInputs); subgraph.outputs = std::move(subOutputs); subgraph.m = submodule; subGraphMap.insert(std::make_pair(key, subgraph)); } return; } struct SubModuleInfo { std::vector opList; std::vector inputs;; std::vector outputs; std::vector tensorMask; bool isBreak = false; }; static void _computeTensorMask(SubModuleInfo& m, const Net* net) { /**Compute All SubModule's inputs and outputs*/ // 0: not use, 1: input, 2: output, 3: mid, 4: valid output m.tensorMask = std::vector(net->tensorName()->size(), 0); auto& tensorMask = m.tensorMask; for (auto opIndex : m.opList) { auto op = net->oplists()->GetAs(opIndex); if (nullptr != op->inputIndexes()) { for (int v=0; vinputIndexes()->size(); ++v) { auto index = op->inputIndexes()->data()[v]; tensorMask[index] = tensorMask[index] | 1; } } if (nullptr != op->outputIndexes()) { for (int v=0; voutputIndexes()->size(); ++v) { auto index = op->outputIndexes()->data()[v]; tensorMask[index] = tensorMask[index] | 2; } } } } static bool isBreakOp(const Op* op) { bool isWhileControlflow = false; if (op->type() == OpType_While && op->main_as_WhileParam() != nullptr) { isWhileControlflow = true; } if (op->type() == OpType_If || isWhileControlflow || op->type() == OpType_Where || op->type() == OpType_Segment || op->type() == OpType_Unique || op->type() == OpType_NonMaxSuppressionV2 || op->type() == OpType_MoE) { return true; } return false; } static std::vector _collectNeededOps(const MNN::Net* net, const std::set& inputIndexes, const std::set& outputIndexes) { // 0: not set, 1: output, 2:input std::vector tensorMask(net->tensorName()->size()); ::memset(tensorMask.data(), 0, tensorMask.size() * sizeof(int)); // 0: use, 1: no use std::vector opMask(net->oplists()->size()); ::memset(opMask.data(), 0, opMask.size() * sizeof(int)); // Set Initial Status for (auto v : outputIndexes) { tensorMask[v] = 1; } for (auto v : inputIndexes) { // If both input/output, set as input tensorMask[v] = 2; } bool change = false; do { change = false; for (int i=0; i 0) { continue; } auto op = net->oplists()->GetAs(i); if (nullptr != op->outputIndexes()) { for (int j=0; joutputIndexes()->size(); ++j) { auto index = op->outputIndexes()->data()[j]; if (tensorMask[index] == 1) { opMask[i] = 1; change = true; } } } if (nullptr != op->inputIndexes() && opMask[i]) { for (int j=0; jinputIndexes()->size(); ++j) { auto index = op->inputIndexes()->data()[j]; if (tensorMask[index] != 2) { tensorMask[index] = 1; } } } } } while (change); std::vector ops; for (int i=0; i 0) { auto op = net->oplists()->GetAs(i); if (needComputeOp(op)) { ops.emplace_back(i); continue; } } } return ops; } static std::vector _findBreakIndex(const SubModuleInfo& info, const Net* net, std::shared_ptr sharedConst) { // 0: not used, 1: const, 2: output std::vector constMask(sharedConst->allTensors.size(), 0); for (int i=0; iallTensors.size(); ++i) { if(sharedConst->allTensors[i].get() != nullptr) { constMask[i] = 1; } } for (int v = 0; v < info.opList.size(); ++v) { auto op = net->oplists()->GetAs(info.opList[v]); if (nullptr == op->outputIndexes()) { continue; } bool isConst = true; if (nullptr != op->inputIndexes()) { for (int i=0; iinputIndexes()->size(); ++i) { auto index = op->inputIndexes()->data()[i]; if (constMask[index]) { continue; } if (OpCommonUtils::opNeedContent(op, i)) { isConst = false; break; } } } if (isConst) { for (int i=0; ioutputIndexes()->size(); ++i) { auto index = op->outputIndexes()->data()[i]; constMask[index] = 1; } } } std::vector res; // Check Break Index for (int v = 0; v < info.opList.size(); ++v) { auto op = net->oplists()->GetAs(info.opList[v]); if (nullptr == op->outputIndexes() || nullptr == op->inputIndexes()) { continue; } int inputNum = op->inputIndexes()->size(); auto dims = SizeComputer::needInputContent(op, inputNum); for (auto index : dims) { if (index < inputNum) { if (constMask[op->inputIndexes()->data()[index]] != 1) { res.emplace_back(v); break; } } } } return res; } static std::vector _splitSubModuleForShapeConst(const std::vector& origin, const Net* net, std::shared_ptr sharedConst) { std::vector res; for (auto& m : origin) { if (m.isBreak) { res.emplace_back(std::move(m)); continue; } auto breakIndexes = _findBreakIndex(m, net, sharedConst); if (breakIndexes.size() > 0) { int current = 0; for (auto breakIndex : breakIndexes) { // Split if (breakIndex > current) { SubModuleInfo m0; m0.opList.insert(m0.opList.begin(), m.opList.begin() + current, m.opList.begin() + breakIndex); res.emplace_back(std::move(m0)); } SubModuleInfo m1; m1.opList = {m.opList[breakIndex]}; res.emplace_back(std::move(m1)); current = breakIndex + 1; } if (current < m.opList.size()) { SubModuleInfo m2; m2.opList.insert(m2.opList.begin(), m.opList.begin() + current, m.opList.end()); res.emplace_back(std::move(m2)); } } else { res.emplace_back(std::move(m)); } } return res; } static std::vector _createSubModuleInfo(std::shared_ptr bufferStorage, const std::set& inputIndexes, const std::set& outputIndexes, const std::set& noComputeIndexes, std::shared_ptr sharedConst, bool& success) { std::vector submodule; auto net = flatbuffers::GetRoot(bufferStorage->buffer()); auto selectOps = _collectNeededOps(net, inputIndexes, outputIndexes); success = true; // Separate the graph to serveral submodule SubModuleInfo current; for (int si=0; sioplists()->GetAs(i); if (isBreakOp(op)) { // TODO: Don't need split segment if (current.opList.size() > 0) { // Not empty submodule.emplace_back(std::move(current)); } SubModuleInfo controlOp; controlOp.opList = {i}; controlOp.isBreak = true; if (nullptr != op->inputIndexes()) { controlOp.inputs.resize(op->inputIndexes()->size()); ::memcpy(controlOp.inputs.data(), op->inputIndexes()->data(), controlOp.inputs.size() * sizeof(int)); } if (nullptr != op->outputIndexes()) { controlOp.outputs.resize(op->outputIndexes()->size()); ::memcpy(controlOp.outputs.data(), op->outputIndexes()->data(), controlOp.outputs.size() * sizeof(int)); } submodule.emplace_back(std::move(controlOp)); continue; } current.opList.emplace_back(i); } if (!current.opList.empty()) { submodule.emplace_back(std::move(current)); } submodule = _splitSubModuleForShapeConst(submodule, net, sharedConst); for (int moduleIndex=0; moduleIndex < submodule.size(); ++moduleIndex) { auto& m = submodule[moduleIndex]; // Compute input / output if (!m.isBreak) { _computeTensorMask(m, net); for (int i=0; itensorName() != nullptr) { MNN_PRINT("%d tensor [ %s ] is input but not found\n", index, net->tensorName()->GetAsString(index)->c_str()); } success = false; return std::vector{}; } } } for (auto& m : submodule) { m.tensorMask.clear(); } return submodule; } struct ModuleRuntimeConfig { bool needGeometry; std::shared_ptr rt; Backend::Info compute; const BackendConfig* userConfig = nullptr; Session::ModeGroup modes; std::string externalFile; }; static Module* _createSubModule(std::shared_ptr bufferStorage, const SubModuleInfo& info, const std::map& subs, std::shared_ptr sharedConst, const Module::Config& config, const ModuleRuntimeConfig& runtimeConfig) { auto net = flatbuffers::GetRoot(bufferStorage->buffer()); if (1 == info.opList.size()) { auto op = net->oplists()->GetAs(info.opList[0]); if (OpType_If == op->type()) { return IfModule::create(op, subs); } if (OpType_While == op->type() && op->main_type() != OpParameter_LoopParam) { return WhileModule::create(op, subs); } if (OpType_NonMaxSuppressionV2 == op->type()) { return NMSModule::create(op); } if (OpType_MoE == op->type()) { return MoEModule::create(op, subs, runtimeConfig.rt, config); } // MNN_ASSERT(false); } Schedule::ScheduleInfo scheduleInfo; scheduleInfo.externalWeightPath = runtimeConfig.externalFile; scheduleInfo.defaultBackend = sharedConst->defaultBackend; scheduleInfo.constReplaceBackend = sharedConst->constReplaceBackend; scheduleInfo.allTensors = sharedConst->allTensors; scheduleInfo.validForResize = initTensors(scheduleInfo.allTensors, net, info.opList.data(), info.opList.size()); std::vector oplists; std::vector ops; ops.reserve(info.opList.size()); for (auto opIndex : info.opList) { ops.emplace_back(net->oplists()->GetAs(opIndex)); } initPipelineInfosFromOps(oplists, ops, scheduleInfo.allTensors); int breakIndex = GeometryComputerUtils::buildConstantTensors(oplists); if (breakIndex >= 0) { scheduleInfo.needInputContentForShape = true; } auto rt = runtimeConfig.rt; auto modes = runtimeConfig.modes; Schedule::BackendCache bnCache; Backend::Info compute = runtimeConfig.compute; if (nullptr != runtimeConfig.userConfig) { bnCache.config = *runtimeConfig.userConfig; compute.user = &bnCache.config; } else { compute.user = nullptr; } bnCache.info = std::move(compute); bnCache.needComputeGeometry = runtimeConfig.needGeometry; scheduleInfo.pipelineInfo.emplace_back(std::make_pair(std::move(bnCache), std::move(oplists))); std::vector> buffers = {bufferStorage}; return new StaticModule(info.inputs, info.outputs, std::move(buffers), std::move(scheduleInfo), sharedConst, std::move(modes), std::move(rt), config); } Module* PipelineModule::load(const std::vector& inputs, const std::vector& outputs, const uint8_t* buffer, size_t length, const std::shared_ptr rtMgr, const Module::Config* config) { // Create Subgraph auto net = GetNet(buffer); if (nullptr == net->oplists() || nullptr == net->tensorName()) { MNN_ERROR("Invalid net, for null oplist or tensorName\n"); return nullptr; } Module::Config defaultConfig; if (nullptr == config) { config = &defaultConfig; } auto subGraphs = net->subgraphs(); if (config->dynamic) { // TODO: Support subgraph if (nullptr == subGraphs) { auto varMap = MNN::Express::Variable::loadMap(buffer, length); std::vector inputsVar(inputs.size()); for (int i=0; i outputsVar(outputs.size()); for (int i=0; i subGraphMap; _createSubGraph(net, rtMgr, config, subGraphMap); std::shared_ptr bufferStorage(new BufferStorage); bufferStorage->storage = new uint8_t[length]; ::memcpy(bufferStorage->storage, buffer, length); bufferStorage->offset = 0; bufferStorage->allocated_size = length; return load(inputs, outputs, bufferStorage, rtMgr, config, subGraphMap); } Module* PipelineModule::load(const std::vector& inputs, const std::vector& outputs, std::shared_ptr bufferStorage, std::shared_ptr rtMgr, const Module::Config* config, std::map& subGraphMap) { MNN_ASSERT(nullptr != rtMgr); MNN_ASSERT(nullptr != config); // Apply before constReplaceBackend / submodule Backends are created. rtMgr->applyMetaToRuntime(); std::shared_ptr sharedConst; auto buffer = bufferStorage->buffer(); auto length = bufferStorage->size(); auto net = GetNet(buffer); bool needGeometry = net->usage() != Usage_INFERENCE_STATIC; // Extra Const Tensors sharedConst.reset(new Schedule::ScheduleInfo); auto curExe = ExecutorScope::Current(); bool preReplaceConstTensor = true; std::shared_ptr modRuntimeCfgPtr(new ModuleRuntimeConfig); if (!rtMgr->getInside()->mContent->mExternalFile.empty()) { modRuntimeCfgPtr->externalFile = rtMgr->getInside()->mContent->mExternalFile; } if (rtMgr->getInside()->mContent->modes.codegenMode == Interpreter::Session_Codegen_Enable || (!config->shapeMutable)) { preReplaceConstTensor = false; } std::shared_ptr defaultBackend = curExe->getAttr()->constantBackend; std::vector> allTensors; sharedConst->allTensors.resize(net->tensorName()->size()); sharedConst->defaultBackend = defaultBackend; ModuleRuntimeConfig& modRuntime = *modRuntimeCfgPtr; modRuntime.needGeometry = needGeometry; { modRuntime.modes = rtMgr->getInside()->mContent->modes; modRuntime.rt = rtMgr; rtMgr->getInside()->mRuntime.first.begin()->second->setRuntimeHint(rtMgr->getInside()->mContent->modes.runtimeHint); modRuntime.externalFile = rtMgr->getInside()->mContent->mExternalFile; modRuntime.userConfig = &rtMgr->getInside()->mContent->mConfig; modRuntime.compute.type = rtMgr->getInside()->mRuntime.first.begin()->first; } auto& rt = modRuntime.rt->getInside()->mRuntime; auto firstRt = rt.first.find(modRuntime.compute.type)->second; sharedConst->constReplaceBackend.reset(firstRt->onCreate(modRuntime.userConfig)); ErrorCode code = NO_ERROR; std::set noneedComputeIndexes; { FileLoader fileLoader(modRuntimeCfgPtr->externalFile.c_str()); initConstTensors(sharedConst->allTensors, net, defaultBackend.get(), code, &fileLoader); } if (NO_ERROR != code) { MNN_ERROR("Alloc memory for const tensor error\n"); return nullptr; } for (int i=0; iallTensors.size(); ++i) { if (sharedConst->allTensors[i].get() != nullptr) { noneedComputeIndexes.insert(i); } } std::map initVars; std::set inputIndexes; std::set outputIndexes; std::map stackMap; std::map outputIndexesMap; std::set outputNameSet; for (auto name : outputs) { outputIndexesMap.insert(std::make_pair(name, -1)); } for (int i=0; itensorName()->size(); ++i) { auto tname = net->tensorName()->GetAsString(i)->str(); for (int j=0; jsecond = i; } } bool valid = true; for (auto& iter : outputIndexesMap) { if (iter.second == -1) { MNN_ERROR("PipelineModule:: Can't find output %s from the model:\n", iter.first.c_str()); valid = false; } } if (!valid) { return nullptr; } bool divideSuccess = true; auto subModulesInfo = _createSubModuleInfo(bufferStorage, inputIndexes, outputIndexes, noneedComputeIndexes, sharedConst, divideSuccess); if (!divideSuccess) { MNN_ERROR("Create module error\n"); return nullptr; } std::vector> subModules(subModulesInfo.size()); for (int i=0; iconstReplaceBackend.get(); if (sharedConst->constReplaceBackend->type() != sharedConst->defaultBackend->type()) { for (auto& t : sharedConst->allTensors) { if (nullptr == t.get()) { continue; } auto des = TensorUtils::getDescribe(t.get()); if (des->isMutable) { continue; } if (!WrapExecution::needWrap(t.get(), curBackend)) { continue; } if (des->stageMask & Tensor::InsideDescribe::GEOMETRY_STAGE) { continue; } if (des->stageMask & Tensor::InsideDescribe::CONVERTED_STAGE) { continue; } std::shared_ptr wrapTensor = WrapExecution::makeCopyTensor(t.get(), curBackend); auto outDes = TensorUtils::getDescribe(wrapTensor.get()); outDes->usage = des->usage; auto tempRes = WrapExecution::allocAndCopy(curBackend, t.get(), wrapTensor.get()); if (!tempRes) { continue; } needReplaceBackend = true; outDes->stageMask |= Tensor::InsideDescribe::CONVERTED_STAGE; WrapExecution::copyReplaceTensor(wrapTensor.get(), t.get()); } } // Clear CPU Const memory rt.second->onGabageCollect(0); } for (auto index : noneedComputeIndexes) { auto tensor = Tensor::clone(sharedConst->allTensors[index].get()); auto constVar = Variable::create(Expr::create(tensor, true)); auto back = TensorUtils::getDescribeOrigin(tensor)->getBackend(); auto x =sharedConst->constReplaceBackend.get(); if (needReplaceBackend && TensorUtils::getDescribeOrigin(tensor)->getBackend() == sharedConst->constReplaceBackend.get()) { constVar->expr().first->inside()->mHoldBackend = sharedConst->constReplaceBackend; } initVars.insert(std::make_pair(index, constVar)); } auto result = new PipelineModule; result->mInputSize = inputs.size(); /** Compute: std::vector, std::vector, std::vector>> mSubModules; std::vector mInputIndexes; std::vector mOutputIndexes; int mStackSize = 0; */ // Make Stack, first: origin, second: new int stackIndex = result->mInputSize; for (auto& p : initVars) { stackMap.insert(std::make_pair(p.first, stackIndex)); result->mInitVars.emplace_back(p.second); stackIndex++; } for (auto& m : subModulesInfo) { for (auto index : m.inputs) { if (stackMap.find(index) == stackMap.end()) { stackMap.insert(std::make_pair(index, stackIndex)); stackIndex++; } } for (auto index : m.outputs) { if (stackMap.find(index) == stackMap.end()) { stackMap.insert(std::make_pair(index, stackIndex)); stackIndex++; } } } result->mOutputIndex.resize(outputs.size()); for (int i=0; imOutputIndex[i] = stackI; } result->mStackSize = stackIndex; MNN_ASSERT(result->mStackSize > 0); for (int i=0; i subInputs(info.inputs.size()); for (int i=0; i subOutputs(info.outputs.size()); for (int i=0; imSubModules.emplace_back(std::make_tuple(subModules[i], subInputs, subOutputs)); } result->registerModel(subModules); result->mSharedConst = sharedConst; return result; } Module* PipelineModule::clone(CloneContext* ctx) const { PipelineModule* module(new PipelineModule); for (const auto& it : mSubModules) { const std::shared_ptr& submodule = std::get<0>(it); const std::vector& input_indices = std::get<1>(it); const std::vector& output_indices = std::get<2>(it); std::shared_ptr replica_submodule(submodule->clone(ctx)); module->mSubModules.push_back( std::make_tuple(replica_submodule, input_indices, output_indices)); module->registerModel({replica_submodule}); } module->mInputSize = mInputSize; module->mOutputIndex = mOutputIndex; module->mStackSize = mStackSize; module->mInitVars = mInitVars; module->mSharedConst = mSharedConst; return this->cloneBaseTo(ctx, module); } } // namespace Express } // namespace MNN