#include #include #include #include #include #include "flatbuffers/flexbuffers.h" #include #include #include #include #include #include "core/MNNFileUtils.h" #include "shape/SizeComputer.hpp" #include "core/OpCommonUtils.hpp" #include "core/Schedule.hpp" #include "rapidjson/document.h" #include #include "utils/InitNet.hpp" #include "core/Command.hpp" #include "geometry/GeometryComputer.hpp" #include "geometry/GeometryComputerUtils.hpp" using namespace MNN; static bool gNeedOffline = false; static int gMaxKVSize = 0; static std::string gNPUName = "CoreML"; static std::string gOfflieSrc; static std::string gOfflieDst; static std::string gGraphName = "graph"; static std::string gCacheDir = "res"; static MNNForwardType gNPUType = MNN_FORWARD_NN; static bool initConstTensorsNoAlloc(std::vector>& tensors, const Net* net) { bool valid = true; tensors.resize(net->tensorName()->size()); // Set up const for (int opIndex = 0; opIndex < net->oplists()->size(); ++opIndex) { auto op = net->oplists()->GetAs(opIndex); if (OpType_Const == op->type() || OpType_TrainableParam == op->type()) { MNN_ASSERT(nullptr != op->outputIndexes()); auto index = op->outputIndexes()->data()[0]; tensors[index].reset(new Tensor); TensorUtils::getDescribe(tensors[index].get())->index = index; auto output = tensors[index].get(); if (op->type() == OpType_TrainableParam) { TensorUtils::getDescribe(output)->usage = Tensor::InsideDescribe::TRAINABLE; } TensorUtils::getDescribe(output)->usage = Tensor::InsideDescribe::CONSTANT; TensorUtils::getDescribe(output)->isMutable = false; } } return true; } struct SubModuleInfo { std::vector opList; std::vector inputs;; std::vector outputs; std::vector tensorMask; bool isBreak = false; }; struct SubModuleIO { std::vector inputs; std::vector outputs; std::vector> kvcache; int seqLen = 0; }; 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 _npuSupportOp(const Op* op) { if (gMaxKVSize > 0) { return true; } if (op->type() == OpType_Attention) { auto attn = op->main_as_AttentionParam(); if (nullptr != attn && attn->kv_cache()) { return false; } } if (op->type() == OpType_LinearAttention) { return false; } return true; } 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) { return true; } if (!_npuSupportOp(op)) { 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; } // Global set to store extra break op indexes (ops between attention_mask and Attention) static std::set gExtraBreakOpIndexes; // Find ops between attention_mask input and Attention/LinearAttention ops that should also be break ops static std::set _findMaskToAttentionOps(const Net* net, const std::set& inputIndexes, const std::set& outputIndexes) { std::set extraBreakOps; if (net->tensorName() == nullptr || net->oplists() == nullptr) { return extraBreakOps; } // 1. Find tensor indexes corresponding to attention_mask inputs std::set maskTensorIndexes; for (auto idx : inputIndexes) { auto name = net->tensorName()->GetAsString(idx)->str(); if (name.find("attention_mask") != std::string::npos) { maskTensorIndexes.insert(idx); } } if (maskTensorIndexes.empty()) { return extraBreakOps; } // 2. Collect all needed ops auto selectOps = _collectNeededOps(net, inputIndexes, outputIndexes); // 3. Build tensor -> producer op index mapping std::map tensorProducer; for (auto opIdx : selectOps) { auto op = net->oplists()->GetAs(opIdx); if (op->outputIndexes() != nullptr) { for (int j = 0; j < op->outputIndexes()->size(); ++j) { tensorProducer[op->outputIndexes()->data()[j]] = opIdx; } } } // 4. Forward propagation: find all tensors that depend on attention_mask // Stop propagation at Attention/LinearAttention ops std::set maskDependentTensors = maskTensorIndexes; bool changed = true; while (changed) { changed = false; for (auto opIdx : selectOps) { auto op = net->oplists()->GetAs(opIdx); if (op->inputIndexes() == nullptr || op->outputIndexes() == nullptr) continue; // Don't propagate through Attention/LinearAttention ops if (op->type() == OpType_Attention || op->type() == OpType_LinearAttention) continue; bool dependsOnMask = false; for (int j = 0; j < op->inputIndexes()->size(); ++j) { if (maskDependentTensors.count(op->inputIndexes()->data()[j])) { dependsOnMask = true; break; } } if (dependsOnMask) { for (int j = 0; j < op->outputIndexes()->size(); ++j) { if (maskDependentTensors.insert(op->outputIndexes()->data()[j]).second) { changed = true; } } } } } // 5. For each Attention/LinearAttention break op, backward trace inputs that depend on mask // Collect all intermediate ops on the path from attention_mask to Attention for (auto opIdx : selectOps) { auto op = net->oplists()->GetAs(opIdx); if (op->type() != OpType_Attention && op->type() != OpType_LinearAttention) continue; if (!isBreakOp(op)) continue; if (op->inputIndexes() == nullptr) continue; std::queue bfsQueue; std::set visitedTensors; for (int i = 0; i < op->inputIndexes()->size(); ++i) { int tensorIdx = op->inputIndexes()->data()[i]; // This input depends on mask but is NOT the mask tensor itself // => there are intermediate ops between mask and Attention if (maskDependentTensors.count(tensorIdx) && !maskTensorIndexes.count(tensorIdx)) { bfsQueue.push(tensorIdx); } } while (!bfsQueue.empty()) { int tensorIdx = bfsQueue.front(); bfsQueue.pop(); if (visitedTensors.count(tensorIdx)) continue; visitedTensors.insert(tensorIdx); if (maskTensorIndexes.count(tensorIdx)) continue; // Reached mask input, stop auto it = tensorProducer.find(tensorIdx); if (it == tensorProducer.end()) continue; int producerOpIdx = it->second; auto producerOp = net->oplists()->GetAs(producerOpIdx); // Skip Const and TrainableParam ops if (producerOp->type() == OpType_Const || producerOp->type() == OpType_TrainableParam) continue; extraBreakOps.insert(producerOpIdx); // Continue tracing this op's inputs if (producerOp->inputIndexes() != nullptr) { for (int j = 0; j < producerOp->inputIndexes()->size(); ++j) { int inputIdx = producerOp->inputIndexes()->data()[j]; if (maskDependentTensors.count(inputIdx)) { bfsQueue.push(inputIdx); } } } } } if (!extraBreakOps.empty()) { MNN_PRINT("Found %d extra break ops between attention_mask and Attention:\n", (int)extraBreakOps.size()); for (auto opIdx : extraBreakOps) { auto op = net->oplists()->GetAs(opIdx); if (op->name() != nullptr) { MNN_PRINT(" Extra break op: %s (type: %s)\n", op->name()->c_str(), EnumNameOpType(op->type())); } } } return extraBreakOps; } static void _setInputOutputForOps(std::vector>& allTensors, const std::vector& ops) { std::set inputIndexes; std::set outputIndexes; // 0. deal virtual tensor for static model: // when : A (Any_Op) -----> B (Raster_Op) // the tensor will be like below: // A_outputs : a_tensor // B_inputs : b_tensor (virtual) // b_tensor.describe.origin = a_tensor_ptr // b_tensor is not a InputTensot, a_tensor is not a OutputTensor // so add b_tensor to OutputIndexes, a_tensor to InputIndexes. // 1. insert all output/input index in outputIndexes/inputIndexes for (auto op : ops) { if (nullptr != op->outputIndexes()) { auto data = op->outputIndexes()->data(); for (int j = 0; j < op->outputIndexes()->size(); ++j) { outputIndexes.insert(data[j]); } } if (nullptr != op->inputIndexes()) { auto data = op->inputIndexes()->data(); for (int j = 0; j < op->inputIndexes()->size(); ++j) { inputIndexes.insert(data[j]); } } MNN_ASSERT(OpType_Input != op->type()); } // 2. the index in outputIndexes/inputIndexed but not in inputIndexes/outputIndexes is output/input std::set input; std::set output; std::set_difference(outputIndexes.begin(), outputIndexes.end(), inputIndexes.begin(), inputIndexes.end(), std::inserter(output, output.begin())); std::set_difference(inputIndexes.begin(), inputIndexes.end(), outputIndexes.begin(), outputIndexes.end(), std::inserter(input, input.begin())); // 3. set usage for Tensor by index for (auto index : input) { auto des = TensorUtils::getDescribe(allTensors[index].get()); if (des->usage == Tensor::InsideDescribe::CONSTANT || des->usage == Tensor::InsideDescribe::TRAINABLE) { continue; } des->usage = Tensor::InsideDescribe::INPUT; } for (auto index : output) { auto des = TensorUtils::getDescribe(allTensors[index].get()); if (des->usage == Tensor::InsideDescribe::NORMAL) { des->usage = TensorUsage::OUTPUT; } } } void _getConstData(const Net* net, std::vector inputs, const std::set& inputIndexes, const std::set& outputIndexes, std::map, std::vector>>& constTensorData, std::string srcpath) { // set a backend and context to run resize ScheduleConfig config; config.type = MNN_FORWARD_CPU; BackendConfig backendConfig; backendConfig.precision = BackendConfig::Precision_High; config.backendConfig = &backendConfig; Backend::Info compute; compute.type = config.type; compute.numThread = config.numThread; compute.user = config.backendConfig; const RuntimeCreator* runtimeCreator(MNNGetExtraRuntimeCreator(compute.type)); std::unique_ptr runtime(runtimeCreator->onCreate(compute)); std::shared_ptr backend(runtime->onCreate()); BackendConfig defaultConfig; defaultConfig.flags = 4; std::shared_ptr defaultBackend(runtime->onCreate(&defaultConfig)); std::vector> allTensors; allTensors.resize(net->tensorName()->size()); ErrorCode code = NO_ERROR; FileLoader loader((srcpath + ".weight").c_str()); initConstTensors(allTensors, net, defaultBackend.get(), code, &loader); if (NO_ERROR != code) { MNN_ERROR("Init tensor error code = %d\n", code); return; } bool valid = initTensors(allTensors, net); // set tensors' shape by inputConfig std::map inputsMap; for (int i = 0; i < inputs.size(); i++) { auto name = inputs[i]->name(); inputsMap[name] = inputs[i]; } for (int i = 0; i < allTensors.size(); i++) { auto name = net->tensorName()->GetAsString(i)->str(); if (inputsMap.find(name) != inputsMap.end()) { auto input = inputsMap[name]; auto info = input->getInfo(); auto& dims = info->dim; allTensors[i]->buffer().dimensions = dims.size(); for (int j = 0; j < dims.size(); j++) { allTensors[i]->setLength(j, dims[j]); } allTensors[i]->buffer().host = (uint8_t*)MNNMemoryAllocAlign(info->size * sizeof(float), MNN_MEMORY_ALIGN_DEFAULT); auto ptr = input->readMap(); std::memcpy(allTensors[i]->buffer().host, ptr, info->size * sizeof(float)); } } std::vector infos; auto selectOps = _collectNeededOps(net, inputIndexes, outputIndexes); { std::vector ops; for (int i = 0; i < selectOps.size(); i++) { auto op = net->oplists()->GetAs(selectOps[i]); if (needComputeOp(op)) { ops.push_back(op); } } initPipelineInfosFromOps(infos, ops, allTensors); _setInputOutputForOps(allTensors, ops); } GeometryComputer::Context ctx(Interpreter::GeometryComputeMask::GEOMETRCOMPUTEMASK_ALL, defaultBackend); // resize the session's info and store to buffer std::vector constTensors; GeometryComputerUtils::buildConstantTensors(infos); GeometryComputerUtils::shapeComputeAndGeometryTransform(runtime.get(), nullptr, infos, ctx, defaultBackend, runtime->onGetCompilerType()); for (int i = 0; i < allTensors.size(); i++) { auto index = TensorUtils::getDescribe(allTensors[i].get())->index; auto iter = constTensorData.find(index); if (iter != constTensorData.end()) { auto ptr = allTensors[i]->host(); if (ptr != nullptr) { auto size = allTensors[i]->size(); auto shape = allTensors[i]->shape(); std::get<0>(iter->second) = TensorUtils::getDescribe(allTensors[i].get())->dimensionFormat; std::get<1>(iter->second) = allTensors[i]->getType().code; std::get<2>(iter->second).resize(0); std::get<3>(iter->second).resize(size); memcpy(std::get<3>(iter->second).data(), ptr, size); for (int i = 0; i < shape.size(); ++i) { std::get<2>(iter->second).push_back(shape[i]); } } } } } static void _findAllConstTensorIndex(const Net* net, const std::set& inputIndexes, const std::set& outputIndexes, std::shared_ptr sharedConst, std::vector& constOpId, std::map, std::vector>>* constTensorData) { auto selectOps = _collectNeededOps(net, inputIndexes, outputIndexes); std::set constTensorIndex; // 0: not used, 1: const, 2: output std::vector constMask(sharedConst->allTensors.size(), 0); for (int i = 0; i < sharedConst->allTensors.size(); ++i) { if (sharedConst->allTensors[i].get() != nullptr) { constMask[i] = 1; } } for (int v = 0; v < selectOps.size(); ++v) { auto op = net->oplists()->GetAs(selectOps[v]); if (nullptr == op->outputIndexes()) { continue; } bool isConst = true; if (nullptr != op->inputIndexes()) { for (int i = 0; i < op->inputIndexes()->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; i < op->outputIndexes()->size(); ++i) { auto index = op->outputIndexes()->data()[i]; constMask[index] = 1; constTensorData->emplace(index, std::make_tuple(0, 0, std::vector(0), std::vector(0))); constOpId.push_back(selectOps[v]); } } } } static NetT* _replaceConstOp(const void* buffer, size_t bufferSize, std::map, std::vector>>& constTensorData, std::vector& constOpId) { auto net = flatbuffers::GetRoot(buffer)->UnPack(); for (int i = 0; i < constOpId.size(); ++i) { auto op = net->oplists[constOpId[i]].get(); auto index = op->outputIndexes[0]; auto iter = constTensorData.find(index); if (iter == constTensorData.end()) { continue; } auto name = op->name; std::unique_ptr newOp(new OpT); newOp->type = OpType_Const; newOp->name = name + "_Const"; auto blob = new BlobT; blob->dataFormat = (MNN_DATA_FORMAT)std::get<0>(iter->second); blob->dims = std::get<2>(iter->second); if (std::get<1>(iter->second) == halide_type_float) { blob->dataType = DataType_DT_FLOAT; blob->float32s.resize(std::get<3>(iter->second).size() / 4); memcpy(blob->float32s.data(), std::get<3>(iter->second).data(), std::get<3>(iter->second).size()); } else if (std::get<1>(iter->second) == halide_type_int) { blob->dataType = DataType_DT_INT32; blob->int32s.resize(std::get<3>(iter->second).size() / 4); memcpy(blob->int32s.data(), std::get<3>(iter->second).data(), std::get<3>(iter->second).size()); } else { continue; } newOp->main.value = blob; newOp->main.type = OpParameter_Blob; newOp->outputIndexes = {index}; net->oplists[constOpId[i]] = std::move(newOp); } auto oplist = std::move(net->oplists); for (auto& op : oplist) { if (nullptr != op.get()) { net->oplists.emplace_back(std::move(op)); } } return net; // flatbuffers::GetRoot(buffer)->Pack(builder, net); // std::ofstream outputOs(dstMNN, std::ios::binary); // outputOs.write((const char*)builder.GetBufferPointer(), builder.GetSize()); } 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 bool _needSplitNet(const Net* net, const std::set& inputIndexes, const std::set& outputIndexes) { auto selectOps = _collectNeededOps(net, inputIndexes, outputIndexes); for (int si = 0; si < selectOps.size(); ++si) { auto i = selectOps[si]; auto op = net->oplists()->GetAs(i); if (isBreakOp(op) || gExtraBreakOpIndexes.count(i) > 0) { return true; } } return false; } static std::vector _createSubModuleInfo(const Net* net, const std::set& inputIndexes, const std::set& outputIndexes, const std::set& noComputeIndexes, std::shared_ptr sharedConst) { std::vector submodule; auto selectOps = _collectNeededOps(net, inputIndexes, outputIndexes); // Separate the graph to serveral submodule SubModuleInfo current; for (int si=0; sioplists()->GetAs(i); if (isBreakOp(op) || gExtraBreakOpIndexes.count(i) > 0) { // 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()); } } MNN_ASSERT(find); } } for (auto& m : submodule) { m.tensorMask.clear(); } // sort input and output for (auto& m : submodule) { std::sort(m.inputs.begin(), m.inputs.end()); std::sort(m.outputs.begin(), m.outputs.end()); } return submodule; } static std::set _getAttentionName(const void* buffer, size_t bufferSize) { std::set attentionNames; auto net = flatbuffers::GetRoot(buffer); if (nullptr == net->oplists()) { return attentionNames; } for (int i=0; ioplists()->size(); ++i) { auto op = net->oplists()->GetAs(i); if (op->type() == OpType_Attention) { if (nullptr != op->main_as_AttentionParam()) { if (op->main_as_AttentionParam()->kv_cache()) { attentionNames.insert(op->name()->str()); } } } } return attentionNames; } static SubModuleIO _getSubModuleIO(std::vector inputs, const SubModuleInfo& info, const void* buffer, size_t bufferSize, std::string srcpath) { // Deep clone output to let the module release SubModuleIO io; std::vector inputNames(info.inputs.size()); std::vector outputNames(info.outputs.size()); auto net = flatbuffers::GetRoot(buffer); for (int i=0; itensorName()->GetAsString(index)->str(); } for (int i=0; itensorName()->GetAsString(index)->str(); } auto attentionNames = _getAttentionName(buffer, bufferSize); MNN::ScheduleConfig config; config.numThread = 1; std::shared_ptr rtmgr(MNN::Express::Executor::RuntimeManager::createRuntimeManager(config)); rtmgr->setExternalFile((srcpath + ".weight").c_str()); rtmgr->setMode(MNN::Interpreter::Session_Debug); std::shared_ptr m(MNN::Express::Module::load(inputNames, outputNames, (const uint8_t*)buffer, bufferSize, rtmgr), MNN::Express::Module::destroy); MNN::TensorCallBackWithInfo beforeCallBack = [&](const std::vector& ntensors, const MNN::OperatorInfo* info) { auto opName = info->name(); if (info->type() != "Attention") { return true; } if (attentionNames.find(opName) != attentionNames.end()) { auto query = ntensors[0]; auto key = ntensors[1]; auto value = ntensors[2]; int seq_len = query->length(1); auto numHead = query->length(2); auto headDim = query->length(3); auto kvNumHead = key->length(2); std::vector kvDims = {kvNumHead, 1, 1, headDim}; io.kvcache.emplace_back(kvDims); io.seqLen = seq_len; } return true; }; MNN::TensorCallBackWithInfo callBack = [&](const std::vector& ntensors, const MNN::OperatorInfo* info) { return true; }; MNN::Express::ExecutorScope::Current()->setCallBack(std::move(beforeCallBack), std::move(callBack)); auto outputs = m->onForward(inputs); io.inputs = inputs; io.outputs.resize(outputs.size()); for (int i=0; i inputNames, std::vector outputNames, std::vector> inputs, const std::set& inputIndexes, const std::set& outputIndexes, const void* buffer, size_t bufferSize, std::string srcpath, std::string dstMNNPath) { int npuIndex = 0; std::vector> outputInfos; std::vector outputInfo; std::map> merges; std::vector graphicNames; auto path = gCacheDir + "/" + gGraphName + std::to_string(npuIndex); if (!gOfflieDst.empty()) { path += ("." + gOfflieDst); } std::vector allInputShape; for (int inputIndex = 0; inputIndex < inputs.size(); ++inputIndex) { std::vector inputInfos(inputs[inputIndex].size()); for (int i = 0; i < inputInfos.size(); ++i) { inputInfos[i] = *inputs[inputIndex][i]->getInfo(); } std::vector currInputShape; for (int i = 0; i < inputInfos.size(); i++) { for (int j = 0; j < inputInfos[i].dim.size(); j++) { currInputShape.emplace_back(inputInfos[i].dim[j]); } } allInputShape.insert(allInputShape.end(), currInputShape.begin(), currInputShape.end()); std::string srcPath; std::string graphicName; if (inputIndex == 0) { srcPath = gCacheDir + "/" + gGraphName + std::to_string(npuIndex); graphicName = gGraphName + std::to_string(npuIndex); } else { srcPath = gCacheDir + "/" + gGraphName + std::to_string(inputIndex) + "_" + std::to_string(npuIndex); graphicName = gGraphName + std::to_string(inputIndex) + "_" + std::to_string(npuIndex); } if (!gOfflieSrc.empty()) { srcPath += ("." + gOfflieSrc); } if (merges.find(path) != merges.end()) { merges[path].emplace_back(srcPath); } else { merges.insert(std::make_pair(path, std::vector{srcPath})); } graphicNames.push_back(graphicName); MNN::ScheduleConfig config; config.type = gNPUType; std::shared_ptr rtmgr( MNN::Express::Executor::RuntimeManager::createRuntimeManager(config)); rtmgr->setExternalFile((srcpath + ".weight").c_str()); rtmgr->setCache(srcPath.c_str()); rtmgr->setHint(MNN::Interpreter::KVCACHE_SIZE_LIMIT, gMaxKVSize); MNN::Express::Module::Config mdconfig; mdconfig.shapeMutable = false; std::shared_ptr m( MNN::Express::Module::load(inputNames, outputNames, (const uint8_t*)buffer, bufferSize, rtmgr, &mdconfig), MNN::Express::Module::destroy); auto outputs = m->onForward(inputs[inputIndex]); std::vector outputInfo(outputs.size()); for (int i = 0; i < outputInfo.size(); ++i) { outputInfo[i] = *outputs[i]->getInfo(); } outputInfos.emplace_back(outputInfo); } /** Fuse to Op*/ std::unique_ptr op(new OpT); for (int i = 0; i < inputs[0].size(); i++) { op->inputIndexes.push_back(i); } for (int i = 0; i < outputInfos[0].size(); i++) { op->outputIndexes.push_back(inputs[0].size() + i); } op->name = "qnn/plugin/op"; op->main.Reset(); op->type = MNN::OpType_Plugin; op->main.type = MNN::OpParameter_Plugin; op->main.value = new MNN::PluginT; auto extra = op->main.AsPlugin(); extra->type = gNPUName; std::unique_ptr attr(new MNN::AttributeT); attr->key = "path"; attr->s = path; extra->attr.emplace_back(std::move(attr)); // Build name -> tensor index mapping to preserve inputNames order auto net = flatbuffers::GetRoot(buffer); std::map nameToTensorIdx; for (auto idx : inputIndexes) { nameToTensorIdx[net->tensorName()->GetAsString(idx)->str()] = idx; } attr.reset(new MNN::AttributeT); attr->key = "inputs"; attr->list.reset(new ListValueT); attr->list->s.resize(inputNames.size()); for (int i = 0; i < inputNames.size(); i++) { auto it = nameToTensorIdx.find(inputNames[i]); MNN_ASSERT(it != nameToTensorIdx.end()); attr->list->s[i] = std::string("t") + std::to_string(it->second); } extra->attr.emplace_back(std::move(attr)); attr.reset(new AttributeT); attr->key = "allGraphName"; attr->list.reset(new ListValueT); attr->list->s = graphicNames; extra->attr.emplace_back(std::move(attr)); // Build name -> tensor index mapping to preserve outputNames order std::map outNameToTensorIdx; for (auto idx : outputIndexes) { outNameToTensorIdx[net->tensorName()->GetAsString(idx)->str()] = idx; } attr.reset(new MNN::AttributeT); attr->key = "outputs"; attr->list.reset(new ListValueT); attr->list->s.resize(outputNames.size()); for (int i = 0; i < outputNames.size(); i++) { auto it = outNameToTensorIdx.find(outputNames[i]); MNN_ASSERT(it != outNameToTensorIdx.end()); attr->list->s[i] = std::string("t") + std::to_string(it->second); } extra->attr.emplace_back(std::move(attr)); attr.reset(new MNN::AttributeT); attr->key = "allInputShape"; attr->list.reset(new ListValueT); attr->list->i.insert(attr->list->i.end(), allInputShape.begin(), allInputShape.end()); extra->attr.emplace_back(std::move(attr)); for (int i = 0; i < outputInfos.size(); ++i) { auto outputInfo = outputInfos[i]; for (int j = 0; j < outputInfo.size(); ++j) { attr.reset(new MNN::AttributeT); attr->key = "o_" + std::to_string(i) + "_" + std::to_string(j); attr->tensor.reset(new BlobT); attr->tensor->dataType = OpCommonUtils::convertDataType(outputInfo[j].type); attr->tensor->dims = outputInfo[j].dim; switch (outputInfo[j].order) { case MNN::Express::NHWC: attr->tensor->dataFormat = MNN_DATA_FORMAT_NHWC; break; case MNN::Express::NCHW: attr->tensor->dataFormat = MNN_DATA_FORMAT_NCHW; break; case MNN::Express::NC4HW4: attr->tensor->dataFormat = MNN_DATA_FORMAT_NC4HW4; break; default: attr->tensor->dataFormat = MNN_DATA_FORMAT_NCHW; break; } extra->attr.emplace_back(std::move(attr)); } } std::shared_ptr dstNet(new NetT); for (int i = 0; i < inputs[0].size(); ++i) { auto inputInfos = *inputs[0][i]->getInfo(); std::unique_ptr input(new OpT); input->type = OpType_Input; auto param(new InputT); param->dims = inputInfos.dim; input->main.type = OpParameter_Input; input->main.value = param; input->name = inputNames[i]; input->outputIndexes.push_back(i); dstNet->oplists.emplace_back(std::move(input)); } dstNet->tensorName = inputNames; dstNet->tensorName.insert(dstNet->tensorName.end(), outputNames.begin(), outputNames.end()); dstNet->tensorName.push_back(op->name); dstNet->outputName = outputNames; std::unique_ptr npuOp; npuOp = std::move(op); // Merge to dst dstNet->oplists.emplace_back(std::move(npuOp)); // Store flatbuffers::FlatBufferBuilder builder; builder.Finish(Net::Pack(builder, dstNet.get())); std::ofstream outputOs(dstMNNPath.c_str(), std::ios::binary); outputOs.write((const char*)builder.GetBufferPointer(), builder.GetSize()); outputOs.close(); // Write Merge Info rapidjson::Document resDocument; resDocument.SetObject(); rapidjson::Value mergeMessages; mergeMessages.SetObject(); for (auto& iter : merges) { rapidjson::Value mergeSrc; mergeSrc.SetArray(); for (auto& v : iter.second) { rapidjson::Value vt; vt.SetString(v.c_str(), resDocument.GetAllocator()); mergeSrc.GetArray().PushBack(vt, resDocument.GetAllocator()); } rapidjson::Value key; key.SetString(iter.first.c_str(), resDocument.GetAllocator()); mergeMessages.AddMember(key, mergeSrc, resDocument.GetAllocator()); } { rapidjson::Value type; type.SetString(gNPUName.c_str(), resDocument.GetAllocator()); resDocument.AddMember("type", type, resDocument.GetAllocator()); } resDocument.AddMember("merge", mergeMessages, resDocument.GetAllocator()); { rapidjson::Value cachedir; cachedir.SetString(gCacheDir.c_str(), resDocument.GetAllocator()); resDocument.AddMember("cache", cachedir, resDocument.GetAllocator()); } rapidjson::StringBuffer buf; rapidjson::PrettyWriter bufwriter(buf); resDocument.Accept(bufwriter); MNN_PRINT("Write config to npu_postreat.json\n"); std::ofstream os("npu_postreat.json"); os << buf.GetString(); return 0; } static std::unique_ptr _compileSubModule(const SubModuleIO& io, SubModuleInfo& info, const void* buffer, size_t bufferSize, const std::string& path, std::string srcpath, const std::string& targetNpuPath, float& cpuTotal, float& npuTotal, int shapeIndex, std::string graphicName) { std::vector inputNames(info.inputs.size()); std::vector outputNames(info.outputs.size()); auto net = flatbuffers::GetRoot(buffer); for (int i=0; itensorName()->GetAsString(index)->str(); } for (int i=0; itensorName()->GetAsString(index)->str(); } /** Get Output shapes*/ std::vector outputInfos(io.outputs.size()); for (int i=0; igetInfo(); } /** Make ML Model*/ do { MNN::ScheduleConfig config; config.type = gNPUType; std::shared_ptr rtmgr(MNN::Express::Executor::RuntimeManager::createRuntimeManager(config)); rtmgr->setExternalFile((srcpath + ".weight").c_str()); rtmgr->setCache(path.c_str()); rtmgr->setHint(MNN::Interpreter::KVCACHE_SIZE_LIMIT, gMaxKVSize); MNN::Express::Module::Config mdconfig; mdconfig.shapeMutable = false; std::shared_ptr m(MNN::Express::Module::load(inputNames, outputNames, (const uint8_t*)buffer, bufferSize, rtmgr, &mdconfig), MNN::Express::Module::destroy); auto predict = m->onForward(io.inputs); if (gNeedOffline) { break; } if (predict.size() != io.outputs.size()) { MNN_ERROR("Failed to compile: %s\n", path.c_str()); info.isBreak = true; return nullptr; } for (int i=0; i(error))); auto maxValue = MNN::Express::_ReduceMax(MNN::Express::_Abs(MNN::Express::_Cast(io.outputs[i])))->readMap()[0]; if (maxValue < 0.01f) { maxValue = 0.01f; } auto errorf = error->readMap()[0]; if (errorf / maxValue > 0.1f) { MNN_ERROR("error = %f, max = %f for %s\n", errorf, maxValue, path.c_str()); info.isBreak = true; return nullptr; } } // Compare Speed int testTime = 20; MNN_PRINT("Start to Test speed for %d times\n", testTime); MNN::Timer timer; for (int i=0; ionForward(io.inputs); ((MNN::Tensor*)predict[0]->getTensor())->wait(MNN::Tensor::MAP_TENSOR_READ, true); } auto npuCost = timer.durationInUs(); MNN::ScheduleConfig configcpu; configcpu.numThread = 4; std::shared_ptr rtmgrCPU(MNN::Express::Executor::RuntimeManager::createRuntimeManager(configcpu)); rtmgrCPU->setExternalFile((srcpath + ".weight").c_str()); m.reset(MNN::Express::Module::load(inputNames, outputNames, (const uint8_t*)buffer, bufferSize, rtmgrCPU, &mdconfig), MNN::Express::Module::destroy); predict = m->onForward(io.inputs); timer.reset(); for (int i=0; ionForward(io.inputs); ((MNN::Tensor*)predict[0]->getTensor())->wait(MNN::Tensor::MAP_TENSOR_READ, true); } auto cpuCost = timer.durationInUs(); float npuF = (float)npuCost/ 1000.0f / testTime; float cpuF = (float)cpuCost / 1000.0f / testTime; MNN_PRINT("%s, Speed Compare: NPU: %f ms : CPU: %f ms\n", path.c_str(), npuF, cpuF); cpuTotal += cpuF; npuTotal += npuF; } while (false); /** Fuse to Op*/ std::unique_ptr op(new OpT); op->inputIndexes = info.inputs; op->outputIndexes = info.outputs; op->name = targetNpuPath; op->main.Reset(); op->type = MNN::OpType_Plugin; op->main.type = MNN::OpParameter_Plugin; op->main.value = new MNN::PluginT; auto extra = op->main.AsPlugin(); extra->type = gNPUName; std::unique_ptr attr(new MNN::AttributeT); attr->key = "path"; attr->s = targetNpuPath; extra->attr.emplace_back(std::move(attr)); attr.reset(new MNN::AttributeT); attr->key = "inputs"; attr->list.reset(new ListValueT); attr->list->s.resize(inputNames.size()); for (int i=0; ilist->s[i] = std::string("t") + std::to_string(info.inputs[i]); } extra->attr.emplace_back(std::move(attr)); attr.reset(new AttributeT); attr->key = "allGraphName"; attr->list.reset(new ListValueT); attr->list->s = {graphicName}; extra->attr.emplace_back(std::move(attr)); if (io.kvcache.size() > 0) { attr.reset(new MNN::AttributeT); attr->key = "seq_len"; attr->list.reset(new ListValueT); attr->list->i = {io.seqLen}; extra->attr.emplace_back(std::move(attr)); attr.reset(new MNN::AttributeT); attr->key = "state"; attr->tensor.reset(new BlobT); attr->tensor->dataType = DataType_DT_UINT8; flexbuffers::Builder builder; auto start = builder.StartMap(); builder.Int("number", io.kvcache.size() * 2); builder.Int("max_length", gMaxKVSize); builder.Int("axis", 2); auto shapeStart = builder.StartVector("shape"); // Add State for (int i=0; itensor->uint8s = builder.GetBuffer(); if (false) { // Try Read auto ref = flexbuffers::GetRoot(attr->tensor->uint8s.data(), attr->tensor->uint8s.size()); auto refMap = ref.AsMap(); auto keys = refMap.Keys(); int readNumber = 0; int maxLength = 0; std::vector> stateShape; for (int i=0; i shapes; for (int v=0; vattr.emplace_back(std::move(attr)); } attr.reset(new MNN::AttributeT); attr->key = "outputs"; attr->list.reset(new ListValueT); attr->list->s.resize(outputNames.size()); for (int i=0; ilist->s[i] = std::string("t") + std::to_string(info.outputs[i]); } extra->attr.emplace_back(std::move(attr)); attr.reset(new MNN::AttributeT); attr->key = "allInputShape"; attr->list.reset(new ListValueT); std::string inputsShapeStr = ""; for (int i = 0; i < io.inputs.size(); i++) { for (int j = 0; j < io.inputs[i]->getInfo()->dim.size(); j++) { attr->list->i.emplace_back(io.inputs[i]->getInfo()->dim[j]); } } extra->attr.emplace_back(std::move(attr)); for (int i=0; ikey = "o_" + std::to_string(shapeIndex) + "_" + std::to_string(i); attr->tensor.reset(new BlobT); attr->tensor->dataType = OpCommonUtils::convertDataType( outputInfos[i].type); attr->tensor->dims = outputInfos[i].dim; switch(outputInfos[i].order) { case MNN::Express::NHWC: attr->tensor->dataFormat = MNN_DATA_FORMAT_NHWC; break; case MNN::Express::NCHW: attr->tensor->dataFormat = MNN_DATA_FORMAT_NCHW; break; case MNN::Express::NC4HW4: attr->tensor->dataFormat = MNN_DATA_FORMAT_NC4HW4; break; default: attr->tensor->dataFormat = MNN_DATA_FORMAT_NCHW; break; } extra->attr.emplace_back(std::move(attr)); } return op; } static bool _fuse(MNN::NetT* net, MNN::NetT* srcNet) { std::map dstPlugin; for (auto& op : net->oplists) { if (op->type == OpType_Plugin) { dstPlugin.insert(std::make_pair(op->name, op.get())); } } for (auto& op : srcNet->oplists) { if (op->type != OpType_Plugin) { continue; } auto iter = dstPlugin.find(op->name); if (iter == dstPlugin.end()) { MNN_ERROR("Can't find plugin: %s\n", op->name.c_str()); continue; } auto dst = iter->second->main.AsPlugin(); auto src = op->main.AsPlugin(); std::map dstKeys; for (auto& dstAttr : dst->attr) { dstKeys.insert(std::make_pair(dstAttr->key, dstAttr.get())); } for (auto&& srcAttr : src->attr) { if (srcAttr->key == "inputs" || srcAttr->key == "outputs") { // Don't fuse same one continue; } auto dstIter = dstKeys.find(srcAttr->key); if (dstIter == dstKeys.end()) { dst->attr.emplace_back(std::move(srcAttr)); continue; } if (dstIter->second->list != nullptr && srcAttr->list != nullptr) { dstIter->second->list->s.insert(dstIter->second->list->s.end(), srcAttr->list->s.begin(), srcAttr->list->s.end()); dstIter->second->list->i.insert(dstIter->second->list->i.end(), srcAttr->list->i.begin(), srcAttr->list->i.end()); } } } return true; } static bool _reOrderOp(MNN::NetT* net) { auto oplist = std::move(net->oplists); std::set validInputs; do { bool empty = true; for (int i=0; iinputIndexes) { if (validInputs.find(index) == validInputs.end()) { valid = false; break; } } if (valid) { for (auto index : oplist[i]->outputIndexes) { validInputs.insert(index); } net->oplists.emplace_back(std::move(oplist[i])); oplist[i] = nullptr; } else { empty = false; } } if (empty) { break; } } while (true); return true; } static bool _reIndexTensor(MNN::NetT* net) { auto& mNet = net; std::map tensorNameIdx; std::map usefulTensorIndexMap; std::vector usefulTensorName; // extraTensorDescribe reindex for (int i = 0; i < mNet->tensorName.size(); i++) { tensorNameIdx.insert(std::make_pair(mNet->tensorName[i], i)); } for (int i = 0; i < mNet->extraTensorDescribe.size(); i++) { auto name = mNet->extraTensorDescribe[i]->name; auto iter = tensorNameIdx.find(name); if (iter == tensorNameIdx.end()) { mNet->extraTensorDescribe[i]->index = -1; } else { mNet->extraTensorDescribe[i]->index = iter->second; } } std::vector tensorValid(mNet->tensorName.size(), false); for (auto& op : mNet->oplists) { for (auto index : op->inputIndexes) { if (index < 0) { continue; // optional input, ignore it } tensorValid[index] = true; } for (auto index : op->outputIndexes) { tensorValid[index] = true; } } for (int i = 0; i < tensorValid.size(); ++i) { if (tensorValid[i]) { usefulTensorIndexMap.insert(std::make_pair(i, usefulTensorName.size())); usefulTensorName.push_back(mNet->tensorName[i]); } } // Re index for (auto& op : mNet->oplists) { for (int i = 0; i < op->inputIndexes.size(); ++i) { if (op->inputIndexes[i] < 0) { continue; } auto iter = usefulTensorIndexMap.find(op->inputIndexes[i]); op->inputIndexes[i] = iter->second; } for (int i = 0; i < op->outputIndexes.size(); ++i) { auto iter = usefulTensorIndexMap.find(op->outputIndexes[i]); op->outputIndexes[i] = iter->second; } } mNet->tensorName = usefulTensorName; for (auto iter = mNet->extraTensorDescribe.begin(); iter != mNet->extraTensorDescribe.end();) { auto index = (*iter)->index; if (usefulTensorIndexMap.find(index) == usefulTensorIndexMap.end()) { iter = mNet->extraTensorDescribe.erase(iter); continue; } (*iter)->index = usefulTensorIndexMap.find(index)->second; iter++; } // Check dup name and modify std::set names; std::set tensorNames; for (int i = 0; i < mNet->oplists.size(); ++i) { auto& op = mNet->oplists[i]; auto opName = op->name; if (opName.empty() || names.find(opName) != names.end()) { std::ostringstream defaultName; defaultName << EnumNameOpType(op->type); defaultName << i; op->name = defaultName.str(); #ifdef DEBUG MNN_PRINT("%d op name is empty or dup, set to %s\n", i, op->name.c_str()); #endif opName = op->name; } names.insert(opName); for (auto output : op->outputIndexes) { auto origin = net->tensorName[output]; if (origin.empty() || tensorNames.find(origin) != tensorNames.end()) { std::ostringstream defaultName; defaultName << output; origin = defaultName.str(); net->tensorName[output] = origin; } tensorNames.insert(origin); } } return true; } int main(int argc, const char* argv[]) { if (argc < 3) { MNN_PRINT("Usage: ./compilefornpu src.mnn dst.mnn npu.json\n"); return 0; } const char* srcMNN = argv[1]; const char* dstMNN = argv[2]; std::vector inputNames; std::vector outputNames; std::vector> inputs; std::set skipOps; if (argc >= 4) { rapidjson::Document document; std::ifstream fileNames(argv[3]); std::ostringstream output; output << fileNames.rdbuf(); auto outputStr = output.str(); document.Parse(outputStr.c_str()); if (document.HasParseError()) { MNN_ERROR("Invalid json\n"); return 0; } gNPUName = document["type"].GetString(); if (gNPUName == "QNN") { MNN_PRINT("Convert for QNN, QualComn's NPU\n"); gNPUType = MNN_CONVERT_QNN; gNeedOffline = true; gOfflieSrc = ""; gOfflieDst = "bin"; } else if (gNPUName == "MLDA") { MNN_PRINT("Convert for MLDA, MTK's NPU\n"); gNPUType = MNN_CONVERT_NEUROPILOT; gNeedOffline = true; gOfflieSrc = "tflite"; gOfflieDst = "dla"; } else if (gNPUName == "CoreML") { MNN_PRINT("Convert for CoreML, Apple's framework\n"); gNPUType = MNN_CONVERT_COREML; gNeedOffline = true; gOfflieSrc = ""; gOfflieDst = ""; } else { MNN_PRINT("Use Native NPU compute\n"); } if (document.HasMember("cache")) { gCacheDir = document["cache"].GetString(); FUNC_PRINT_ALL(gCacheDir.c_str(), s); MNNCreateDir(gCacheDir.c_str()); } if (document.HasMember("graph_name")) { gGraphName = document["graph_name"].GetString(); } if (document.HasMember("skips")) { auto skips = document["skips"].GetArray(); for (auto iter = skips.Begin(); iter != skips.End(); iter++) { skipOps.insert(iter->GetString()); } } if (document.HasMember("KVCACHE_SIZE_LIMIT")) { gMaxKVSize = document["KVCACHE_SIZE_LIMIT"].GetInt(); } if (document.HasMember("testdir")) { auto testdir = document["testdir"].GetArray(); for (auto iter = testdir.Begin(); iter != testdir.End(); iter++) { std::string dirname = iter->GetString(); auto subinputs = MNN::Express::Variable::load((dirname + "/input.mnn").c_str()); if (subinputs.empty()) { MNN_ERROR("Failed to load test inputs from %s/input.mnn\n", dirname.c_str()); return 1; } inputs.emplace_back(subinputs); inputNames.clear(); for (int i=0; iname()); } auto outputs = MNN::Express::Variable::load((dirname + "/output.mnn").c_str()); if (outputs.empty()) { MNN_ERROR("Failed to load test outputs from %s/output.mnn\n", dirname.c_str()); return 1; } outputNames.clear(); for (int i=0; iname()); } } } } if (outputNames.empty()) { std::shared_ptr m(MNN::Express::Module::load(inputNames, outputNames, srcMNN), MNN::Express::Module::destroy); if (nullptr == m.get()) { MNN_ERROR("Failed to load source module from %s\n", srcMNN); return 1; } auto minfo = m->getInfo(); outputNames = minfo->outputNames; inputNames = minfo->inputNames; std::vector subinputs; subinputs.resize(minfo->inputs.size()); for (int i=0; iinputs.size(); ++i) { auto& info = minfo->inputs[i]; auto varp = MNN::Express::_Input(info.dim, info.order, info.type); varp->writeMap(); subinputs[i] = varp; } inputs = {subinputs}; } // Registor size computor MNN::Express::Executor::getGlobalExecutor(); // Get Net struct std::shared_ptr netC(MNN::Interpreter::createFromFile(srcMNN), MNN::Interpreter::destroy); auto bufferPair = netC->getModelBuffer(); std::shared_ptr sharedConst; auto buffer = bufferPair.first; auto length = bufferPair.second; auto net = GetNet(buffer); std::map tensorIndexMap; for (int i=0; itensorName()->size(); ++i) { auto tname = net->tensorName()->GetAsString(i)->str(); tensorIndexMap.insert(std::make_pair(tname, i)); } // Extra Const Tensors sharedConst.reset(new Schedule::ScheduleInfo); std::vector> allTensors; sharedConst->allTensors.resize(net->tensorName()->size()); initConstTensorsNoAlloc(sharedConst->allTensors, net); std::set noneedComputeIndexes; for (int i=0; iallTensors.size(); ++i) { if (sharedConst->allTensors[i].get() != nullptr) { noneedComputeIndexes.insert(i); } } std::set inputIndexes; std::set outputIndexes; std::map outputIndexesMap; for (int i=0; itensorName()->size(); ++i) { auto tname = net->tensorName()->GetAsString(i)->str(); for (int j=0; j firstOutputIndex; for (int i=0; ioplists()->size(); ++i) { auto op = net->oplists()->GetAs(i); if (skipOps.find(op->name()->str()) != skipOps.end()) { MNN_PRINT("Skip %s op\n", op->name()->c_str()); auto outputSize = op->outputIndexes()->size(); for (int v=0; voutputIndexes()->data()[v]); } } } if (!firstOutputIndex.empty()) { // Get New Inputs std::vector firstOutputNames; // Compute New Input for (auto output : firstOutputIndex) { inputIndexes.insert(output); firstOutputNames.emplace_back(net->tensorName()->GetAsString(output)->str()); } std::vector newInputNames; for (auto index : inputIndexes) { newInputNames.emplace_back(net->tensorName()->GetAsString(index)->str()); } std::shared_ptr m(MNN::Express::Module::load(inputNames, firstOutputNames, (const uint8_t*)bufferPair.first, bufferPair.second), MNN::Express::Module::destroy); for (int i=0; i vars; for (int v=0; vonForward(inputs[i]); for (int v=0; v constOpId; std::map, std::vector>> constTensorData; std::vector netReplace(inputs.size()); std::vector BuilderTmp(inputs.size()); std::vector netVec(inputs.size()); _findAllConstTensorIndex(net, inputIndexes, outputIndexes, sharedConst, constOpId, &constTensorData); for (int inputIndex = 0; inputIndex < inputs.size(); ++inputIndex) { _getConstData(net, inputs[inputIndex], inputIndexes, outputIndexes, constTensorData, srcMNN); netReplace[inputIndex] = _replaceConstOp(bufferPair.first, bufferPair.second, constTensorData, constOpId); BuilderTmp[inputIndex].Finish(Net::Pack(BuilderTmp[inputIndex], netReplace[inputIndex])); } auto bufferPair0 = std::make_pair(BuilderTmp[0].GetBufferPointer(), BuilderTmp[0].GetSize()); auto net0 = GetNet(bufferPair0.first); // Extra Const Tensors sharedConst.reset(new Schedule::ScheduleInfo); sharedConst->allTensors.resize(net->tensorName()->size()); initConstTensorsNoAlloc(sharedConst->allTensors, net0); for (int i = 0; i < sharedConst->allTensors.size(); ++i) { if (sharedConst->allTensors[i].get() != nullptr) { noneedComputeIndexes.insert(i); } } std::vector keepOp(net->oplists()->size(), false); { auto subModulesInfo = _createSubModuleInfo(net0, inputIndexes, outputIndexes, noneedComputeIndexes, sharedConst); for (int moduleIndex = 0; moduleIndex < subModulesInfo.size(); ++moduleIndex) { auto moduleInfo = subModulesInfo[moduleIndex]; for (auto& index : moduleInfo.opList) { keepOp[index] = true; } } } // Split Module auto subModulesInfo = _createSubModuleInfo(net0, inputIndexes, outputIndexes, noneedComputeIndexes, sharedConst); // TODO: Insert pass to split submodule to npu and not npu std::map> merges; std::vector> allNets; for (int inputIndex=0; inputIndex < inputs.size(); ++inputIndex) { auto bufferPairTmp = std::make_pair(BuilderTmp[inputIndex].GetBufferPointer(), BuilderTmp[inputIndex].GetSize()); auto net = GetNet(bufferPairTmp.first); std::map stackes; // Compute module's io for (int i=0; itensorName()->size(); ++i) { auto tname = net->tensorName()->GetAsString(i)->str(); for (int j=0; j moduleIO(subModulesInfo.size()); for (int i=0; i subInputs; for (auto index : current.inputs) { subInputs.emplace_back(stackes[index]); } moduleIO[i] = _getSubModuleIO(subInputs, current, bufferPairTmp.first, bufferPairTmp.second, srcMNN); for (int j=0; joplists()->GetAs(opIndex); if (op->type() == OpType_Convolution) { hasConvolution = true; break; } } if (!hasConvolution) { subModulesInfo[i].isBreak = true; } } // Compile NPU Module std::vector> npuOps(subModulesInfo.size()); int npuIndex = 0; float npuTotal = 0.0f; float cpuTotal = 0.0f; for (int i=0; i{srcPath})); } npuOps[i] = std::move(_compileSubModule(moduleIO[i], subModulesInfo[i], bufferPairTmp.first, bufferPairTmp.second, srcPath, srcMNN, path, cpuTotal, npuTotal, inputIndex, graphicName)); npuIndex++; } } MNN_PRINT("Total Speed Compare: NPU: %f ms : CPU: %f ms\n", npuTotal, cpuTotal); // Merge to dst std::shared_ptr dstNet(flatbuffers::GetRoot(bufferPairTmp.first)->UnPack()); for (int i=0; ioplists[i]->inputIndexes.empty()) { continue; } if (!keepOp[i]) { dstNet->oplists[i].reset(); } } for (int moduleIndex=0; moduleIndexoplists[index].reset(); } dstNet->oplists[moduleInfo.opList[0]] = std::move(npuOps[moduleIndex]); } auto oplist = std::move(dstNet->oplists); for (auto& op : oplist) { if (nullptr != op.get()) { dstNet->oplists.emplace_back(std::move(op)); } } _reIndexTensor(dstNet.get()); _reOrderOp(dstNet.get()); allNets.emplace_back(std::move(dstNet)); } // Fuse And Store auto dstNet = allNets[0].get(); for (int i=1; i bufwriter(buf); resDocument.Accept(bufwriter); MNN_PRINT("Write config to npu_postreat.json\n"); std::ofstream os("npu_postreat.json"); os << buf.GetString(); return 0; }