2 * Copyright (c) 2019 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 <gtest/gtest.h>
21 #include "compiler/Compiler.h"
22 #include "exec/Execution.h"
23 #include "ir/operation/Add.h"
28 using namespace onert::ir;
30 class CompiledMockUpModel
35 // Model: two elementwise add operation
36 // model input: lhs, rhs1
37 // model output: second add result (result2)
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);
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();
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();
81 std::shared_ptr<Graph> graph;
82 std::shared_ptr<onert::exec::ExecutorMap> executors;
85 TEST(ExecInstance, simple)
87 auto mockup = CompiledMockUpModel();
88 auto graph = mockup.graph;
89 auto executors = mockup.executors;
91 auto input1 = IOIndex{0};
92 auto input2 = IOIndex{1};
93 auto output = IOIndex{0};
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};
100 onert::exec::Execution execution{executors};
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);
107 for (auto i = 0; i < 4; i++)
109 EXPECT_EQ(output_buffer[i], output_expected[i]);
113 TEST(ExecInstance, twoCompile)
115 auto mockup = CompiledMockUpModel();
116 auto graph = mockup.graph;
117 auto executors1 = mockup.executors;
118 onert::exec::Execution execution1{executors1};
120 auto input1 = IOIndex{0};
121 auto input2 = IOIndex{1};
122 auto output = IOIndex{0};
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};
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);
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};
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};
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);
149 execution1.execute();
150 execution2.execute();
152 for (auto i = 0; i < 4; i++)
154 EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
155 EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
159 // Support two initialized execution instance then ordered execution
160 TEST(ExecInstance, twoExecution)
162 auto mockup = CompiledMockUpModel();
163 auto executors = mockup.executors;
164 auto input1 = IOIndex{0};
165 auto input2 = IOIndex{1};
166 auto output1 = IOIndex{0};
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};
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);
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] = {};
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);
189 execution1.execute();
190 execution2.execute();
192 for (auto i = 0; i < 4; i++)
194 EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
195 EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
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}
211 auto input1 = IOIndex{0};
212 auto input2 = IOIndex{1};
213 auto output1 = IOIndex{0};
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);
224 const float (&_input1)[4];
225 const float (&_input2)[4];
227 std::shared_ptr<onert::exec::ExecutorMap> &_executors;
230 // Support multi-thread execution
231 TEST(ExecInstance, twoThreads)
233 auto mockup = CompiledMockUpModel();
234 auto executors = mockup.executors;
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};
241 Inference execution1{exe1_input1_buffer, exe1_input2_buffer, exe1_output_buffer, executors};
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};
248 Inference execution2{exe2_input1_buffer, exe2_input2_buffer, exe2_output_buffer, executors};
250 std::thread t1{&Inference::inference, &execution1};
251 std::thread t2{&Inference::inference, &execution2};
256 for (auto i = 0; i < 4; i++)
258 EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
259 EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
263 // Support asynchronous execution
264 TEST(ExecInstance, async)
266 auto mockup = CompiledMockUpModel();
267 auto graph = mockup.graph;
268 auto executors = mockup.executors;
270 auto input1 = IOIndex{0};
271 auto input2 = IOIndex{1};
272 auto output = IOIndex{0};
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};
279 onert::exec::Execution execution{executors};
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();
287 for (auto i = 0; i < 4; i++)
289 EXPECT_EQ(output_buffer[i], output_expected[i]);