Imported Upstream version 1.25.0
[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 "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"
30
31 #include "compiler/StaticShapeInferer.h"
32
33 #include <misc/string_helpers.h>
34 #include <misc/polymorphic_downcast.h>
35
36 namespace onert
37 {
38 namespace compiler
39 {
40
41 MultiModelCompiler::MultiModelCompiler(const std::shared_ptr<ir::NNPkg> &nnpkg,
42                                        std::vector<std::unique_ptr<CompilerOptions>> &copts)
43   : _nnpkg{nnpkg}, _voptions{}
44 {
45   assert(nnpkg->model_count() != 1);
46
47   for (uint32_t i = 0; i < copts.size(); i++)
48   {
49     _voptions.push_back(copts[i].get());
50   }
51 }
52
53 std::shared_ptr<CompilerArtifact> MultiModelCompiler::compile(void)
54 {
55   /***************************************************
56    * Prepare compilation phase
57    ***************************************************/
58   for (auto &&options : _voptions)
59   {
60     if (!options)
61       throw std::runtime_error{"Empty compile option"};
62
63     // Mode check
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");
67
68     if (!options->minmax_filepath.empty())
69       throw std::runtime_error("Recording minmax is not supported for multiple models");
70
71     options->forceInternalOptions();
72     options->verboseOptions();
73   }
74
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"};
79
80   for (uint16_t i = 0; i < model_count; i++)
81   {
82     if (!_nnpkg->model(ir::ModelIndex{i})->hasOnly<ir::Graph>())
83       throw std::runtime_error("MultiModelCompiler can only compile models for inference.");
84   }
85
86   for (uint16_t i = 0; i < model_count; i++)
87   {
88     _nnpkg->model(ir::ModelIndex{i})->iterate([&](const ir::SubgraphIndex &, ir::IGraph &graph) {
89       auto &subg = nnfw::misc::polymorphic_downcast<ir::Graph &>(graph);
90
91       // Mandatory passes
92       pass::PassRunner{}
93         .append(std::make_unique<pass::ConstantOutputPass>(subg))
94         .append(std::make_unique<pass::OddOutputPass>(subg))
95         .run();
96
97       // Optimizations
98       pass::PassRunner{}.append(std::make_unique<pass::UnusedOperandEliminationPass>(subg)).run();
99     });
100   }
101
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);
108
109   // Tracing context
110   // TODO Support tracing_ctx for multiple model
111   std::unique_ptr<util::TracingCtx> tracing_ctx = nullptr;
112
113   // Model edge context: copy model edge context
114   auto model_edges = std::make_unique<ir::ModelEdges>(_nnpkg->model_edges());
115
116   // Custom kernels
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++)
120   {
121     auto const model_index = ir::ModelIndex{i};
122     custom_kernel_builders[model_index] = _nnpkg->model(model_index)->getKernelBuilder();
123   }
124
125   // Lower: Assign backend
126   std::unordered_map<ir::ModelIndex,
127                      std::unordered_map<ir::SubgraphIndex, std::unique_ptr<compiler::LoweredGraph>>>
128     lowered_subgs;
129
130   for (uint16_t i = 0; i < model_count; i++)
131   {
132     auto const model_index = ir::ModelIndex{i};
133     auto model = _nnpkg->model(model_index);
134
135     model->iterate([&](const ir::SubgraphIndex &subg_index, ir::IGraph &graph) {
136       auto &subg = nnfw::misc::polymorphic_downcast<ir::Graph &>(graph);
137
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()),
146                                       subg_index.value());
147     });
148   }
149
150   _nnpkg.reset();
151
152   for (const auto &pair : lowered_subgs)
153   {
154     const auto &model_index = pair.first;
155     const auto &model_lsubg = pair.second;
156
157     for (const auto &pair_inner : model_lsubg)
158     {
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()));
163     }
164   }
165
166   // Shape inference.
167   for (auto &&pair : lowered_subgs)
168   {
169     auto &model_lsubgs = pair.second;
170     // Run the StaticShapeInfer of primary subg. All child StaticShapeInferers are called
171     // recursively
172     std::unordered_map<ir::SubgraphIndex, std::unique_ptr<StaticShapeInferer>> inferers =
173       createStaticShapeInferers(model_lsubgs);
174
175     const auto primary_subg_idx = ir::SubgraphIndex{0};
176     inferers.at(primary_subg_idx)->infer();
177
178     for (const auto &pair_inferer : inferers)
179     {
180       const auto inferer = pair_inferer.second.get();
181       inferer->dump();
182     }
183   }
184
185   // Shape validation
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)
193   {
194     const auto &model_lsubgs = pair.second;
195
196     for (const auto &pair_inner : model_lsubgs)
197     {
198       const auto &lowered_subg = pair_inner.second;
199       compiler::ShapeValidator{lowered_subg->graph()}();
200     }
201   }
202
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)
208   {
209     auto const &model_index = pair.first;
210     auto &model_lsubgs = pair.second;
211
212     for (auto &&pair_inner : model_lsubgs)
213     {
214       auto const subg_index = pair_inner.first;
215       auto &lowered_subg = pair_inner.second;
216       auto const indexed_ranks = lowered_subg->indexed_ranks();
217
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); });
222
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));
232     }
233   }
234
235   /********************************
236    * Code generation phase finished
237    ********************************/
238   return std::make_shared<CompilerArtifact>(executors, std::move(tracing_ctx));
239 }
240
241 } // namespace compiler
242 } // namespace onert