Files
paddlepaddle--paddle/paddle/phi/kernels/fusion/xpu/resnet_basic_block_kernel.cc
T
2026-07-13 12:40:42 +08:00

709 lines
30 KiB
C++

// Copyright (c) 2024 PaddlePaddle Authors. 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 "paddle/phi/backends/xpu/enforce_xpu.h"
#include "paddle/phi/backends/xpu/xpu_header.h"
#include "paddle/phi/core/kernel_registry.h"
namespace phi {
namespace fusion {
class ResnetBasicBlockAttr {
public:
explicit ResnetBasicBlockAttr(const XPUContext &dev_ctx,
const DenseTensor &x_in,
const DenseTensor &filter1_in,
const DenseTensor &scale1_in,
const DenseTensor &bias1_in,
const DenseTensor &mean1_in,
const DenseTensor &var1_in,
const DenseTensor &filter2_in,
const DenseTensor &scale2_in,
const DenseTensor &bias2_in,
const DenseTensor &mean2_in,
const DenseTensor &var2_in,
const optional<DenseTensor> &filter3_in,
const optional<DenseTensor> &scale3_in,
const optional<DenseTensor> &bias3_in,
const optional<DenseTensor> &mean3_in,
const optional<DenseTensor> &var3_in,
int stride1_in,
int stride2_in,
int stride3_in,
int padding1_in,
int padding2_in,
int padding3_in,
int dilation1_in,
int dilation2_in,
int dilation3_in,
int group_in,
float momentum_in,
float epsilon_in,
const std::string &data_format_in,
bool has_shortcut_in,
bool use_global_stats_in,
bool is_test_in,
bool trainable_statistics_in,
const std::string &act_type_in,
bool find_conv_input_max_in,
DenseTensor *out,
DenseTensor *conv1,
DenseTensor *saved_mean1,
DenseTensor *saved_invstd1,
DenseTensor *mean1_out,
DenseTensor *var1_out,
DenseTensor *conv2,
DenseTensor *conv2_input,
DenseTensor *saved_mean2,
DenseTensor *saved_invstd2,
DenseTensor *mean2_out,
DenseTensor *var2_out,
DenseTensor *conv3,
DenseTensor *saved_mean3,
DenseTensor *saved_invstd3,
DenseTensor *mean3_out,
DenseTensor *var3_out,
DenseTensor *max_input1,
DenseTensor *max_filter1,
DenseTensor *max_input2,
DenseTensor *max_filter2,
DenseTensor *max_input3,
DenseTensor *max_filter3) {
padding1 = padding1_in;
padding2 = padding2_in;
padding3 = padding3_in;
stride1 = stride1_in;
stride2 = stride2_in;
stride3 = stride3_in;
dilation1 = dilation1_in;
dilation2 = dilation2_in;
dilation3 = dilation3_in;
group = group_in;
eps = static_cast<double>(epsilon_in);
momentum = static_cast<double>(momentum_in);
has_shortcut = has_shortcut_in;
find_max = find_conv_input_max_in;
const auto is_test = is_test_in;
const auto use_global_stats = use_global_stats_in;
const auto trainable_stats = trainable_statistics_in;
bool test_mode = is_test && (!trainable_stats);
global_stats = test_mode || use_global_stats;
// init shape
auto input1 = &x_in;
auto filter1 = &filter1_in;
auto conv1_out = conv1;
auto filter2 = &filter2_in;
auto conv2_out = conv2;
conv1_input_shape = vectorize<int>(input1->dims());
conv1_output_shape = vectorize<int>(conv1_out->dims());
conv1_filter_shape = vectorize<int>(filter1->dims());
conv1_filter_numel = filter1->numel();
conv1_input_numel = input1->numel();
conv1_output_numel = conv1_out->numel();
conv2_input_shape = vectorize<int>(conv1_out->dims());
conv2_output_shape = vectorize<int>(conv2_out->dims());
conv2_filter_shape = vectorize<int>(filter2->dims());
conv2_filter_numel = filter2->numel();
conv2_input_numel = conv1_out->numel();
conv2_output_numel = conv2_out->numel();
if (has_shortcut) {
auto filter3 = filter3_in.get_ptr();
auto conv3_out = conv3;
conv3_input_shape = vectorize<int>(input1->dims());
conv3_output_shape = vectorize<int>(conv3_out->dims());
conv3_filter_shape = vectorize<int>(filter3->dims());
conv3_filter_numel = filter3->numel();
conv3_input_numel = input1->numel();
conv3_output_numel = conv3_out->numel();
}
}
int padding1;
int padding2;
int padding3;
int stride1;
int stride2;
int stride3;
int dilation1;
int dilation2;
int dilation3;
int group;
double eps;
double momentum;
bool has_shortcut;
bool find_max;
bool global_stats;
std::vector<int> conv1_input_shape;
std::vector<int> conv1_output_shape;
std::vector<int> conv1_filter_shape;
std::vector<int> conv2_input_shape;
std::vector<int> conv2_output_shape;
std::vector<int> conv2_filter_shape;
std::vector<int> conv3_input_shape;
std::vector<int> conv3_output_shape;
std::vector<int> conv3_filter_shape;
int conv1_filter_numel;
int conv2_filter_numel;
int conv3_filter_numel;
int conv1_input_numel;
int conv2_input_numel;
int conv3_input_numel;
int conv1_output_numel;
int conv2_output_numel;
int conv3_output_numel;
};
template <typename T>
static inline void xpu_conv2d(xpu::Context *ctx,
const T *input_data,
const T *filter_data,
T *output_data,
float *input_max_data,
float *filter_max_data,
const std::vector<int> &input_shape,
const std::vector<int> &filter_shape,
int padding,
int stride,
int dilation,
int group) {
std::vector<int64_t> ksize{filter_shape[2], filter_shape[3]};
std::vector<int64_t> stride_vec{stride, stride};
std::vector<int64_t> dilation_vec{dilation, dilation};
std::vector<int64_t> padding_vec{padding, padding};
int64_t N = static_cast<int64_t>(input_shape[0]);
int64_t C = static_cast<int64_t>(input_shape[1]);
int64_t H = static_cast<int64_t>(input_shape[2]);
int64_t W = static_cast<int64_t>(input_shape[3]);
int r = xpu::conv2d<T, T, T, int16_t>(ctx,
input_data,
filter_data,
output_data,
N,
C,
H,
W,
filter_shape[0],
ksize,
stride_vec,
padding_vec,
dilation_vec,
group,
input_max_data,
filter_max_data,
nullptr,
true);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "conv2d");
}
template <typename T, typename Context>
void ResNetBasicBlockXPUKernel(const Context &dev_ctx,
const DenseTensor &x_in,
const DenseTensor &filter1_in,
const DenseTensor &scale1_in,
const DenseTensor &bias1_in,
const DenseTensor &mean1_in,
const DenseTensor &var1_in,
const DenseTensor &filter2_in,
const DenseTensor &scale2_in,
const DenseTensor &bias2_in,
const DenseTensor &mean2_in,
const DenseTensor &var2_in,
const optional<DenseTensor> &filter3_in,
const optional<DenseTensor> &scale3_in,
const optional<DenseTensor> &bias3_in,
const optional<DenseTensor> &mean3_in,
const optional<DenseTensor> &var3_in,
int stride1,
int stride2,
int stride3,
int padding1,
int padding2,
int padding3,
int dilation1,
int dilation2,
int dilation3,
int group,
float momentum_in,
float epsilon,
const std::string &data_format,
bool has_shortcut,
bool use_global_stats,
bool is_test,
bool trainable_statistics,
const std::string &act_type,
bool find_conv_input_max,
DenseTensor *out,
DenseTensor *conv1,
DenseTensor *saved_mean1,
DenseTensor *saved_invstd1,
DenseTensor *mean1_out,
DenseTensor *var1_out,
DenseTensor *conv2,
DenseTensor *conv2_input,
DenseTensor *saved_mean2,
DenseTensor *saved_invstd2,
DenseTensor *mean2_out,
DenseTensor *var2_out,
DenseTensor *conv3,
DenseTensor *saved_mean3,
DenseTensor *saved_invstd3,
DenseTensor *mean3_out,
DenseTensor *var3_out,
DenseTensor *max_input1,
DenseTensor *max_filter1,
DenseTensor *max_input2,
DenseTensor *max_filter2,
DenseTensor *max_input3,
DenseTensor *max_filter3) {
using XPUType = typename XPUTypeTrait<T>::Type;
// input
const DenseTensor *x = &x_in;
const DenseTensor *filter1 = &filter1_in;
const DenseTensor *scale1 = &scale1_in;
const DenseTensor *bias1 = &bias1_in;
const DenseTensor *filter2 = &filter2_in;
const DenseTensor *scale2 = &scale2_in;
const DenseTensor *bias2 = &bias2_in;
// output
DenseTensor *conv1_output = conv1;
DenseTensor *conv2_output = conv2;
DenseTensor *output = out;
auto x_data = reinterpret_cast<const XPUType *>(x->data<T>());
auto conv1_filter_data =
reinterpret_cast<const XPUType *>(filter1->data<T>());
auto conv2_filter_data =
reinterpret_cast<const XPUType *>(filter2->data<T>());
auto conv1_output_data =
reinterpret_cast<XPUType *>(dev_ctx.template Alloc<T>(conv1_output));
auto conv2_input_data =
reinterpret_cast<XPUType *>(dev_ctx.template Alloc<T>(conv2_input));
auto conv2_output_data =
reinterpret_cast<XPUType *>(dev_ctx.template Alloc<T>(conv2_output));
auto scale1_data = scale1->data<float>();
auto scale2_data = scale2->data<float>();
auto bias1_data = bias1->data<float>();
auto bias2_data = bias2->data<float>();
auto output_data =
reinterpret_cast<XPUType *>(dev_ctx.template Alloc<T>(output));
float *conv1_input_max_data = nullptr;
float *conv1_filter_max_data = nullptr;
float *conv2_input_max_data = nullptr;
float *conv2_filter_max_data = nullptr;
float *conv3_input_max_data = nullptr;
float *conv3_filter_max_data = nullptr;
ResnetBasicBlockAttr attr(dev_ctx,
x_in,
filter1_in,
scale1_in,
bias1_in,
mean1_in,
var1_in,
filter2_in,
scale2_in,
bias2_in,
mean2_in,
var2_in,
filter3_in,
scale3_in,
bias3_in,
mean3_in,
var3_in,
stride1,
stride2,
stride3,
padding1,
padding2,
padding3,
dilation1,
dilation2,
dilation3,
group,
momentum_in,
epsilon,
data_format,
has_shortcut,
use_global_stats,
is_test,
trainable_statistics,
act_type,
find_conv_input_max,
out,
conv1,
saved_mean1,
saved_invstd1,
mean1_out,
var1_out,
conv2,
conv2_input,
saved_mean2,
saved_invstd2,
mean2_out,
var2_out,
conv3,
saved_mean3,
saved_invstd3,
mean3_out,
var3_out,
max_input1,
max_filter1,
max_input2,
max_filter2,
max_input3,
max_filter3);
// init find max
if (attr.find_max) {
conv1_input_max_data = dev_ctx.template Alloc<float>(max_input1);
conv1_filter_max_data = dev_ctx.template Alloc<float>(max_filter1);
conv2_input_max_data = dev_ctx.template Alloc<float>(max_input2);
conv2_filter_max_data = dev_ctx.template Alloc<float>(max_filter2);
if (attr.has_shortcut) {
conv3_input_max_data = dev_ctx.template Alloc<float>(max_input3);
conv3_filter_max_data = dev_ctx.template Alloc<float>(max_filter3);
}
}
xpu::ctx_guard RAII_GUARD(dev_ctx.x_context());
int r = XPU_SUCCESS;
// 1. short
const XPUType *z_out_data = nullptr;
if (attr.has_shortcut) {
DenseTensor *conv3_out = conv3;
const DenseTensor *filter3 = filter3_in.get_ptr();
auto conv3_filter_data =
reinterpret_cast<const XPUType *>(filter3->data<T>());
auto conv3_output_data =
reinterpret_cast<XPUType *>(dev_ctx.template Alloc<T>(conv3_out));
XPUType *conv3_input_l3_data = nullptr;
XPUType *conv3_filter_l3_data =
RAII_GUARD.alloc_l3_or_gm<XPUType>(attr.conv3_filter_numel);
if (attr.find_max) {
r = xpu::findmax_copy_fusion(dev_ctx.x_context(),
x_data,
conv3_input_max_data,
conv3_input_l3_data,
attr.conv3_input_numel);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "findmax_copy_fusion");
r = xpu::findmax_copy_fusion(dev_ctx.x_context(),
conv3_filter_data,
conv3_filter_max_data,
conv3_filter_l3_data,
attr.conv3_filter_numel);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "findmax_copy_fusion");
}
xpu_conv2d(dev_ctx.x_context(),
conv3_input_l3_data != nullptr ? conv3_input_l3_data : x_data,
conv3_filter_l3_data,
conv3_output_data,
conv3_input_max_data,
conv3_filter_max_data,
attr.conv3_input_shape,
attr.conv3_filter_shape,
attr.padding3,
attr.stride3,
attr.dilation3,
attr.group);
// bn3
const DenseTensor *scale3 = scale3_in.get_ptr();
const DenseTensor *bias3 = bias3_in.get_ptr();
auto bias3_data = bias3->data<float>();
auto scale3_data = scale3->data<float>();
auto bn3_output_data = RAII_GUARD.alloc<XPUType>(attr.conv3_output_numel);
PADDLE_ENFORCE_XDNN_NOT_NULL(bn3_output_data);
if (!attr.global_stats) {
DenseTensor *running_mean3 = mean3_out;
DenseTensor *running_var3 = var3_out;
auto saved_mean3_data = dev_ctx.template Alloc<float>(saved_mean3);
auto saved_invstd3_data = dev_ctx.template Alloc<float>(saved_invstd3);
auto running_mean3_data = dev_ctx.template Alloc<float>(running_mean3);
auto running_var3_data = dev_ctx.template Alloc<float>(running_var3);
r = xpu::batch_norm_fusion<XPUType>(dev_ctx.x_context(),
conv3_output_data,
bn3_output_data,
attr.conv3_output_shape[0],
attr.conv3_output_shape[1],
attr.conv3_output_shape[3],
attr.conv3_output_shape[3],
attr.eps,
attr.momentum,
scale3_data,
bias3_data,
saved_mean3_data,
saved_invstd3_data,
running_mean3_data,
running_var3_data,
true,
nullptr,
xpu::Activation_t::LINEAR,
nullptr,
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "batch_norm_fusion");
} else {
const auto *mean3 = mean3_in.get_ptr();
const auto *var3 = var3_in.get_ptr();
const auto *mean3_data = mean3->data<float>();
const auto *variance3_data = var3->data<float>();
r = xpu::batch_norm_infer<XPUType>(dev_ctx.x_context(),
conv3_output_data,
bn3_output_data,
attr.conv3_output_shape[0],
attr.conv3_output_shape[1],
attr.conv3_output_shape[2],
attr.conv3_output_shape[3],
attr.eps,
scale3_data,
bias3_data,
mean3_data,
variance3_data,
true);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "batch_norm_infer");
}
z_out_data = reinterpret_cast<const XPUType *>(bn3_output_data);
} else {
z_out_data = x_data;
}
// 2. conv1
XPUType *conv1_input_l3_data = nullptr;
XPUType *conv1_filter_l3_data =
RAII_GUARD.alloc_l3_or_gm<XPUType>(attr.conv1_filter_numel);
if (attr.find_max) {
r = xpu::findmax_copy_fusion(dev_ctx.x_context(),
x_data,
conv1_input_max_data,
conv1_input_l3_data,
attr.conv1_input_numel);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "findmax_copy_fusion");
r = xpu::findmax_copy_fusion(dev_ctx.x_context(),
conv1_filter_data,
conv1_filter_max_data,
conv1_filter_l3_data,
attr.conv1_filter_numel);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "findmax_copy_fusion");
}
xpu_conv2d(dev_ctx.x_context(),
conv1_input_l3_data != nullptr ? conv1_input_l3_data : x_data,
conv1_filter_l3_data,
conv1_output_data,
conv1_input_max_data,
conv1_filter_max_data,
attr.conv1_input_shape,
attr.conv1_filter_shape,
attr.padding1,
attr.stride1,
attr.dilation1,
attr.group);
// 3. bn1 + relu
if (!attr.global_stats) {
DenseTensor *running_mean1 = mean1_out;
DenseTensor *running_var1 = var1_out;
auto saved_mean1_data = dev_ctx.template Alloc<float>(saved_mean1);
auto saved_invstd1_data = dev_ctx.template Alloc<float>(saved_invstd1);
auto running_mean1_data = dev_ctx.template Alloc<float>(running_mean1);
auto running_var1_data = dev_ctx.template Alloc<float>(running_var1);
r = xpu::batch_norm_fusion<XPUType>(dev_ctx.x_context(),
conv1_output_data,
conv2_input_data,
attr.conv1_output_shape[0],
attr.conv1_output_shape[1],
attr.conv1_output_shape[2],
attr.conv1_output_shape[3],
attr.eps,
attr.momentum,
scale1_data,
bias1_data,
saved_mean1_data,
saved_invstd1_data,
running_mean1_data,
running_var1_data,
true,
nullptr,
xpu::Activation_t::RELU,
nullptr,
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "batch_norm_fusion");
} else {
// bn --> relu
auto bn1_output_data = RAII_GUARD.alloc<XPUType>(attr.conv1_output_numel);
PADDLE_ENFORCE_XDNN_NOT_NULL(bn1_output_data);
const auto *mean1 = &mean1_in;
const auto *var1 = &var1_in;
const auto *mean_data = mean1->data<float>();
const auto *variance_data = var1->data<float>();
r = xpu::batch_norm_infer<XPUType>(dev_ctx.x_context(),
conv1_output_data,
bn1_output_data,
attr.conv1_output_shape[0],
attr.conv1_output_shape[1],
attr.conv1_output_shape[2],
attr.conv1_output_shape[3],
attr.eps,
scale1_data,
bias1_data,
mean_data,
variance_data,
true);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "batch_norm_infer");
r = xpu::relu(dev_ctx.x_context(),
bn1_output_data,
conv2_input_data,
attr.conv1_output_numel);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "relu");
}
// 4. conv2
XPUType *conv2_input_l3_data = nullptr;
XPUType *conv2_filter_l3_data =
RAII_GUARD.alloc_l3_or_gm<XPUType>(attr.conv2_filter_numel);
if (attr.find_max) {
conv2_input_max_data = dev_ctx.template Alloc<float>(max_input2);
conv2_filter_max_data = dev_ctx.template Alloc<float>(max_filter2);
r = xpu::findmax_copy_fusion(dev_ctx.x_context(),
conv2_input_data,
conv2_input_max_data,
conv2_input_l3_data,
attr.conv2_input_numel);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "findmax_copy_fusion");
r = xpu::findmax_copy_fusion(dev_ctx.x_context(),
conv2_filter_data,
conv2_filter_max_data,
conv2_filter_l3_data,
attr.conv2_filter_numel);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "findmax_copy_fusion");
}
xpu_conv2d(
dev_ctx.x_context(),
conv2_input_l3_data != nullptr ? conv2_input_l3_data : conv2_input_data,
conv2_filter_l3_data,
conv2_output_data,
conv2_input_max_data,
conv2_filter_max_data,
attr.conv2_input_shape,
attr.conv2_filter_shape,
attr.padding2,
attr.stride2,
attr.dilation2,
attr.group);
// 5. bn2
if (!attr.global_stats) {
DenseTensor *saved_var2 = saved_invstd2;
DenseTensor *running_mean2 = mean2_out;
DenseTensor *running_var2 = var2_out;
auto saved_mean2_data = dev_ctx.template Alloc<float>(saved_mean2);
auto saved_var2_data = dev_ctx.template Alloc<float>(saved_var2);
auto running_mean2_data = dev_ctx.template Alloc<float>(running_mean2);
auto running_var2_data = dev_ctx.template Alloc<float>(running_var2);
r = xpu::batch_norm_fusion<XPUType>(dev_ctx.x_context(),
conv2_output_data,
output_data,
attr.conv2_output_shape[0],
attr.conv2_output_shape[1],
attr.conv2_output_shape[2],
attr.conv2_output_shape[3],
attr.eps,
attr.momentum,
scale2_data,
bias2_data,
saved_mean2_data,
saved_var2_data,
running_mean2_data,
running_var2_data,
true,
z_out_data,
xpu::Activation_t::RELU,
nullptr,
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "batch_norm_fusion");
} else {
auto bn2_out_data = RAII_GUARD.alloc<XPUType>(attr.conv2_output_numel);
PADDLE_ENFORCE_XDNN_NOT_NULL(bn2_out_data);
const auto *mean2 = &mean2_in;
const auto *var2 = &var2_in;
const auto *mean_data = mean2->data<float>();
const auto *variance_data = var2->data<float>();
r = xpu::batch_norm_infer<XPUType>(dev_ctx.x_context(),
conv2_output_data,
bn2_out_data,
attr.conv2_output_shape[0],
attr.conv2_output_shape[1],
attr.conv2_output_shape[2],
attr.conv2_output_shape[3],
attr.eps,
scale2_data,
bias2_data,
mean_data,
variance_data,
true);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "batch_norm_infer");
r = xpu::add_activation_fusion<XPUType>(dev_ctx.x_context(),
bn2_out_data,
z_out_data,
output_data,
output->numel(),
nullptr,
nullptr,
nullptr,
xpu::Activation_t::RELU);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "add_activation_fusion");
}
}
} // namespace fusion
} // namespace phi
PD_REGISTER_KERNEL(resnet_basic_block,
XPU,
ALL_LAYOUT,
phi::fusion::ResNetBasicBlockXPUKernel,
float) {}