diff options
Diffstat (limited to 'runtimes/neurun/src/backend')
36 files changed, 3155 insertions, 0 deletions
diff --git a/runtimes/neurun/src/backend/BackendManager.cc b/runtimes/neurun/src/backend/BackendManager.cc new file mode 100644 index 000000000..fb7d69108 --- /dev/null +++ b/runtimes/neurun/src/backend/BackendManager.cc @@ -0,0 +1,88 @@ +/* + * 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 "BackendManager.h" + +#include "backend/acl_cl/BackendConfig.h" +#include "backend/acl_cl/TensorBuilder.h" +#include "backend/acl_cl/InitializerGenerator.h" +#include "backend/acl_cl/StageGenerator.h" +#include "backend/cpu/BackendConfig.h" +#include "backend/cpu/TensorBuilder.h" +#include "backend/cpu/InitializerGenerator.h" +#include "backend/cpu/StageGenerator.h" + +namespace neurun +{ +namespace backend +{ + +Backend::Backend(const std::shared_ptr<neurun::backend::IBackendConfig> &backend_config, + const std::shared_ptr<neurun::backend::IInitializerGenerator> &initializer_gen, + const std::shared_ptr<neurun::backend::IStageGenerator> &stage_gen) + : _config(backend_config), _initializer_gen(initializer_gen), _stage_gen(stage_gen) +{ + backend_config->initialize(); +} + +const std::shared_ptr<neurun::backend::IBackendConfig> Backend::config() const { return _config; } + +const std::shared_ptr<neurun::backend::IInitializerGenerator> Backend::initializer_gen() const +{ + return _initializer_gen; +} + +const std::shared_ptr<neurun::backend::IStageGenerator> Backend::stage_gen() const +{ + return _stage_gen; +} + +const std::shared_ptr<neurun::backend::ITensorBuilder> Backend::tensor_builder() const +{ + return _stage_gen->tensor_builder(); +} + +BackendManager::BackendManager(const neurun::graph::operand::Set &operands) +{ + // Add arm_compute backend + { + using namespace ::neurun::backend::acl_cl; + auto acl_backend_initializer = std::make_shared<BackendConfig>(); + auto acl_tensor_builder = std::make_shared<TensorBuilder>(); + auto acl_initializer_gen = std::make_shared<InitializerGenerator>(operands); + auto acl_stage_gen = std::make_shared<StageGenerator>(operands, acl_tensor_builder); + + // TODO Do not use magic string for backend id + _gen_map["acl_cl"] = {acl_backend_initializer, acl_initializer_gen, acl_stage_gen}; + } + + // Add CPU backend + { + using namespace ::neurun::backend::cpu; + auto cpu_backend_initializer = std::make_shared<BackendConfig>(); + auto cpu_tensor_builder = std::make_shared<TensorBuilder>(); + auto cpu_initializer_gen = std::make_shared<InitializerGenerator>(operands); + auto cpu_stage_gen = std::make_shared<StageGenerator>(operands, cpu_tensor_builder); + + // TODO Do not use magic string for backend id + _gen_map["cpu"] = {cpu_backend_initializer, cpu_initializer_gen, cpu_stage_gen}; + } +} + +Backend BackendManager::get(const std::string &key) { return _gen_map.at(key); } + +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/BackendManager.h b/runtimes/neurun/src/backend/BackendManager.h new file mode 100644 index 000000000..6f862ffe6 --- /dev/null +++ b/runtimes/neurun/src/backend/BackendManager.h @@ -0,0 +1,73 @@ +/* + * 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 __NEURUN_BACKEND_BACKEND_MANAGER_H__ +#define __NEURUN_BACKEND_BACKEND_MANAGER_H__ + +#include <memory> +#include <map> + +#include "graph/operand/Set.h" + +namespace neurun +{ +namespace backend +{ + +struct IBackendConfig; +struct IInitializerGenerator; +struct IStageGenerator; +struct ITensorBuilder; + +class Backend +{ +public: + Backend(const std::shared_ptr<neurun::backend::IBackendConfig> &backend_config, + const std::shared_ptr<neurun::backend::IInitializerGenerator> &initializer_gen, + const std::shared_ptr<neurun::backend::IStageGenerator> &stage_gen); + + Backend(void) : _config(nullptr), _initializer_gen(nullptr), _stage_gen(nullptr) + { + // DO NOTHING + } + +public: + const std::shared_ptr<neurun::backend::IBackendConfig> config() const; + const std::shared_ptr<neurun::backend::IInitializerGenerator> initializer_gen() const; + const std::shared_ptr<neurun::backend::IStageGenerator> stage_gen() const; + const std::shared_ptr<neurun::backend::ITensorBuilder> tensor_builder() const; + +private: + std::shared_ptr<neurun::backend::IBackendConfig> _config; + std::shared_ptr<neurun::backend::IInitializerGenerator> _initializer_gen; + std::shared_ptr<neurun::backend::IStageGenerator> _stage_gen; +}; + +class BackendManager +{ +public: + BackendManager(const neurun::graph::operand::Set &operands); + + Backend get(const std::string &key); + +private: + std::map<std::string, Backend> _gen_map; +}; + +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_BACKEND_MANAGER_H__ diff --git a/runtimes/neurun/src/backend/CMakeLists.txt b/runtimes/neurun/src/backend/CMakeLists.txt new file mode 100644 index 000000000..a39823102 --- /dev/null +++ b/runtimes/neurun/src/backend/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(cpu) +add_subdirectory(acl_cl) diff --git a/runtimes/neurun/src/backend/IBackendConfig.h b/runtimes/neurun/src/backend/IBackendConfig.h new file mode 100644 index 000000000..a6c7ce517 --- /dev/null +++ b/runtimes/neurun/src/backend/IBackendConfig.h @@ -0,0 +1,39 @@ +/* + * 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 __INTERNAL_IBACKEND_CONFIG_H__ +#define __INTERNAL_IBACKEND_CONFIG_H__ + +#include "graph/operand/Layout.h" + +namespace neurun +{ +namespace backend +{ + +struct IBackendConfig +{ + virtual ~IBackendConfig() = default; + + virtual void initialize() = 0; + // NOTE Assume backend has only one type of operand layout + virtual graph::operand::Layout getOperandLayout() = 0; +}; + +} // namespace backend +} // namespace neurun + +#endif // __INTERNAL_IBACKEND_CONFIG_H__ diff --git a/runtimes/neurun/src/backend/IInitializerGenerator.h b/runtimes/neurun/src/backend/IInitializerGenerator.h new file mode 100644 index 000000000..83cf87a52 --- /dev/null +++ b/runtimes/neurun/src/backend/IInitializerGenerator.h @@ -0,0 +1,46 @@ +/* + * 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 __INTERNAL_IINITIALIZER_GENERATOR_H__ +#define __INTERNAL_IINITIALIZER_GENERATOR_H__ + +#include "arm_compute/core/ITensor.h" + +#include "graph/operation/Conv2D.h" +#include "graph/operation/FullyConnected.h" + +using Initializer = std::function<void(::arm_compute::ITensor &)>; + +namespace neurun +{ +namespace backend +{ + +struct IInitializerGenerator +{ + virtual ~IInitializerGenerator() = default; + + virtual Initializer generateWeight(const graph::operation::Conv2D::Implicit::Node &node) = 0; + virtual Initializer generateWeight(const graph::operation::FullyConnected::Node &node) = 0; + + virtual Initializer generateBias(const graph::operation::Conv2D::Implicit::Node &node) = 0; + virtual Initializer generateBias(const graph::operation::FullyConnected::Node &node) = 0; +}; + +} // namespace backend +} // namespace neurun + +#endif // __INTERNAL_IINITIALIZER_GENERATOR_H__ diff --git a/runtimes/neurun/src/backend/IObject.h b/runtimes/neurun/src/backend/IObject.h new file mode 100644 index 000000000..f7d511095 --- /dev/null +++ b/runtimes/neurun/src/backend/IObject.h @@ -0,0 +1,42 @@ +/* + * 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 __NEURUN_BACKEND_OPERAND_I_OBJECT_H__ +#define __NEURUN_BACKEND_OPERAND_I_OBJECT_H__ + +#include <functional> + +#include <arm_compute/core/ITensor.h> + +namespace neurun +{ +namespace backend +{ +namespace operand +{ + +struct IObject +{ + virtual ~IObject() = default; + virtual ::arm_compute::ITensor *ptr(void) const = 0; + virtual void access(const std::function<void(::arm_compute::ITensor &tensor)> &fn) const = 0; +}; + +} // namespace operand +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_OPERAND_I_OBJECT_H__ diff --git a/runtimes/neurun/src/backend/IStageGenerator.h b/runtimes/neurun/src/backend/IStageGenerator.h new file mode 100644 index 000000000..05959e2b1 --- /dev/null +++ b/runtimes/neurun/src/backend/IStageGenerator.h @@ -0,0 +1,68 @@ +/* + * 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 __INTERNAL_ISTAGE_GENERATOR_H__ +#define __INTERNAL_ISTAGE_GENERATOR_H__ + +#include <memory> +#include <functional> + +#include <arm_compute/runtime/IFunction.h> + +#include "backend/ITensorBuilder.h" +#include "graph/operation/Conv2D.h" +#include "graph/operation/MaxPool2D.h" +#include "graph/operation/AvgPool2D.h" +#include "graph/operation/Concat.h" +#include "graph/operation/FullyConnected.h" +#include "graph/operation/Reshape.h" +#include "graph/operation/Softmax.h" +#include "graph/operation/NOP.h" + +struct IExecutionBuilder +{ + virtual ~IExecutionBuilder() = default; + + virtual void append(std::unique_ptr<::arm_compute::IFunction> &&f) = 0; +}; + +using Stage = std::function<void(IExecutionBuilder &)>; + +namespace neurun +{ +namespace backend +{ + +struct IStageGenerator +{ + virtual ~IStageGenerator() = default; + + virtual std::shared_ptr<ITensorBuilder> tensor_builder() = 0; + + virtual Stage generate(const graph::operation::Conv2D::Implicit::Node &node) = 0; + virtual Stage generate(const graph::operation::MaxPool2D::Implicit::Node &node) = 0; + virtual Stage generate(const graph::operation::AvgPool2D::Implicit::Node &node) = 0; + virtual Stage generate(const graph::operation::Concat::Node &node) = 0; + virtual Stage generate(const graph::operation::FullyConnected::Node &node) = 0; + virtual Stage generate(const graph::operation::Reshape::Node &node) = 0; + virtual Stage generate(const graph::operation::Softmax::Node &node) = 0; + virtual Stage generate(const graph::operation::NOP::Node &node) = 0; +}; + +} // namespace backend +} // namespace neurun + +#endif // __INTERNAL_ISTAGE_GENERATOR_H__ diff --git a/runtimes/neurun/src/backend/ITensorBuilder.h b/runtimes/neurun/src/backend/ITensorBuilder.h new file mode 100644 index 000000000..c3a07ffeb --- /dev/null +++ b/runtimes/neurun/src/backend/ITensorBuilder.h @@ -0,0 +1,57 @@ +/* + * 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 __INTERNAL_ITENSOR_BUILDER_H__ +#define __INTERNAL_ITENSOR_BUILDER_H__ + +#include <map> +#include <arm_compute/core/TensorInfo.h> + +#include "graph/operand/Index.h" +#include "codegen/Plan.h" + +namespace neurun +{ +namespace backend +{ + +struct ITensorBuilder +{ + virtual ~ITensorBuilder(void) = default; + virtual void mark(const ::neurun::graph::operand::Index &ind) = 0; + // TODO Add an interface for adding subsumption info + virtual void prepare(codegen::Plan &plan, + const std::map<int, ::arm_compute::TensorInfo> &tensor_info_ctx) = 0; + virtual void allocate(void) = 0; +}; + +} // namespace backend +} // namespace neurun + +#include <set> +#include <memory> + +namespace neurun +{ +namespace backend +{ + +using TensorBuilderSet = std::set<std::shared_ptr<backend::ITensorBuilder>>; + +} // namespace backend +} // namespace neurun + +#endif // __INTERNAL_ITENSOR_BUILDER_H__ diff --git a/runtimes/neurun/src/backend/acl_cl/BackendConfig.cc b/runtimes/neurun/src/backend/acl_cl/BackendConfig.cc new file mode 100644 index 000000000..6b3e6b3a3 --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/BackendConfig.cc @@ -0,0 +1,32 @@ +/* + * 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 <arm_compute/runtime/CL/CLScheduler.h> + +#include "backend/acl_cl/BackendConfig.h" + +namespace neurun +{ +namespace backend +{ +namespace acl_cl +{ + +void BackendConfig::initialize() { arm_compute::CLScheduler::get().default_init(); } + +} // namespace acl_cl +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/acl_cl/BackendConfig.h b/runtimes/neurun/src/backend/acl_cl/BackendConfig.h new file mode 100644 index 000000000..8eec9e795 --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/BackendConfig.h @@ -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. + */ + +#ifndef __NEURUN_BACKEND_ACL_CL_BACKEND_CONFIG_H__ +#define __NEURUN_BACKEND_ACL_CL_BACKEND_CONFIG_H__ + +#include "backend/IBackendConfig.h" + +namespace neurun +{ +namespace backend +{ +namespace acl_cl +{ + +class BackendConfig : public IBackendConfig +{ +public: + BackendConfig() + { + // DO NOTHING + } + + virtual void initialize() override; + virtual graph::operand::Layout getOperandLayout() { return graph::operand::Layout::NCHW; } +}; + +} // namespace acl_cl +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_ACL_CL_BACKEND_CONFIG_H__ diff --git a/runtimes/neurun/src/backend/acl_cl/CMakeLists.txt b/runtimes/neurun/src/backend/acl_cl/CMakeLists.txt new file mode 100644 index 000000000..d64c23a80 --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/CMakeLists.txt @@ -0,0 +1,17 @@ +file(GLOB_RECURSE SOURCES "*.cc") + +add_library(${LIB_NEURUN_BACKEND_ACL_CL} STATIC ${SOURCES}) + +target_include_directories(${LIB_NEURUN_BACKEND_ACL_CL} PUBLIC ${NNFW_INCLUDE_DIR}) +target_include_directories(${LIB_NEURUN_BACKEND_ACL_CL} PUBLIC ${NEURUN_INCLUDE_DIR}) +target_include_directories(${LIB_NEURUN_BACKEND_ACL_CL} PUBLIC ${CMAKE_SOURCE_DIR}/externals/tensorflow) # TODO Remove this file. We should not need this. + +target_link_libraries(${LIB_NEURUN_BACKEND_ACL_CL} arm_compute) +target_link_libraries(${LIB_NEURUN_BACKEND_ACL_CL} nnfw_support_nnapi) +target_link_libraries(${LIB_NEURUN_BACKEND_ACL_CL} ${LIB_NEURUN_KERNEL_ACL_CL}) + +target_compile_options(${LIB_NEURUN_BACKEND_ACL_CL} PRIVATE -Wall -Wextra -Werror) + +set_target_properties(${LIB_NEURUN_BACKEND_ACL_CL} PROPERTIES POSITION_INDEPENDENT_CODE ON) +set_target_properties(${LIB_NEURUN_BACKEND_ACL_CL} PROPERTIES OUTPUT_NAME backend_acl_cl) +install(TARGETS ${LIB_NEURUN_BACKEND_ACL_CL} DESTINATION lib/neurun) diff --git a/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.cc b/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.cc new file mode 100644 index 000000000..9a681b3de --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.cc @@ -0,0 +1,144 @@ +/* + * 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 "backend/acl_cl/InitializerGenerator.h" + +#include <arm_compute/core/Coordinates.h> + +#include "backend/acl_cl/kernel/View.h" +#include "internal/nnapi/kernel/Reader.h" +#include "util/kernel/IndexIterator.h" + +namespace neurun +{ +namespace backend +{ +namespace acl_cl +{ + +InitializerGenerator::InitializerGenerator(const neurun::graph::operand::Set &ctx) : _ctx(ctx) +{ + // DO NOTHING +} + +Initializer +InitializerGenerator::generateWeight(const graph::operation::Conv2D::Implicit::Node &node) +{ + const ::neurun::graph::operand::Index ker_index{node.getInputs().at(1)}; + + const auto ker_shape = _ctx.at(ker_index).shape().asKernel(); + auto ker_base = _ctx.at(ker_index).data().base(); + auto ker_size = _ctx.at(ker_index).data().size(); + + return [ker_shape, ker_base, ker_size](::arm_compute::ITensor &tensor) { + const ::internal::nnapi::kernel::Reader<float> from{ker_shape, ker_base, ker_size}; + ::internal::arm_compute::kernel::View<float> into{&tensor}; + + ::nnfw::util::kernel::iterate(ker_shape) + << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) { + const auto value = from.at(nth, ch, row, col); + into.at(nth, ch, row, col) = value; + }; + }; +} + +Initializer InitializerGenerator::generateWeight(const graph::operation::FullyConnected::Node &node) +{ + const ::neurun::graph::operand::Index weight_index{node.getInputs().at(1)}; + const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)}; + + const auto num_output = _ctx.at(weight_index).shape().dim(0); + auto weight_base = _ctx.at(weight_index).data().base(); + auto weight_size = _ctx.at(weight_index).data().size(); + + // NOTE We assume that input is a feature map + // TODO Remove this restriction! + const auto ifm_shape = _ctx.at(input_index).shape().asFeature(); + + return [num_output, ifm_shape, weight_base, weight_size](::arm_compute::ITensor &tensor) { + const ::nnfw::util::kernel::Shape ker_shape{num_output, ifm_shape.C, ifm_shape.H, ifm_shape.W}; + const ::internal::nnapi::kernel::Reader<float> from{ker_shape, weight_base, weight_size}; + + ::nnfw::util::kernel::iterate(ker_shape) + << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) { + const auto value = from.at(nth, ch, row, col); + + uint32_t offset = 0; + + // ARM Compute Library uses 'NCHW' ordering + offset += nth * ifm_shape.C * ifm_shape.H * ifm_shape.W; + offset += ch * ifm_shape.H * ifm_shape.W; + offset += row * ifm_shape.W; + offset += col; + + const ::arm_compute::Coordinates coordinate{offset}; + + auto into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate)); + + *into = value; + }; + }; +} + +Initializer InitializerGenerator::generateBias(const graph::operation::Conv2D::Implicit::Node &node) +{ + // TODO Refactor so we can reuse the common code + + const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)}; + + auto bias_base = _ctx.at(bias_index).data().base(); + const auto bias_size = _ctx.at(bias_index).shape().asVector(); + + return [bias_base, bias_size](::arm_compute::ITensor &tensor) { + for (int32_t n = 0; n < bias_size; ++n) + { + const ::arm_compute::Coordinates coordinate{n}; + + float *into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate)); + + const float *from = reinterpret_cast<const float *>(bias_base) + n; + const auto value = *from; + + *into = value; + } + }; +} + +Initializer InitializerGenerator::generateBias(const graph::operation::FullyConnected::Node &node) +{ + const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)}; + + auto bias_base = _ctx.at(bias_index).data().base(); + const auto bias_size = _ctx.at(bias_index).shape().asVector(); + + return [bias_base, bias_size](::arm_compute::ITensor &tensor) { + for (int32_t n = 0; n < bias_size; ++n) + { + const ::arm_compute::Coordinates coordinate{n}; + + float *into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate)); + + const float *from = reinterpret_cast<const float *>(bias_base) + n; + const auto value = *from; + + *into = value; + } + }; +} + +} // namespace acl_cl +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.h b/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.h new file mode 100644 index 000000000..78b7efb5e --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.h @@ -0,0 +1,50 @@ +/* + * 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 __NEURUN_BACKEND_ACL_CL_INITIALIZER_GENERATOR_H__ +#define __NEURUN_BACKEND_ACL_CL_INITIALIZER_GENERATOR_H__ + +#include "backend/IInitializerGenerator.h" + +#include "graph/operand/Set.h" + +namespace neurun +{ +namespace backend +{ +namespace acl_cl +{ + +class InitializerGenerator : public IInitializerGenerator +{ +public: + InitializerGenerator(const neurun::graph::operand::Set &ctx); + + Initializer generateWeight(const graph::operation::Conv2D::Implicit::Node &node) override; + Initializer generateWeight(const graph::operation::FullyConnected::Node &node) override; + + Initializer generateBias(const graph::operation::Conv2D::Implicit::Node &node) override; + Initializer generateBias(const graph::operation::FullyConnected::Node &node) override; + +private: + const neurun::graph::operand::Set &_ctx; +}; + +} // namespace acl_cl +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_ACL_CL_INITIALIZER_GENERATOR_H__ diff --git a/runtimes/neurun/src/backend/acl_cl/StageGenerator.cc b/runtimes/neurun/src/backend/acl_cl/StageGenerator.cc new file mode 100644 index 000000000..c63698fd8 --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/StageGenerator.cc @@ -0,0 +1,538 @@ +/* + * 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 "backend/acl_cl/StageGenerator.h" + +#include <arm_compute/runtime/CL/functions/CLConvolutionLayer.h> +#include <arm_compute/runtime/CL/functions/CLPoolingLayer.h> +#include <arm_compute/runtime/CL/functions/CLActivationLayer.h> +#include <arm_compute/runtime/CL/functions/CLReshapeLayer.h> +#include <arm_compute/runtime/CL/functions/CLFullyConnectedLayer.h> +#include <arm_compute/runtime/CL/functions/CLSoftmaxLayer.h> + +#include "kernel/acl_cl/ConcatLayer.h" + +#include "internal/Padding.h" + +#include "graph/operand/Index.h" + +#include "logging.h" + +#include "NeuralNetworks.h" + +#include "support/nnapi/Utils.h" + +template <typename T> std::unique_ptr<T> make_layer(void) { return std::unique_ptr<T>{new T}; } + +::arm_compute::PadStrideInfo asPadStringInfo(const ::internal::Padding &padding, + const ::internal::Stride &stride) +{ + return ::arm_compute::PadStrideInfo{stride.horizontal, + stride.vertical, + padding.left, + padding.right, + padding.top, + padding.bottom, + ::arm_compute::DimensionRoundingType::FLOOR}; +} + +namespace neurun +{ +namespace backend +{ +namespace acl_cl +{ + +// +// ActivationBuilder +// +class ActivationBuilder +{ +public: + ActivationBuilder(IExecutionBuilder &builder) : _builder(builder) + { + // DO NOTHING + } + +private: + void appendReLU(::arm_compute::ICLTensor *tensor); + +public: + void append(FuseCode code, ::arm_compute::ICLTensor *tensor); + +private: + IExecutionBuilder &_builder; +}; + +void ActivationBuilder::appendReLU(::arm_compute::ICLTensor *ifm_alloc) +{ + const ::arm_compute::ActivationLayerInfo act_info{ + ::arm_compute::ActivationLayerInfo::ActivationFunction::RELU}; + + auto fn = make_layer<::arm_compute::CLActivationLayer>(); + + fn->configure(ifm_alloc, nullptr, act_info); + + _builder.append(std::move(fn)); +} + +void ActivationBuilder::append(FuseCode code, ::arm_compute::ICLTensor *ifm_alloc) +{ + switch (code) + { + case ANEURALNETWORKS_FUSED_NONE: + { + // DO NOTHING + break; + } + case ANEURALNETWORKS_FUSED_RELU: + { + appendReLU(ifm_alloc); + break; + } + default: + { + throw std::runtime_error("Not supported, yet"); + } + } +} + +// +// StageGenerator +// +StageGenerator::StageGenerator(const neurun::graph::operand::Set &ctx, + const std::shared_ptr<TensorBuilder> &tensor_builder) + : _ctx(ctx), _tensor_builder(tensor_builder) +{ + // DO NOTHING +} + +Stage StageGenerator::generate(const graph::operation::Conv2D::Implicit::Node &node) +{ + const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)}; + const ::neurun::graph::operand::Index ker_index{node.getInputs().at(1)}; + const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)}; + + const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index}; + const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index}; + + const ::neurun::graph::operand::Index padding_index{node.param().padding_index}; + const ::neurun::graph::operand::Index activation_index{node.param().activation_index}; + + const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature(); + const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature(); + const auto ker_shape = _ctx.at(ker_index).shape().asKernel(); + + const PaddingCode padding_type = + static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>()); + + assert((ANEURALNETWORKS_PADDING_SAME == padding_type) || + (ANEURALNETWORKS_PADDING_VALID == padding_type)); + + ::internal::Stride stride; + + stride.vertical = _ctx.at(vstride_index).asScalar<int32_t>(); + stride.horizontal = _ctx.at(hstride_index).asScalar<int32_t>(); + + // Construct operation parameters + struct Param + { + int ofm_index; + int ifm_index; + int ker_index; + int bias_index; + + ::internal::Padding padding; + ::internal::Stride stride; + + FuseCode activation; + }; + + Param param; + + param.ofm_index = ofm_index.asInt(); + param.ifm_index = ifm_index.asInt(); + param.ker_index = ker_index.asInt(); + param.bias_index = bias_index.asInt(); + + param.stride = stride; + param.padding = + (padding_type == ANEURALNETWORKS_PADDING_SAME) + ? ::internal::same_padding(ifm_shape, ofm_shape, stride, ker_shape.W, ker_shape.H) + : ::internal::valid_padding(); + + param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>()); + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}).get(); + auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}).get(); + auto ker_alloc = tensors->at(::neurun::graph::operand::Index{param.ker_index}).get(); + auto bias_alloc = tensors->at(::neurun::graph::operand::Index{param.bias_index}).get(); + + const auto conv_info = asPadStringInfo(param.padding, param.stride); + + std::unique_ptr<::arm_compute::CLConvolutionLayer> fn{new ::arm_compute::CLConvolutionLayer}; + + fn->configure(ifm_alloc, ker_alloc, bias_alloc, ofm_alloc, conv_info); + + builder.append(std::move(fn)); + + ActivationBuilder{builder}.append(param.activation, ofm_alloc); + }; +} + +Stage StageGenerator::generate(const graph::operation::MaxPool2D::Implicit::Node &node) +{ + const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)}; + + const ::neurun::graph::operand::Index kh_index{node.param().kh_index}; + const ::neurun::graph::operand::Index kw_index{node.param().kw_index}; + + const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index}; + const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index}; + + const ::neurun::graph::operand::Index padding_index{node.param().padding_index}; + + const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature(); + const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature(); + + const int32_t kh = _ctx.at(kh_index).asScalar<int32_t>(); + const int32_t kw = _ctx.at(kw_index).asScalar<int32_t>(); + + const int32_t vstride = _ctx.at(vstride_index).asScalar<int32_t>(); + const int32_t hstride = _ctx.at(hstride_index).asScalar<int32_t>(); + + const PaddingCode padding_type = + static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>()); + + // Construct operation parameters + struct Param + { + int ofm_index; + int ifm_index; + + uint32_t kw; + uint32_t kh; + + ::internal::Padding padding; + ::internal::Stride stride; + + // TODO Add 'activation' field + }; + + Param param; + + param.ofm_index = ofm_index.asInt(); + param.ifm_index = ifm_index.asInt(); + + param.kh = kh; + param.kw = kw; + + param.stride.vertical = vstride; + param.stride.horizontal = hstride; + + param.padding = (padding_type == ANEURALNETWORKS_PADDING_SAME) + ? ::internal::same_padding(ifm_shape, ofm_shape, param.stride, kw, kh) + : ::internal::valid_padding(); + + VERBOSE(MaxPool2D) << "IFM_H: " << ifm_shape.H << std::endl; + VERBOSE(MaxPool2D) << "IFM_W: " << ifm_shape.W << std::endl; + VERBOSE(MaxPool2D) << "OFM_H: " << ofm_shape.H << std::endl; + VERBOSE(MaxPool2D) << "OFM_W: " << ofm_shape.W << std::endl; + VERBOSE(MaxPool2D) << "KER_H: " << kh << std::endl; + VERBOSE(MaxPool2D) << "KER_W: " << kw << std::endl; + VERBOSE(MaxPool2D) << "STRIDE_H: " << vstride << std::endl; + VERBOSE(MaxPool2D) << "STRIDE_W: " << hstride << std::endl; + VERBOSE(MaxPool2D) << "PAD(T): " << param.padding.top << std::endl; + VERBOSE(MaxPool2D) << "PAD(B): " << param.padding.bottom << std::endl; + VERBOSE(MaxPool2D) << "PAD(L): " << param.padding.left << std::endl; + VERBOSE(MaxPool2D) << "PAD(R): " << param.padding.right << std::endl; + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}).get(); + auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}).get(); + + ::arm_compute::PoolingLayerInfo info{::arm_compute::PoolingType::MAX, + ::arm_compute::Size2D{param.kw, param.kh}, + asPadStringInfo(param.padding, param.stride)}; + + std::unique_ptr<::arm_compute::CLPoolingLayer> fn{new ::arm_compute::CLPoolingLayer}; + + fn->configure(ifm_alloc, ofm_alloc, info); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::AvgPool2D::Implicit::Node &node) +{ + const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)}; + + const ::neurun::graph::operand::Index kh_index{node.param().kh_index}; + const ::neurun::graph::operand::Index kw_index{node.param().kw_index}; + + const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index}; + const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index}; + + const ::neurun::graph::operand::Index padding_index{node.param().padding_index}; + + const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature(); + const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature(); + + const int32_t kh = _ctx.at(kh_index).asScalar<int32_t>(); + const int32_t kw = _ctx.at(kw_index).asScalar<int32_t>(); + + const int32_t vstride = _ctx.at(vstride_index).asScalar<int32_t>(); + const int32_t hstride = _ctx.at(hstride_index).asScalar<int32_t>(); + + const PaddingCode padding_type = + static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>()); + + assert((ANEURALNETWORKS_PADDING_SAME == padding_type) || + (ANEURALNETWORKS_PADDING_VALID == padding_type)); + + // Construct operation parameters + struct Param + { + int ofm_index; + int ifm_index; + + uint32_t kw; + uint32_t kh; + + ::internal::Padding padding; + ::internal::Stride stride; + + // TODO Add 'activation' field + }; + + Param param; + + param.ofm_index = ofm_index.asInt(); + param.ifm_index = ifm_index.asInt(); + + param.kh = kh; + param.kw = kw; + + param.stride.vertical = vstride; + param.stride.horizontal = hstride; + + param.padding = (padding_type == ANEURALNETWORKS_PADDING_SAME) + ? ::internal::same_padding(ifm_shape, ofm_shape, param.stride, kw, kh) + : ::internal::valid_padding(); + + VERBOSE(AvgPool2D) << "IFM_H: " << ifm_shape.H << std::endl; + VERBOSE(AvgPool2D) << "IFM_W: " << ifm_shape.W << std::endl; + VERBOSE(AvgPool2D) << "OFM_H: " << ofm_shape.H << std::endl; + VERBOSE(AvgPool2D) << "OFM_W: " << ofm_shape.W << std::endl; + VERBOSE(AvgPool2D) << "KER_H: " << kh << std::endl; + VERBOSE(AvgPool2D) << "KER_W: " << kw << std::endl; + VERBOSE(AvgPool2D) << "STRIDE_H: " << vstride << std::endl; + VERBOSE(AvgPool2D) << "STRIDE_W: " << hstride << std::endl; + VERBOSE(AvgPool2D) << "PAD: " << ::nnfw::support::nnapi::to_string(padding_type) << std::endl; + VERBOSE(AvgPool2D) << "PAD(T): " << param.padding.top << std::endl; + VERBOSE(AvgPool2D) << "PAD(B): " << param.padding.bottom << std::endl; + VERBOSE(AvgPool2D) << "PAD(L): " << param.padding.left << std::endl; + VERBOSE(AvgPool2D) << "PAD(R): " << param.padding.right << std::endl; + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}).get(); + auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}).get(); + + ::arm_compute::PoolingLayerInfo info{ + ::arm_compute::PoolingType::AVG, ::arm_compute::Size2D{param.kw, param.kh}, + asPadStringInfo(param.padding, param.stride), true /* exclude_padding */}; + + std::unique_ptr<::arm_compute::CLPoolingLayer> fn{new ::arm_compute::CLPoolingLayer}; + + fn->configure(ifm_alloc, ofm_alloc, info); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::Concat::Node &node) +{ + const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index axis_index{node.param().axis_index}; + + struct Param + { + int32_t output_index; + std::vector<int32_t> input_indexes; + + int32_t axis; + }; + + Param param; + + param.output_index = ofm_index.asInt(); + for (const auto &e : node.getInputs()) + { + param.input_indexes.emplace_back(e.asInt()); + } + param.axis = _ctx.at(axis_index).asScalar<int32_t>(); + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get(); + + std::vector<::arm_compute::ICLTensor *> input_allocs; + for (auto ifm_ind : param.input_indexes) + { + input_allocs.emplace_back(tensors->at(::neurun::graph::operand::Index{ifm_ind}).get()); + } + + std::unique_ptr<::neurun::kernel::acl_cl::ConcatLayer> fn{ + new ::neurun::kernel::acl_cl::ConcatLayer}; + + fn->configure(input_allocs, param.axis, output_alloc); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::FullyConnected::Node &node) +{ + const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)}; + const ::neurun::graph::operand::Index weight_index{node.getInputs().at(1)}; + const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)}; + const ::neurun::graph::operand::Index activation_index{node.param().activation_index}; + + // Construct operation parameters + struct Param + { + int output_index; + + int input_index; + int weight_index; + int bias_index; + + FuseCode activation; + }; + + Param param; + + param.output_index = output_index.asInt(); + param.input_index = input_index.asInt(); + param.weight_index = weight_index.asInt(); + param.bias_index = bias_index.asInt(); + + param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>()); + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get(); + auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get(); + auto weight_alloc = tensors->at(::neurun::graph::operand::Index{param.weight_index}).get(); + auto bias_alloc = tensors->at(::neurun::graph::operand::Index{param.bias_index}).get(); + + auto fn = make_layer<::arm_compute::CLFullyConnectedLayer>(); + + fn->configure(input_alloc, weight_alloc, bias_alloc, output_alloc); + + builder.append(std::move(fn)); + + ActivationBuilder{builder}.append(param.activation, output_alloc); + }; +} + +Stage StageGenerator::generate(const graph::operation::Reshape::Node &node) +{ + const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)}; + + struct Param + { + int output_index; + int input_index; + }; + + Param param; + + param.output_index = output_index.asInt(); + param.input_index = input_index.asInt(); + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get(); + auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get(); + + auto fn = make_layer<::arm_compute::CLReshapeLayer>(); + + fn->configure(input_alloc, output_alloc); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::Softmax::Node &node) +{ + const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)}; + const ::neurun::graph::operand::Index scale_index{node.param().scale_index}; + + assert(_ctx.at(scale_index).shape().rank() == 0); + + struct Param + { + int output_index; + int input_index; + float scale; + }; + + Param param; + + param.output_index = output_index.asInt(); + param.input_index = input_index.asInt(); + param.scale = _ctx.at(scale_index).asScalar<float>(); + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get(); + auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get(); + + auto fn = make_layer<::arm_compute::CLSoftmaxLayer>(); + + fn->configure(input_alloc, output_alloc, param.scale); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::NOP::Node & /* node */) +{ + // DO NOTHING + return nullptr; +} + +} // namespace acl_cl +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/acl_cl/StageGenerator.h b/runtimes/neurun/src/backend/acl_cl/StageGenerator.h new file mode 100644 index 000000000..921604649 --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/StageGenerator.h @@ -0,0 +1,58 @@ +/* + * 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 __NEURUN_BACKEND_ACL_CL_STAGE_GENERATOR_H__ +#define __NEURUN_BACKEND_ACL_CL_STAGE_GENERATOR_H__ + +#include "backend/IStageGenerator.h" + +#include "graph/operand/Set.h" +#include "backend/acl_cl/TensorBuilder.h" + +namespace neurun +{ +namespace backend +{ +namespace acl_cl +{ + +class StageGenerator : public IStageGenerator +{ +public: + StageGenerator(const neurun::graph::operand::Set &ctx, + const std::shared_ptr<TensorBuilder> &tensor_builder); + + virtual std::shared_ptr<ITensorBuilder> tensor_builder() override { return _tensor_builder; } + + virtual Stage generate(const graph::operation::Conv2D::Implicit::Node &node) override; + virtual Stage generate(const graph::operation::MaxPool2D::Implicit::Node &node) override; + virtual Stage generate(const graph::operation::AvgPool2D::Implicit::Node &node) override; + virtual Stage generate(const graph::operation::Concat::Node &node) override; + virtual Stage generate(const graph::operation::FullyConnected::Node &node) override; + virtual Stage generate(const graph::operation::Reshape::Node &node) override; + virtual Stage generate(const graph::operation::Softmax::Node &node) override; + virtual Stage generate(const graph::operation::NOP::Node &node) override; + +private: + const neurun::graph::operand::Set &_ctx; + std::shared_ptr<TensorBuilder> _tensor_builder; +}; + +} // namespace acl_cl +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_ACL_CL_STAGE_GENERATOR_H__ diff --git a/runtimes/neurun/src/backend/acl_cl/TensorBuilder.cc b/runtimes/neurun/src/backend/acl_cl/TensorBuilder.cc new file mode 100644 index 000000000..05943c26a --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/TensorBuilder.cc @@ -0,0 +1,79 @@ +/* + * 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 "backend/acl_cl/TensorBuilder.h" + +#include <cassert> + +#include "operand/Object.h" + +namespace neurun +{ +namespace backend +{ +namespace acl_cl +{ + +TensorBuilder::TensorBuilder() +{ + // DO NOTHING +} + +void TensorBuilder::mark(const ::neurun::graph::operand::Index &ind) +{ + assert(_tensors.size() == 0); + + _inds.insert(ind); +} + +void TensorBuilder::prepare(codegen::Plan &plan, + const std::map<int, ::arm_compute::TensorInfo> &tensor_info_ctx) +{ + assert(_tensors.size() == 0); + + // TODO Handle SubTensor(subsumption) + // Currently this TensorBuilder does not have subsumption info yet + + for (auto ind_int : _inds) + { + ::neurun::graph::operand::Index ind{ind_int}; + auto tensor = std::make_shared<::arm_compute::CLTensor>(); + tensor->allocator()->init(tensor_info_ctx.at(ind.asInt())); + plan.operands().set(ind, std::make_shared<operand::Object>(tensor)); + _tensors[ind] = tensor; + } +} + +void TensorBuilder::allocate(void) +{ + assert(_inds.size() == _tensors.size()); + + for (const auto &tensor_entry : _tensors) + { + auto tensor = tensor_entry.second; + tensor->allocator()->allocate(); + } +} + +std::shared_ptr<::arm_compute::CLTensor> +TensorBuilder::at(const ::neurun::graph::operand::Index &ind) +{ + return _tensors.at(ind); +} + +} // namespace acl_cl +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/acl_cl/TensorBuilder.h b/runtimes/neurun/src/backend/acl_cl/TensorBuilder.h new file mode 100644 index 000000000..0a0f4e9ca --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/TensorBuilder.h @@ -0,0 +1,57 @@ +/* + * 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 __NEURUN_BACKEND_ACL_CL_TENSOR_BUILDER_H__ +#define __NEURUN_BACKEND_ACL_CL_TENSOR_BUILDER_H__ + +#include "backend/ITensorBuilder.h" + +#include <unordered_map> +#include <unordered_set> + +#include <arm_compute/runtime/CL/CLTensor.h> + +namespace neurun +{ +namespace backend +{ +namespace acl_cl +{ + +class Plan; + +class TensorBuilder : public ITensorBuilder +{ +public: + TensorBuilder(); + + virtual void mark(const ::neurun::graph::operand::Index &ind) override; + virtual void prepare(codegen::Plan &plan, + const std::map<int, ::arm_compute::TensorInfo> &tensor_info_ctx) override; + virtual void allocate(void) override; + + std::shared_ptr<::arm_compute::CLTensor> at(const ::neurun::graph::operand::Index &ind); + +private: + std::unordered_set<graph::operand::Index> _inds; + std::unordered_map<graph::operand::Index, std::shared_ptr<::arm_compute::CLTensor>> _tensors; +}; + +} // namespace acl_cl +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_ACL_CL_TENSOR_BUILDER_H__ diff --git a/runtimes/neurun/src/backend/acl_cl/feature/View.h b/runtimes/neurun/src/backend/acl_cl/feature/View.h new file mode 100644 index 000000000..12025ce01 --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/feature/View.h @@ -0,0 +1,110 @@ +/* + * 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 __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__ +#define __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__ + +#include "util/feature/Reader.h" + +#include <arm_compute/core/ITensor.h> + +#include <cassert> + +namespace internal +{ +namespace arm_compute +{ +namespace feature +{ + +template <typename T> class View; + +template <> class View<float> final : public nnfw::util::feature::Reader<float> +{ +public: + View(::arm_compute::ITensor *tensor) : _tensor{tensor} + { + assert(tensor->info()->data_type() == ::arm_compute::DataType::F32); + + // TODO Validate whether tensor is a feature map, or not + + _shape.C = tensor->info()->dimension(2); + _shape.H = tensor->info()->dimension(1); + _shape.W = tensor->info()->dimension(0); + } + +public: + const ::nnfw::util::feature::Shape &shape(void) const { return _shape; } + +public: + float at(uint32_t ch, uint32_t row, uint32_t col) const override + { + const auto offset = feature_index_to_byte_offset(ch, row, col); + + float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset); + + return *ptr; + } + float at(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) const override + { + const auto offset = feature_index_to_byte_offset(batch, ch, row, col); + + float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset); + + return *ptr; + } + +public: + float &at(uint32_t ch, uint32_t row, uint32_t col) + { + const auto offset = feature_index_to_byte_offset(ch, row, col); + + float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset); + + return *ptr; + } + float &at(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) + { + const auto offset = feature_index_to_byte_offset(batch, ch, row, col); + + float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset); + + return *ptr; + } + +private: + size_t feature_index_to_byte_offset(uint32_t ch, uint32_t row, uint32_t col) const + { + // ARM Compute uses CHW ordering + return _tensor->info()->offset_element_in_bytes(::arm_compute::Coordinates{col, row, ch}); + } + size_t feature_index_to_byte_offset(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) const + { + // ARM Compute uses CHW ordering + return _tensor->info()->offset_element_in_bytes( + ::arm_compute::Coordinates{col, row, ch, batch}); + } + +private: + ::nnfw::util::feature::Shape _shape; + ::arm_compute::ITensor *_tensor; +}; + +} // namespace feature +} // namespace arm_compute +} // namespace internal + +#endif // __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__ diff --git a/runtimes/neurun/src/backend/acl_cl/kernel/View.h b/runtimes/neurun/src/backend/acl_cl/kernel/View.h new file mode 100644 index 000000000..aec9a8892 --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/kernel/View.h @@ -0,0 +1,87 @@ +/* + * 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 __INTERNAL_ARM_COMPUTE_KERNEL_VIEW_H__ +#define __INTERNAL_ARM_COMPUTE_KERNEL_VIEW_H__ + +#include "util/kernel/Shape.h" +#include "util/kernel/Reader.h" + +#include <arm_compute/core/ITensor.h> + +#include <cassert> + +namespace internal +{ +namespace arm_compute +{ +namespace kernel +{ + +template <typename T> class View; + +template <> class View<float> final : public nnfw::util::kernel::Reader<float> +{ +public: + View(::arm_compute::ITensor *tensor) : _tensor{tensor} + { + assert(tensor->info()->data_type() == ::arm_compute::DataType::F32); + + _shape.N = tensor->info()->dimension(3); + _shape.C = tensor->info()->dimension(2); + _shape.H = tensor->info()->dimension(1); + _shape.W = tensor->info()->dimension(0); + } + +public: + const ::nnfw::util::kernel::Shape &shape(void) const { return _shape; } + +public: + float at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const override + { + const auto offset = kernel_index_to_byte_offset(nth, ch, row, col); + + float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset); + + return *ptr; + } + +public: + float &at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) + { + const auto offset = kernel_index_to_byte_offset(nth, ch, row, col); + + float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset); + + return *ptr; + } + +private: + size_t kernel_index_to_byte_offset(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const + { + return _tensor->info()->offset_element_in_bytes(::arm_compute::Coordinates{col, row, ch, nth}); + } + +private: + ::nnfw::util::kernel::Shape _shape; + ::arm_compute::ITensor *_tensor; +}; + +} // namespace kernel +} // namespace arm_compute +} // namespace internal + +#endif // __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__ diff --git a/runtimes/neurun/src/backend/acl_cl/operand/Object.cc b/runtimes/neurun/src/backend/acl_cl/operand/Object.cc new file mode 100644 index 000000000..98b96a11a --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/operand/Object.cc @@ -0,0 +1,42 @@ +/* + * 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 "Object.h" + +#include <arm_compute/runtime/CL/CLScheduler.h> + +namespace neurun +{ +namespace backend +{ +namespace acl_cl +{ +namespace operand +{ + +void Object::access(const std::function<void(::arm_compute::ITensor &tensor)> &fn) const +{ + auto &queue = ::arm_compute::CLScheduler::get().queue(); + + _tensor->map(queue); + fn(*_tensor); + _tensor->unmap(queue); +} + +} // namespace operand +} // namespace acl_cl +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/acl_cl/operand/Object.h b/runtimes/neurun/src/backend/acl_cl/operand/Object.h new file mode 100644 index 000000000..da33c0549 --- /dev/null +++ b/runtimes/neurun/src/backend/acl_cl/operand/Object.h @@ -0,0 +1,60 @@ +/* + * 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 __NEURUN_BACKEND_ACL_CL_OPERAND_OBJECT_H__ +#define __NEURUN_BACKEND_ACL_CL_OPERAND_OBJECT_H__ + +#include <memory> +#include <arm_compute/core/CL/ICLTensor.h> + +#include "backend/IObject.h" + +namespace neurun +{ +namespace backend +{ +namespace acl_cl +{ +namespace operand +{ + +class Object : public backend::operand::IObject +{ +public: + Object() = default; + +public: + Object(const std::shared_ptr<::arm_compute::ICLTensor> &tensor) : _tensor{tensor} + { + // DO NOTHING + } + +public: + ::arm_compute::ICLTensor *ptr(void) const override { return _tensor.get(); } + +private: + std::shared_ptr<::arm_compute::ICLTensor> _tensor; + +public: + void access(const std::function<void(::arm_compute::ITensor &tensor)> &fn) const override; +}; + +} // namespace operand +} // namespace acl_cl +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_ACL_CL_OPERAND_OBJECT_H__ diff --git a/runtimes/neurun/src/backend/cpu/BackendConfig.cc b/runtimes/neurun/src/backend/cpu/BackendConfig.cc new file mode 100644 index 000000000..34fc3491a --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/BackendConfig.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 "backend/cpu/BackendConfig.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ + +void BackendConfig::initialize() +{ + // DO NOTHING +} + +} // namespace cpu +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/cpu/BackendConfig.h b/runtimes/neurun/src/backend/cpu/BackendConfig.h new file mode 100644 index 000000000..109235bb1 --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/BackendConfig.h @@ -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. + */ + +#ifndef __NEURUN_BACKEND_CPU_BACKEND_CONFIG_H__ +#define __NEURUN_BACKEND_CPU_BACKEND_CONFIG_H__ + +#include "backend/IBackendConfig.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ + +class BackendConfig : public IBackendConfig +{ +public: + BackendConfig() + { + // DO NOTHING + } + + virtual void initialize() override; + virtual graph::operand::Layout getOperandLayout() { return graph::operand::Layout::NHWC; } +}; + +} // namespace cpu +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_CPU_BACKEND_CONFIG_H__ diff --git a/runtimes/neurun/src/backend/cpu/CMakeLists.txt b/runtimes/neurun/src/backend/cpu/CMakeLists.txt new file mode 100644 index 000000000..95e9af687 --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/CMakeLists.txt @@ -0,0 +1,19 @@ +file(GLOB_RECURSE SOURCES "*.cc") + +add_library(${LIB_NEURUN_BACKEND_CPU} STATIC ${SOURCES}) + +target_include_directories(${LIB_NEURUN_BACKEND_CPU} PUBLIC ${NNFW_INCLUDE_DIR}) +target_include_directories(${LIB_NEURUN_BACKEND_CPU} PUBLIC ${NEURUN_INCLUDE_DIR}) +target_include_directories(${LIB_NEURUN_BACKEND_CPU} PUBLIC ${CMAKE_SOURCE_DIR}/externals/tensorflow) + +target_link_libraries(${LIB_NEURUN_BACKEND_CPU} arm_compute) # TODO We should not need this +target_link_libraries(${LIB_NEURUN_BACKEND_CPU} tensorflow-lite) +target_link_libraries(${LIB_NEURUN_BACKEND_CPU} nnfw_util) +target_link_libraries(${LIB_NEURUN_BACKEND_CPU} nnfw_support_nnapi) +target_link_libraries(${LIB_NEURUN_BACKEND_CPU} ${LIB_NEURUN_KERNEL_CPU}) + +target_compile_options(${LIB_NEURUN_BACKEND_CPU} PRIVATE -Wall -Wextra -Werror) + +set_target_properties(${LIB_NEURUN_BACKEND_CPU} PROPERTIES POSITION_INDEPENDENT_CODE ON) +set_target_properties(${LIB_NEURUN_BACKEND_CPU} PROPERTIES OUTPUT_NAME backend_cpu) +install(TARGETS ${LIB_NEURUN_BACKEND_CPU} DESTINATION lib/neurun) diff --git a/runtimes/neurun/src/backend/cpu/InitializerGenerator.cc b/runtimes/neurun/src/backend/cpu/InitializerGenerator.cc new file mode 100644 index 000000000..7b08c7131 --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/InitializerGenerator.cc @@ -0,0 +1,208 @@ +/* + * 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 "InitializerGenerator.h" + +#include "internal/nnapi/kernel/Reader.h" +#include "internal/nnapi/kernel/View.h" +#include "util/kernel/IndexIterator.h" + +#include "NeuralNetworks.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ + +InitializerGenerator::InitializerGenerator(const neurun::graph::operand::Set &ctx) : _ctx(ctx) +{ + // DO NOTHING +} + +Initializer +InitializerGenerator::generateWeight(const graph::operation::Conv2D::Implicit::Node &node) +{ + const ::neurun::graph::operand::Index ker_index{node.getInputs().at(1)}; + + const auto ker_shape = _ctx.at(ker_index).shape().asKernel(); + auto ker_base = _ctx.at(ker_index).data().base(); + auto ker_size = _ctx.at(ker_index).data().size(); + + return [ker_shape, ker_base, ker_size](::arm_compute::ITensor &tensor) { + const ::internal::nnapi::kernel::Reader<float> from{ker_shape, ker_base, ker_size}; + ::internal::nnapi::kernel::View<float> into{&tensor}; + + ::nnfw::util::kernel::iterate(ker_shape) + << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) { + const auto value = from.at(nth, ch, row, col); + into.at(nth, row, col, ch) = value; + }; + }; +} + +Initializer InitializerGenerator::generateWeight(const graph::operation::FullyConnected::Node &node) +{ + const ::neurun::graph::operand::Index weight_index{node.getInputs().at(1)}; + const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)}; + + const auto num_output = _ctx.at(weight_index).shape().dim(0); + auto weight_base = _ctx.at(weight_index).data().base(); + auto weight_size = _ctx.at(weight_index).data().size(); + auto weight_type = _ctx.at(weight_index).typeInfo().type(); + + // NOTE We assume that input is a feature map + // TODO Remove this restriction! + const auto ifm_shape = _ctx.at(input_index).shape().asFeature(); + + switch (weight_type) + { + case ::neurun::graph::operand::DataType::TENSOR_FLOAT32: + { + return [num_output, ifm_shape, weight_base, weight_size](::arm_compute::ITensor &tensor) { + const ::nnfw::util::kernel::Shape ker_shape{num_output, ifm_shape.C, ifm_shape.H, + ifm_shape.W}; + const ::internal::nnapi::kernel::Reader<float> from{ker_shape, weight_base, weight_size}; + + ::nnfw::util::kernel::iterate(ker_shape) + << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) { + const auto value = from.at(nth, ch, row, col); + + uint32_t offset = 0; + + // NNAPI uses NHWC ordering + offset += nth * ifm_shape.H * ifm_shape.W * ifm_shape.C; + offset += row * ifm_shape.W * ifm_shape.C; + offset += col * ifm_shape.C; + offset += ch; + + const ::arm_compute::Coordinates coordinate{offset}; + + auto into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate)); + + *into = value; + }; + }; + } + case ::neurun::graph::operand::DataType::TENSOR_QUANT8_ASYMM: + { + return [num_output, ifm_shape, weight_base, weight_size](::arm_compute::ITensor &tensor) { + const ::nnfw::util::kernel::Shape ker_shape{num_output, ifm_shape.C, ifm_shape.H, + ifm_shape.W}; + const ::internal::nnapi::kernel::Reader<uint8_t> from{ker_shape, weight_base, weight_size}; + ::nnfw::util::kernel::iterate(ker_shape) + << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) { + const auto value = from.at(nth, ch, row, col); + uint32_t offset = 0; + + // NNAPI uses NHWC ordering + offset += nth * ifm_shape.H * ifm_shape.W * ifm_shape.C; + offset += row * ifm_shape.W * ifm_shape.C; + offset += col * ifm_shape.C; + offset += ch; + + const ::arm_compute::Coordinates coordinate{offset}; + + auto into = reinterpret_cast<uint8_t *>(tensor.ptr_to_element(coordinate)); + + *into = value; + }; + }; + } + default: + { + throw std::runtime_error("Not supported weight type"); + } + } +} + +Initializer InitializerGenerator::generateBias(const graph::operation::Conv2D::Implicit::Node &node) +{ + // TODO Refactor so we can reuse the common code + + const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)}; + + auto bias_base = _ctx.at(bias_index).data().base(); + const auto bias_size = _ctx.at(bias_index).shape().asVector(); + + return [bias_base, bias_size](::arm_compute::ITensor &tensor) { + for (int32_t n = 0; n < bias_size; ++n) + { + const ::arm_compute::Coordinates coordinate{n}; + + float *into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate)); + + const float *from = reinterpret_cast<const float *>(bias_base) + n; + const auto value = *from; + + *into = value; + } + }; +} + +Initializer InitializerGenerator::generateBias(const graph::operation::FullyConnected::Node &node) +{ + const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)}; + + auto bias_base = _ctx.at(bias_index).data().base(); + auto bias_type = _ctx.at(bias_index).typeInfo().type(); + const auto bias_size = _ctx.at(bias_index).shape().asVector(); + + switch (bias_type) + { + case ::neurun::graph::operand::DataType::TENSOR_FLOAT32: + { + return [bias_base, bias_size](::arm_compute::ITensor &tensor) { + for (int32_t n = 0; n < bias_size; ++n) + { + const ::arm_compute::Coordinates coordinate{n}; + + float *into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate)); + + const float *from = reinterpret_cast<const float *>(bias_base) + n; + const auto value = *from; + + *into = value; + } + }; + } + case ::neurun::graph::operand::DataType::TENSOR_QUANT8_ASYMM: + { + return [bias_base, bias_size](::arm_compute::ITensor &tensor) { + for (int32_t n = 0; n < bias_size; ++n) + { + const ::arm_compute::Coordinates coordinate{n}; + + uint8_t *into = reinterpret_cast<uint8_t *>(tensor.ptr_to_element(coordinate)); + + const uint8_t *from = reinterpret_cast<const uint8_t *>(bias_base) + n; + const auto value = *from; + + *into = value; + } + }; + } + default: + { + throw std::runtime_error("Not supported bias type"); + } + } +} + +} // namespace cpu +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/cpu/InitializerGenerator.h b/runtimes/neurun/src/backend/cpu/InitializerGenerator.h new file mode 100644 index 000000000..42d37f48b --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/InitializerGenerator.h @@ -0,0 +1,50 @@ +/* + * 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 __NEURUN_BACKEND_CPU_INITIALIZER_GENERATOR_H__ +#define __NEURUN_BACKEND_CPU_INITIALIZER_GENERATOR_H__ + +#include "backend/IInitializerGenerator.h" + +#include "graph/operand/Set.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ + +class InitializerGenerator : public IInitializerGenerator +{ +public: + InitializerGenerator(const neurun::graph::operand::Set &ctx); + + Initializer generateWeight(const graph::operation::Conv2D::Implicit::Node &node) override; + Initializer generateWeight(const graph::operation::FullyConnected::Node &node) override; + + Initializer generateBias(const graph::operation::Conv2D::Implicit::Node &node) override; + Initializer generateBias(const graph::operation::FullyConnected::Node &node) override; + +private: + const neurun::graph::operand::Set &_ctx; +}; + +} // namespace cpu +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_CPU_INITIALIZER_GENERATOR_H__ diff --git a/runtimes/neurun/src/backend/cpu/MemoryAllocator.cc b/runtimes/neurun/src/backend/cpu/MemoryAllocator.cc new file mode 100644 index 000000000..13d2a7ffc --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/MemoryAllocator.cc @@ -0,0 +1,17 @@ +/* + * 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 "internal/cpu/MemoryAllocator.h" diff --git a/runtimes/neurun/src/backend/cpu/MemoryAllocator.h b/runtimes/neurun/src/backend/cpu/MemoryAllocator.h new file mode 100644 index 000000000..e3550ac07 --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/MemoryAllocator.h @@ -0,0 +1,123 @@ +/* + * 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 __INTERNAL_CPU_MEMORY_ALLOCATOR_H__ +#define __INTERNAL_CPU_MEMORY_ALLOCATOR_H__ + +#include "arm_compute/runtime/ITensorAllocator.h" +#include "arm_compute/runtime/Memory.h" + +#include <cstdint> +#include <memory> +#include <vector> + +namespace arm_compute +{ +class Coordinates; +class TensorInfo; +class Tensor; +}; + +/** Basic implementation of a CPU memory tensor allocator. */ +class TensorAllocator : public ITensorAllocator +{ +public: + /** Default constructor. */ + TensorAllocator(Tensor *owner = nullptr); + /** Default destructor */ + ~TensorAllocator(); + + /** Make ITensorAllocator's init methods available */ + using ITensorAllocator::init; + + /** Shares the same backing memory with another tensor allocator, while the tensor info might be + * different. + * In other words this can be used to create a sub-tensor from another tensor while sharing the + * same memory. + * + * @note TensorAllocator have to be of the same specialized type. + * + * @param[in] allocator The allocator that owns the backing memory to be shared. Ownership becomes + * shared afterwards. + * @param[in] coords The starting coordinates of the new tensor inside the parent tensor. + * @param[in] sub_info The new tensor information (e.g. shape etc) + */ + void init(const TensorAllocator &allocator, const Coordinates &coords, TensorInfo sub_info); + + /** Returns the pointer to the allocated data. */ + uint8_t *data() const; + + /** Allocate size specified by TensorInfo of CPU memory. + * + * @note The tensor must not already be allocated when calling this function. + * + */ + void allocate() override; + + /** Free allocated CPU memory. + * + * @note The tensor must have been allocated when calling this function. + * + */ + void free() override; + /** Import an existing memory as a tensor's backing memory + * + * @warning If the tensor is flagged to be managed by a memory manager, + * this call will lead to an error. + * @warning Ownership of memory depends on the way the @ref Memory object was constructed + * @note Calling free on a tensor with imported memory will just clear + * the internal pointer value. + * + * @param[in] memory Memory to import + * + * @return error status + */ + arm_compute::Status import_memory(Memory memory); + /** Associates the tensor with a memory group + * + * @param[in] associated_memory_group Memory group to associate the tensor with + */ + void set_associated_memory_group(MemoryGroup *associated_memory_group); + +protected: + /** No-op for CPU memory + * + * @return A pointer to the beginning of the tensor's allocation. + */ + uint8_t *lock() override; + + /** No-op for CPU memory. */ + void unlock() override; + +private: + MemoryGroup *_associated_memory_group; /**< Registered memory manager */ + Memory _memory; /**< CPU memory */ + Tensor *_owner; /**< Owner of the allocator */ +}; + +namespace internal +{ +namespace cpu +{ + +class MemoryAllocator : public +{ +}; + +} // namespace cpu +} // namespace internal + +#endif // __INTERNAL_CPU_MEMORY_ALLOCATOR_H__ diff --git a/runtimes/neurun/src/backend/cpu/StageGenerator.cc b/runtimes/neurun/src/backend/cpu/StageGenerator.cc new file mode 100644 index 000000000..b7a3fa24a --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/StageGenerator.cc @@ -0,0 +1,536 @@ +/* + * 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 "StageGenerator.h" + +#include <stdexcept> + +#include "internal/Padding.h" +#include "kernel/cpu/OperationUtils.h" +#include "kernel/cpu/ConvolutionLayer.h" +#include "kernel/cpu/AvgPoolLayer.h" +#include "kernel/cpu/MaxPoolLayer.h" +#include "kernel/cpu/ConcatLayer.h" +#include "kernel/cpu/FullyConnectedLayer.h" +#include "kernel/cpu/ReshapeLayer.h" +#include "kernel/cpu/SoftMaxLayer.h" + +#include "logging.h" + +#include "support/nnapi/Utils.h" + +#include "logging.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ + +StageGenerator::StageGenerator(const neurun::graph::operand::Set &operand_ctx, + const std::shared_ptr<TensorBuilder> &tensor_builder) + : _ctx(operand_ctx), _tensor_builder(tensor_builder) +{ + // DO NOTHING +} + +Stage StageGenerator::generate(const graph::operation::Conv2D::Implicit::Node &node) +{ + const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)}; + const ::neurun::graph::operand::Index ker_index{node.getInputs().at(1)}; + const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)}; + + const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index}; + const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index}; + + const ::neurun::graph::operand::Index padding_index{node.param().padding_index}; + const ::neurun::graph::operand::Index activation_index{node.param().activation_index}; + + const PaddingCode padding_type = + static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>()); + + assert((ANEURALNETWORKS_PADDING_SAME == padding_type) || + (ANEURALNETWORKS_PADDING_VALID == padding_type)); + + ::internal::Stride stride; + + stride.vertical = _ctx.at(vstride_index).asScalar<int32_t>(); + stride.horizontal = _ctx.at(hstride_index).asScalar<int32_t>(); + + // Construct operation parameters + struct Param + { + int ofm_index; + int ifm_index; + int ker_index; + int bias_index; + + ::neurun::kernel::cpu::Shape ofm_shape; + ::neurun::kernel::cpu::Shape ifm_shape; + ::neurun::kernel::cpu::Shape ker_shape; + ::neurun::kernel::cpu::Shape bias_shape; + + ::internal::Padding padding; + ::internal::Stride stride; + + FuseCode activation; + }; + + Param param; + + param.ofm_index = ofm_index.asInt(); + param.ifm_index = ifm_index.asInt(); + param.ker_index = ker_index.asInt(); + param.bias_index = bias_index.asInt(); + + param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ofm_index)); + param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ifm_index)); + param.ker_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ker_index)); + param.bias_shape = ::neurun::kernel::cpu::getShape(_ctx.at(bias_index)); + + param.stride = stride; + param.padding = (padding_type == ANEURALNETWORKS_PADDING_SAME) + ? ::internal::same_padding(_ctx.at(ifm_index).shape().asFeature(), + _ctx.at(ofm_index).shape().asFeature(), stride, + _ctx.at(ker_index).shape().asKernel().W, + _ctx.at(ker_index).shape().asKernel().H) + : ::internal::valid_padding(); + + param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>()); + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}); + auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}); + auto ker_alloc = tensors->at(::neurun::graph::operand::Index{param.ker_index}); + auto bias_alloc = tensors->at(::neurun::graph::operand::Index{param.bias_index}); + + std::unique_ptr<::neurun::kernel::cpu::ConvolutionLayer> fn{ + new ::neurun::kernel::cpu::ConvolutionLayer}; + + fn->configure(ifm_alloc->buffer(), param.ifm_shape, ker_alloc->buffer(), param.ker_shape, + bias_alloc->buffer(), param.bias_shape, param.padding.left, param.padding.right, + param.padding.top, param.padding.bottom, param.stride.horizontal, + param.stride.vertical, param.activation, ofm_alloc->buffer(), param.ofm_shape); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::MaxPool2D::Implicit::Node &node) +{ + VERBOSE(MaxPool2D) << "generate CPU MaxPool2D" << std::endl; + + const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)}; + + const ::neurun::graph::operand::Index kh_index{node.param().kh_index}; + const ::neurun::graph::operand::Index kw_index{node.param().kw_index}; + + const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index}; + const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index}; + + const ::neurun::graph::operand::Index padding_index{node.param().padding_index}; + const ::neurun::graph::operand::Index activation_index{node.param().activation_index}; + + const int32_t kh = _ctx.at(kh_index).asScalar<int32_t>(); + const int32_t kw = _ctx.at(kw_index).asScalar<int32_t>(); + + const int32_t vstride = _ctx.at(vstride_index).asScalar<int32_t>(); + const int32_t hstride = _ctx.at(hstride_index).asScalar<int32_t>(); + + const PaddingCode padding_type = + static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>()); + + // Construct operation parameters + struct Param + { + int ofm_index; + int ifm_index; + + uint32_t kw; + uint32_t kh; + + ::neurun::kernel::cpu::Shape ofm_shape; + ::neurun::kernel::cpu::Shape ifm_shape; + + ::internal::Padding padding; + ::internal::Stride stride; + + FuseCode activation; + }; + + Param param; + + param.ofm_index = ofm_index.asInt(); + param.ifm_index = ifm_index.asInt(); + + param.kh = kh; + param.kw = kw; + + param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ofm_index)); + param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ifm_index)); + + param.stride.vertical = vstride; + param.stride.horizontal = hstride; + + param.padding = + (padding_type == ANEURALNETWORKS_PADDING_SAME) + ? ::internal::same_padding(_ctx.at(ifm_index).shape().asFeature(), + _ctx.at(ofm_index).shape().asFeature(), param.stride, kw, kh) + : ::internal::valid_padding(); + + param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>()); + + VERBOSE(MaxPool2D) << "IFM_H: " << _ctx.at(ifm_index).shape().asFeature().H << std::endl; + VERBOSE(MaxPool2D) << "IFM_W: " << _ctx.at(ifm_index).shape().asFeature().W << std::endl; + VERBOSE(MaxPool2D) << "OFM_H: " << _ctx.at(ofm_index).shape().asFeature().H << std::endl; + VERBOSE(MaxPool2D) << "OFM_W: " << _ctx.at(ofm_index).shape().asFeature().W << std::endl; + VERBOSE(MaxPool2D) << "KER_H: " << kh << std::endl; + VERBOSE(MaxPool2D) << "KER_W: " << kw << std::endl; + VERBOSE(MaxPool2D) << "STRIDE_H: " << vstride << std::endl; + VERBOSE(MaxPool2D) << "STRIDE_W: " << hstride << std::endl; + VERBOSE(MaxPool2D) << "PAD(T): " << param.padding.top << std::endl; + VERBOSE(MaxPool2D) << "PAD(B): " << param.padding.bottom << std::endl; + VERBOSE(MaxPool2D) << "PAD(L): " << param.padding.left << std::endl; + VERBOSE(MaxPool2D) << "PAD(R): " << param.padding.right << std::endl; + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}).get(); + auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}).get(); + + std::unique_ptr<::neurun::kernel::cpu::MaxPoolLayer> fn{ + new ::neurun::kernel::cpu::MaxPoolLayer}; + + fn->configure(ifm_alloc->buffer(), param.ifm_shape, param.padding.left, param.padding.right, + param.padding.top, param.padding.bottom, param.stride.horizontal, + param.stride.vertical, param.kw, param.kh, param.activation, ofm_alloc->buffer(), + param.ofm_shape); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::AvgPool2D::Implicit::Node &node) +{ + VERBOSE(AvgPool2D) << "generate CPU AvgPool2D" << std::endl; + + const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)}; + + const ::neurun::graph::operand::Index kh_index{node.param().kh_index}; + const ::neurun::graph::operand::Index kw_index{node.param().kw_index}; + + const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index}; + const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index}; + + const ::neurun::graph::operand::Index padding_index{node.param().padding_index}; + const ::neurun::graph::operand::Index activation_index{node.param().activation_index}; + + const int32_t kh = _ctx.at(kh_index).asScalar<int32_t>(); + const int32_t kw = _ctx.at(kw_index).asScalar<int32_t>(); + + const int32_t vstride = _ctx.at(vstride_index).asScalar<int32_t>(); + const int32_t hstride = _ctx.at(hstride_index).asScalar<int32_t>(); + + const PaddingCode padding_type = + static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>()); + + assert((ANEURALNETWORKS_PADDING_SAME == padding_type) || + (ANEURALNETWORKS_PADDING_VALID == padding_type)); + + // Construct operation parameters + struct Param + { + int ofm_index; + int ifm_index; + + uint32_t kw; + uint32_t kh; + + ::neurun::kernel::cpu::Shape ofm_shape; + ::neurun::kernel::cpu::Shape ifm_shape; + + ::internal::Padding padding; + ::internal::Stride stride; + + FuseCode activation; + }; + + Param param; + + param.ofm_index = ofm_index.asInt(); + param.ifm_index = ifm_index.asInt(); + + param.kh = kh; + param.kw = kw; + + param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ofm_index)); + param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ifm_index)); + + param.stride.vertical = vstride; + param.stride.horizontal = hstride; + + param.padding = + (padding_type == ANEURALNETWORKS_PADDING_SAME) + ? ::internal::same_padding(_ctx.at(ifm_index).shape().asFeature(), + _ctx.at(ofm_index).shape().asFeature(), param.stride, kw, kh) + : ::internal::valid_padding(); + + param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>()); + + VERBOSE(AvgPool2D) << "IFM_H: " << _ctx.at(ifm_index).shape().asFeature().H << std::endl; + VERBOSE(AvgPool2D) << "IFM_W: " << _ctx.at(ifm_index).shape().asFeature().W << std::endl; + VERBOSE(AvgPool2D) << "OFM_H: " << _ctx.at(ofm_index).shape().asFeature().H << std::endl; + VERBOSE(AvgPool2D) << "OFM_W: " << _ctx.at(ofm_index).shape().asFeature().W << std::endl; + VERBOSE(AvgPool2D) << "KER_H: " << kh << std::endl; + VERBOSE(AvgPool2D) << "KER_W: " << kw << std::endl; + VERBOSE(AvgPool2D) << "STRIDE_H: " << vstride << std::endl; + VERBOSE(AvgPool2D) << "STRIDE_W: " << hstride << std::endl; + VERBOSE(AvgPool2D) << "PAD: " << ::nnfw::support::nnapi::to_string(padding_type) << std::endl; + VERBOSE(AvgPool2D) << "PAD(T): " << param.padding.top << std::endl; + VERBOSE(AvgPool2D) << "PAD(B): " << param.padding.bottom << std::endl; + VERBOSE(AvgPool2D) << "PAD(L): " << param.padding.left << std::endl; + VERBOSE(AvgPool2D) << "PAD(R): " << param.padding.right << std::endl; + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}).get(); + auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}).get(); + + std::unique_ptr<::neurun::kernel::cpu::AvgPoolLayer> fn{ + new ::neurun::kernel::cpu::AvgPoolLayer}; + + fn->configure(ifm_alloc->buffer(), param.ifm_shape, param.padding.left, param.padding.right, + param.padding.top, param.padding.bottom, param.stride.horizontal, + param.stride.vertical, param.kw, param.kh, param.activation, ofm_alloc->buffer(), + param.ofm_shape); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::Concat::Node &node) +{ + VERBOSE(Concat) << "generate CPU Concat" << std::endl; + + const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index axis_index{node.param().axis_index}; + + struct Param + { + int32_t output_index; + std::vector<int32_t> input_indexes; + + int32_t axis; + + ::neurun::kernel::cpu::Shape ofm_shape; + std::vector<::neurun::kernel::cpu::Shape> ifm_shapes; + }; + + Param param; + + param.output_index = ofm_index.asInt(); + for (const auto &e : node.getInputs()) + { + param.input_indexes.emplace_back(e.asInt()); + } + param.axis = _ctx.at(axis_index).asScalar<int32_t>(); + + param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ofm_index)); + + for (auto e : node.getInputs()) + { + param.ifm_shapes.emplace_back(::neurun::kernel::cpu::getShape(_ctx.at(e))); + } + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get(); + + std::vector<const uint8_t *> input_buffers; + for (auto ifm_ind : param.input_indexes) + { + input_buffers.emplace_back( + tensors->at(::neurun::graph::operand::Index{ifm_ind}).get()->buffer()); + } + + std::unique_ptr<::neurun::kernel::cpu::ConcatLayer> fn{new ::neurun::kernel::cpu::ConcatLayer}; + + fn->configure(input_buffers, param.ifm_shapes, param.axis, output_alloc->buffer(), + param.ofm_shape); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::FullyConnected::Node &node) +{ + VERBOSE(FullyConnected) << "generate CPU FullyConnected" << std::endl; + + const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)}; + const ::neurun::graph::operand::Index weight_index{node.getInputs().at(1)}; + const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)}; + const ::neurun::graph::operand::Index activation_index{node.param().activation_index}; + + // Construct operation parameters + struct Param + { + int output_index; + int input_index; + int weight_index; + int bias_index; + + ::neurun::kernel::cpu::Shape ofm_shape; + ::neurun::kernel::cpu::Shape ifm_shape; + ::neurun::kernel::cpu::Shape weight_shape; + ::neurun::kernel::cpu::Shape bias_shape; + + FuseCode activation; + }; + + Param param; + + param.output_index = output_index.asInt(); + param.input_index = input_index.asInt(); + param.weight_index = weight_index.asInt(); + param.bias_index = bias_index.asInt(); + + param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(output_index)); + param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(input_index)); + param.weight_shape = ::neurun::kernel::cpu::getShape(_ctx.at(weight_index)); + param.bias_shape = ::neurun::kernel::cpu::getShape(_ctx.at(bias_index)); + + param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>()); + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get(); + auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get(); + auto weight_alloc = tensors->at(::neurun::graph::operand::Index{param.weight_index}).get(); + auto bias_alloc = tensors->at(::neurun::graph::operand::Index{param.bias_index}).get(); + + std::unique_ptr<::neurun::kernel::cpu::FullyConnectedLayer> fn{ + new ::neurun::kernel::cpu::FullyConnectedLayer}; + + fn->configure(input_alloc->buffer(), param.ifm_shape, weight_alloc->buffer(), + param.weight_shape, bias_alloc->buffer(), param.bias_shape, param.activation, + output_alloc->buffer(), param.ofm_shape); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::Reshape::Node &node) +{ + const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)}; + + struct Param + { + int output_index; + int input_index; + + ::neurun::kernel::cpu::Shape ofm_shape; + ::neurun::kernel::cpu::Shape ifm_shape; + }; + + Param param; + + param.output_index = output_index.asInt(); + param.input_index = input_index.asInt(); + + param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(output_index)); + param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(input_index)); + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get(); + auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get(); + + std::unique_ptr<::neurun::kernel::cpu::ReshapeLayer> fn{ + new ::neurun::kernel::cpu::ReshapeLayer}; + + fn->configure(input_alloc->buffer(), param.ifm_shape, output_alloc->buffer(), param.ofm_shape); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::Softmax::Node &node) +{ + VERBOSE(Softmax) << "generate CPU Softmax" << std::endl; + + const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)}; + const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)}; + const ::neurun::graph::operand::Index scale_index{node.param().scale_index}; + + struct Param + { + int output_index; + int input_index; + + ::neurun::kernel::cpu::Shape ofm_shape; + ::neurun::kernel::cpu::Shape ifm_shape; + + float scale; + }; + + Param param; + + param.output_index = output_index.asInt(); + param.input_index = input_index.asInt(); + + param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(output_index)); + param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(input_index)); + + param.scale = _ctx.at(scale_index).asScalar<float>(); + + auto tensors = _tensor_builder; + + return [tensors, param](IExecutionBuilder &builder) { + auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get(); + auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get(); + + std::unique_ptr<::neurun::kernel::cpu::SoftMaxLayer> fn{ + new ::neurun::kernel::cpu::SoftMaxLayer}; + + fn->configure(input_alloc->buffer(), param.ifm_shape, param.scale, output_alloc->buffer(), + param.ofm_shape); + + builder.append(std::move(fn)); + }; +} + +Stage StageGenerator::generate(const graph::operation::NOP::Node & /* node */) +{ + // DO NOTHING + return nullptr; +} + +} // namespace neurun +} // namespace backend +} // namespace cpu diff --git a/runtimes/neurun/src/backend/cpu/StageGenerator.h b/runtimes/neurun/src/backend/cpu/StageGenerator.h new file mode 100644 index 000000000..acdd2c8b2 --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/StageGenerator.h @@ -0,0 +1,59 @@ +/* + * 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 __NEURUN_BACKEND_CPU_STAGE_GENERATOR_H__ +#define __NEURUN_BACKEND_CPU_STAGE_GENERATOR_H__ + +#include "backend/IStageGenerator.h" + +#include "graph/operand/Set.h" +#include "backend/cpu/operand/Tensor.h" +#include "TensorBuilder.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ + +class StageGenerator : public IStageGenerator +{ +public: + StageGenerator(const neurun::graph::operand::Set &ctx, + const std::shared_ptr<TensorBuilder> &tensor_builder); + + virtual std::shared_ptr<ITensorBuilder> tensor_builder() override { return _tensor_builder; } + + virtual Stage generate(const graph::operation::Conv2D::Implicit::Node &node) override; + virtual Stage generate(const graph::operation::MaxPool2D::Implicit::Node &node) override; + virtual Stage generate(const graph::operation::AvgPool2D::Implicit::Node &node) override; + virtual Stage generate(const graph::operation::Concat::Node &node) override; + virtual Stage generate(const graph::operation::FullyConnected::Node &node) override; + virtual Stage generate(const graph::operation::Reshape::Node &node) override; + virtual Stage generate(const graph::operation::Softmax::Node &node) override; + virtual Stage generate(const graph::operation::NOP::Node &node) override; + +private: + const neurun::graph::operand::Set &_ctx; + std::shared_ptr<TensorBuilder> _tensor_builder; +}; + +} // namespace cpu +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_CPU_STAGE_GENERATOR_H__ diff --git a/runtimes/neurun/src/backend/cpu/TensorBuilder.cc b/runtimes/neurun/src/backend/cpu/TensorBuilder.cc new file mode 100644 index 000000000..1b972a830 --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/TensorBuilder.cc @@ -0,0 +1,73 @@ +/* + * 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 "TensorBuilder.h" + +#include <cassert> + +#include "operand/Object.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ + +TensorBuilder::TensorBuilder() +{ + // DO NOTHING +} + +void TensorBuilder::mark(const ::neurun::graph::operand::Index &ind) +{ + assert(_tensors.size() == 0); + + _inds.insert(ind); +} + +void TensorBuilder::prepare(codegen::Plan &plan, + const std::map<int, ::arm_compute::TensorInfo> &tensor_info_ctx) +{ + assert(_tensors.size() == 0); + + for (auto ind_int : _inds) + { + ::neurun::graph::operand::Index ind{ind_int}; + auto tensor = std::make_shared<operand::Tensor>(tensor_info_ctx.at(ind.asInt())); + // TODO Fix allocation here. When Tensor object is created the memory for tensor is also + // allocated, and this must be fixed. + plan.operands().set(ind, std::make_shared<operand::Object>(tensor)); + _tensors[ind] = tensor; + } +} + +void TensorBuilder::allocate(void) +{ + assert(_inds.size() == _tensors.size()); + + // NOTE For now nothing to do. Allocation is done in prepare stage, which is wrong + // See also: comment in `prepare()` +} + +std::shared_ptr<operand::Tensor> TensorBuilder::at(const ::neurun::graph::operand::Index &ind) +{ + return _tensors.at(ind); +} + +} // namespace cpu +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/cpu/TensorBuilder.h b/runtimes/neurun/src/backend/cpu/TensorBuilder.h new file mode 100644 index 000000000..f61a930fe --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/TensorBuilder.h @@ -0,0 +1,57 @@ +/* + * 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 __NEURUN_BACKEND_CPU_TENSOR_BUILDER_H__ +#define __NEURUN_BACKEND_CPU_TENSOR_BUILDER_H__ + +#include <unordered_map> +#include <unordered_set> + +#include "backend/ITensorBuilder.h" +#include "backend/cpu/operand/Tensor.h" +#include "graph/operand/Index.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ + +class Plan; + +class TensorBuilder : public ITensorBuilder +{ +public: + TensorBuilder(); + + virtual void mark(const ::neurun::graph::operand::Index &ind) override; + virtual void prepare(codegen::Plan &plan, + const std::map<int, ::arm_compute::TensorInfo> &tensor_info_ctx) override; + virtual void allocate(void) override; + + std::shared_ptr<operand::Tensor> at(const ::neurun::graph::operand::Index &ind); + +private: + std::unordered_set<graph::operand::Index> _inds; + std::unordered_map<graph::operand::Index, std::shared_ptr<operand::Tensor>> _tensors; +}; + +} // namespace cpu +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_CPU_TENSOR_BUILDER_H__ diff --git a/runtimes/neurun/src/backend/cpu/operand/Object.cc b/runtimes/neurun/src/backend/cpu/operand/Object.cc new file mode 100644 index 000000000..52b63fba7 --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/operand/Object.cc @@ -0,0 +1,36 @@ +/* + * 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 "Object.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ +namespace operand +{ + +void Object::access(const std::function<void(::arm_compute::ITensor &tensor)> &fn) const +{ + fn(*_tensor); +} + +} // namespace operand +} // namespace cpu +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/cpu/operand/Object.h b/runtimes/neurun/src/backend/cpu/operand/Object.h new file mode 100644 index 000000000..08f63f3dc --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/operand/Object.h @@ -0,0 +1,60 @@ +/* + * 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 __NEURUN_BACKEND_CPU_OPERAND_OBJECT_H__ +#define __NEURUN_BACKEND_CPU_OPERAND_OBJECT_H__ + +#include <memory> +#include <arm_compute/core/ITensor.h> + +#include "backend/IObject.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ +namespace operand +{ + +class Object : public backend::operand::IObject +{ +public: + Object() = default; + +public: + Object(const std::shared_ptr<::arm_compute::ITensor> &tensor) : _tensor{tensor} + { + // DO NOTHING + } + +public: + ::arm_compute::ITensor *ptr(void) const override { return _tensor.get(); } + +private: + std::shared_ptr<::arm_compute::ITensor> _tensor; + +public: + void access(const std::function<void(::arm_compute::ITensor &tensor)> &fn) const override; +}; + +} // namespace operand +} // namespace cpu +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_CPU_OPERAND_OBJECT_H__ diff --git a/runtimes/neurun/src/backend/cpu/operand/Tensor.cc b/runtimes/neurun/src/backend/cpu/operand/Tensor.cc new file mode 100644 index 000000000..0e4f34aac --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/operand/Tensor.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 "Tensor.h" + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ +namespace operand +{ + +// NO IMPLEMENTATION YET + +} // namespace operand +} // namespace cpu +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/src/backend/cpu/operand/Tensor.h b/runtimes/neurun/src/backend/cpu/operand/Tensor.h new file mode 100644 index 000000000..83a99acf2 --- /dev/null +++ b/runtimes/neurun/src/backend/cpu/operand/Tensor.h @@ -0,0 +1,72 @@ +/* + * 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 __NEURUN_BACKEND_CPU_OPERAND_TENSOR_H__ +#define __NEURUN_BACKEND_CPU_OPERAND_TENSOR_H__ + +#include <arm_compute/core/ITensor.h> +#include <arm_compute/core/TensorInfo.h> + +namespace neurun +{ +namespace backend +{ +namespace cpu +{ +namespace operand +{ + +class Tensor : public ::arm_compute::ITensor +{ +public: + Tensor() = default; + + Tensor(::arm_compute::TensorInfo info) : _info(info) + { + // TODO Do not allocate buffer here. This tensor is just an abstract Tensor object for cpu. + uint32_t size = _info.total_size(); // NOTE This size may not be accurate + _buffer = new uint8_t[size]; // NOTE The allocated buffer is never deallocated. + } + + Tensor(uint8_t *buffer) : _buffer(buffer) + { + // DO NOTHING + } + +public: + void setBuffer(uint8_t *buffer) { _buffer = buffer; } + +public: + ::arm_compute::TensorInfo *info() const override + { + return const_cast<::arm_compute::TensorInfo *>(&_info); + } + + ::arm_compute::TensorInfo *info() override { return &_info; } + + uint8_t *buffer() const override { return _buffer; } + +private: + ::arm_compute::TensorInfo _info; + uint8_t *_buffer = nullptr; +}; + +} // namespace operand +} // namespace cpu +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_CPU_OPERAND_TENSOR_H__ |