fea6a7f2560205a0a7c0456074a09191872565fa
[platform/core/ml/nnfw.git] / runtime / onert / core / src / compiler / MultiModelCompiler.cc
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "MultiModelCompiler.h"
18
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"
29
30 #include "compiler/StaticShapeInferer.h"
31
32 #include <misc/string_helpers.h>
33
34 namespace onert
35 {
36 namespace compiler
37 {
38
39 MultiModelCompiler::MultiModelCompiler(const std::shared_ptr<ir::NNPkg> &nnpkg,
40                                        std::vector<std::unique_ptr<CompilerOptions>> &copts)
41   : _nnpkg{nnpkg}, _voptions{}
42 {
43   assert(nnpkg->model_count() != 1);
44
45   for (uint32_t i = 0; i < copts.size(); i++)
46   {
47     _voptions.push_back(copts[i].get());
48   }
49 }
50
51 std::shared_ptr<CompilerArtifact> MultiModelCompiler::compile(void)
52 {
53   /***************************************************
54    * Prepare compilation phase
55    ***************************************************/
56   for (auto options : _voptions)
57   {
58     if (!options)
59       throw std::runtime_error{"Empty compile option"};
60
61     // Mode check
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");
65
66     options->forceInternalOptions();
67     options->verboseOptions();
68   }
69
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"};
74
75   for (uint16_t i = 0; i < model_count; i++)
76   {
77     _nnpkg->model(ir::ModelIndex{i})->iterate([&](const ir::SubgraphIndex &, ir::Graph &subg) {
78       // Mandatory passes
79       pass::PassRunner{}
80         .append(std::make_unique<pass::ConstantOutputPass>(subg))
81         .append(std::make_unique<pass::OddOutputPass>(subg))
82         .run();
83
84       // Optimizations
85       pass::PassRunner{}.append(std::make_unique<pass::UnusedOperandEliminationPass>(subg)).run();
86     });
87   }
88
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);
95
96   // Tracing context
97   // TODO Support tracing_ctx for multiple model
98   std::unique_ptr<util::TracingCtx> tracing_ctx = nullptr;
99
100   // Model edge context: copy model edge context
101   auto model_edges = std::make_unique<ir::ModelEdges>(_nnpkg->model_edges());
102
103   // Lower: Assign backend
104   std::unordered_map<ir::ModelIndex,
105                      std::unordered_map<ir::SubgraphIndex, std::unique_ptr<compiler::LoweredGraph>>>
106     lowered_subgs;
107
108   for (uint16_t i = 0; i < model_count; i++)
109   {
110     auto const model_index = ir::ModelIndex{i};
111     auto model = _nnpkg->model(model_index);
112
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()),
122                                       subg_index.value());
123     });
124   }
125
126   _nnpkg.reset();
127
128   for (const auto &pair : lowered_subgs)
129   {
130     const auto &model_index = pair.first;
131     const auto &model_lsubg = pair.second;
132
133     for (const auto &pair_inner : model_lsubg)
134     {
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()));
139     }
140   }
141
142   // Shape inference.
143   for (auto &&pair : lowered_subgs)
144   {
145     auto &model_lsubgs = pair.second;
146     // Run the StaticShapeInfer of primary subg. All child StaticShapeInferers are called
147     // recursively
148     std::unordered_map<ir::SubgraphIndex, std::unique_ptr<StaticShapeInferer>> inferers =
149       StaticShapeInferer::createStaticShapeInferers(model_lsubgs);
150
151     const auto primary_subg_idx = ir::SubgraphIndex{0};
152     inferers.at(primary_subg_idx)->infer();
153
154     for (const auto &pair_inferer : inferers)
155     {
156       const auto inferer = pair_inferer.second.get();
157       inferer->dump();
158     }
159   }
160
161   // Shape validation
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)
169   {
170     const auto &model_lsubgs = pair.second;
171
172     for (const auto &pair_inner : model_lsubgs)
173     {
174       const auto &lowered_subg = pair_inner.second;
175       compiler::ShapeValidator{lowered_subg->graph()}();
176     }
177   }
178
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)
184   {
185     auto const &model_index = pair.first;
186     auto &model_lsubgs = pair.second;
187
188     for (auto &&pair_inner : model_lsubgs)
189     {
190       auto const subg_index = pair_inner.first;
191       auto &lowered_subg = pair_inner.second;
192       auto const indexed_ranks = lowered_subg->indexed_ranks();
193
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); });
198
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));
204     }
205   }
206
207   /********************************
208    * Code generation phase finished
209    ********************************/
210   return std::make_shared<CompilerArtifact>(executors, std::move(tracing_ctx));
211 }
212
213 } // namespace compiler
214 } // namespace onert