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 auto compiler = new onert::compiler::Compiler{subgs};
77 executors = compiler->compile();
82 std::shared_ptr<Graph> graph;
83 std::shared_ptr<onert::exec::ExecutorMap> executors;
86 TEST(ExecInstance, simple)
88 auto mockup = CompiledMockUpModel();
89 auto graph = mockup.graph;
90 auto executors = mockup.executors;
92 auto input1 = IOIndex{0};
93 auto input2 = IOIndex{1};
94 auto output = IOIndex{0};
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};
101 auto execution = new onert::exec::Execution(executors);
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();
108 for (auto i = 0; i < 4; i++)
110 EXPECT_EQ(output_buffer[i], output_expected[i]);
116 TEST(ExecInstance, twoCompile)
118 auto mockup = CompiledMockUpModel();
119 auto graph = mockup.graph;
120 auto executors1 = mockup.executors;
121 auto execution1 = new onert::exec::Execution(executors1);
123 auto input1 = IOIndex{0};
124 auto input2 = IOIndex{1};
125 auto output = IOIndex{0};
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};
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);
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);
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};
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);
152 execution1->execute();
153 execution2->execute();
155 for (auto i = 0; i < 4; i++)
157 EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
158 EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
166 // Support two initialized execution instance then ordered execution
167 TEST(ExecInstance, twoExecution)
169 auto mockup = CompiledMockUpModel();
170 auto executors = mockup.executors;
171 auto input1 = IOIndex{0};
172 auto input2 = IOIndex{1};
173 auto output1 = IOIndex{0};
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};
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);
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] = {};
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);
196 execution1->execute();
197 execution2->execute();
199 for (auto i = 0; i < 4; i++)
201 EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
202 EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
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}
221 auto input1 = IOIndex{0};
222 auto input2 = IOIndex{1};
223 auto output1 = IOIndex{0};
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);
230 execution->execute();
236 const float (&_input1)[4];
237 const float (&_input2)[4];
239 std::shared_ptr<onert::exec::ExecutorMap> &_executors;
242 // Support multi-thread execution
243 TEST(ExecInstance, twoThreads)
245 auto mockup = CompiledMockUpModel();
246 auto executors = mockup.executors;
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};
253 Inference execution1{exe1_input1_buffer, exe1_input2_buffer, exe1_output_buffer, executors};
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};
260 Inference execution2{exe2_input1_buffer, exe2_input2_buffer, exe2_output_buffer, executors};
262 std::thread t1{&Inference::inference, &execution1};
263 std::thread t2{&Inference::inference, &execution2};
268 for (auto i = 0; i < 4; i++)
270 EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]);
271 EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]);
275 // Support asynchronous execution
276 TEST(ExecInstance, async)
278 auto mockup = CompiledMockUpModel();
279 auto graph = mockup.graph;
280 auto executors = mockup.executors;
282 auto input1 = IOIndex{0};
283 auto input2 = IOIndex{1};
284 auto output = IOIndex{0};
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};
291 auto execution = new onert::exec::Execution(executors);
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();
299 for (auto i = 0; i < 4; i++)
301 EXPECT_EQ(output_buffer[i], output_expected[i]);