Imported Upstream version 1.8.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     onert::compiler::Compiler compiler{subgs};
77     executors = compiler.compile();
78   }
79
80 public:
81   std::shared_ptr<Graph> graph;
82   std::shared_ptr<onert::exec::ExecutorMap> executors;
83 };
84
85 TEST(ExecInstance, simple)
86 {
87   auto mockup = CompiledMockUpModel();
88   auto graph = mockup.graph;
89   auto executors = mockup.executors;
90
91   auto input1 = IOIndex{0};
92   auto input2 = IOIndex{1};
93   auto output = IOIndex{0};
94
95   const float input1_buffer[4] = {1, 0, -1, -2};
96   const float input2_buffer[4] = {1, -3, 2, -4};
97   float output_buffer[4] = {};
98   const float output_expected[4] = {5, -2, 0, -1};
99
100   onert::exec::Execution execution{executors};
101
102   execution.setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16);
103   execution.setInput(input2, reinterpret_cast<const void *>(input2_buffer), 16);
104   execution.setOutput(output, reinterpret_cast<void *>(output_buffer), 16);
105   execution.execute();
106
107   for (auto i = 0; i < 4; i++)
108   {
109     EXPECT_EQ(output_buffer[i], output_expected[i]);
110   }
111 }
112
113 TEST(ExecInstance, twoCompile)
114 {
115   auto mockup = CompiledMockUpModel();
116   auto graph = mockup.graph;
117   auto executors1 = mockup.executors;
118   onert::exec::Execution execution1{executors1};
119
120   auto input1 = IOIndex{0};
121   auto input2 = IOIndex{1};
122   auto output = IOIndex{0};
123
124   const float exe1_input1_buffer[4] = {1, 0, -1, -2};
125   const float exe1_input2_buffer[4] = {1, -3, 2, -4};
126   float exe1_output_buffer[4] = {};
127   const float exe1_output_expected[4] = {5, -2, 0, -1};
128
129   execution1.setInput(input1, reinterpret_cast<const void *>(exe1_input1_buffer), 16);
130   execution1.setInput(input2, reinterpret_cast<const void *>(exe1_input2_buffer), 16);
131   execution1.setOutput(output, reinterpret_cast<void *>(exe1_output_buffer), 16);
132
133   // Make new executor: compile again
134   auto subgs = std::make_shared<onert::ir::Subgraphs>();
135   subgs->push(onert::ir::SubgraphIndex{0}, graph);
136   onert::compiler::Compiler compiler{subgs};
137   std::shared_ptr<onert::exec::ExecutorMap> executors2 = compiler.compile();
138   onert::exec::Execution execution2{executors2};
139
140   const float exe2_input1_buffer[4] = {2, 1, -2, 0};
141   const float exe2_input2_buffer[4] = {-3, 3, 1, 2};
142   float exe2_output_buffer[4] = {};
143   const float exe2_output_expected[4] = {2, 5, -2, 7};
144
145   execution2.setInput(input1, reinterpret_cast<const void *>(exe2_input1_buffer), 16);
146   execution2.setInput(input2, reinterpret_cast<const void *>(exe2_input2_buffer), 16);
147   execution2.setOutput(output, reinterpret_cast<void *>(exe2_output_buffer), 16);
148
149   execution1.execute();
150   execution2.execute();
151
152   for (auto i = 0; i < 4; i++)
153   {
154     EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
155     EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
156   }
157 }
158
159 // Support two initialized execution instance then ordered execution
160 TEST(ExecInstance, twoExecution)
161 {
162   auto mockup = CompiledMockUpModel();
163   auto executors = mockup.executors;
164   auto input1 = IOIndex{0};
165   auto input2 = IOIndex{1};
166   auto output1 = IOIndex{0};
167
168   const float exe1_input1_buffer[4] = {1, 0, -1, -2};
169   const float exe1_input2_buffer[4] = {1, -3, 2, -4};
170   float exe1_output_buffer[4] = {};
171   const float exe1_output_expected[4] = {5, -2, 0, -1};
172   const float exe2_output_expected[4] = {2, 5, -2, 7};
173
174   onert::exec::Execution execution1{executors};
175   execution1.setInput(input1, reinterpret_cast<const void *>(exe1_input1_buffer), 16);
176   execution1.setInput(input2, reinterpret_cast<const void *>(exe1_input2_buffer), 16);
177   execution1.setOutput(output1, reinterpret_cast<void *>(exe1_output_buffer), 16);
178
179   const float exe2_input1_buffer[4] = {2, 1, -2, 0};
180   const float exe2_input2_buffer[4] = {-3, 3, 1, 2};
181   float exe2_output_buffer[4] = {};
182
183   // Make new execution
184   onert::exec::Execution execution2{executors};
185   execution2.setInput(input1, reinterpret_cast<const void *>(exe2_input1_buffer), 16);
186   execution2.setInput(input2, reinterpret_cast<const void *>(exe2_input2_buffer), 16);
187   execution2.setOutput(output1, reinterpret_cast<void *>(exe2_output_buffer), 16);
188
189   execution1.execute();
190   execution2.execute();
191
192   for (auto i = 0; i < 4; i++)
193   {
194     EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
195     EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
196   }
197 }
198
199 class Inference
200 {
201 public:
202   Inference(const float (&input1)[4], const float (&input2)[4], float (&output)[4],
203             std::shared_ptr<onert::exec::ExecutorMap> &executors)
204       : _input1{input1}, _input2{input2}, _output{output}, _executors{executors}
205   {
206     // DO NOTHING
207   }
208
209   void inference(void)
210   {
211     auto input1 = IOIndex{0};
212     auto input2 = IOIndex{1};
213     auto output1 = IOIndex{0};
214
215     onert::exec::Execution execution{_executors};
216     execution.setInput(input1, reinterpret_cast<const void *>(_input1), 16);
217     execution.setInput(input2, reinterpret_cast<const void *>(_input2), 16);
218     execution.setOutput(output1, reinterpret_cast<void *>(_output), 16);
219
220     execution.execute();
221   }
222
223 private:
224   const float (&_input1)[4];
225   const float (&_input2)[4];
226   float (&_output)[4];
227   std::shared_ptr<onert::exec::ExecutorMap> &_executors;
228 };
229
230 // Support multi-thread execution
231 TEST(ExecInstance, twoThreads)
232 {
233   auto mockup = CompiledMockUpModel();
234   auto executors = mockup.executors;
235
236   const float exe1_input1_buffer[4] = {1, 0, -1, -2};
237   const float exe1_input2_buffer[4] = {1, -3, 2, -4};
238   float exe1_output_buffer[4] = {};
239   const float exe1_output_expected[4] = {5, -2, 0, -1};
240
241   Inference execution1{exe1_input1_buffer, exe1_input2_buffer, exe1_output_buffer, executors};
242
243   const float exe2_input1_buffer[4] = {2, 1, -2, 0};
244   const float exe2_input2_buffer[4] = {-3, 3, 1, 2};
245   float exe2_output_buffer[4] = {};
246   const float exe2_output_expected[4] = {2, 5, -2, 7};
247
248   Inference execution2{exe2_input1_buffer, exe2_input2_buffer, exe2_output_buffer, executors};
249
250   std::thread t1{&Inference::inference, &execution1};
251   std::thread t2{&Inference::inference, &execution2};
252
253   t1.join();
254   t2.join();
255
256   for (auto i = 0; i < 4; i++)
257   {
258     EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
259     EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
260   }
261 }
262
263 // Support asynchronous execution
264 TEST(ExecInstance, async)
265 {
266   auto mockup = CompiledMockUpModel();
267   auto graph = mockup.graph;
268   auto executors = mockup.executors;
269
270   auto input1 = IOIndex{0};
271   auto input2 = IOIndex{1};
272   auto output = IOIndex{0};
273
274   const float input1_buffer[4] = {1, 0, -1, -2};
275   const float input2_buffer[4] = {1, -3, 2, -4};
276   float output_buffer[4] = {};
277   const float output_expected[4] = {5, -2, 0, -1};
278
279   onert::exec::Execution execution{executors};
280
281   execution.setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16);
282   execution.setInput(input2, reinterpret_cast<const void *>(input2_buffer), 16);
283   execution.setOutput(output, reinterpret_cast<void *>(output_buffer), 16);
284   execution.startExecute();
285   execution.waitFinish();
286
287   for (auto i = 0; i < 4; i++)
288   {
289     EXPECT_EQ(output_buffer[i], output_expected[i]);
290   }
291 }
292
293 } // namespace