From e2ef8438a24f7c56a0744eb579a6e293ee2fbf8e Mon Sep 17 00:00:00 2001 From: Chunseok Lee Date: Thu, 23 Apr 2020 14:45:49 +0900 Subject: Imported Upstream version 1.4.0 --- runtime/onert/test/CMakeLists.txt | 15 + runtime/onert/test/core/compiler/Scheduler.cc | 569 ++++++++++++++++++++++++++ runtime/onert/test/core/exec/ExecInstance.cc | 306 ++++++++++++++ runtime/onert/test/core/exec/ExecTime.test.cc | 99 +++++ runtime/onert/test/core/interp/ExecManager.cc | 333 +++++++++++++++ runtime/onert/test/graph/Graph.cc | 52 +++ runtime/onert/test/graph/Index.cc | 34 ++ runtime/onert/test/graph/MockNode.h | 47 +++ runtime/onert/test/graph/operand/IndexSet.cc | 52 +++ runtime/onert/test/graph/operand/LayoutSet.cc | 43 ++ runtime/onert/test/graph/operand/Set.cc | 45 ++ runtime/onert/test/graph/operand/UseDef.cc | 85 ++++ runtime/onert/test/graph/operation/Set.cc | 33 ++ runtime/onert/test/graph/operation/SetIO.cc | 99 +++++ runtime/onert/test/graph/verifier/Verifier.cc | 49 +++ runtime/onert/test/util/ObjectManager.cc | 97 +++++ runtime/onert/test/util/ShapeInference.cc | 230 +++++++++++ 17 files changed, 2188 insertions(+) create mode 100644 runtime/onert/test/CMakeLists.txt create mode 100644 runtime/onert/test/core/compiler/Scheduler.cc create mode 100644 runtime/onert/test/core/exec/ExecInstance.cc create mode 100644 runtime/onert/test/core/exec/ExecTime.test.cc create mode 100644 runtime/onert/test/core/interp/ExecManager.cc create mode 100644 runtime/onert/test/graph/Graph.cc create mode 100644 runtime/onert/test/graph/Index.cc create mode 100644 runtime/onert/test/graph/MockNode.h create mode 100644 runtime/onert/test/graph/operand/IndexSet.cc create mode 100644 runtime/onert/test/graph/operand/LayoutSet.cc create mode 100644 runtime/onert/test/graph/operand/Set.cc create mode 100644 runtime/onert/test/graph/operand/UseDef.cc create mode 100644 runtime/onert/test/graph/operation/Set.cc create mode 100644 runtime/onert/test/graph/operation/SetIO.cc create mode 100644 runtime/onert/test/graph/verifier/Verifier.cc create mode 100644 runtime/onert/test/util/ObjectManager.cc create mode 100644 runtime/onert/test/util/ShapeInference.cc (limited to 'runtime/onert/test') diff --git a/runtime/onert/test/CMakeLists.txt b/runtime/onert/test/CMakeLists.txt new file mode 100644 index 000000000..0abdd4880 --- /dev/null +++ b/runtime/onert/test/CMakeLists.txt @@ -0,0 +1,15 @@ +set(TEST_ONERT test_onert) + +file(GLOB_RECURSE TESTS "*.cc") + +add_executable(${TEST_ONERT} ${TESTS}) + +target_include_directories(${TEST_ONERT} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../core/src) + +target_link_libraries(${TEST_ONERT} onert_core) +target_link_libraries(${TEST_ONERT} gtest) +target_link_libraries(${TEST_ONERT} gtest_main) +target_link_libraries(${TEST_ONERT} ${LIB_PTHREAD} dl) +add_test(${TEST_ONERT} ${TEST_ONERT}) + +install(TARGETS ${TEST_ONERT} DESTINATION unittest) diff --git a/runtime/onert/test/core/compiler/Scheduler.cc b/runtime/onert/test/core/compiler/Scheduler.cc new file mode 100644 index 000000000..926b82d94 --- /dev/null +++ b/runtime/onert/test/core/compiler/Scheduler.cc @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace +{ +using namespace onert; +using namespace ir; +using namespace backend; +using namespace operation; +using namespace exec; + +// +// Mock backends classes +// + +// Backend could be created without ShapeFixer. +// But it is used by scheduler to detect which operations are supported by backend. +struct MockShapeFixer : IShapeFixer +{ + void visit(const Add &) override {} + void visit(const Sub &) override {} + void visit(const Mul &) override {} + void visit(const FullyConnected &) override {} +}; + +struct MockConfigCPU : public IConfig +{ + std::string id() override { return "cpu"; } + bool initialize() override { return true; }; + bool SupportPermutation() override { return false; } +}; + +struct MockBackendCPU : public Backend +{ + std::shared_ptr config() const override { return std::make_shared(); } + std::unique_ptr + newContext(const Graph &, const std::shared_ptr &, bool) const override + { + return std::unique_ptr(new BackendContext{ + this, nullptr, nullptr, nullptr, nullptr, std::make_shared()}); + } +}; + +struct MockConfigGPU : public IConfig +{ + std::string id() override { return "gpu"; } + bool initialize() override { return true; }; + bool SupportPermutation() override { return false; } +}; + +struct MockBackendGPU : public Backend +{ + std::shared_ptr config() const override { return std::make_shared(); } + std::unique_ptr + newContext(const Graph &, const std::shared_ptr &, bool) const override + { + return std::unique_ptr(new BackendContext{ + this, nullptr, nullptr, nullptr, nullptr, std::make_shared()}); + } +}; + +struct MockConfigNPU : public IConfig +{ + std::string id() override { return "npu"; } + bool initialize() override { return true; }; + bool SupportPermutation() override { return false; } +}; + +struct MockBackendNPU : public Backend +{ + std::shared_ptr config() const override { return std::make_shared(); } + std::unique_ptr + newContext(const Graph &, const std::shared_ptr &, bool) const override + { + return std::unique_ptr(new BackendContext{ + this, nullptr, nullptr, nullptr, nullptr, std::make_shared()}); + } +}; + +// +// Constants +// + +const int OPERAND_ELEMS = 268203; +const int OPERAND_SIZE = OPERAND_ELEMS * 4; +const int OPERATION_SIZE = OPERAND_SIZE * 3; + +const std::string LINEAR("Linear"); +const std::string DATAFLOW("Dataflow"); +const std::string PARALLEL("Parallel"); + +// +// Helper functions +// + +// Set executor through environment variable +void setExecutor(const std::string &executor) { setenv("EXECUTOR", executor.c_str(), true); } + +// Set profiling mode through environment variable +void setProfilingMode(const bool value) { setenv("PROFILING_MODE", value ? "1" : "0", true); } + +// Calculate operation size by addition sizes of all input and output operands +uint32_t calcOpSize(const std::shared_ptr &graph, const OperationIndex &op_idx) +{ + uint32_t size = 0; + const auto &op = graph->operations().at(op_idx); + for (const auto &ind : op.getInputs() + op.getOutputs()) + size += graph->operands().at(ind).info().total_size(); + return size; +} + +// Set execution operation time. This method is needed since ExecutionTime has only +// 'updateOperationExecTime' method. +void setOperationExecTime(ExecTime &et, const Backend *backend, const std::string &operation, + bool quant, uint32_t op_size, int64_t time) +{ + // You shouldn't set negative time with this method since nnfw JSON deserializer can't read it + assert(time > 0); + int64_t prev_time = et.getOperationExecTime(backend, operation, quant, op_size); + int64_t time_to_set = prev_time == ExecTime::NOT_FOUND ? time : 2 * time - prev_time; + et.updateOperationExecTime(backend, operation, quant, op_size, time_to_set); + assert(et.getOperationExecTime(backend, operation, quant, op_size) == time); +} + +// Set same execution time for all given backends/operations +void setOperationsExecutionTime(const std::vector &backends, + const std::vector &op_names, + const std::vector &op_sizes, int64_t exec_time) +{ + assert(op_names.size() == op_sizes.size()); + ExecTime et(backends); + for (int i = 0; i < op_names.size(); ++i) + { + for (auto &backend : backends) + setOperationExecTime(et, backend, op_names[i], false, op_sizes[i], exec_time); + } + et.uploadOperationsExecTime(); +} + +// Set permute time from one backend to another. This method is needed since ExecutionTime has only +// 'updatePermuteTime' method. +void setPermutationTime(ExecTime &et, const Backend *from_backend, const Backend *to_backend, + bool quant, uint32_t op_size, int64_t time) +{ + // You shouldn't set negative time with this method since nnfw JSON deserializer can't read it + assert(time > 0); + int64_t prev_time = et.getPermuteTime(from_backend, to_backend, quant, op_size); + int64_t time_to_set = prev_time == ExecTime::NOT_FOUND ? time : 2 * time - prev_time; + et.updatePermuteTime(from_backend, to_backend, quant, op_size, time_to_set); + assert(et.getPermuteTime(from_backend, to_backend, quant, op_size) == time); +} + +// Set same permutation time between all given backends +void setPermutationsExecutionTime(const std::vector &backends, + const int operand_size, const int64_t exec_time) +{ + ExecTime et(backends); + for (const auto &backend : backends) + { + for (auto &other_backend : backends) + { + if (backend == other_backend) + continue; + setPermutationTime(et, backend, other_backend, false, operand_size, exec_time); + } + } + et.uploadOperationsExecTime(); +} + +// +// Functions for creating graphs +// + +using OIS = OperandIndexSequence; + +template +OperationIndex create(std::shared_ptr graph, Types &&... args) +{ + typename NodeT::Param op_params{Activation::NONE}; + auto op = std::make_unique(std::forward(args)..., op_params); + auto op_idx = graph->addOperation(std::move(op)); + // For now in scheduler test all operations in tested graphs has same size (for simplicity) + assert(calcOpSize(graph, op_idx) == OPERATION_SIZE); + return op_idx; +} + +// Create straight graph: Add->Sub->Mul +std::shared_ptr createStraightGraph() +{ + auto graph = std::make_shared(); + const TypeInfo float_op(DataType::FLOAT32); + + // Create add node + auto add_lhs_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + auto add_rhs_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + auto add_out_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + create(graph, OIS{add_lhs_idx, add_rhs_idx}, OIS{add_out_idx}); + + // Create sub node + auto sub_const_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + auto sub_out_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + create(graph, OIS{add_out_idx, sub_const_idx}, OIS{sub_out_idx}); + + // Create mul node + auto mul_const_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + auto mul_out_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + create(graph, OIS{sub_out_idx, mul_const_idx}, OIS{mul_out_idx}); + + graph->finishBuilding(); + return graph; +} + +/* Create branched graph: + * [Add] + * // \\ + * [Mul1] [FC2] + * || || + * [Mul2] [FC2] + * \\ // + * [Sub] + */ +std::shared_ptr createBranchedGraph() +{ + auto graph = std::make_shared(); + const TypeInfo float_op(DataType::FLOAT32); + + // Create add node + auto add_lhs_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + auto add_rhs_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + auto add_out_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + create(graph, OIS{add_lhs_idx, add_rhs_idx}, OIS{add_out_idx}); + + // Create mul1 node + auto mul1_const_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + auto mul1_out_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + create(graph, OIS{add_out_idx, mul1_const_idx}, OIS{mul1_out_idx}); + + // Create mul2 node + auto mul2_const_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + auto mul2_out_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + create(graph, OIS{mul1_out_idx, mul2_const_idx}, OIS{mul2_out_idx}); + + // Create fc1 node + auto fc1_const_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + auto fc1_out_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + create(graph, OIS{add_out_idx, fc1_const_idx}, OIS{fc1_out_idx}); + + // Create fc2 node + auto fc2_const_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + auto fc2_out_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + create(graph, OIS{fc1_out_idx, fc2_const_idx}, OIS{fc2_out_idx}); + + // Create add2 node + auto sub_out_idx = graph->addOperand(ir::Shape{OPERAND_ELEMS}, float_op); + create(graph, OIS{mul2_out_idx, fc2_out_idx}, OIS{sub_out_idx}); + + graph->finishBuilding(); + return graph; +} + +// +// Tests setup/teardown +// + +// SetUp/TearDown methods runs before/after each test and performs actions common for each test +class SchedulerTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Initialize mock backends + _cpu_backend = new MockBackendCPU(); + _gpu_backend = new MockBackendGPU(); + _npu_backend = new MockBackendNPU(); + _mock_backends = {_cpu_backend, _gpu_backend, _npu_backend}; + + // Remove previous profile data if it exists + if (!remove("exec_time.json")) + { + // DO NOTHING (no profile data) + } + + // Remember original value of 'EXECUTOR' environment variable + char *executor = std::getenv("EXECUTOR"); + _original_executor = executor == nullptr ? "" : executor; + + // Remember original value of 'PROFILING_MODE' environment variable + char *profiling_mode = std::getenv("PROFILING_MODE"); + _original_profiling_mode = profiling_mode == nullptr ? "" : profiling_mode; + } + + void TearDown() override + { + delete _cpu_backend; + delete _gpu_backend; + delete _npu_backend; + EXPECT_EQ(remove("exec_time.json"), 0); + setenv("EXECUTOR", _original_executor.c_str(), true); + setenv("PROFILING_MODE", _original_profiling_mode.c_str(), true); + } + + backend::BackendContexts buildBackendContexts(const Graph &graph) + { + backend::BackendContexts contexts; + for (auto backend : _mock_backends) + { + contexts.emplace(backend, backend->newContext(graph, nullptr, false)); + } + return contexts; + } + + const MockBackendCPU *_cpu_backend{nullptr}; + const MockBackendGPU *_gpu_backend{nullptr}; + const MockBackendNPU *_npu_backend{nullptr}; + std::vector _mock_backends; + + std::string _original_executor; + std::string _original_profiling_mode; +}; + +class SchedulerTestWithExecutorParam : public SchedulerTest, + public testing::WithParamInterface +{ +}; + +// +// HEScheduler tests +// + +// Test scheduler behavior for straight graph with known execution time of all nodes and permutes. +TEST_P(SchedulerTestWithExecutorParam, straight_graph_known_exec_time) +{ + setExecutor(GetParam()); + + // Prepare graph + auto graph(createStraightGraph()); + OperationIndex add_op_idx(0), sub_op_idx(1), mul_op_idx(2); + + // Set default execution and transfer time + setPermutationsExecutionTime(_mock_backends, OPERAND_SIZE, 1); + setOperationsExecutionTime(_mock_backends, {"Add", "Sub", "Mul"}, + {OPERATION_SIZE, OPERATION_SIZE, OPERATION_SIZE}, 1e4); + + // Test 1 + // Expected behaviour: scheduler assigns different backend to each node + { + // For each backend reduce execution time of one node + ExecTime et(_mock_backends); + setOperationExecTime(et, _cpu_backend, "Add", false, OPERATION_SIZE, 1); + setOperationExecTime(et, _gpu_backend, "Sub", false, OPERATION_SIZE, 1); + setOperationExecTime(et, _npu_backend, "Mul", false, OPERATION_SIZE, 1); + et.uploadOperationsExecTime(); + + // Test scheduler + auto backend_contexts = buildBackendContexts(*graph); + auto scheduler = compiler::HEScheduler(backend_contexts, + compiler::fetchCompilerOptionsFromGlobalConfig(*graph)); + const auto br = scheduler.schedule(*graph); + ASSERT_EQ(br->getBackend(add_op_idx)->config()->id(), "cpu"); + ASSERT_EQ(br->getBackend(sub_op_idx)->config()->id(), "gpu"); + ASSERT_EQ(br->getBackend(mul_op_idx)->config()->id(), "npu"); + } + + // Test 2 + // Expected behaviour: scheduler assigns single backend to all nodes because of big transfer time + { + // Increase transfer time + setPermutationsExecutionTime(_mock_backends, OPERAND_SIZE, 1e5); + + // Test scheduler + auto backend_contexts = buildBackendContexts(*graph); + auto scheduler = compiler::HEScheduler(backend_contexts, + compiler::fetchCompilerOptionsFromGlobalConfig(*graph)); + const auto br = scheduler.schedule(*graph); + ASSERT_EQ(br->getBackend(add_op_idx)->config()->id(), "cpu"); + ASSERT_EQ(br->getBackend(sub_op_idx)->config()->id(), "cpu"); + ASSERT_EQ(br->getBackend(mul_op_idx)->config()->id(), "cpu"); + } +} + +// Test scheduler behavior for branched graph with known execution time of all nodes and permutes +TEST_P(SchedulerTestWithExecutorParam, branched_graph_known_exec_time) +{ + const int64_t NPU_ET = 5000; + setExecutor(GetParam()); + + // Prepare graph + auto graph(createBranchedGraph()); + OperationIndex add_op_idx(0), mul1_op_idx(1), mul2_op_idx(2), fc1_op_idx(3), fc2_op_idx(4), + sub_op_idx(5); + + // Set default execution and transfer time + setPermutationsExecutionTime(_mock_backends, OPERAND_SIZE, 1000); + setOperationsExecutionTime(_mock_backends, {"Add", "Sub", "Mul", "FullyConnected"}, + {OPERATION_SIZE, OPERATION_SIZE, OPERATION_SIZE, OPERATION_SIZE}, 1e4); + + // Test 1 + // Expected behaviour: for dataflow and linear executors scheduler assigns fastest backend to all + // nodes, in case of parallel executor scheduler assigns different backends to branches. + { + // Reduce execution time + ExecTime et(_mock_backends); + setOperationExecTime(et, _npu_backend, "Add", false, OPERATION_SIZE, NPU_ET); + setOperationExecTime(et, _npu_backend, "Mul", false, OPERATION_SIZE, NPU_ET); + setOperationExecTime(et, _npu_backend, "Sub", false, OPERATION_SIZE, NPU_ET); + setOperationExecTime(et, _npu_backend, "FullyConnected", false, OPERATION_SIZE, NPU_ET); + setOperationExecTime(et, _gpu_backend, "Mul", false, OPERATION_SIZE, NPU_ET + 1000); + setOperationExecTime(et, _gpu_backend, "FullyConnected", false, OPERATION_SIZE, NPU_ET + 1000); + et.uploadOperationsExecTime(); + + // Test scheduler + auto backend_contexts = buildBackendContexts(*graph); + auto scheduler = compiler::HEScheduler(backend_contexts, + compiler::fetchCompilerOptionsFromGlobalConfig(*graph)); + const auto br = scheduler.schedule(*graph); + + std::string branch1_expected_backend("npu"), branch2_expected_backend("npu"); + if (GetParam() == PARALLEL) + { + branch1_expected_backend = + br->getBackend(mul1_op_idx)->config()->id() == "npu" ? "npu" : "gpu"; + branch2_expected_backend = branch1_expected_backend == "npu" ? "gpu" : "npu"; + } + + ASSERT_EQ(br->getBackend(add_op_idx)->config()->id(), "npu"); + ASSERT_EQ(br->getBackend(mul1_op_idx)->config()->id(), branch1_expected_backend); + ASSERT_EQ(br->getBackend(mul2_op_idx)->config()->id(), branch1_expected_backend); + ASSERT_EQ(br->getBackend(fc1_op_idx)->config()->id(), branch2_expected_backend); + ASSERT_EQ(br->getBackend(fc2_op_idx)->config()->id(), branch2_expected_backend); + ASSERT_EQ(br->getBackend(sub_op_idx)->config()->id(), "npu"); + } + + // Test 2 + // Expected behaviour: scheduler assigns single backend to all nodes + { + // Increase execution time for GPU backend + ExecTime et(_mock_backends); + /* for parallel executor: set a time, that is larger than sum_of_other_branches_nodes_cnt * + * npu_exec_time so that npu is prefered: the ith branch will wait for npu until it finishes the + * [0;i-1] branches nodes in DFS order. In each branch it goes deep intul doesn't encounter + * branching or scheduler assigns another backend to a node*/ + setOperationExecTime(et, _gpu_backend, "Mul", false, OPERATION_SIZE, NPU_ET * 3 + 1); + setOperationExecTime(et, _gpu_backend, "FullyConnected", false, OPERATION_SIZE, NPU_ET * 3 + 1); + et.uploadOperationsExecTime(); + + // Test scheduler + auto backend_contexts = buildBackendContexts(*graph); + auto scheduler = compiler::HEScheduler(backend_contexts, + compiler::fetchCompilerOptionsFromGlobalConfig(*graph)); + const auto br = scheduler.schedule(*graph); + ASSERT_EQ(br->getBackend(add_op_idx)->config()->id(), "npu"); + ASSERT_EQ(br->getBackend(mul1_op_idx)->config()->id(), "npu"); + ASSERT_EQ(br->getBackend(mul2_op_idx)->config()->id(), "npu"); + ASSERT_EQ(br->getBackend(fc1_op_idx)->config()->id(), "npu"); + ASSERT_EQ(br->getBackend(fc2_op_idx)->config()->id(), "npu"); + ASSERT_EQ(br->getBackend(sub_op_idx)->config()->id(), "npu"); + } +} + +// SchedulerTestWithExecutorParam tests are parameterized with executor name and runs three times - +// one time for each executor +INSTANTIATE_TEST_CASE_P(AllExecutors, SchedulerTestWithExecutorParam, + testing::Values(LINEAR, DATAFLOW, PARALLEL)); + +// Test scheduler behavior for branched graph and enabled profiling mode +TEST_F(SchedulerTest, branched_graph_profiling_mode) +{ + const int ET = 1e5; + + // Turn on profiling mode + setProfilingMode(true); + setExecutor(DATAFLOW); + + // Prepare graph + auto graph(createBranchedGraph()); + OperationIndex add_op_idx(0), mul1_op_idx(1), mul2_op_idx(2), fc1_op_idx(3), fc2_op_idx(4), + sub_op_idx(5); + + // Test 1 + // Expected behaviour: scheduler assigns backends to nodes with unknown execution time + { + // Set execution time for all backends/nodes except for cpu/Sub, npu/Mul, gpu/FC + ExecTime et(_mock_backends); + setOperationExecTime(et, _cpu_backend, "Add", false, OPERATION_SIZE, ET); + setOperationExecTime(et, _cpu_backend, "Mul", false, OPERATION_SIZE, ET + 1); + setOperationExecTime(et, _cpu_backend, "FullyConnected", false, OPERATION_SIZE, ET); + setOperationExecTime(et, _npu_backend, "Add", false, OPERATION_SIZE, ET); + setOperationExecTime(et, _npu_backend, "FullyConnected", false, OPERATION_SIZE, ET); + setOperationExecTime(et, _npu_backend, "Sub", false, OPERATION_SIZE, ET); + setOperationExecTime(et, _gpu_backend, "Add", false, OPERATION_SIZE, ET); + setOperationExecTime(et, _gpu_backend, "Mul", false, OPERATION_SIZE, ET + 1); + setOperationExecTime(et, _gpu_backend, "Sub", false, OPERATION_SIZE, ET); + et.uploadOperationsExecTime(); + + // Test scheduler + auto backend_contexts = buildBackendContexts(*graph); + auto scheduler = compiler::HEScheduler(backend_contexts, + compiler::fetchCompilerOptionsFromGlobalConfig(*graph)); + const auto br = scheduler.schedule(*graph); + ASSERT_EQ(br->getBackend(mul1_op_idx)->config()->id(), "npu"); + ASSERT_EQ(br->getBackend(mul2_op_idx)->config()->id(), "npu"); + ASSERT_EQ(br->getBackend(fc1_op_idx)->config()->id(), "gpu"); + ASSERT_EQ(br->getBackend(fc2_op_idx)->config()->id(), "gpu"); + ASSERT_EQ(br->getBackend(sub_op_idx)->config()->id(), "cpu"); + } + + // Test 2 + // Expected behaviour: scheduler shuffling backends, so different backends are assigned to + // neighbor nodes + { + // Set execution time for rest backends/nodes (cpu/Sub, npu/Mul, gpu/FC) + ExecTime et(_mock_backends); + setOperationExecTime(et, _cpu_backend, "Sub", false, OPERATION_SIZE, ET); + setOperationExecTime(et, _npu_backend, "Mul", false, OPERATION_SIZE, ET + 1); + setOperationExecTime(et, _gpu_backend, "FullyConnected", false, OPERATION_SIZE, ET); + et.uploadOperationsExecTime(); + + // Test scheduler + auto backend_contexts = buildBackendContexts(*graph); + auto scheduler = compiler::HEScheduler(backend_contexts, + compiler::fetchCompilerOptionsFromGlobalConfig(*graph)); + const auto br = scheduler.schedule(*graph); + ASSERT_NE(br->getBackend(add_op_idx)->config()->id(), + br->getBackend(mul1_op_idx)->config()->id()); + ASSERT_NE(br->getBackend(add_op_idx)->config()->id(), + br->getBackend(fc1_op_idx)->config()->id()); + ASSERT_NE(br->getBackend(mul1_op_idx)->config()->id(), + br->getBackend(mul2_op_idx)->config()->id()); + ASSERT_NE(br->getBackend(fc1_op_idx)->config()->id(), + br->getBackend(fc2_op_idx)->config()->id()); + ASSERT_NE(br->getBackend(mul2_op_idx)->config()->id(), + br->getBackend(sub_op_idx)->config()->id()); + ASSERT_NE(br->getBackend(fc2_op_idx)->config()->id(), + br->getBackend(sub_op_idx)->config()->id()); + } +} + +// TODO: Add tests with unknown execution and permutation time + +} // unnamed namespace diff --git a/runtime/onert/test/core/exec/ExecInstance.cc b/runtime/onert/test/core/exec/ExecInstance.cc new file mode 100644 index 000000000..7242486a0 --- /dev/null +++ b/runtime/onert/test/core/exec/ExecInstance.cc @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "ir/Graph.h" +#include "compiler/Compiler.h" +#include "exec/Execution.h" +#include "ir/operation/Add.h" + +namespace +{ + +using namespace onert::ir; + +class CompiledMockUpModel +{ +public: + CompiledMockUpModel() + { + // Model: two elementwise add operation + // model input: lhs, rhs1 + // model output: second add result (result2) + // constant: rhs2 + // result1 <= (lhs + rhs) + // result2 <= (result1 + rhs2) + // lhs, rhs1, rh2, result1, result2 shape: {1, 2, 2, 1} + // activation: none (constant) + graph = std::make_shared(); + // 1st add operands (result1 <= lhs + rhs1) + Shape shape{1, 2, 2, 1}; + TypeInfo type{DataType::FLOAT32}; + static float rhs2_data[4] = {3, 1, -1, 5}; + auto operand_lhs = graph->addOperand(shape, type); + auto operand_rhs1 = graph->addOperand(shape, type); + auto operand_result1 = graph->addOperand(shape, type); + auto operand_rhs2 = graph->addOperand(shape, type); + auto operand_result2 = graph->addOperand(shape, type); + graph->operands() + .at(operand_rhs2) + .data(std::make_unique(reinterpret_cast(&rhs2_data), 16)); + // 2nd add operations (result2 <= result1 + rhs2) + operation::Add::Param param1; + param1.activation = Activation::NONE; + auto input_set1 = OperandIndexSequence{operand_lhs, operand_rhs1}; + auto output_set1 = OperandIndexSequence{operand_result1}; + graph->addOperation(std::make_unique(input_set1, output_set1, param1)); + operation::Add::Param param2; + param2.activation = Activation::NONE; + auto input_set2 = OperandIndexSequence{operand_result1, operand_rhs2}; + auto output_set2 = OperandIndexSequence{operand_result2}; + graph->addOperation(std::make_unique(input_set2, output_set2, param2)); + // Identify model inputs and outputs + graph->addInput(operand_lhs); + graph->addInput(operand_rhs1); + graph->addOutput(operand_result2); + graph->finishBuilding(); + + // Compile + auto compiler = new onert::compiler::Compiler{graph}; + compiler->compile(); + compiler->release(executor); + delete compiler; + } + +public: + std::shared_ptr graph; + std::shared_ptr executor; +}; + +TEST(ExecInstance, simple) +{ + auto mockup = CompiledMockUpModel(); + auto graph = mockup.graph; + auto executor = mockup.executor; + + auto input1 = IOIndex{0}; + auto input2 = IOIndex{1}; + auto output = IOIndex{0}; + + const float input1_buffer[4] = {1, 0, -1, -2}; + const float input2_buffer[4] = {1, -3, 2, -4}; + float output_buffer[4] = {}; + const float output_expected[4] = {5, -2, 0, -1}; + + auto execution = new onert::exec::Execution(executor); + + execution->setInput(input1, reinterpret_cast(input1_buffer), 16); + execution->setInput(input2, reinterpret_cast(input2_buffer), 16); + execution->setOutput(output, reinterpret_cast(output_buffer), 16); + execution->execute(); + + for (auto i = 0; i < 4; i++) + { + EXPECT_EQ(output_buffer[i], output_expected[i]); + } + + delete execution; +} + +TEST(ExecInstance, twoCompile) +{ + auto mockup = CompiledMockUpModel(); + auto graph = mockup.graph; + auto executor1 = mockup.executor; + auto execution1 = new onert::exec::Execution(executor1); + + auto input1 = IOIndex{0}; + auto input2 = IOIndex{1}; + auto output = IOIndex{0}; + + const float exe1_input1_buffer[4] = {1, 0, -1, -2}; + const float exe1_input2_buffer[4] = {1, -3, 2, -4}; + float exe1_output_buffer[4] = {}; + const float exe1_output_expected[4] = {5, -2, 0, -1}; + + execution1->setInput(input1, reinterpret_cast(exe1_input1_buffer), 16); + execution1->setInput(input2, reinterpret_cast(exe1_input2_buffer), 16); + execution1->setOutput(output, reinterpret_cast(exe1_output_buffer), 16); + + // Make new executor: compile again + auto compiler = new onert::compiler::Compiler{graph}; + compiler->compile(); + std::shared_ptr executor2; + compiler->release(executor2); + auto execution2 = new onert::exec::Execution(executor2); + + const float exe2_input1_buffer[4] = {2, 1, -2, 0}; + const float exe2_input2_buffer[4] = {-3, 3, 1, 2}; + float exe2_output_buffer[4] = {}; + const float exe2_output_expected[4] = {2, 5, -2, 7}; + + execution2->setInput(input1, reinterpret_cast(exe2_input1_buffer), 16); + execution2->setInput(input2, reinterpret_cast(exe2_input2_buffer), 16); + execution2->setOutput(output, reinterpret_cast(exe2_output_buffer), 16); + + execution1->execute(); + execution2->execute(); + + for (auto i = 0; i < 4; i++) + { + EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]); + EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]); + } + + delete compiler; + delete execution1; + delete execution2; +} + +// Support two initialized execution instance then ordered execution +TEST(ExecInstance, twoExecution) +{ + auto mockup = CompiledMockUpModel(); + auto executor = mockup.executor; + auto input1 = IOIndex{0}; + auto input2 = IOIndex{1}; + auto output1 = IOIndex{0}; + + const float exe1_input1_buffer[4] = {1, 0, -1, -2}; + const float exe1_input2_buffer[4] = {1, -3, 2, -4}; + float exe1_output_buffer[4] = {}; + const float exe1_output_expected[4] = {5, -2, 0, -1}; + const float exe2_output_expected[4] = {2, 5, -2, 7}; + + auto execution1 = new onert::exec::Execution(executor); + execution1->setInput(input1, reinterpret_cast(exe1_input1_buffer), 16); + execution1->setInput(input2, reinterpret_cast(exe1_input2_buffer), 16); + execution1->setOutput(output1, reinterpret_cast(exe1_output_buffer), 16); + + const float exe2_input1_buffer[4] = {2, 1, -2, 0}; + const float exe2_input2_buffer[4] = {-3, 3, 1, 2}; + float exe2_output_buffer[4] = {}; + + // Make new execution + auto execution2 = new onert::exec::Execution(executor); + execution2->setInput(input1, reinterpret_cast(exe2_input1_buffer), 16); + execution2->setInput(input2, reinterpret_cast(exe2_input2_buffer), 16); + execution2->setOutput(output1, reinterpret_cast(exe2_output_buffer), 16); + + execution1->execute(); + execution2->execute(); + + for (auto i = 0; i < 4; i++) + { + EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]); + EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]); + } + + delete execution1; + delete execution2; +} + +class Inference +{ +public: + Inference(const float (&input1)[4], const float (&input2)[4], float (&output)[4], + std::shared_ptr &executor) + : _input1{input1}, _input2{input2}, _output{output}, _executor{executor} + { + // DO NOTHING + } + + void inference(void) + { + auto input1 = IOIndex{0}; + auto input2 = IOIndex{1}; + auto output1 = IOIndex{0}; + + auto execution = new onert::exec::Execution(_executor); + execution->setInput(input1, reinterpret_cast(_input1), 16); + execution->setInput(input2, reinterpret_cast(_input2), 16); + execution->setOutput(output1, reinterpret_cast(_output), 16); + + execution->execute(); + + delete execution; + } + +private: + const float (&_input1)[4]; + const float (&_input2)[4]; + float (&_output)[4]; + std::shared_ptr &_executor; +}; + +// Support multi-thread execution +TEST(ExecInstance, twoThreads) +{ + auto mockup = CompiledMockUpModel(); + auto executor = mockup.executor; + + const float exe1_input1_buffer[4] = {1, 0, -1, -2}; + const float exe1_input2_buffer[4] = {1, -3, 2, -4}; + float exe1_output_buffer[4] = {}; + const float exe1_output_expected[4] = {5, -2, 0, -1}; + + Inference execution1{exe1_input1_buffer, exe1_input2_buffer, exe1_output_buffer, executor}; + + const float exe2_input1_buffer[4] = {2, 1, -2, 0}; + const float exe2_input2_buffer[4] = {-3, 3, 1, 2}; + float exe2_output_buffer[4] = {}; + const float exe2_output_expected[4] = {2, 5, -2, 7}; + + Inference execution2{exe2_input1_buffer, exe2_input2_buffer, exe2_output_buffer, executor}; + + std::thread t1{&Inference::inference, &execution1}; + std::thread t2{&Inference::inference, &execution2}; + + t1.join(); + t2.join(); + + for (auto i = 0; i < 4; i++) + { + EXPECT_EQ(exe1_output_buffer[i], exe1_output_expected[i]); + EXPECT_EQ(exe2_output_buffer[i], exe2_output_expected[i]); + } +} + +// Support asynchronous execution +TEST(ExecInstance, async) +{ + auto mockup = CompiledMockUpModel(); + auto graph = mockup.graph; + auto executor = mockup.executor; + + auto input1 = IOIndex{0}; + auto input2 = IOIndex{1}; + auto output = IOIndex{0}; + + const float input1_buffer[4] = {1, 0, -1, -2}; + const float input2_buffer[4] = {1, -3, 2, -4}; + float output_buffer[4] = {}; + const float output_expected[4] = {5, -2, 0, -1}; + + auto execution = new onert::exec::Execution(executor); + + execution->setInput(input1, reinterpret_cast(input1_buffer), 16); + execution->setInput(input2, reinterpret_cast(input2_buffer), 16); + execution->setOutput(output, reinterpret_cast(output_buffer), 16); + execution->startExecute(); + execution->waitFinish(); + + for (auto i = 0; i < 4; i++) + { + EXPECT_EQ(output_buffer[i], output_expected[i]); + } + + delete execution; +} + +} // namespace diff --git a/runtime/onert/test/core/exec/ExecTime.test.cc b/runtime/onert/test/core/exec/ExecTime.test.cc new file mode 100644 index 000000000..ab8b79935 --- /dev/null +++ b/runtime/onert/test/core/exec/ExecTime.test.cc @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "exec/ExecTime.h" +#include "backend/IConfig.h" +#include "backend/Backend.h" +#include +#include + +namespace +{ +using namespace onert; +using namespace exec; +using namespace backend; + +struct MockConfig : public IConfig +{ + std::string id() override { return "b1"; } + bool initialize() override { return true; }; + bool SupportPermutation() override { return false; } +}; + +struct MockBackend : public ::onert::backend::Backend +{ + std::shared_ptr config() const override + { + return std::make_shared(); + } + std::unique_ptr newContext(const ir::Graph &, + const std::shared_ptr &kb, + bool) const override + { + return nullptr; + } +}; + +TEST(ExecTime, roundtrip_ok) +{ + const auto *b = new MockBackend(); + std::vector bs = {b}; + { + ExecTime et(bs); + et.updateOperationExecTime(b, "op1", true, 100, 100); + et.updateOperationExecTime(b, "op1", true, 200, 200); + et.updateOperationExecTime(b, "op1", false, 100, 888); + et.uploadOperationsExecTime(); + } + { + ExecTime et(bs); + auto time = et.getOperationExecTime(b, "op1", true, 100); + ASSERT_EQ(time, 100); + // Check interpolation + time = et.getOperationExecTime(b, "op1", true, 150); + ASSERT_EQ(time, 150); + time = et.getOperationExecTime(b, "op1", false, 100); + ASSERT_EQ(time, 888); + et.uploadOperationsExecTime(); + } + // clean up + EXPECT_EQ(remove("exec_time.json"), 0); +} + +TEST(ExecTime, structure) +{ + + const auto *b = new MockBackend(); + std::vector bs = {b}; + { + ExecTime et(bs); + et.updateOperationExecTime(b, "op1", true, 100, 100); + et.updateOperationExecTime(b, "op1", true, 200, 200); + et.uploadOperationsExecTime(); + } + { + ExecTime et(bs); + auto time = et.getOperationExecTime(b, "op1", true, 100); + ASSERT_EQ(time, 100); + // Check interpolation + time = et.getOperationExecTime(b, "op1", true, 200); + ASSERT_EQ(time, 200); + et.uploadOperationsExecTime(); + } + // clean up + EXPECT_EQ(remove("exec_time.json"), 0); +} +} // unnamed namespace diff --git a/runtime/onert/test/core/interp/ExecManager.cc b/runtime/onert/test/core/interp/ExecManager.cc new file mode 100644 index 000000000..2b56357c2 --- /dev/null +++ b/runtime/onert/test/core/interp/ExecManager.cc @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include "ir/Graph.h" +#include "interp/InterpExecutor.h" +#include "exec/Execution.h" +#include "ir/operation/Add.h" + +namespace +{ + +using namespace onert::ir; +using InterpExecutor = onert::interp::InterpExecutor; +using Execution = onert::exec::Execution; + +class InterpExecutorTest : public ::testing::Test +{ +protected: + virtual void SetUp() {} + void CreateSimpleModel() + { + // Model: one elementwise add operation + // model input: lhs, rhs + // model output: add result + // lhs, rhs, result shape: {1, 2, 2, 1} + // activation: none (constant) + _graph = std::make_unique(); + + // Add operands + + Shape shape{1, 2, 2, 1}; + TypeInfo type{DataType::INT32}; + Shape shape_scalar(0); + TypeInfo type_scalar{DataType::INT32}; + + auto operand_lhs = _graph->addOperand(shape, type); + auto operand_rhs = _graph->addOperand(shape, type); + auto operand_result = _graph->addOperand(shape, type); + + // Add operations + + operation::Add::Param param; + param.activation = Activation::NONE; + auto input_set = OperandIndexSequence{operand_lhs, operand_rhs}; + auto output_set = OperandIndexSequence{operand_result}; + _graph->addOperation(std::make_unique(input_set, output_set, param)); + + // Identify model inputs and outputs + + _graph->getInputs().append(operand_lhs); + _graph->getInputs().append(operand_rhs); + _graph->getOutputs().append(operand_result); + + _graph->finishBuilding(); + + _executor = std::make_unique(*_graph); + } + + void CreateTwoStepModel() + { + // Model: two elementwise add operation + // model input: lhs, rhs1 + // model output: second add result (result2) + // constant: rhs2 + // result1 <= (lhs + rhs) + // result2 <= (result1 + rhs2) + // lhs, rhs1, rh2, result1, result2 shape: {1, 2, 2, 1} + // activation: none (constant) + _graph = std::make_unique(); + + // 1st add operands (result1 <= lhs + rhs1) + + Shape shape{1, 2, 2, 1}; + TypeInfo type{DataType::INT32}; + Shape shape_scalar(0); + TypeInfo type_scalar{DataType::INT32}; + + static int32_t rhs2_data[4] = {3, 1, -1, 5}; + + auto operand_lhs = _graph->addOperand(shape, type); + auto operand_rhs1 = _graph->addOperand(shape, type); + auto operand_result1 = _graph->addOperand(shape, type); + auto operand_rhs2 = _graph->addOperand(shape, type); + auto operand_result2 = _graph->addOperand(shape, type); + _graph->operands() + .at(operand_rhs2) + .data(std::make_unique(reinterpret_cast(&rhs2_data), 16)); + + // 2nd add operations (result2 <= result1 + rhs2) + + operation::Add::Param param1; + param1.activation = Activation::NONE; + auto input_set1 = OperandIndexSequence{operand_lhs, operand_rhs1}; + auto output_set1 = OperandIndexSequence{operand_result1}; + _graph->addOperation(std::make_unique(input_set1, output_set1, param1)); + + operation::Add::Param param2; + param2.activation = Activation::NONE; + auto input_set2 = OperandIndexSequence{operand_result1, operand_rhs2}; + auto output_set2 = OperandIndexSequence{operand_result2}; + _graph->addOperation(std::make_unique(input_set2, output_set2, param2)); + + // Identify model inputs and outputs + + _graph->getInputs().append(operand_lhs); + _graph->getInputs().append(operand_rhs1); + _graph->getOutputs().append(operand_result2); + + _graph->finishBuilding(); + + _executor = std::make_unique(*_graph); + } + + void CreateUnspecifiedDimensionsModel() + { + // Model: one elementwise add operation + // model input: lhs, rhs + // model output: add result + // lhs, rhs, result shape: {1, unknown, 2, 1} + // activation: none (constant) + _graph = std::make_unique(); + + // Add operands + + Shape shape{1, 0, 2, 1}; + TypeInfo type{DataType::INT32}; + Shape shape_scalar(0); + TypeInfo type_scalar{DataType::INT32}; + + auto operand_lhs = _graph->addOperand(shape, type); + auto operand_rhs = _graph->addOperand(shape, type); + + auto operand_activation = _graph->addOperand(shape_scalar, type_scalar); + _graph->operands() + .at(operand_activation) + .data( + std::make_unique(reinterpret_cast(&_activation_value), 4)); + + auto operand_result = _graph->addOperand(shape, type); + + // Add operations + + operation::Add::Param param; + param.activation = Activation::NONE; + auto input_set = OperandIndexSequence{operand_lhs, operand_rhs}; + auto output_set = OperandIndexSequence{operand_result}; + _graph->addOperation(std::make_unique(input_set, output_set, param)); + + // Identify model inputs and outputs + + _graph->getInputs().append(operand_lhs); + _graph->getInputs().append(operand_rhs); + _graph->getOutputs().append(operand_result); + + _graph->finishBuilding(); + + _executor = std::make_unique(*_graph); + } + + void createExecution() { _execution = std::make_unique(_executor); } + + virtual void TearDown() { _executor = nullptr; } + + std::unique_ptr _graph{nullptr}; + std::shared_ptr _executor{nullptr}; + std::unique_ptr _execution{nullptr}; + const int32_t _activation_value{0}; +}; + +TEST_F(InterpExecutorTest, create_empty) +{ + Graph graph; + graph.finishBuilding(); + _executor = std::make_unique(graph); + ASSERT_NE(_executor, nullptr); +} + +TEST_F(InterpExecutorTest, create_simple) +{ + CreateSimpleModel(); + ASSERT_NE(_executor, nullptr); +} + +TEST_F(InterpExecutorTest, setInput) +{ + CreateSimpleModel(); + createExecution(); + + auto input1 = IOIndex{0}; + const int32_t input1_buffer[4] = {1, 0, -1, -2}; + + EXPECT_THROW(_execution->setInput(input1, reinterpret_cast(input1_buffer), 4), + std::runtime_error); + EXPECT_THROW(_execution->setInput(input1, reinterpret_cast(input1_buffer), 12), + std::runtime_error); + EXPECT_NO_THROW(_execution->setInput(input1, reinterpret_cast(input1_buffer), 16)); +} + +TEST_F(InterpExecutorTest, setOutput) +{ + CreateSimpleModel(); + createExecution(); + + auto output = IOIndex{0}; + auto output_idx = _graph->getOutputs().at(output); + + int32_t output_buffer[4] = {}; + + EXPECT_THROW(_execution->setOutput(output, reinterpret_cast(output_buffer), 4), + std::runtime_error); + EXPECT_THROW(_execution->setOutput(output, reinterpret_cast(output_buffer), 12), + std::runtime_error); + EXPECT_NO_THROW(_execution->setOutput(output, reinterpret_cast(output_buffer), 16)); +} + +TEST_F(InterpExecutorTest, setInputForUnspecifiedDimensions) +{ + CreateUnspecifiedDimensionsModel(); + createExecution(); + + auto input1 = IOIndex{0}; + const int32_t input1_buffer[4] = {1, 0, -1, -2}; + + TypeInfo operand_type{DataType::INT32}; + Shape operand_shape{1, 2, 2, 1}; + + EXPECT_THROW(_execution->setInput(input1, operand_type, operand_shape, + reinterpret_cast(input1_buffer), 4), + std::runtime_error); + EXPECT_THROW(_execution->setInput(input1, operand_type, operand_shape, + reinterpret_cast(input1_buffer), 12), + std::runtime_error); + EXPECT_NO_THROW(_execution->setInput(input1, operand_type, operand_shape, + reinterpret_cast(input1_buffer), 16)); +} + +TEST_F(InterpExecutorTest, setOutputForUnspecifiedDimensions) +{ + CreateUnspecifiedDimensionsModel(); + createExecution(); + + auto output = IOIndex{0}; + auto output_idx = _graph->getOutputs().at(output); + + TypeInfo operand_type{DataType::INT32}; + Shape operand_shape{1, 2, 2, 1}; + + int32_t output_buffer[4] = {}; + + EXPECT_THROW(_execution->setOutput(output, operand_type, operand_shape, + reinterpret_cast(output_buffer), 4), + std::runtime_error); + EXPECT_THROW(_execution->setOutput(output, operand_type, operand_shape, + reinterpret_cast(output_buffer), 12), + std::runtime_error); + EXPECT_NO_THROW(_execution->setOutput(output, operand_type, operand_shape, + reinterpret_cast(output_buffer), 16)); +} + +TEST_F(InterpExecutorTest, execute) +{ + CreateSimpleModel(); + createExecution(); + + auto input1 = IOIndex{0}; + auto input2 = IOIndex{1}; + auto input1_idx = _graph->getInputs().at(input1); + auto input2_idx = _graph->getInputs().at(input2); + + const int32_t input1_buffer[4] = {1, 0, -1, -2}; + const int32_t input2_buffer[4] = {1, -3, 2, -4}; + + auto output = IOIndex{0}; + auto output_idx = _graph->getOutputs().at(output); + + int32_t output_buffer[4] = {}; + + EXPECT_NO_THROW(_execution->setInput(input1, reinterpret_cast(input1_buffer), 16)); + EXPECT_NO_THROW(_execution->setInput(input2, reinterpret_cast(input2_buffer), 16)); + EXPECT_NO_THROW(_execution->setOutput(output, reinterpret_cast(output_buffer), 16)); + EXPECT_NO_THROW(_execution->execute()); + EXPECT_EQ(output_buffer[0], 2); + EXPECT_EQ(output_buffer[1], -3); + EXPECT_EQ(output_buffer[2], 1); + EXPECT_EQ(output_buffer[3], -6); +} + +TEST_F(InterpExecutorTest, executeTwoStep) +{ + CreateTwoStepModel(); + createExecution(); + + auto input1 = IOIndex{0}; + auto input2 = IOIndex{1}; + auto input1_idx = _graph->getInputs().at(input1); + auto input2_idx = _graph->getInputs().at(input2); + + const int32_t input1_buffer[4] = {1, 0, -1, -2}; + const int32_t input2_buffer[4] = {1, -3, 2, -4}; + + auto output = IOIndex{0}; + auto output_idx = _graph->getOutputs().at(output); + + int32_t output_buffer[4] = {}; + + EXPECT_NO_THROW(_execution->setInput(input1, reinterpret_cast(input1_buffer), 16)); + EXPECT_NO_THROW(_execution->setInput(input2, reinterpret_cast(input2_buffer), 16)); + EXPECT_NO_THROW(_execution->setOutput(output, reinterpret_cast(output_buffer), 16)); + EXPECT_NO_THROW(_execution->execute()); + EXPECT_EQ(output_buffer[0], 5); + EXPECT_EQ(output_buffer[1], -2); + EXPECT_EQ(output_buffer[2], 0); + EXPECT_EQ(output_buffer[3], -1); +} + +} // namespace diff --git a/runtime/onert/test/graph/Graph.cc b/runtime/onert/test/graph/Graph.cc new file mode 100644 index 000000000..34e9fe002 --- /dev/null +++ b/runtime/onert/test/graph/Graph.cc @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ir/Graph.h" + +TEST(Graph, inputs_and_outputs) +{ + onert::ir::Graph graph; + + onert::ir::OperandIndex index0{0u}; + onert::ir::OperandIndex index1{1u}; + + graph.addInput({index0}); + graph.addInput({index1}); + + onert::ir::OperandIndex index10{10u}; + onert::ir::OperandIndex index11{11u}; + onert::ir::OperandIndex index12{12u}; + + graph.addOutput({index10}); + graph.addOutput({index11}); + graph.addOutput({index12}); + + ASSERT_EQ(graph.getInputs().size(), 2); + ASSERT_EQ(graph.getOutputs().size(), 3); + + onert::ir::IOIndex io_index0{0}; + onert::ir::IOIndex io_index1{1}; + onert::ir::IOIndex io_index2{2}; + + ASSERT_EQ(graph.getInputs().at(io_index0), 0); + ASSERT_EQ(graph.getInputs().at(io_index1), 1); + + ASSERT_EQ(graph.getOutputs().at(io_index0), 10); + ASSERT_EQ(graph.getOutputs().at(io_index1), 11); + ASSERT_EQ(graph.getOutputs().at(io_index2), 12); +} diff --git a/runtime/onert/test/graph/Index.cc b/runtime/onert/test/graph/Index.cc new file mode 100644 index 000000000..358e64c82 --- /dev/null +++ b/runtime/onert/test/graph/Index.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "util/Index.h" + +using Index = ::onert::util::Index; + +TEST(Index, index_test) +{ + Index idx1{1u}; + Index idx2{2u}; + Index idx3{idx1}; + + ASSERT_EQ(idx1, 1); + ASSERT_EQ(idx1, 1u); + ASSERT_EQ(idx1.value(), 1u); + ASSERT_NE(idx1, idx2); + ASSERT_EQ(idx1, idx3); +} diff --git a/runtime/onert/test/graph/MockNode.h b/runtime/onert/test/graph/MockNode.h new file mode 100644 index 000000000..60b4719ed --- /dev/null +++ b/runtime/onert/test/graph/MockNode.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ONERT_TEST_GRAPH_MOCK_NODE_H__ +#define __ONERT_TEST_GRAPH_MOCK_NODE_H__ + +#include "ir/Operation.h" +#include "ir/OperandIndexSequence.h" + +namespace onert_test +{ +namespace ir +{ + +class SimpleMock : public onert::ir::Operation +{ +public: + SimpleMock(const onert::ir::OperandIndexSequence &inputs, + const onert::ir::OperandIndexSequence &outputs) + : Operation{onert::ir::OperandConstraint::createAny()} + { + setInputs(inputs); + setOutputs(outputs); + } + +public: + void accept(onert::ir::OperationVisitor &) const override {} + onert::ir::OpCode opcode() const final { return onert::ir::OpCode::Invalid; } +}; + +} // namespace ir +} // namespace onert_test + +#endif // __ONERT_TEST_GRAPH_MOCK_NODE_H__ diff --git a/runtime/onert/test/graph/operand/IndexSet.cc b/runtime/onert/test/graph/operand/IndexSet.cc new file mode 100644 index 000000000..6215e0d24 --- /dev/null +++ b/runtime/onert/test/graph/operand/IndexSet.cc @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ir/OperandIndexSequence.h" + +using onert::ir::OperandIndex; +using onert::ir::OperandIndexSequence; + +TEST(graph_OperandIndexSequence, append) +{ + OperandIndexSequence iset{0, 2, 4, 8}; + + ASSERT_EQ(iset.size(), 4); + + iset.append(OperandIndex{10}); + + ASSERT_EQ(iset.size(), 5); + + onert::ir::IOIndex index1{1}; + onert::ir::IOIndex index2{4}; + + ASSERT_EQ(iset.at(index1), 2); + ASSERT_EQ(iset.at(index2), 10); + + ASSERT_TRUE(iset.contains(OperandIndex{2})); + ASSERT_TRUE(iset.contains(OperandIndex{10})); + ASSERT_FALSE(iset.contains(OperandIndex{11})); +} + +TEST(graph_OperandIndexSequence, replace) +{ + OperandIndexSequence iset{0, 1, 2, 3}; + + iset.replace(OperandIndex{1}, OperandIndex{9}); + ASSERT_FALSE(iset.contains(OperandIndex{1})); + ASSERT_TRUE(iset.contains(OperandIndex{9})); +} diff --git a/runtime/onert/test/graph/operand/LayoutSet.cc b/runtime/onert/test/graph/operand/LayoutSet.cc new file mode 100644 index 000000000..e35bddd8b --- /dev/null +++ b/runtime/onert/test/graph/operand/LayoutSet.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ir/LayoutSet.h" + +using onert::ir::Layout; +using onert::ir::LayoutSet; + +TEST(graph_operand_LayoutSet, layout_set_operators) +{ + LayoutSet set1{Layout::NCHW}; + LayoutSet set2{Layout::NHWC}; + LayoutSet set3 = set1 | set2; + + ASSERT_EQ(set3.size(), 2); + + ASSERT_EQ((set3 - set1).size(), 1); + ASSERT_EQ((set3 - set1).contains(Layout::NHWC), true); + ASSERT_EQ((set3 - set2).size(), 1); + ASSERT_EQ((set3 - set2).contains(Layout::NCHW), true); + ASSERT_EQ((set3 - set3).size(), 0); + + ASSERT_EQ((set3 & set1).size(), 1); + ASSERT_EQ((set3 & set1).contains(Layout::NCHW), true); + ASSERT_EQ((set3 & set2).size(), 1); + ASSERT_EQ((set3 & set2).contains(Layout::NHWC), true); + ASSERT_EQ((set1 & set2).size(), 0); +} diff --git a/runtime/onert/test/graph/operand/Set.cc b/runtime/onert/test/graph/operand/Set.cc new file mode 100644 index 000000000..0d35b5581 --- /dev/null +++ b/runtime/onert/test/graph/operand/Set.cc @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ir/Operands.h" + +TEST(graph_operand_Set, set_test) +{ + onert::ir::Operands set; + + onert::ir::Shape shape0{1, 2, 3}; + + onert::ir::Shape shape1(4); + shape1.dim(0) = 10; + shape1.dim(1) = 20; + shape1.dim(2) = 30; + shape1.dim(3) = 40; + + onert::ir::TypeInfo type{onert::ir::DataType::INT32}; + + set.emplace(shape0, type); + set.emplace(shape1, type); + + ASSERT_EQ(set.exist(onert::ir::OperandIndex{0u}), true); + ASSERT_EQ(set.exist(onert::ir::OperandIndex{1u}), true); + ASSERT_EQ(set.exist(onert::ir::OperandIndex{2u}), false); + + ASSERT_EQ(set.at(onert::ir::OperandIndex{0u}).shape().dim(0), 1); + ASSERT_EQ(set.at(onert::ir::OperandIndex{0u}).shape().dim(1), 2); + ASSERT_EQ(set.at(onert::ir::OperandIndex{0u}).shape().dim(2), 3); +} diff --git a/runtime/onert/test/graph/operand/UseDef.cc b/runtime/onert/test/graph/operand/UseDef.cc new file mode 100644 index 000000000..3e8b14b8b --- /dev/null +++ b/runtime/onert/test/graph/operand/UseDef.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ir/Graph.h" +#include "ir/verifier/Verifier.h" +#include +#include "../MockNode.h" + +#include + +namespace +{ + +using IndexSet = onert::ir::OperandIndexSequence; +using Mock = onert_test::ir::SimpleMock; + +} // namespace + +TEST(graph_operand_usedef, usedef_test) +{ + onert::ir::Graph graph; + onert::ir::verifier::DAGChecker verifier; + + onert::ir::Shape shape(3); + onert::ir::TypeInfo type{onert::ir::DataType::INT32}; + + // Model Input/Output + auto input_operand = graph.addOperand(shape, type); + auto output_operand = graph.addOperand(shape, type); + + graph.addInput(input_operand); + graph.addOutput(output_operand); + + // MockNode1 + auto operand_index1 = graph.addOperand(shape, type); + auto mocknode_index1 = + graph.addOperation(std::make_unique(IndexSet{input_operand}, IndexSet{operand_index1})); + + // MockNode2 + auto operand_index2 = graph.addOperand(shape, type); + auto mocknode_index2 = + graph.addOperation(std::make_unique(IndexSet{input_operand}, IndexSet{operand_index2})); + + // MockNode3(two input) + auto multiinput_index = graph.addOperation( + std::make_unique(IndexSet{operand_index1, operand_index2}, IndexSet{output_operand})); + + graph.finishBuilding(); + + ASSERT_EQ(verifier.verify(graph), true); + + // Check def + ASSERT_EQ(graph.operands().at(operand_index1).getDef().contains(mocknode_index1), true); + ASSERT_EQ(graph.operands().at(operand_index2).getDef().contains(mocknode_index2), true); + ASSERT_EQ(graph.operands().at(output_operand).getDef().contains(multiinput_index), true); + + ASSERT_EQ(graph.operands().at(operand_index1).getDef().contains(mocknode_index2), false); + ASSERT_EQ(graph.operands().at(operand_index1).getDef().contains(multiinput_index), false); + + // Check use + ASSERT_EQ(graph.operands().at(input_operand).getUses().contains(mocknode_index1), true); + ASSERT_EQ(graph.operands().at(input_operand).getUses().contains(mocknode_index2), true); + ASSERT_EQ(graph.operands().at(input_operand).getUses().contains(multiinput_index), false); + ASSERT_EQ(graph.operands().at(operand_index1).getUses().contains(multiinput_index), true); + ASSERT_EQ(graph.operands().at(operand_index2).getUses().contains(multiinput_index), true); + + ASSERT_EQ(graph.operands().at(input_operand).getUses().size(), 2); + ASSERT_EQ(graph.operands().at(operand_index1).getUses().size(), 1); + ASSERT_EQ(graph.operands().at(output_operand).getUses().size(), 0); +} diff --git a/runtime/onert/test/graph/operation/Set.cc b/runtime/onert/test/graph/operation/Set.cc new file mode 100644 index 000000000..088c44b8c --- /dev/null +++ b/runtime/onert/test/graph/operation/Set.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "../MockNode.h" +#include "ir/Operations.h" + +using onert::ir::Operation; +using onert::ir::OperationIndex; +using onert::ir::Operations; + +TEST(graph_operation_Set, operation_test) +{ + Operations ops; + ops.push(std::unique_ptr(new onert_test::ir::SimpleMock({1, 2, 3, 4}, {5, 6, 7}))); + OperationIndex idx{0u}; + ASSERT_EQ(ops.at(idx).getInputs().size(), 4); + ASSERT_EQ(ops.at(idx).getOutputs().size(), 3); +} diff --git a/runtime/onert/test/graph/operation/SetIO.cc b/runtime/onert/test/graph/operation/SetIO.cc new file mode 100644 index 000000000..378c5b4b9 --- /dev/null +++ b/runtime/onert/test/graph/operation/SetIO.cc @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ir/Graph.h" +#include "ir/Index.h" +#include "ir/OperandIndexSequence.h" +#include "ir/operation/Conv2D.h" +#include "ir/operation/Concat.h" + +#include + +#include + +using Index = onert::ir::IOIndex; +using IndexSet = onert::ir::OperandIndexSequence; + +TEST(graph_operation_setIO, operation_setIO_conv) +{ + onert::ir::Graph graph; + + onert::ir::Shape shape{3}; + onert::ir::TypeInfo type{onert::ir::DataType::INT32}; + + // Add Conv + using Graph = onert::ir::operation::Conv2D; + + auto input_operand = graph.addOperand(shape, type); + auto kernel_operand = graph.addOperand(shape, type); + auto bias_operand = graph.addOperand(shape, type); + IndexSet inputs{input_operand, kernel_operand, bias_operand}; + + Graph::Param conv_params; + conv_params.padding.type = onert::ir::PaddingType::SAME; + conv_params.stride.horizontal = 1; + conv_params.stride.vertical = 1; + conv_params.activation = onert::ir::Activation::NONE; + + auto output_operand = graph.addOperand(shape, type).value(); + IndexSet outputs{output_operand}; + + auto conv = std::make_unique(inputs, outputs, conv_params); + + ASSERT_NE(conv, nullptr); + ASSERT_EQ(conv->getInputs().at(Index{0}).value(), inputs.at(0).value()); + conv->setInputs({8, 9, 10}); + ASSERT_NE(conv->getInputs().at(Index{0}).value(), inputs.at(0).value()); + ASSERT_EQ(conv->getInputs().at(Index{0}).value(), 8); +} + +TEST(graph_operation_setIO, operation_setIO_concat) +{ + onert::ir::Graph graph; + + onert::ir::Shape shape{3}; + + onert::ir::TypeInfo type{onert::ir::DataType::INT32}; + + using Graph = onert::ir::operation::Concat; + + // Add Concat + IndexSet inputs; + for (int i = 0; i < 6; ++i) + { + inputs.append(graph.addOperand(shape, type)); + } + + Graph::Param concat_params{0}; + + auto output_operand = graph.addOperand(shape, type).value(); + IndexSet outputs{output_operand}; + + auto concat = std::make_unique(inputs, outputs, concat_params); + + ASSERT_NE(concat, nullptr); + ASSERT_EQ(concat->getInputs().size(), 6); + ASSERT_EQ(concat->getInputs().at(Index{0}).value(), inputs.at(0).value()); + + concat->setInputs({80, 6, 9, 11}); + ASSERT_EQ(concat->getInputs().size(), 4); + ASSERT_NE(concat->getInputs().at(Index{0}).value(), inputs.at(0).value()); + ASSERT_EQ(concat->getInputs().at(Index{0}).value(), 80); + ASSERT_EQ(concat->getInputs().at(Index{2}).value(), 9); + ASSERT_THROW(concat->getInputs().at(Index{5}), std::out_of_range); +} diff --git a/runtime/onert/test/graph/verifier/Verifier.cc b/runtime/onert/test/graph/verifier/Verifier.cc new file mode 100644 index 000000000..f8c7557e3 --- /dev/null +++ b/runtime/onert/test/graph/verifier/Verifier.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ir/Operation.h" +#include "ir/Graph.h" +#include "ir/verifier/Verifier.h" +#include +#include "ir/Operand.h" +#include "../MockNode.h" + +using IndexSet = onert::ir::OperandIndexSequence; +using Mock = onert_test::ir::SimpleMock; + +TEST(Verifier, dag_checker) +{ + onert::ir::Graph graph; + + onert::ir::Shape shape{3}; + onert::ir::TypeInfo type{onert::ir::DataType::INT32}; + + auto operand1 = graph.addOperand(shape, type); + auto operand2 = graph.addOperand(shape, type); + + graph.addInput(operand1); + graph.addOutput(operand2); + + graph.addOperation(std::make_unique(IndexSet{operand1}, IndexSet{operand2})); + + graph.finishBuilding(); + + onert::ir::verifier::DAGChecker verifier; + + ASSERT_EQ(verifier.verify(graph), true); +} diff --git a/runtime/onert/test/util/ObjectManager.cc b/runtime/onert/test/util/ObjectManager.cc new file mode 100644 index 000000000..5051bcfa6 --- /dev/null +++ b/runtime/onert/test/util/ObjectManager.cc @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "util/ObjectManager.h" +#include "util/Index.h" + +using namespace onert; + +struct TestTag; +using Index = typename util::Index; + +TEST(ObjectManager, emplace) +{ + util::ObjectManager man; + + auto index = man.emplace(100); + ASSERT_EQ(man.at(index), 100); +} + +TEST(ObjectManager, remove_1) +{ + util::ObjectManager man; + + Index index = man.emplace(100); + ASSERT_TRUE(man.exist(index)); + ASSERT_EQ(man.at(index), 100); + + man.remove(index); + ASSERT_FALSE(man.exist(index)); +} + +TEST(ObjectManager, remove_2) +{ + util::ObjectManager man; + + auto index0 = man.emplace(100); + auto index1 = man.emplace(200); + ASSERT_TRUE(man.exist(index0)); + ASSERT_EQ(man.at(index0), 100); + ASSERT_TRUE(man.exist(index1)); + ASSERT_EQ(man.at(index1), 200); + + man.remove(index0); + ASSERT_FALSE(man.exist(index0)); + ASSERT_TRUE(man.exist(index1)); + ASSERT_EQ(man.at(index1), 200); +} + +TEST(ObjectManager, push) +{ + util::ObjectManager man; + + auto index = man.push(std::unique_ptr{new int{100}}); + ASSERT_EQ(man.at(index), 100); +} + +TEST(ObjectManager, const_iterate) +{ + util::ObjectManager man; + + auto index0 = man.emplace(100); + auto index1 = man.emplace(200); + auto index2 = man.emplace(300); + + int sum = 0; + man.iterate([&](const Index &index, const int &val) { sum += val; }); + ASSERT_EQ(sum, 600); +} + +TEST(ObjectManager, non_const_iterate) +{ + util::ObjectManager man; + + auto index0 = man.emplace(100); + auto index1 = man.emplace(200); + auto index2 = man.emplace(300); + + man.iterate([&](const Index &index, int &val) { val += 1; }); + ASSERT_EQ(man.at(index0), 101); + ASSERT_EQ(man.at(index1), 201); + ASSERT_EQ(man.at(index2), 301); +} diff --git a/runtime/onert/test/util/ShapeInference.cc b/runtime/onert/test/util/ShapeInference.cc new file mode 100644 index 000000000..053b635f2 --- /dev/null +++ b/runtime/onert/test/util/ShapeInference.cc @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ir/Layout.h" +#include "util/ShapeInference.h" + +using namespace onert::ir; + +TEST(ShapeInference, Elementwise) +{ + Shape lhs_shape{1, 299, 299, 3}; + Shape rhs_shape{3}; + auto infered_shapes = onert::shape_inference::inferEltwiseShape(lhs_shape, rhs_shape); + auto infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.dim(0), 1); + ASSERT_EQ(infered_out_shape.dim(1), 299); + ASSERT_EQ(infered_out_shape.dim(2), 299); + ASSERT_EQ(infered_out_shape.dim(3), 3); +} + +TEST(ShapeInference, IncorrectElementwise) +{ + Shape lhs_shape{1, 299, 299, 3}; + Shape rhs_shape{5, 3}; + ASSERT_THROW(onert::shape_inference::inferEltwiseShape(lhs_shape, rhs_shape), std::runtime_error); +} + +TEST(ShapeInference, Pool2DNodeSame) +{ + Shape in_shape{10, 6, 12, 20}; + Stride stride{3, 7}; + Padding padding{PaddingType::SAME}; + + operation::AvgPool2D::Param avg_pool_param{3, 6, stride, padding, Activation::NONE}; + auto infered_shapes = onert::shape_inference::inferAvgPoolShape(in_shape, avg_pool_param); + auto infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 20); + + operation::MaxPool2D::Param max_pool_param{3, 6, stride, padding, Activation::NONE}; + infered_shapes = onert::shape_inference::inferMaxPoolShape(in_shape, max_pool_param); + infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 20); +} + +TEST(ShapeInference, Pool2DNodeValid) +{ + Shape in_shape{10, 6, 12, 20}; + Stride stride{3, 7}; + Padding padding{PaddingType::VALID}; + + operation::AvgPool2D::Param avg_pool_param{3, 6, stride, padding, Activation::NONE}; + auto infered_shapes = onert::shape_inference::inferAvgPoolShape(in_shape, avg_pool_param); + auto infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 1); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 20); + + operation::MaxPool2D::Param max_pool_param{3, 6, stride, padding, Activation::NONE}; + infered_shapes = onert::shape_inference::inferMaxPoolShape(in_shape, max_pool_param); + infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 1); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 20); +} + +TEST(ShapeInference, Pool2DNodeExplicit) +{ + Shape in_shape{10, 3, 5, 20}; + + Stride stride{3, 7}; + Padding padding{4, 3, 2, 1}; + + operation::AvgPool2D::Param avg_pool_param{3, 6, stride, padding, Activation::NONE}; + auto infered_shapes = onert::shape_inference::inferAvgPoolShape(in_shape, avg_pool_param); + auto infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 1); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 20); + + operation::MaxPool2D::Param max_pool_param{3, 6, stride, padding, Activation::NONE}; + infered_shapes = onert::shape_inference::inferMaxPoolShape(in_shape, max_pool_param); + infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 1); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 20); +} + +TEST(ShapeInference, Conv2D) +{ + Shape in_shape{10, 6, 12, 20}; + Shape ker_shape{30, 3, 6, 20}; + + operation::Conv2D::Param param{Stride{3, 7}, Padding{PaddingType::VALID}, Activation::NONE}; + auto infered_shapes = onert::shape_inference::inferConv2DShape(in_shape, ker_shape, param); + auto infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 1); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 30); + + param = operation::Conv2D::Param{Stride{3, 7}, Padding{PaddingType::SAME}, Activation::NONE}; + infered_shapes = onert::shape_inference::inferConv2DShape(in_shape, ker_shape, param); + infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 30); + + param = operation::Conv2D::Param{Stride{3, 7}, Padding{4, 3, 2, 1}, Activation::NONE}; + infered_shapes = onert::shape_inference::inferConv2DShape(in_shape, ker_shape, param); + infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 3); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 30); +} + +TEST(ShapeInference, DepthwiseConv2D) +{ + Shape in_shape{10, 6, 12, 20}; + Shape ker_shape{1, 3, 6, 60}; + + operation::DepthwiseConv2D::Param param{Stride{3, 7}, Padding{PaddingType::VALID}, 3, + Activation::NONE}; + auto infered_shapes = + onert::shape_inference::inferDepthwiseConv2DShape(in_shape, ker_shape, param); + auto infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 1); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 60); + + param = operation::DepthwiseConv2D::Param{Stride{3, 7}, Padding{PaddingType::SAME}, 3, + Activation::NONE}; + infered_shapes = onert::shape_inference::inferDepthwiseConv2DShape(in_shape, ker_shape, param); + infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 60); + + param = operation::DepthwiseConv2D::Param{Stride{3, 7}, Padding{4, 3, 2, 1}, 3, Activation::NONE}; + infered_shapes = onert::shape_inference::inferDepthwiseConv2DShape(in_shape, ker_shape, param); + infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 4); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).N, 10); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).H, 3); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).W, 2); + ASSERT_EQ(infered_out_shape.asFeature(Layout::NHWC).C, 60); +} + +TEST(ShapeInference, Concat) +{ + Shape in1{10, 20, 30, 3, 50}; + Shape in2{10, 20, 30, 2, 50}; + Shape in3{10, 20, 30, 2, 50}; + + operation::Concat::Param param{3}; + auto infered_shapes = onert::shape_inference::inferConcatShape({in1, in2, in3}, param); + auto infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 5); + ASSERT_EQ(infered_out_shape.dim(0), 10); + ASSERT_EQ(infered_out_shape.dim(1), 20); + ASSERT_EQ(infered_out_shape.dim(2), 30); + ASSERT_EQ(infered_out_shape.dim(3), 7); + ASSERT_EQ(infered_out_shape.dim(4), 50); +} + +TEST(ShapeInference, FullyConnected) +{ + Shape in_shape{3, 4, 5, 6}; + Shape ker_shape{3, 10}; + auto infered_shapes = onert::shape_inference::inferFullyConnectedShape(in_shape, ker_shape); + auto infered_out_shape = infered_shapes[0]; + + ASSERT_EQ(infered_out_shape.rank(), 2); + ASSERT_EQ(infered_out_shape.dim(0), 36); + ASSERT_EQ(infered_out_shape.dim(1), 3); +} -- cgit v1.2.3