diff options
Diffstat (limited to 'runtime/neurun/backend/cpu_common')
-rw-r--r-- | runtime/neurun/backend/cpu_common/CMakeLists.txt | 28 | ||||
-rw-r--r-- | runtime/neurun/backend/cpu_common/MemoryPlanner.cc | 220 | ||||
-rw-r--r-- | runtime/neurun/backend/cpu_common/MemoryPlanner.h | 217 | ||||
-rw-r--r-- | runtime/neurun/backend/cpu_common/MemoryPlanner.test.cc | 193 | ||||
-rw-r--r-- | runtime/neurun/backend/cpu_common/MemoryPlannerFactory.cc | 51 | ||||
-rw-r--r-- | runtime/neurun/backend/cpu_common/MemoryPlannerFactory.h | 45 |
6 files changed, 754 insertions, 0 deletions
diff --git a/runtime/neurun/backend/cpu_common/CMakeLists.txt b/runtime/neurun/backend/cpu_common/CMakeLists.txt new file mode 100644 index 000000000..5b6161ba7 --- /dev/null +++ b/runtime/neurun/backend/cpu_common/CMakeLists.txt @@ -0,0 +1,28 @@ +file(GLOB SOURCES "*.cc") +file(GLOB_RECURSE TESTS "*.test.cc") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(${LIB_NEURUN_BACKEND_CPU_COMMON} STATIC ${SOURCES}) + +target_include_directories(${LIB_NEURUN_BACKEND_CPU_COMMON} PUBLIC ${NEURUN_INCLUDE_DIR}) +target_include_directories(${LIB_NEURUN_BACKEND_CPU_COMMON} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(${LIB_NEURUN_BACKEND_CPU_COMMON} PUBLIC nnfw_lib_cpp14) +target_link_libraries(${LIB_NEURUN_BACKEND_CPU_COMMON} PRIVATE nnfw_lib_misc) +target_link_libraries(${LIB_NEURUN_BACKEND_CPU_COMMON} PRIVATE neurun_core) +target_link_libraries(${LIB_NEURUN_BACKEND_CPU_COMMON} PRIVATE nnfw_common) +target_link_libraries(${LIB_NEURUN_BACKEND_CPU_COMMON} PRIVATE nnfw_coverage) + +set_target_properties(${LIB_NEURUN_BACKEND_CPU_COMMON} PROPERTIES POSITION_INDEPENDENT_CODE ON) +set_target_properties(${LIB_NEURUN_BACKEND_CPU_COMMON} PROPERTIES OUTPUT_NAME backend_cpu_common) + +# Unit Tests +set(TEST_NEURUN_BACKEND_CPU_COMMON test_neurun_backend_cpu_common) + +add_executable(${TEST_NEURUN_BACKEND_CPU_COMMON} ${TESTS}) + +target_link_libraries(${TEST_NEURUN_BACKEND_CPU_COMMON} ${LIB_NEURUN_BACKEND_CPU_COMMON}) +target_link_libraries(${TEST_NEURUN_BACKEND_CPU_COMMON} gtest gtest_main dl ${LIB_PTHREAD}) +target_include_directories(${TEST_NEURUN_BACKEND_CPU_COMMON} PRIVATE ${NEURUN_INCLUDE_DIR}) + +add_test(${TEST_NEURUN_BACKEND_CPU_COMMON} ${TEST_NEURUN_BACKEND_CPU_COMMON}) +install(TARGETS ${TEST_NEURUN_BACKEND_CPU_COMMON} DESTINATION unittest) diff --git a/runtime/neurun/backend/cpu_common/MemoryPlanner.cc b/runtime/neurun/backend/cpu_common/MemoryPlanner.cc new file mode 100644 index 000000000..19961362e --- /dev/null +++ b/runtime/neurun/backend/cpu_common/MemoryPlanner.cc @@ -0,0 +1,220 @@ +/* + * 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 "MemoryPlanner.h" +#include "util/logging.h" +#include <cassert> + +namespace neurun +{ +namespace backend +{ +namespace cpu_common +{ + +Allocator::Allocator(uint32_t capacity) +{ + _base = nnfw::cpp14::make_unique<uint8_t[]>(capacity); + + VERBOSE(ALLOC) << "allocation capacity: " << capacity << std::endl; + VERBOSE(ALLOC) << "base pointer: " << static_cast<void *>(_base.get()) << std::endl; +} + +void BumpPlanner::claim(const ir::OperandIndex &ind, size_t size) +{ + assert(size != 0); + + Block blk{_capacity, size}; + _mem_plans[ind] = blk; + _capacity += size; + + VERBOSE(BP_PLANNER) << "CLAIM(#" << ind.value() << "): " << blk.offset << ", " << blk.size + << std::endl; +} + +void BumpPlanner::release(const ir::OperandIndex &ind) +{ + VERBOSE(BP_PLANNER) << "RELEASE(#" << ind.value() << "): " + << "NOTHING does" << std::endl; +} + +// There are some assumptions for claiming memory(== making a reservation for memory). +// 1. About _claim_table(std::map). +// - The table's data structure is std::map so that it always sorts +// value(OperandIndex) by key(base_offset). +// - This claim() inserts key/value into _claim_table and the release() removes the key/value from +// _claim_table. +// - _claim_table shows the memory status at a certain point in time. Therefore, +// - If _claim_table has an offset and a certain size at a certain point in time, +// it means the place at the offset has been already claimed(== can't claim now. need to find +// someplace new). +// - If _claim_table doesn't have any element for an offset and a certain size at a certain +// point in time, it means the place at the offset can be claimed. +// 2. In the loop for _claim_table, we can assume the current claim_base_offset value is bigger than +// the previous claim_base_offset. +void FirstFitPlanner::claim(const ir::OperandIndex &ind, size_t size) +{ + assert(size != 0); + + // Find the right position for claiming + uint32_t next_offset = 0; + for (auto &mem_claim : _claim_table) + { + auto claimed_base_offset = mem_claim.first; + auto claimed_size = _mem_plans[mem_claim.second].size; + if (next_offset + size <= claimed_base_offset) + { + break; + } + else + { + next_offset = claimed_base_offset + claimed_size; + } + } + + // Now next_offset is set to the proper offset + _claim_table[next_offset] = ind; + _mem_plans[ind] = {next_offset, size}; + + VERBOSE(FF_PLANNER) << "claim(#" << ind.value() << "): [+" << next_offset << ", " << size << "sz]" + << std::endl; + + if (_capacity < next_offset + size) + { + _capacity = next_offset + size; + } +} + +void FirstFitPlanner::release(const ir::OperandIndex &ind) +{ + for (auto it = _claim_table.cbegin(); it != _claim_table.cend(); ++it) + { + if (it->second == ind) + { + uint32_t offset = it->first; + uint32_t index = ind.value(); + uint32_t size = _mem_plans[ind].size; + + _claim_table.erase(it); + + VERBOSE(FF_PLANNER) << "release(#" << index << "): [+" << offset << ", " << size << "sz]" + << std::endl; + return; + } + } + assert(!"Cannot release for given index. It has been not claimed or released already."); +} + +WICPlanner::WICPlanner() + : _initialized(false), _capacity(0), _mem_plans(), _live_operands(), _interference_graph(), + _map_size_to_operands(), _claim_table() +{ + // DO NOTHING +} + +void WICPlanner::claim(const ir::OperandIndex &ind, size_t size) +{ + assert(size != 0); + + _map_size_to_operands.insert({size, ind}); + for (auto &live_operand : _live_operands) + { + _interference_graph[live_operand].insert(ind); + _interference_graph[ind].insert(live_operand); + } + _live_operands.insert(ind); + + VERBOSE(WIC_PLANNER) << "claim(#" << ind.value() << "): [" << size << "sz]" << std::endl; +} + +void WICPlanner::release(const ir::OperandIndex &ind) +{ + _live_operands.erase(ind); + VERBOSE(WIC_PLANNER) << "release(#" << ind.value() << ")" << std::endl; +} + +/* + * Build memory plans using liveness and size of operands + * 1. Build inference graph at claim + * - Two operands interfere if they have overlapped live range + * 2. Sort operands descending order of size + * - Use std::multimap to sort operands + * 3. Allocate memory block for sorted operands + * - Find free memory block which does not overlap with interfered operands + */ +void WICPlanner::buildMemoryPlans() +{ + for (auto &size_to_operand : _map_size_to_operands) + { + uint32_t size = size_to_operand.first; + ir::OperandIndex ind = size_to_operand.second; + VERBOSE(WIC_PLANNER) << "build_plan(#" << ind.value() << "): [" << size << "sz]" << std::endl; + + // Find firstfit which does not interfere with live operands + uint32_t next_offset = 0; + if (_interference_graph.find(ind) != _interference_graph.end()) + { + std::unordered_set<ir::OperandIndex> &interferences = _interference_graph.find(ind)->second; + for (auto &mem_claim : _claim_table) + { + if (interferences.find(mem_claim.second) != interferences.end()) + { + auto claimed_base_offset = mem_claim.first; + auto claimed_size = _mem_plans[mem_claim.second].size; + VERBOSE(WIC_PLANNER) << "interfere (#" << mem_claim.second.value() << "): [+" + << claimed_base_offset << ", " << claimed_size << "sz]" << std::endl; + if (next_offset + size <= claimed_base_offset) + { + break; + } + else if (next_offset < claimed_base_offset + claimed_size) + { + next_offset = claimed_base_offset + claimed_size; + } + } + } + } + else + { + VERBOSE(WIC_PLANNER) << "No interference" << std::endl; + } + + _claim_table.insert({next_offset, ind}); + _mem_plans[ind] = {next_offset, size}; + VERBOSE(WIC_PLANNER) << "alloc(#" << ind.value() << "): [+" << next_offset << ", " << size + << "sz]" << std::endl; + + if (_capacity < next_offset + size) + { + _capacity = next_offset + size; + } + } + _initialized = true; + _interference_graph.clear(); + _map_size_to_operands.clear(); + _claim_table.clear(); +} + +WICPlanner::MemoryPlans &WICPlanner::memory_plans() +{ + if (!_initialized) + buildMemoryPlans(); + return _mem_plans; +} + +} // namespace cpu_common +} // namespace backend +} // namespace neurun diff --git a/runtime/neurun/backend/cpu_common/MemoryPlanner.h b/runtime/neurun/backend/cpu_common/MemoryPlanner.h new file mode 100644 index 000000000..c4f5e6a9e --- /dev/null +++ b/runtime/neurun/backend/cpu_common/MemoryPlanner.h @@ -0,0 +1,217 @@ +/* + * 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. + */ + +/** + * @file MemoryPlanner.h + * @brief This file contains Memory Planning related classes + */ + +#ifndef __NEURUN_BACKEND_CPU_COMMON_MEMORY_PLANNER_H__ +#define __NEURUN_BACKEND_CPU_COMMON_MEMORY_PLANNER_H__ + +#include <map> +#include <unordered_set> +#include <cpp14/memory.h> + +#include "ir/OperandIndexMap.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu_common +{ + +/** + * @brief Structure to have memory offset and size + */ +struct Block +{ + uint32_t offset; + size_t size; +}; + +/** + * @brief Class to allocate memory + */ +class Allocator +{ +public: + Allocator(uint32_t capacity); + /** + * @brief Get memory base pointer + * @return base pointer + */ + uint8_t *base() const { return _base.get(); } + void release() { _base.reset(); } + +private: + std::unique_ptr<uint8_t[]> _base; +}; + +/** + * @brief Interface to plan memory + */ +struct IMemoryPlanner +{ + using MemoryPlans = ir::OperandIndexMap<Block>; + + /** + * @brief Claim memory for operand + * @param[in] index The operand index + * @param[in] size The size of the memory + */ + virtual void claim(const ir::OperandIndex &, size_t) = 0; + /** + * @brief Release memory for operand + * @param[in] index The operand index + */ + virtual void release(const ir::OperandIndex &) = 0; + /** + * @brief Get capacity for memory planning + * @return The value of capacity + */ + virtual uint32_t capacity() = 0; + /** + * @brief Get MemoryPlans + * @return MemoryPlans + */ + virtual MemoryPlans &memory_plans() = 0; + + virtual ~IMemoryPlanner() = default; +}; + +/** + * @brief Class to plan memory by bump way + */ +class BumpPlanner : public IMemoryPlanner +{ +public: + /** + * @brief Claim memory for operand by bump way + * @param[in] index The operand index + * @param[in] size The size of the memory + */ + void claim(const ir::OperandIndex &, size_t) override; + /** + * @brief Release memory for operand by bump way + * @param[in] index The operand index + */ + void release(const ir::OperandIndex &) override; + /** + * @brief Get capacity for memory planning + * @return The value of capacity + */ + uint32_t capacity() override { return _capacity; } + /** + * @brief Get MemoryPlans + * @return MemoryPlans + */ + MemoryPlans &memory_plans() override { return _mem_plans; } + +private: + uint32_t _capacity = 0; + MemoryPlans _mem_plans; +}; + +/** + * @brief Class to plan memory by firstfit way + */ +class FirstFitPlanner : public IMemoryPlanner +{ +public: + /** + * @brief Claim memory for operand by firstfit way + * @param[in] index The operand index + * @param[in] size The size of the memory + */ + void claim(const ir::OperandIndex &, size_t) override; + /** + * @brief Release memory for operand by firstfit way + * @param[in] index The operand index + */ + void release(const ir::OperandIndex &) override; + /** + * @brief Get capacity for memory planning + * @return The value of capacity + */ + uint32_t capacity() override { return _capacity; } + /** + * @brief Get MemoryPlans + * @return MemoryPlans + */ + MemoryPlans &memory_plans() override { return _mem_plans; } + +private: + uint32_t _capacity = 0; + MemoryPlans _mem_plans; + // Use std::map because claim() assumes that _claim_table is sorted by uint32_t(base_offset) + std::map<uint32_t, ir::OperandIndex> _claim_table; +}; + +/** + * @brief Class to plan memory by Weighted Interval Color algorithm + */ +class WICPlanner : public IMemoryPlanner +{ +public: + WICPlanner(); + + /** + * @brief Claim memory for operand by WIC algorithm + * @param[in] index The operand index + * @param[in] size The size of the memory + */ + void claim(const ir::OperandIndex &, size_t) override; + /** + * @brief Release memory for operand by WIC algorithm + * @param[in] index The operand index + */ + void release(const ir::OperandIndex &) override; + /** + * @brief Get capacity for memory planning + * @return The value of capacity + */ + uint32_t capacity() override + { + if (!_initialized) + buildMemoryPlans(); + return _capacity; + } + /** + * @brief Get MemoryPlans + * @return MemoryPlans + */ + MemoryPlans &memory_plans() override; + +private: + void buildMemoryPlans(); + + bool _initialized; + uint32_t _capacity; + MemoryPlans _mem_plans; + std::unordered_set<ir::OperandIndex> _live_operands; + ir::OperandIndexMap<std::unordered_set<ir::OperandIndex>> _interference_graph; + // Sort operands by descending order of size + std::multimap<uint32_t, ir::OperandIndex, std::greater<uint32_t>> _map_size_to_operands; + std::multimap<uint32_t, ir::OperandIndex> _claim_table; +}; + +} // namespace cpu_common +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_CPU_COMMON_MEMORY_PLANNER_H__ diff --git a/runtime/neurun/backend/cpu_common/MemoryPlanner.test.cc b/runtime/neurun/backend/cpu_common/MemoryPlanner.test.cc new file mode 100644 index 000000000..b2be7db24 --- /dev/null +++ b/runtime/neurun/backend/cpu_common/MemoryPlanner.test.cc @@ -0,0 +1,193 @@ +/* + * 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 <gtest/gtest.h> + +#include "MemoryPlanner.h" +#include "ir/Index.h" + +TEST(Allocator, allocate_test) +{ + ::neurun::backend::cpu_common::Allocator allocator(1024); + ASSERT_NE(allocator.base(), nullptr); +} + +TEST(BumpPlanner, claim_test) +{ + ::neurun::backend::cpu_common::BumpPlanner planner; + + auto claim = [&planner](uint32_t index, size_t size, uint32_t expected_offset) { + neurun::ir::OperandIndex mem_idx(index); + planner.claim(mem_idx, size); + auto mem_blk = planner.memory_plans()[mem_idx]; + ASSERT_EQ(mem_blk.offset, expected_offset); + ASSERT_EQ(mem_blk.size, size); + }; + + claim(0, 10, 0); + claim(1, 20, 10); + claim(2, 30, 30); +} + +TEST(FirstFitPlanner, claim_release_test) +{ + ::neurun::backend::cpu_common::FirstFitPlanner planner; + + auto claim = [&planner](uint32_t index, size_t size, uint32_t expected_offset) { + neurun::ir::OperandIndex mem_idx(index); + planner.claim(mem_idx, size); + auto mem_blk = planner.memory_plans()[mem_idx]; + ASSERT_EQ(mem_blk.offset, expected_offset); + ASSERT_EQ(mem_blk.size, size); + }; + + auto release = [&planner](uint32_t index) { + neurun::ir::OperandIndex mem_idx(index); + planner.release(mem_idx); + }; + + // 0 CLAIM - 10 + claim(0, 10, 0); + + // 1 CLAIM - 20 + claim(1, 20, 10); + + // 2 CLAIM - 30 + claim(2, 30, 30); + + // 0 RELEASE - 10 + release(0); + + // 3 CLAIM - 20 + claim(3, 20, 60); + + // 4 CLAIM - 5 + claim(4, 5, 0); + + // 5 CLAIM - 10 + claim(5, 10, 80); + + // 6 CLAIM - 5 + claim(6, 5, 5); + + // 2 RELEASE - 30 + release(2); + + // 7 CLAIM - 35 + claim(7, 35, 90); + + // 8 CLAIM - 10 + claim(8, 10, 30); + + // 4 RELEASE - 5 + release(4); + + // 9 CLAIM - 10 + claim(9, 10, 40); + + // 10 CLAIM - 10 + claim(10, 10, 50); + + // 6 RELEASE + release(6); + + // 1 RELEASE + release(1); + + // 8 RELEASE + release(8); + + // 9 RELEASE + release(9); + + // 10 RELEASE + release(10); + + // 3 RELEASE + release(3); + + // 5 RELEASE + release(5); + + // 7 RELEASE + release(7); +} + +TEST(WICPlanner, claim_release_test) +{ + ::neurun::backend::cpu_common::WICPlanner planner; + + auto claim = [&planner](uint32_t index, size_t size) { + neurun::ir::OperandIndex mem_idx(index); + planner.claim(mem_idx, size); + }; + + auto release = [&planner](uint32_t index) { + neurun::ir::OperandIndex mem_idx(index); + planner.release(mem_idx); + }; + + auto verify = [&planner](uint32_t index, uint32_t size, uint32_t expected_offset) { + neurun::ir::OperandIndex mem_idx(index); + auto mem_blk = planner.memory_plans()[mem_idx]; + ASSERT_EQ(mem_blk.offset, expected_offset); + ASSERT_EQ(mem_blk.size, size); + }; + + auto capacity = [&planner](uint32_t expected_capacity) { + auto actual_capacity = planner.capacity(); + ASSERT_EQ(actual_capacity, expected_capacity); + }; + + claim(0, 20); + claim(1, 5); + release(0); + claim(2, 10); + release(1); + claim(3, 10); + release(2); + claim(4, 10); + release(3); + claim(5, 20); + release(4); + claim(6, 20); + release(5); + release(7); + + // VERIFY 0 - 0 + verify(0, 20, 0); + + // VERIFY 1 - 20 + verify(1, 5, 20); + + // VERIFY 2 - 0 + verify(2, 10, 0); + + // VERIFY 3 - 10 + verify(3, 10, 10); + + // VERIFY 4 - 20 + verify(4, 10, 20); + + // VERIFY 5 - 0 + verify(5, 20, 0); + + // VERIFY 6 - 20 + verify(6, 20, 20); + + // CAPACITY - 40 + capacity(40); +} diff --git a/runtime/neurun/backend/cpu_common/MemoryPlannerFactory.cc b/runtime/neurun/backend/cpu_common/MemoryPlannerFactory.cc new file mode 100644 index 000000000..9da987aa4 --- /dev/null +++ b/runtime/neurun/backend/cpu_common/MemoryPlannerFactory.cc @@ -0,0 +1,51 @@ +/* + * 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 "MemoryPlannerFactory.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu_common +{ + +MemoryPlannerFactory &MemoryPlannerFactory::get() +{ + static MemoryPlannerFactory instance; + return instance; +} + +IMemoryPlanner *MemoryPlannerFactory::create(const std::string &key) +{ + if (key == "FirstFit") + { + return new FirstFitPlanner; + } + else if (key == "Bump") + { + return new BumpPlanner; + } + else if (key == "WIC") + { + return new WICPlanner; + } + return new FirstFitPlanner; // Default Planner +} + +} // namespace cpu_common +} // namespace backend +} // namespace neurun diff --git a/runtime/neurun/backend/cpu_common/MemoryPlannerFactory.h b/runtime/neurun/backend/cpu_common/MemoryPlannerFactory.h new file mode 100644 index 000000000..829600e27 --- /dev/null +++ b/runtime/neurun/backend/cpu_common/MemoryPlannerFactory.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef __NEURUN_BACKEND_CPU_COMMON_MEMORY_PLANNER_FACTORY_H__ +#define __NEURUN_BACKEND_CPU_COMMON_MEMORY_PLANNER_FACTORY_H__ + +#include "MemoryPlanner.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu_common +{ + +class MemoryPlannerFactory +{ +public: + static MemoryPlannerFactory &get(); + +private: + MemoryPlannerFactory() = default; + +public: + IMemoryPlanner *create(const std::string &key); +}; + +} // namespace cpu_common +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_CPU_COMMON_MEMORY_PLANNER_FACTORY_H__ |