2 * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "MultiModelCompiler.h"
19 #include "ExecutorFactory.h"
20 #include "ShapeValidator.h"
21 #include "pass/ConstantOutputPass.h"
22 #include "pass/OddOutputPass.h"
23 #include "pass/PassRunner.h"
24 #include "pass/UnusedOperandEliminationPass.h"
25 #include "../dumper/dot/DotDumper.h"
26 #include "../exec/Executors.h"
27 #include "../ir/OperationDumper.h"
28 #include "../ir/verifier/Verifier.h"
30 #include "compiler/StaticShapeInferer.h"
32 #include <misc/string_helpers.h>
39 MultiModelCompiler::MultiModelCompiler(const std::shared_ptr<ir::NNPkg> &nnpkg,
40 std::vector<std::unique_ptr<CompilerOptions>> &copts)
41 : _nnpkg{nnpkg}, _voptions{}
43 assert(nnpkg->model_count() != 1);
45 for (uint32_t i = 0; i < copts.size(); i++)
47 _voptions.push_back(copts[i].get());
51 std::shared_ptr<CompilerArtifact> MultiModelCompiler::compile(void)
53 /***************************************************
54 * Prepare compilation phase
55 ***************************************************/
56 for (auto options : _voptions)
59 throw std::runtime_error{"Empty compile option"};
62 // TODO handle option for each model
63 if (options->he_profiling_mode)
64 throw std::runtime_error("NYI: Profiling mode for multiple model is not supported yet");
66 options->forceInternalOptions();
67 options->verboseOptions();
70 // NYI: allow one model compilation
71 auto const model_count = _nnpkg->model_count();
72 if (model_count != _voptions.size())
73 throw std::runtime_error{"Model count and option vector size mismatch"};
75 for (uint16_t i = 0; i < model_count; i++)
77 _nnpkg->model(ir::ModelIndex{i})->iterate([&](const ir::SubgraphIndex &, ir::Graph &subg) {
80 .append(std::make_unique<pass::ConstantOutputPass>(subg))
81 .append(std::make_unique<pass::OddOutputPass>(subg))
85 pass::PassRunner{}.append(std::make_unique<pass::UnusedOperandEliminationPass>(subg)).run();
89 /***************************************************
90 * Backend independent analysis & optimization phase
91 ***************************************************/
92 // TODO Handle dump level for each model
93 auto dump_level = static_cast<dumper::dot::DotDumper::Level>(_voptions[0]->graph_dump_level);
94 onert::dumper::dot::DotDumper dot_dumper(dump_level);
97 // TODO Support tracing_ctx for multiple model
98 std::unique_ptr<util::TracingCtx> tracing_ctx = nullptr;
100 // Model edge context: copy model edge context
101 auto model_edges = std::make_unique<ir::ModelEdges>(_nnpkg->model_edges());
103 // Lower: Assign backend
104 std::unordered_map<ir::ModelIndex,
105 std::unordered_map<ir::SubgraphIndex, std::unique_ptr<compiler::LoweredGraph>>>
108 for (uint16_t i = 0; i < model_count; i++)
110 auto const model_index = ir::ModelIndex{i};
111 auto model = _nnpkg->model(model_index);
113 model->iterate([&](const ir::SubgraphIndex &subg_index, ir::Graph &subg) {
114 dot_dumper.dump(subg,
115 nnfw::misc::str("before_lower_model-", i, "-subg-", subg_index.value()));
116 // Lower: Assign backend
117 lowered_subgs[model_index][subg_index] =
118 std::make_unique<compiler::LoweredGraph>(subg, *_voptions[i]);
119 // Set tracing_ctx for copied graph
120 if (tracing_ctx != nullptr)
121 tracing_ctx->setSubgraphIndex(&(lowered_subgs[model_index][subg_index]->graph()),
128 for (const auto &pair : lowered_subgs)
130 const auto &model_index = pair.first;
131 const auto &model_lsubg = pair.second;
133 for (const auto &pair_inner : model_lsubg)
135 const auto &subg_index = pair_inner.first;
136 const auto &lowered_subg = pair_inner.second;
137 dot_dumper.dump(*lowered_subg, nnfw::misc::str("after_lower_model-", model_index.value(),
138 "-subg-", subg_index.value()));
143 for (auto &&pair : lowered_subgs)
145 auto &model_lsubgs = pair.second;
146 // Run the StaticShapeInfer of primary subg. All child StaticShapeInferers are called
148 std::unordered_map<ir::SubgraphIndex, std::unique_ptr<StaticShapeInferer>> inferers =
149 StaticShapeInferer::createStaticShapeInferers(model_lsubgs);
151 const auto primary_subg_idx = ir::SubgraphIndex{0};
152 inferers.at(primary_subg_idx)->infer();
154 for (const auto &pair_inferer : inferers)
156 const auto inferer = pair_inferer.second.get();
162 // TODO Move shape independent feature check from ShapeValidator to OperationValidator
163 // TODO Move ShapeValidator into shape inference
164 // - Check input tensor shape validation
165 // - Check parameter value validation which valid value is depend on input tensor shape
166 // - Output tensor shape validation check is needless because
167 // static/dynamic shape inferer will make valid output shape
168 for (const auto &pair : lowered_subgs)
170 const auto &model_lsubgs = pair.second;
172 for (const auto &pair_inner : model_lsubgs)
174 const auto &lowered_subg = pair_inner.second;
175 compiler::ShapeValidator{lowered_subg->graph()}();
179 /*************************************************************
180 * Backend independent analysis & optimization phase finished
181 *************************************************************/
182 auto executors = std::make_shared<exec::Executors>(std::move(model_edges));
183 for (auto &&pair : lowered_subgs)
185 auto const &model_index = pair.first;
186 auto &model_lsubgs = pair.second;
188 for (auto &&pair_inner : model_lsubgs)
190 auto const subg_index = pair_inner.first;
191 auto &lowered_subg = pair_inner.second;
192 auto const indexed_ranks = lowered_subg->indexed_ranks();
194 ir::OperationDumper dumper("Executor generation of Subgraph " +
195 std::to_string(subg_index.value()));
196 lowered_subg->graph().operations().iterate(
197 [&](const ir::OperationIndex &, const ir::Operation &op) { op.accept(dumper); });
199 auto &options = *_voptions[model_index.value()];
200 auto executor = std::unique_ptr<exec::IExecutor>{ExecutorFactory::get().create(
201 std::move(lowered_subg), tracing_ctx.get(), options, executors, model_index)};
202 executor->setIndexedRanks(indexed_ranks);
203 executors->emplace(model_index, subg_index, std::move(executor));
207 /********************************
208 * Code generation phase finished
209 ********************************/
210 return std::make_shared<CompilerArtifact>(executors, std::move(tracing_ctx));
213 } // namespace compiler