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 "CompilerHelpers.h"
20 #include "ExecutorFactory.h"
21 #include "ShapeValidator.h"
22 #include "pass/ConstantOutputPass.h"
23 #include "pass/OddOutputPass.h"
24 #include "pass/PassRunner.h"
25 #include "pass/UnusedOperandEliminationPass.h"
26 #include "../dumper/dot/DotDumper.h"
27 #include "../exec/Executors.h"
28 #include "../ir/OperationDumper.h"
29 #include "../ir/verifier/Verifier.h"
31 #include "compiler/StaticShapeInferer.h"
33 #include <misc/string_helpers.h>
34 #include <misc/polymorphic_downcast.h>
41 MultiModelCompiler::MultiModelCompiler(const std::shared_ptr<ir::NNPkg> &nnpkg,
42 std::vector<std::unique_ptr<CompilerOptions>> &copts)
43 : _nnpkg{nnpkg}, _voptions{}
45 assert(nnpkg->model_count() != 1);
47 for (uint32_t i = 0; i < copts.size(); i++)
49 _voptions.push_back(copts[i].get());
53 std::shared_ptr<CompilerArtifact> MultiModelCompiler::compile(void)
55 /***************************************************
56 * Prepare compilation phase
57 ***************************************************/
58 for (auto &&options : _voptions)
61 throw std::runtime_error{"Empty compile option"};
64 // TODO handle option for each model
65 if (options->he_profiling_mode)
66 throw std::runtime_error("NYI: Profiling mode for multiple model is not supported yet");
68 if (!options->minmax_filepath.empty())
69 throw std::runtime_error("Recording minmax is not supported for multiple models");
71 options->forceInternalOptions();
72 options->verboseOptions();
75 // NYI: allow one model compilation
76 auto const model_count = _nnpkg->model_count();
77 if (model_count != _voptions.size())
78 throw std::runtime_error{"Model count and option vector size mismatch"};
80 for (uint16_t i = 0; i < model_count; i++)
82 if (!_nnpkg->model(ir::ModelIndex{i})->hasOnly<ir::Graph>())
83 throw std::runtime_error("MultiModelCompiler can only compile models for inference.");
86 for (uint16_t i = 0; i < model_count; i++)
88 _nnpkg->model(ir::ModelIndex{i})->iterate([&](const ir::SubgraphIndex &, ir::IGraph &graph) {
89 auto &subg = nnfw::misc::polymorphic_downcast<ir::Graph &>(graph);
93 .append(std::make_unique<pass::ConstantOutputPass>(subg))
94 .append(std::make_unique<pass::OddOutputPass>(subg))
98 pass::PassRunner{}.append(std::make_unique<pass::UnusedOperandEliminationPass>(subg)).run();
102 /***************************************************
103 * Backend independent analysis & optimization phase
104 ***************************************************/
105 // TODO Handle dump level for each model
106 auto dump_level = static_cast<dumper::dot::DotDumper::Level>(_voptions[0]->graph_dump_level);
107 onert::dumper::dot::DotDumper dot_dumper(dump_level);
110 // TODO Support tracing_ctx for multiple model
111 std::unique_ptr<util::TracingCtx> tracing_ctx = nullptr;
113 // Model edge context: copy model edge context
114 auto model_edges = std::make_unique<ir::ModelEdges>(_nnpkg->model_edges());
117 std::unordered_map<ir::ModelIndex, std::shared_ptr<backend::custom::IKernelBuilder>>
118 custom_kernel_builders;
119 for (uint16_t i = 0; i < model_count; i++)
121 auto const model_index = ir::ModelIndex{i};
122 custom_kernel_builders[model_index] = _nnpkg->model(model_index)->getKernelBuilder();
125 // Lower: Assign backend
126 std::unordered_map<ir::ModelIndex,
127 std::unordered_map<ir::SubgraphIndex, std::unique_ptr<compiler::LoweredGraph>>>
130 for (uint16_t i = 0; i < model_count; i++)
132 auto const model_index = ir::ModelIndex{i};
133 auto model = _nnpkg->model(model_index);
135 model->iterate([&](const ir::SubgraphIndex &subg_index, ir::IGraph &graph) {
136 auto &subg = nnfw::misc::polymorphic_downcast<ir::Graph &>(graph);
138 dot_dumper.dump(subg,
139 nnfw::misc::str("before_lower_model-", i, "-subg-", subg_index.value()));
140 // Lower: Assign backend
141 lowered_subgs[model_index][subg_index] =
142 std::make_unique<compiler::LoweredGraph>(subg, *_voptions[i]);
143 // Set tracing_ctx for copied graph
144 if (tracing_ctx != nullptr)
145 tracing_ctx->setSubgraphIndex(&(lowered_subgs[model_index][subg_index]->graph()),
152 for (const auto &pair : lowered_subgs)
154 const auto &model_index = pair.first;
155 const auto &model_lsubg = pair.second;
157 for (const auto &pair_inner : model_lsubg)
159 const auto &subg_index = pair_inner.first;
160 const auto &lowered_subg = pair_inner.second;
161 dot_dumper.dump(*lowered_subg, nnfw::misc::str("after_lower_model-", model_index.value(),
162 "-subg-", subg_index.value()));
167 for (auto &&pair : lowered_subgs)
169 auto &model_lsubgs = pair.second;
170 // Run the StaticShapeInfer of primary subg. All child StaticShapeInferers are called
172 std::unordered_map<ir::SubgraphIndex, std::unique_ptr<StaticShapeInferer>> inferers =
173 createStaticShapeInferers(model_lsubgs);
175 const auto primary_subg_idx = ir::SubgraphIndex{0};
176 inferers.at(primary_subg_idx)->infer();
178 for (const auto &pair_inferer : inferers)
180 const auto inferer = pair_inferer.second.get();
186 // TODO Move shape independent feature check from ShapeValidator to OperationValidator
187 // TODO Move ShapeValidator into shape inference
188 // - Check input tensor shape validation
189 // - Check parameter value validation which valid value is depend on input tensor shape
190 // - Output tensor shape validation check is needless because
191 // static/dynamic shape inferer will make valid output shape
192 for (const auto &pair : lowered_subgs)
194 const auto &model_lsubgs = pair.second;
196 for (const auto &pair_inner : model_lsubgs)
198 const auto &lowered_subg = pair_inner.second;
199 compiler::ShapeValidator{lowered_subg->graph()}();
203 /*************************************************************
204 * Backend independent analysis & optimization phase finished
205 *************************************************************/
206 auto executors = std::make_shared<exec::Executors>(std::move(model_edges));
207 for (auto &&pair : lowered_subgs)
209 auto const &model_index = pair.first;
210 auto &model_lsubgs = pair.second;
212 for (auto &&pair_inner : model_lsubgs)
214 auto const subg_index = pair_inner.first;
215 auto &lowered_subg = pair_inner.second;
216 auto const indexed_ranks = lowered_subg->indexed_ranks();
218 ir::OperationDumper dumper("Executor generation of Subgraph " +
219 std::to_string(subg_index.value()));
220 lowered_subg->graph().operations().iterate(
221 [&](const ir::OperationIndex &, const ir::IOperation &op) { op.accept(dumper); });
223 ExecutorFactoryArgs args;
224 args.tracing_ctx = tracing_ctx.get();
225 args.options = _voptions[model_index.value()];
226 args.model_index = model_index;
227 args.custom_kernel_builder = custom_kernel_builders[model_index];
228 auto executor = std::unique_ptr<exec::IExecutor>{
229 ExecutorFactory::get().create(std::move(lowered_subg), executors, args)};
230 executor->setIndexedRanks(indexed_ranks);
231 executors->emplace(model_index, subg_index, std::move(executor));
235 /********************************
236 * Code generation phase finished
237 ********************************/
238 return std::make_shared<CompilerArtifact>(executors, std::move(tracing_ctx));
241 } // namespace compiler