xref: /aosp_15_r20/external/ComputeLibrary/src/cpu/operators/CpuDirectConv2d.cpp (revision c217d954acce2dbc11938adb493fc0abd69584f3)
1 /*
2  * Copyright (c) 2021 Arm Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #include "src/cpu/operators/CpuDirectConv2d.h"
25 
26 #include "arm_compute/core/PixelValue.h"
27 #include "arm_compute/core/Utils.h"
28 #include "arm_compute/core/Validate.h"
29 #include "arm_compute/runtime/NEON/NEScheduler.h"
30 #include "src/common/utils/Log.h"
31 
32 namespace arm_compute
33 {
34 namespace cpu
35 {
36 CpuDirectConv2d::~CpuDirectConv2d() = default;
37 
CpuDirectConv2d(std::shared_ptr<IMemoryManager> memory_manager)38 CpuDirectConv2d::CpuDirectConv2d(std::shared_ptr<IMemoryManager> memory_manager)
39     : _memory_group(std::move(memory_manager)), _output_stage_kernel(), _conv_kernel(), _input_border_handler(), _activationlayer_function(), _accumulator(), _has_bias(false),
40       _is_activationlayer_enabled(false), _dim_split(Window::DimZ), _is_padding_required()
41 {
42 }
43 
configure(ITensorInfo * src,ITensorInfo * weights,const ITensorInfo * bias,ITensorInfo * dst,const PadStrideInfo & conv_info,const ActivationLayerInfo & act_info)44 void CpuDirectConv2d::configure(ITensorInfo *src, ITensorInfo *weights, const ITensorInfo *bias, ITensorInfo *dst, const PadStrideInfo &conv_info, const ActivationLayerInfo &act_info)
45 {
46     ARM_COMPUTE_ERROR_ON(src->data_layout() == DataLayout::UNKNOWN);
47     ARM_COMPUTE_LOG_PARAMS(src, weights, bias, dst, conv_info, act_info);
48 
49     _output_stage_kernel  = std::make_unique<kernels::CpuDirectConv2dOutputStageKernel>();
50     _conv_kernel          = std::make_unique<kernels::CpuDirectConv2dKernel>();
51     _input_border_handler = std::make_unique<NEFillBorderKernel>();
52 
53     // Free accumulator
54     if(_accumulator.buffer() != nullptr)
55     {
56         _accumulator.allocator()->free();
57     }
58 
59     _dim_split = src->data_layout() == DataLayout::NCHW ? Window::DimZ : Window::DimY;
60 
61     // Check if bias should be added in the convolution result
62     _has_bias = (bias != nullptr);
63 
64     _conv_kernel->configure(src, weights, dst, conv_info);
65     if(_has_bias)
66     {
67         _output_stage_kernel->configure(dst, bias);
68     }
69     _is_padding_required = !_conv_kernel->border_size().empty();
70 
71     if(_is_padding_required)
72     {
73         // Add zero padding XY
74         _input_border_handler->configure(src, _conv_kernel->border_size(), BorderMode::CONSTANT, PixelValue(static_cast<float>(0.f)));
75     }
76 
77     //Configure Activation Layer
78     _is_activationlayer_enabled = act_info.enabled();
79     if(_is_activationlayer_enabled)
80     {
81         _activationlayer_function = std::make_unique<CpuActivation>();
82         _activationlayer_function->configure(dst, dst, act_info);
83     }
84 }
85 
validate(const ITensorInfo * src,const ITensorInfo * weights,const ITensorInfo * bias,const ITensorInfo * dst,const PadStrideInfo & conv_info,const ActivationLayerInfo & act_info)86 Status CpuDirectConv2d::validate(const ITensorInfo *src, const ITensorInfo *weights, const ITensorInfo *bias, const ITensorInfo *dst, const PadStrideInfo &conv_info,
87                                  const ActivationLayerInfo &act_info)
88 {
89     ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(src, weights, dst);
90 
91     // output might not be initialized since it can be an intermediate tensor of another layer
92     DataType   data_type = src->data_type();
93     TensorInfo accumulator(dst->clone()->set_is_resizable(true).reset_padding().set_data_type(data_type));
94 
95     // Validate Convolution kernel
96     ARM_COMPUTE_RETURN_ON_ERROR(kernels::CpuDirectConv2dKernel::validate(src, weights, &accumulator, conv_info));
97 
98     if(bias != nullptr)
99     {
100         ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(weights, bias);
101         ARM_COMPUTE_RETURN_ERROR_ON_MSG(bias->dimension(0) != weights->dimension(3),
102                                         "Biases size and number of input feature maps should match");
103         ARM_COMPUTE_RETURN_ERROR_ON_MSG(bias->num_dimensions() > 1, "Biases should be one dimensional");
104     }
105 
106     // Validate bias kernel
107     ARM_COMPUTE_RETURN_ON_ERROR(kernels::CpuDirectConv2dOutputStageKernel::validate(&accumulator, bias, dst));
108 
109     if(act_info.enabled())
110     {
111         ARM_COMPUTE_RETURN_ON_ERROR(CpuActivation::validate(dst, nullptr, act_info));
112     }
113 
114     return Status{};
115 }
116 
run(ITensorPack & tensors)117 void CpuDirectConv2d::run(ITensorPack &tensors)
118 {
119     MemoryGroupResourceScope scope_mg(_memory_group);
120 
121     auto src  = tensors.get_tensor(TensorType::ACL_SRC_0);
122     auto bias = tensors.get_const_tensor(TensorType::ACL_SRC_2);
123     auto dst  = tensors.get_tensor(TensorType::ACL_DST);
124 
125     if(_is_padding_required)
126     {
127         ITensorPack pack;
128         pack.add_tensor(TensorType::ACL_SRC_DST, src);
129         NEScheduler::get().schedule_op(_input_border_handler.get(), Window::DimZ, _input_border_handler->window(), pack);
130     }
131     NEScheduler::get().schedule_op(_conv_kernel.get(), _dim_split, _conv_kernel->window(), tensors);
132     if(_has_bias)
133     {
134         ITensorPack pack;
135         pack.add_tensor(TensorType::ACL_SRC_0, dst);
136         pack.add_tensor(TensorType::ACL_SRC_1, bias);
137         pack.add_tensor(TensorType::ACL_DST, dst);
138         NEScheduler::get().schedule_op(_output_stage_kernel.get(), Window::DimY, _output_stage_kernel->window(), pack);
139     }
140 
141     if(_is_activationlayer_enabled)
142     {
143         ITensorPack pack;
144         pack.add_tensor(TensorType::ACL_SRC, dst);
145         pack.add_tensor(TensorType::ACL_DST, dst);
146         _activationlayer_function->run(pack);
147     }
148 }
149 } // namespace cpu
150 } // namespace arm_compute
151