// // TemplateMerge.cpp // MNNConverter // // Created by MNN on 2019/09/16. // Copyright © 2018, Alibaba Group Holding Limited // #include "TemplateMerge.hpp" #include #include #include #include namespace MNN { namespace Express { static std::vector splitInBoundary(const std::vector& execute, const std::vector& boundary) { std::unordered_set blacklist; for (const auto expr : Variable::getExecuteOrder(boundary)) { blacklist.insert(expr); } std::vector executeValid; for (const auto expr : execute) { if (blacklist.count(expr) == 0) { executeValid.push_back(expr); } } return executeValid; } static bool crossBoundary(EXPRP origin, EXPRP opt, const std::unordered_set& boundary) { if (boundary.empty()) { return false; } int flag[] = {1, 2, 4, 8}; // four state: origin visited, opt visited, origin contained, opt contained std::unordered_map exprState; std::unordered_set edge[] = {{origin}, {opt}}; auto step = [&](int index) { if (edge[index].empty()) { return; } std::unordered_set nextEdge; while (!edge[index].empty()) { auto now = *edge[index].begin(); edge[index].erase(now); if (exprState[now] & flag[index]) { continue; } exprState[now] &= flag[index]; // opposite side contain the expr too, remove itself and parent nodes if (exprState[now] & flag[1 - index + 2]) { Expr::visit(now, [&](EXPRP expr) { if (!(exprState[expr] & flag[1 - index + 2])) { return false; } exprState[expr] ^= flag[1 - index + 2]; edge[1 - index].erase(expr); return true; }, [](EXPRP expr) { return true; }); continue; } exprState[now] &= flag[index + 2]; for (auto input : now->inputs()) { if (input.get() == nullptr) { continue; } auto next = input->expr().first; if (!(exprState[next] & flag[index])) { if (edge[index].count(next) == 0) { nextEdge.insert(next); } } } } edge[index] = std::move(nextEdge); }; bool optDone = false; while (!(edge[0].empty() && edge[1].empty())) { // alternate iterate origin pass and opt pass, which control time complexity step(0); step(1); if (edge[1].empty()) { // opt pass step done, origin pass expr won't be remove. check whether cross boundary if (!optDone) { optDone = true; for (auto expr : boundary) { if (exprState[expr] & flag[2]) { return true; } } } for (auto expr : edge[0]) { // check whether new step edge of origin pass cross boundary if (boundary.count(expr) && !(exprState[expr] & flag[3])) { return true; } } } } // check whether origin pass (replaced by opt pass) cross boundary for (auto expr : boundary) { if (exprState[expr] & flag[2]) { return true; } } return false; } static std::map updateInputVarOfExpr(EXPRP expr) { std::map res; auto inputs = expr->inputs(); for (int i = 0; i < inputs.size(); ++i) { VARP input = inputs.at(i); res[input->name()] = input; } return res; } bool TemplateMerge::onExecute(const std::vector& outputs, PassPriority priority, std::map& updateVars, const std::vector& boundary) { if (mPriorities.size() <= priority) { return false; } auto config = Global::Get(); bool dumpPass = config != nullptr && config->dumpPass; bool hasChange = false; std::unordered_set boundaryExpr; for (auto it : boundary) { boundaryExpr.insert(it->expr().first); } do { hasChange = false; for (const auto& pass_name : mPriorities.at(priority)) { auto& pass = mTemplates.at(pass_name); std::set invalidVARP; auto execute = splitInBoundary(Variable::getExecuteOrder(outputs), boundary); int changeCount = 0; for (int i=0; iget() == nullptr) { continue; } if (invalidVARP.find(var) != invalidVARP.end()) { continue; } // track arguments need by Expr::create, not create backup expr to avoid influence optimize auto originArgs = make_tuple(var->extra(), var->inputs(), var->outputSize()); if (pass(var)) { auto originVar = Expr::create(std::get<0>(originArgs), std::move(std::get<1>(originArgs)), std::get<2>(originArgs)); if (crossBoundary(originVar, var, boundaryExpr)) { Expr::replace(var, originVar); invalidVARP.insert(var); continue; } hasChange = true; changeCount++; if (dumpPass) { MNN_PRINT("[DumpPass] Merge::%s changed node: %s\n", pass_name.c_str(), var->name().c_str()); } } else { invalidVARP.insert(var); } } if (dumpPass && changeCount > 0) { MNN_PRINT("[DumpPass] Merge::%s: %d nodes changed\n", pass_name.c_str(), changeCount); } } MNN::Express::ExecutorScope::Current()->gc(); } while (hasChange); return true; } TemplateMerge& TemplateMerge::getInstance(const std::string& pass) { static std::map gMerge; if (gMerge.find(pass) == gMerge.end()) { gMerge.insert(std::make_pair(pass, TemplateMerge())); } auto iter = gMerge.find(pass); return iter->second; } void TemplateMerge::insertTemplateV2(std::string key, std::function transform, PassPriority priority) { if (mPriorities.size() <= priority) { mPriorities.resize(priority + 1); } mPriorities[priority].push_back(key); mTemplates.insert(std::make_pair(key, transform)); } void TemplateMerge::insertTemplate(std::string key, std::function compare, std::function transform, PassPriority priority) { auto wrap = [compare, transform](EXPRP expr) { if (!compare(expr)) { return false; } return transform(expr); }; insertTemplateV2(key, wrap, priority); } } // namespace Express } // namespace MNN