Imported Upstream version 1.7.0
[platform/core/ml/nnfw.git] / runtime / onert / test / core / exec / ExecInstance.cc
1 /*
2  * Copyright (c) 2019 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 <gtest/gtest.h>
18 #include <thread>
19
20 #include "ir/Graph.h"
21 #include "compiler/Compiler.h"
22 #include "exec/Execution.h"
23 #include "ir/operation/Add.h"
24
25 namespace
26 {
27
28 using namespace onert::ir;
29
30 class CompiledMockUpModel
31 {
32 public:
33   CompiledMockUpModel()
34   {
35     // Model: two elementwise add operation
36     // model input: lhs, rhs1
37     // model output: second add result (result2)
38     // constant: rhs2
39     // result1 <= (lhs + rhs)
40     // result2 <= (result1 + rhs2)
41     // lhs, rhs1, rh2, result1, result2 shape: {1, 2, 2, 1}
42     // activation: none (constant)
43     graph = std::make_shared<Graph>();
44     // 1st add operands (result1 <= lhs + rhs1)
45     Shape shape{1, 2, 2, 1};
46     TypeInfo type{DataType::FLOAT32};
47     static float rhs2_data[4] = {3, 1, -1, 5};
48     auto operand_lhs = graph->addOperand(shape, type);
49     auto operand_rhs1 = graph->addOperand(shape, type);
50     auto operand_result1 = graph->addOperand(shape, type);
51     auto operand_rhs2 = graph->addOperand(shape, type);
52     auto operand_result2 = graph->addOperand(shape, type);
53     graph->operands()
54         .at(operand_rhs2)
55         .data(std::make_unique<CachedData>(reinterpret_cast<const uint8_t *>(&rhs2_data), 16));
56     // 2nd add operations (result2 <= result1 + rhs2)
57     operation::Add::Param param1;
58     param1.activation = Activation::NONE;
59     auto input_set1 = OperandIndexSequence{operand_lhs, operand_rhs1};
60     auto output_set1 = OperandIndexSequence{operand_result1};
61     graph->addOperation(std::make_unique<operation::Add>(input_set1, output_set1, param1));
62     operation::Add::Param param2;
63     param2.activation = Activation::NONE;
64     auto input_set2 = OperandIndexSequence{operand_result1, operand_rhs2};
65     auto output_set2 = OperandIndexSequence{operand_result2};
66     graph->addOperation(std::make_unique<operation::Add>(input_set2, output_set2, param2));
67     // Identify model inputs and outputs
68     graph->addInput(operand_lhs);
69     graph->addInput(operand_rhs1);
70     graph->addOutput(operand_result2);
71     graph->finishBuilding();
72
73     // Compile
74     auto subgs = std::make_shared<onert::ir::Subgraphs>();
75     subgs->push(onert::ir::SubgraphIndex{0}, graph);
76     auto compiler = new onert::compiler::Compiler{subgs};
77     executors = compiler->compile();
78     delete compiler;
79   }
80
81 public:
82   std::shared_ptr<Graph> graph;
83   std::shared_ptr<onert::exec::ExecutorMap> executors;
84 };
85
86 TEST(ExecInstance, simple)
87 {
88   auto mockup = CompiledMockUpModel();
89   auto graph = mockup.graph;
90   auto executors = mockup.executors;
91
92   auto input1 = IOIndex{0};
93   auto input2 = IOIndex{1};
94   auto output = IOIndex{0};
95
96   const float input1_buffer[4] = {1, 0, -1, -2};
97   const float input2_buffer[4] = {1, -3, 2, -4};
98   float output_buffer[4] = {};
99   const float output_expected[4] = {5, -2, 0, -1};
100
101   auto execution = new onert::exec::Execution(executors);
102
103   execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16);
104   execution->setInput(input2, reinterpret_cast<const void *>(input2_buffer), 16);
105   execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 16);
106   execution->execute();
107
108   for (auto i = 0; i < 4; i++)
109   {
110     EXPECT_EQ(output_buffer[i], output_expected[i]);
111   }
112
113   delete execution;
114 }
115
116 TEST(ExecInstance, twoCompile)
117 {
118   auto mockup = CompiledMockUpModel();
119   auto graph = mockup.graph;
120   auto executors1 = mockup.executors;
121   auto execution1 = new onert::exec::Execution(executors1);
122
123   auto input1 = IOIndex{0};
124   auto input2 = IOIndex{1};
125   auto output = IOIndex{0};
126
127   const float exe1_input1_buffer[4] = {1, 0, -1, -2};
128   const float exe1_input2_buffer[4] = {1, -3, 2, -4};
129   float exe1_output_buffer[4] = {};
130   const float exe1_output_expected[4] = {5, -2, 0, -1};
131
132   execution1->setInput(input1, reinterpret_cast<const void *>(exe1_input1_buffer), 16);
133   execution1->setInput(input2, reinterpret_cast<const void *>(exe1_input2_buffer), 16);
134   execution1->setOutput(output, reinterpret_cast<void *>(exe1_output_buffer), 16);
135
136   // Make new executor: compile again
137   auto subgs = std::make_shared<onert::ir::Subgraphs>();
138   subgs->push(onert::ir::SubgraphIndex{0}, graph);
139   auto compiler = new onert::compiler::Compiler{subgs};
140   std::shared_ptr<onert::exec::ExecutorMap> executors2 = compiler->compile();
141   auto execution2 = new onert::exec::Execution(executors2);
142
143   const float exe2_input1_buffer[4] = {2, 1, -2, 0};
144   const float exe2_input2_buffer[4] = {-3, 3, 1, 2};
145   float exe2_output_buffer[4] = {};
146   const float exe2_output_expected[4] = {2, 5, -2, 7};
147
148   execution2->setInput(input1, reinterpret_cast<const void *>(exe2_input1_buffer), 16);
149   execution2->setInput(input2, reinterpret_cast<const void *>(exe2_input2_buffer), 16);
150   execution2->setOutput(output, reinterpret_cast<void *>(exe2_output_buffer), 16);
151
152   execution1->execute();
153   execution2->execute();
154
155   for (auto i = 0; i < 4; i++)
156   {
157     EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
158     EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
159   }
160
161   delete compiler;
162   delete execution1;
163   delete execution2;
164 }
165
166 // Support two initialized execution instance then ordered execution
167 TEST(ExecInstance, twoExecution)
168 {
169   auto mockup = CompiledMockUpModel();
170   auto executors = mockup.executors;
171   auto input1 = IOIndex{0};
172   auto input2 = IOIndex{1};
173   auto output1 = IOIndex{0};
174
175   const float exe1_input1_buffer[4] = {1, 0, -1, -2};
176   const float exe1_input2_buffer[4] = {1, -3, 2, -4};
177   float exe1_output_buffer[4] = {};
178   const float exe1_output_expected[4] = {5, -2, 0, -1};
179   const float exe2_output_expected[4] = {2, 5, -2, 7};
180
181   auto execution1 = new onert::exec::Execution(executors);
182   execution1->setInput(input1, reinterpret_cast<const void *>(exe1_input1_buffer), 16);
183   execution1->setInput(input2, reinterpret_cast<const void *>(exe1_input2_buffer), 16);
184   execution1->setOutput(output1, reinterpret_cast<void *>(exe1_output_buffer), 16);
185
186   const float exe2_input1_buffer[4] = {2, 1, -2, 0};
187   const float exe2_input2_buffer[4] = {-3, 3, 1, 2};
188   float exe2_output_buffer[4] = {};
189
190   // Make new execution
191   auto execution2 = new onert::exec::Execution(executors);
192   execution2->setInput(input1, reinterpret_cast<const void *>(exe2_input1_buffer), 16);
193   execution2->setInput(input2, reinterpret_cast<const void *>(exe2_input2_buffer), 16);
194   execution2->setOutput(output1, reinterpret_cast<void *>(exe2_output_buffer), 16);
195
196   execution1->execute();
197   execution2->execute();
198
199   for (auto i = 0; i < 4; i++)
200   {
201     EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
202     EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
203   }
204
205   delete execution1;
206   delete execution2;
207 }
208
209 class Inference
210 {
211 public:
212   Inference(const float (&input1)[4], const float (&input2)[4], float (&output)[4],
213             std::shared_ptr<onert::exec::ExecutorMap> &executors)
214       : _input1{input1}, _input2{input2}, _output{output}, _executors{executors}
215   {
216     // DO NOTHING
217   }
218
219   void inference(void)
220   {
221     auto input1 = IOIndex{0};
222     auto input2 = IOIndex{1};
223     auto output1 = IOIndex{0};
224
225     auto execution = new onert::exec::Execution(_executors);
226     execution->setInput(input1, reinterpret_cast<const void *>(_input1), 16);
227     execution->setInput(input2, reinterpret_cast<const void *>(_input2), 16);
228     execution->setOutput(output1, reinterpret_cast<void *>(_output), 16);
229
230     execution->execute();
231
232     delete execution;
233   }
234
235 private:
236   const float (&_input1)[4];
237   const float (&_input2)[4];
238   float (&_output)[4];
239   std::shared_ptr<onert::exec::ExecutorMap> &_executors;
240 };
241
242 // Support multi-thread execution
243 TEST(ExecInstance, twoThreads)
244 {
245   auto mockup = CompiledMockUpModel();
246   auto executors = mockup.executors;
247
248   const float exe1_input1_buffer[4] = {1, 0, -1, -2};
249   const float exe1_input2_buffer[4] = {1, -3, 2, -4};
250   float exe1_output_buffer[4] = {};
251   const float exe1_output_expected[4] = {5, -2, 0, -1};
252
253   Inference execution1{exe1_input1_buffer, exe1_input2_buffer, exe1_output_buffer, executors};
254
255   const float exe2_input1_buffer[4] = {2, 1, -2, 0};
256   const float exe2_input2_buffer[4] = {-3, 3, 1, 2};
257   float exe2_output_buffer[4] = {};
258   const float exe2_output_expected[4] = {2, 5, -2, 7};
259
260   Inference execution2{exe2_input1_buffer, exe2_input2_buffer, exe2_output_buffer, executors};
261
262   std::thread t1{&Inference::inference, &execution1};
263   std::thread t2{&Inference::inference, &execution2};
264
265   t1.join();
266   t2.join();
267
268   for (auto i = 0; i < 4; i++)
269   {
270     EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
271     EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
272   }
273 }
274
275 // Support asynchronous execution
276 TEST(ExecInstance, async)
277 {
278   auto mockup = CompiledMockUpModel();
279   auto graph = mockup.graph;
280   auto executors = mockup.executors;
281
282   auto input1 = IOIndex{0};
283   auto input2 = IOIndex{1};
284   auto output = IOIndex{0};
285
286   const float input1_buffer[4] = {1, 0, -1, -2};
287   const float input2_buffer[4] = {1, -3, 2, -4};
288   float output_buffer[4] = {};
289   const float output_expected[4] = {5, -2, 0, -1};
290
291   auto execution = new onert::exec::Execution(executors);
292
293   execution->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16);
294   execution->setInput(input2, reinterpret_cast<const void *>(input2_buffer), 16);
295   execution->setOutput(output, reinterpret_cast<void *>(output_buffer), 16);
296   execution->startExecute();
297   execution->waitFinish();
298
299   for (auto i = 0; i < 4; i++)
300   {
301     EXPECT_EQ(output_buffer[i], output_expected[i]);
302   }
303
304   delete execution;
305 }
306
307 } // namespace