Files
2026-07-13 12:40:42 +08:00

2340 lines
97 KiB
C++

/* Copyright (c) 2016 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. */
#pragma once
#include <cstdint>
#include <vector>
#include "paddle/common/enforce.h"
#include "paddle/common/hostdevice.h"
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/backends/gpu/gpu_device_function.h"
#include "paddle/phi/backends/gpu/gpu_primitives.h"
#include "paddle/phi/kernels/funcs/cub.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/gpudnn/conv_gpudnn.h"
#include "paddle/phi/kernels/impl/conv_cudnn_impl.h"
namespace phi {
// To determine use cudnn or not.
struct DWConvParams {
bool has_fuse_relu_;
std::string data_format_;
std::vector<int> strides_;
std::vector<int> dilations_;
DWConvParams(const bool has_fuse_relu,
const std::string& data_format,
const std::vector<int>& strides,
const std::vector<int>& dilations)
: has_fuse_relu_(has_fuse_relu),
data_format_(data_format),
strides_(strides),
dilations_(dilations) {}
bool is_strided() const {
for (const auto& stride : strides_) {
if (stride != 1) return true;
}
return false;
}
bool is_dilated() const {
for (const auto& dilation : dilations_) {
if (dilation != 1) return true;
}
return false;
}
// Check if use cudnn for NHWC and NCHW FP16.
template <typename Context>
bool UseCudnnDepthwise(const Context& dev_ctx,
const DenseTensor& input,
const DenseTensor& filter) const {
// No fuse supported yet.
if (has_fuse_relu_) {
return false;
}
// Tensor Core introduced from Volta GPUs.
if (!IsVoltaOrLater(dev_ctx)) {
return false;
}
// Cudnn enable
if (!dynload::HasCUDNN()) {
return false;
}
// Only support FP16.
if (input.type() != DataType::FLOAT16 &&
filter.type() != DataType::FLOAT16) {
return false;
}
// Only support depthwise 2D.
if (input.dims().size() != 4) {
return false;
}
// No dilation and stride.
if (is_dilated() || is_strided()) {
return false;
}
// Make sure square filter.
// TODO(large-tensor): downstream functors may still use int; guard until
// upgraded.
int64_t ksize_height = filter.dims()[2];
// TODO(large-tensor): downstream functors may still use int; guard until
// upgraded.
int64_t ksize_width = filter.dims()[3];
if (ksize_height != ksize_width) {
return false;
}
// For 1/3/5/7 filter.
if (ksize_height != 1 && ksize_height != 3 && ksize_height != 5 &&
ksize_height != 7) {
return false;
}
// Use cudnn for nhwc fp16.
if (data_format_ == "NHWC") {
return true;
}
// TODO(Dmovic): Data format here is NCHW, enable when channel
// greater than 32, need benchmarks.
if (input.dims()[1] < 32) {
return false;
}
return true;
}
};
} // namespace phi
namespace phi::math {
/*
* \brief Compute the depthwise convolution which include
* forward process and backpropagation process
*/
template <typename Context, typename T, bool fuse_relu_before_conv = false>
class DepthwiseConvFunctor {
public:
void operator()(const Context& dev_ctx,
const DenseTensor& input,
const DenseTensor& filter,
const std::vector<int>& strides,
const std::vector<int>& paddings,
const std::vector<int>& dilations,
DenseTensor* output,
const DataLayout data_layout = DataLayout::NCHW);
};
template <typename Context, typename T, bool fuse_relu_before_conv = false>
class DepthwiseConvInputGradFunctor {
public:
void operator()(const Context& dev_ctx,
const DenseTensor& input,
const DenseTensor& filter,
const DenseTensor& output_grad,
const std::vector<int>& strides,
const std::vector<int>& paddings,
const std::vector<int>& dilations,
DenseTensor* input_grad,
const DataLayout data_layout = DataLayout::NCHW);
};
template <typename Context, typename T, bool fuse_relu_before_conv = false>
class DepthwiseConvFilterGradFunctor {
public:
void operator()(const Context& dev_ctx,
const DenseTensor& input,
const DenseTensor& output_grad,
const std::vector<int>& strides,
const std::vector<int>& paddings,
const std::vector<int>& dilations,
DenseTensor* filter_grad,
const DataLayout data_layout = DataLayout::NCHW);
};
#define FINAL_MASK 0xffffffff
#define HALF_WARP 16
#define WARP_SIZE 32
#define SMALL_THRESHOLD 64
template <typename T>
__forceinline__ __device__ T WarpReduceSum(T val, unsigned lane_mask) {
for (int mask = HALF_WARP; mask > 0; mask >>= 1)
val += backends::gpu::CudaShuffleDownSync(lane_mask, val, mask);
return val;
}
template <typename T>
__forceinline__ __device__ T BlockReduceSum(T val, unsigned mask = FINAL_MASK) {
static __shared__ T shared[WARP_SIZE];
int tid = threadIdx.y * blockDim.x + threadIdx.x;
int lane = tid & 0x1f;
int wid = tid >> 5;
val = WarpReduceSum<T>(val, mask);
__syncthreads();
if (lane == 0) shared[wid] = val;
__syncthreads();
// align block_span to WARP_SIZE
int block_span = (blockDim.x * blockDim.y + WARP_SIZE - 1) >> 5;
val = (lane < block_span) ? shared[lane] : static_cast<T>(0.0f);
val = WarpReduceSum<T>(val, mask);
return val;
}
#define ARG_DEFINE_KernelDepthwiseConv \
const T *const input_data, const T *const filter_data, const int batch_size, \
const int output_channels, const int output_height, \
const int output_width, const int input_channels, \
const int input_height, const int input_width, \
const int filter_multiplier, const int filter_height, \
const int filter_width, const int stride_height, const int stride_width, \
const int padding_height, const int padding_width, \
const int dilate_height, const int dilate_width, T *const output_data
// A Cuda kernel to compute the depthwise convolution forward pass
// in NCHW format.
template <typename T, int c_filter, bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvNCHW(
ARG_DEFINE_KernelDepthwiseConv) {
const int fw_size = c_filter != -1 ? c_filter : filter_width;
const int fh_size = c_filter != -1 ? c_filter : filter_height;
int64_t idx =
static_cast<int64_t>(threadIdx.x) +
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x);
if (idx >= static_cast<int64_t>(output_channels) * batch_size *
output_height * output_width)
return;
int64_t tmp_1 = idx / output_width;
const int64_t w_out = idx - tmp_1 * output_width;
int64_t tmp_2 = tmp_1 / output_height;
const int64_t h_out = tmp_1 - tmp_2 * output_height;
tmp_1 = tmp_2;
tmp_2 = tmp_1 / output_channels;
const int64_t c_out = tmp_1 - tmp_2 * output_channels;
const int64_t batch = tmp_2;
const int64_t c_in = c_out / filter_multiplier;
T value(0);
int64_t in_offset = (static_cast<int64_t>(batch) * input_channels + c_in) *
input_height * input_width;
int64_t weight_offset =
static_cast<int64_t>(c_out) * filter_height * filter_width;
int64_t h_in_start =
-padding_height + static_cast<int64_t>(h_out) * stride_height;
int64_t w_in_start =
-padding_width + static_cast<int64_t>(w_out) * stride_width;
int64_t h_in = h_in_start;
#pragma unroll
for (int fh = 0; fh < fh_size; fh++, h_in += dilate_height) {
int64_t w_in = w_in_start;
#pragma unroll
for (int fw = 0; fw < fw_size; fw++, w_in += dilate_width) {
if (h_in >= 0 && h_in < input_height && w_in >= 0 && w_in < input_width) {
int64_t offset = in_offset + h_in * input_width + w_in;
T in_data = input_data[offset];
if (fuse_relu_before_conv) {
value += filter_data[weight_offset] *
static_cast<T>(max(0.0f, static_cast<double>(in_data)));
} else {
value += filter_data[weight_offset] * in_data;
}
}
weight_offset++;
}
}
output_data[idx] = value;
}
// A Cuda kernel to compute the depthwise convolution forward pass
// in NHWC format.
template <typename T, int c_filter, bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvNHWC(
ARG_DEFINE_KernelDepthwiseConv) {
const int fw_size = c_filter != -1 ? c_filter : filter_width;
const int fh_size = c_filter != -1 ? c_filter : filter_height;
int64_t idx =
static_cast<int64_t>(threadIdx.x) +
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x);
if (idx >= static_cast<int64_t>(output_channels) * batch_size *
output_height * output_width) {
return;
}
int64_t tmp_1 = idx / output_channels;
const int64_t c_out = idx - tmp_1 * output_channels;
int64_t tmp_2 = tmp_1 / output_width;
const int64_t w_out = tmp_1 - tmp_2 * output_width;
tmp_1 = tmp_2;
tmp_2 = tmp_1 / output_height;
const int64_t h_out = tmp_1 - tmp_2 * output_height;
const int64_t batch = tmp_2;
const int64_t c_in = c_out / filter_multiplier;
T value(0);
const int64_t in_offset = static_cast<int64_t>(batch) * input_height *
input_width * input_channels +
c_in;
const int64_t h_in_start =
-padding_height + static_cast<int64_t>(h_out) * stride_height;
const int64_t w_in_start =
-padding_width + static_cast<int64_t>(w_out) * stride_width;
int64_t weight_offset = 0;
int64_t h_in = h_in_start;
#pragma unroll
for (int fh = 0; fh < fh_size; ++fh, h_in += dilate_height) {
int64_t w_in = w_in_start;
#pragma unroll
for (int fw = 0; fw < fw_size; ++fw, w_in += dilate_width) {
if (h_in >= 0 && h_in < input_height && w_in >= 0 && w_in < input_width) {
int64_t offset =
in_offset + (h_in * input_width + w_in) * input_channels;
T in_data = input_data[offset];
const T* weight = filter_data + weight_offset * output_channels + c_out;
if (fuse_relu_before_conv) {
value += weight[0] *
static_cast<T>(max(0.0f, static_cast<double>(in_data)));
} else {
value += weight[0] * in_data;
}
}
weight_offset++;
}
}
output_data[idx] = value;
}
template <typename T, int c_filter, bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvCFilterNCHW(
ARG_DEFINE_KernelDepthwiseConv) {
const int kWeightSize =
static_cast<int>(static_cast<int64_t>(c_filter) * c_filter);
T r_weight[kWeightSize];
const int batch = blockIdx.y;
const int c_out = blockIdx.x;
const T* weight = filter_data + static_cast<int64_t>(c_out) * kWeightSize;
for (int i = 0; i < kWeightSize; i++) r_weight[i] = weight[i];
for (int w_out = threadIdx.x; w_out < output_width; w_out += blockDim.x) {
for (int h_out = threadIdx.y; h_out < output_height; h_out += blockDim.y) {
const int batch = blockIdx.y;
const int c_out = blockIdx.x;
const int64_t c_in = c_out / filter_multiplier;
T value(0);
const int64_t h_in_start =
-padding_height + static_cast<int64_t>(h_out) * stride_height;
const int64_t w_in_start =
-padding_width + static_cast<int64_t>(w_out) * stride_width;
int64_t in_offset =
(static_cast<int64_t>(batch) * input_channels + c_in) * input_height *
input_width;
int64_t h_in = h_in_start;
for (int h_f = 0; h_f < c_filter; ++h_f, h_in += dilate_height) {
int64_t w_in = w_in_start;
for (int w_f = 0; w_f < c_filter; ++w_f, w_in += dilate_width) {
if (h_in >= 0 && h_in < input_height && w_in >= 0 &&
w_in < input_width) {
int64_t offset = in_offset + h_in * input_width + w_in;
if (fuse_relu_before_conv) {
value += r_weight[static_cast<int64_t>(h_f) * c_filter + w_f] *
static_cast<T>(
max(0.0f, static_cast<double>(input_data[offset])));
} else {
value += r_weight[static_cast<int64_t>(h_f) * c_filter + w_f] *
input_data[offset];
}
}
}
}
const int64_t index =
((static_cast<int64_t>(batch) * gridDim.x + c_out) * output_height +
h_out) *
output_width +
w_out;
output_data[index] = value;
}
}
}
template <typename T, int c_filter, bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvCFilterNHWC(
ARG_DEFINE_KernelDepthwiseConv) {
const int batch = blockIdx.z;
int64_t h_out = static_cast<int64_t>(blockIdx.x) * dilate_height +
static_cast<int64_t>(blockIdx.y);
if (h_out >= output_height) {
return;
}
int64_t in_offset =
static_cast<int64_t>(batch) * input_height * input_width * input_channels;
int64_t out_offset = (static_cast<int64_t>(batch) * output_height + h_out) *
output_width * output_channels;
const int64_t h_in_start =
-padding_height + static_cast<int64_t>(h_out) * stride_height;
const int64_t wi_size =
(static_cast<int64_t>(output_width) + dilate_width - 1) / dilate_width;
const int kWeightSize =
static_cast<int>(static_cast<int64_t>(c_filter) * c_filter);
T r_weight[kWeightSize];
for (int c_out = threadIdx.x; c_out < output_channels; c_out += blockDim.x) {
for (int i = 0; i < kWeightSize; i++) {
const T* weight =
filter_data + static_cast<int64_t>(i) * output_channels + c_out;
r_weight[i] = weight[0];
}
const int64_t c_in = c_out / filter_multiplier;
for (int64_t i = threadIdx.y; i < wi_size * dilate_width; i += blockDim.y) {
const int64_t i_dw = i / wi_size;
const int64_t i_wi = i - i_dw * wi_size;
const int64_t w_out = i_wi * dilate_width + i_dw;
if (w_out >= output_width) {
continue;
}
T value(0);
const int64_t w_in_start =
-padding_width + static_cast<int64_t>(w_out) * stride_width;
int64_t h_in = h_in_start;
for (int h_f = 0; h_f < c_filter; ++h_f, h_in += dilate_height) {
int64_t w_in = w_in_start;
for (int w_f = 0; w_f < c_filter; ++w_f, w_in += dilate_width) {
if (h_in >= 0 && h_in < input_height && w_in >= 0 &&
w_in < input_width) {
int64_t offset = in_offset +
(static_cast<int64_t>(h_in) * input_width + w_in) *
input_channels +
c_in;
if (fuse_relu_before_conv) {
value += r_weight[static_cast<int64_t>(h_f) * c_filter + w_f] *
static_cast<T>(
max(0.0, static_cast<double>(input_data[offset])));
} else {
value += r_weight[static_cast<int64_t>(h_f) * c_filter + w_f] *
input_data[offset];
}
}
}
}
int64_t index =
out_offset + static_cast<int64_t>(w_out) * output_channels + c_out;
output_data[index] = value;
}
}
}
template <typename T,
int c_filter_multiplier,
int c_stride,
int c_filter,
DataLayout data_layout,
bool fuse_relu_before_conv>
__global__ void KernelDepthwiseConvSp(ARG_DEFINE_KernelDepthwiseConv) {
int final_filter_multiplier = filter_multiplier;
int h_stride = stride_height;
int w_stride = stride_width;
if (c_filter_multiplier != 0) {
final_filter_multiplier = c_filter_multiplier;
h_stride = c_stride;
w_stride = c_stride;
}
if (c_filter == -1) {
if (data_layout != DataLayout::NHWC) {
KernelDepthwiseConvNCHW<T, c_filter, fuse_relu_before_conv>(
input_data,
filter_data,
batch_size,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
output_data);
} else {
KernelDepthwiseConvNHWC<T, c_filter, fuse_relu_before_conv>(
input_data,
filter_data,
batch_size,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
output_data);
}
} else {
if (data_layout != DataLayout::NHWC) {
KernelDepthwiseConvCFilterNCHW<T, c_filter, fuse_relu_before_conv>(
input_data,
filter_data,
batch_size,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
output_data);
} else {
KernelDepthwiseConvCFilterNHWC<T, c_filter, fuse_relu_before_conv>(
input_data,
filter_data,
batch_size,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
output_data);
}
}
}
// CUDA kernel to compute the depthwise convolution backprop w.r.t input.
#define ARG_DEFINE_KernelDepthwiseConvInputGrad \
const T *const input_data, const T *const output_grad_data, \
const T *const filter_data, const int batch_size, \
const int output_channels, const int output_height, \
const int output_width, const int input_channels, \
const int input_height, const int input_width, \
const int filter_multiplier, const int filter_height, \
const int filter_width, const int stride_height, const int stride_width, \
const int padding_height, const int padding_width, \
const int dilate_height, const int dilate_width, \
T *const input_grad_data
template <typename T, int c_filter, bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvInputGradNCHW(
ARG_DEFINE_KernelDepthwiseConvInputGrad) {
const int fw_size = c_filter != -1 ? c_filter : filter_width;
const int fh_size = c_filter != -1 ? c_filter : filter_height;
int64_t idx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
if (idx >= static_cast<int64_t>(batch_size) * input_channels * input_height *
input_width) {
return;
}
if (fuse_relu_before_conv) {
if (input_data[idx] <= static_cast<T>(0.0f)) {
input_grad_data[idx] = 0;
return;
}
}
int64_t tmp_1 = idx / input_width;
const int64_t w_in = idx - tmp_1 * input_width;
int64_t tmp_2 = tmp_1 / input_height;
const int64_t h_in = tmp_1 - tmp_2 * input_height;
tmp_1 = tmp_2;
tmp_2 = tmp_1 / input_channels;
const int64_t c_in = tmp_1 - tmp_2 * input_channels;
const int64_t batch = tmp_2;
T value(0);
for (int c_mul = 0; c_mul < filter_multiplier; ++c_mul) {
int64_t c_out = c_in * filter_multiplier + c_mul;
int64_t filter_offset =
static_cast<int64_t>(c_out) * filter_height * filter_width;
#pragma unroll
for (int fh = 0; fh < fh_size; ++fh) {
#pragma unroll
for (int fw = 0; fw < fw_size; ++fw) {
int64_t h_out =
h_in + padding_height - static_cast<int64_t>(fh) * dilate_height;
int64_t w_out =
w_in + padding_width - static_cast<int64_t>(fw) * dilate_width;
if ((h_out - h_out / stride_height * stride_height == 0) &&
(w_out - w_out / stride_width * stride_width == 0)) {
h_out /= stride_height;
w_out /= stride_width;
if (h_out >= 0 && h_out < output_height && w_out >= 0 &&
w_out < output_width) {
int64_t output_grad_offset =
((static_cast<int64_t>(batch) * output_channels + c_out) *
output_height +
h_out) *
output_width +
w_out;
value += output_grad_data[output_grad_offset] *
filter_data[filter_offset];
}
}
filter_offset++;
}
}
}
input_grad_data[idx] = value;
}
template <typename T, bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvInputGradNHWC(
ARG_DEFINE_KernelDepthwiseConvInputGrad) {
const int batch = blockIdx.z;
int64_t h_in = static_cast<int64_t>(blockIdx.x) * dilate_height +
static_cast<int64_t>(blockIdx.y);
if (h_in >= input_height) {
return;
}
for (int c_in = threadIdx.x; c_in < input_channels; c_in += blockDim.x) {
for (int w_in = threadIdx.y; w_in < input_width; w_in += blockDim.y) {
int64_t h_out_start =
h_in - (filter_height - 1) * static_cast<int64_t>(dilate_height) +
padding_height;
int64_t w_out_start =
w_in - (filter_width - 1) * static_cast<int64_t>(dilate_width) +
padding_width;
T value(0);
int64_t index =
((static_cast<int64_t>(batch) * input_height + h_in) * input_width +
w_in) *
input_channels +
c_in;
if (fuse_relu_before_conv) {
if (input_data[index] <= T(0)) {
input_grad_data[index] = 0;
continue;
}
}
for (int c_i = 0; c_i < filter_multiplier; c_i++) {
int64_t c_out = static_cast<int64_t>(c_in) * filter_multiplier + c_i;
int64_t weight_offset =
static_cast<int64_t>(filter_height) * filter_width;
int64_t h_out = h_out_start;
for (int h_f = 0; h_f < filter_height; ++h_f, h_out += dilate_height) {
int64_t w_out = w_out_start;
for (int w_f = 0; w_f < filter_width; ++w_f, w_out += dilate_width) {
weight_offset--;
int64_t s_h_out = h_out / stride_height;
int64_t s_w_out = w_out / stride_width;
if (h_out % stride_height == 0 && w_out % stride_width == 0 &&
s_h_out >= 0 && s_h_out < output_height && s_w_out >= 0 &&
s_w_out < output_width) {
int64_t output_grad_offset =
((static_cast<int64_t>(batch) * output_height + s_h_out) *
output_width +
s_w_out) *
output_channels +
c_out;
int64_t filter_offset = weight_offset * output_channels + c_out;
value += output_grad_data[output_grad_offset] *
filter_data[filter_offset];
}
}
}
}
input_grad_data[index] = value;
}
}
}
template <typename T,
int c_filter,
int c_filter_multiplier,
bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvInputGradCFilterNCHW(
ARG_DEFINE_KernelDepthwiseConvInputGrad) {
const int kFilterSize =
static_cast<int>(static_cast<int64_t>(c_filter) * c_filter);
const int kWeightSize = static_cast<int>(static_cast<int64_t>(kFilterSize) *
c_filter_multiplier) +
1;
T r_weight[kWeightSize];
const int batch = blockIdx.y;
const int c_in = blockIdx.x;
for (int c_i = 0; c_i < filter_multiplier; c_i++) {
int64_t c_out = static_cast<int64_t>(c_in) * filter_multiplier + c_i;
const T* weight = filter_data + static_cast<int64_t>(c_out) * kFilterSize;
for (int i = 0; i < kFilterSize; i++)
r_weight[i + c_i * kFilterSize] = weight[kFilterSize - i - 1];
}
for (int w_in = threadIdx.x; w_in < input_width; w_in += blockDim.x) {
for (int h_in = threadIdx.y; h_in < input_height; h_in += blockDim.y) {
int64_t h_out_start =
h_in - (c_filter - 1) * static_cast<int64_t>(dilate_height) +
padding_height;
int64_t w_out_start =
w_in - (c_filter - 1) * static_cast<int64_t>(dilate_width) +
padding_width;
T value(0);
const int64_t index =
((static_cast<int64_t>(batch) * gridDim.x + c_in) * input_height +
h_in) *
input_width +
w_in;
if (fuse_relu_before_conv) {
if (input_data[index] <= T(0)) {
input_grad_data[index] = 0;
continue;
}
}
for (int c_i = 0; c_i < filter_multiplier; c_i++) {
int64_t c_out = static_cast<int64_t>(c_in) * filter_multiplier + c_i;
int64_t h_out = h_out_start;
for (int h_f = 0; h_f < c_filter; ++h_f, h_out += dilate_height) {
int64_t w_out = w_out_start;
for (int w_f = 0; w_f < c_filter; ++w_f, w_out += dilate_width) {
int64_t s_h_out = h_out / stride_height;
int64_t s_w_out = w_out / stride_width;
if (h_out % stride_height == 0 && w_out % stride_width == 0 &&
s_h_out >= 0 && s_h_out < output_height && s_w_out >= 0 &&
s_w_out < output_width) {
int64_t output_grad_offset =
((static_cast<int64_t>(batch) * output_channels + c_out) *
output_height +
s_h_out) *
output_width +
s_w_out;
value += output_grad_data[output_grad_offset] *
r_weight[static_cast<int64_t>(h_f) * c_filter + w_f +
c_i * kFilterSize];
}
}
}
}
input_grad_data[index] = value;
}
}
}
template <typename T,
int c_filter,
int c_filter_multiplier,
bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvInputGradCFilterNHWC(
ARG_DEFINE_KernelDepthwiseConvInputGrad) {
int64_t h_in = static_cast<int64_t>(blockIdx.x) * dilate_height +
static_cast<int64_t>(blockIdx.y);
if (h_in >= input_height) {
return;
}
const int kFilterSize =
static_cast<int>(static_cast<int64_t>(c_filter) * c_filter);
const int kWeightSize = static_cast<int>(static_cast<int64_t>(kFilterSize) *
c_filter_multiplier) +
1;
T r_weight[kWeightSize];
const int batch = blockIdx.z;
const int64_t wi_size =
(static_cast<int64_t>(input_width) + dilate_width - 1) / dilate_width;
const int64_t h_out_start =
h_in - (c_filter - 1) * static_cast<int64_t>(dilate_height) +
padding_height;
for (int c_in = threadIdx.x; c_in < input_channels; c_in += blockDim.x) {
for (int c_i = 0; c_i < c_filter_multiplier; c_i++) {
int64_t c_out = static_cast<int64_t>(c_in) * c_filter_multiplier + c_i;
for (int i = 0; i < kFilterSize; i++)
r_weight[i + c_i * kFilterSize] =
filter_data[static_cast<int64_t>(kFilterSize - i - 1) *
output_channels +
c_out];
}
for (int64_t i = threadIdx.y; i < wi_size * dilate_width; i += blockDim.y) {
const int64_t i_dw = i / wi_size;
const int64_t i_wi = i - i_dw * wi_size;
const int64_t w_in = i_wi * dilate_width + i_dw;
if (w_in >= input_width) {
continue;
}
int64_t w_out_start =
w_in - (c_filter - 1) * static_cast<int64_t>(dilate_width) +
padding_width;
T value(0);
int64_t index =
((static_cast<int64_t>(batch) * input_height + h_in) * input_width +
w_in) *
input_channels +
c_in;
if (fuse_relu_before_conv) {
if (input_data[index] <= T(0)) {
input_grad_data[index] = 0;
continue;
}
}
for (int c_i = 0; c_i < c_filter_multiplier; c_i++) {
int64_t c_out = static_cast<int64_t>(c_in) * c_filter_multiplier + c_i;
int64_t h_out = h_out_start;
for (int h_f = 0; h_f < c_filter; ++h_f, h_out += dilate_height) {
int64_t w_out = w_out_start;
for (int w_f = 0; w_f < c_filter; ++w_f, w_out += dilate_width) {
int64_t s_h_out = h_out / stride_height;
int64_t s_w_out = w_out / stride_width;
if (h_out % stride_height == 0 && w_out % stride_width == 0 &&
s_h_out >= 0 && s_h_out < output_height && s_w_out >= 0 &&
s_w_out < output_width) {
int64_t output_grad_offset =
((static_cast<int64_t>(batch) * output_height + s_h_out) *
output_width +
s_w_out) *
output_channels +
c_out;
value += output_grad_data[output_grad_offset] *
r_weight[static_cast<int64_t>(h_f) * c_filter + w_f +
c_i * kFilterSize];
}
}
}
}
input_grad_data[index] = value;
}
}
}
template <typename T,
int c_filter_multiplier,
int c_stride,
int c_filter,
DataLayout data_layout,
bool fuse_relu_before_conv>
__global__ void KernelDepthwiseConvInputGradSp(
ARG_DEFINE_KernelDepthwiseConvInputGrad) {
int final_filter_multiplier = filter_multiplier;
int h_stride = stride_height;
int w_stride = stride_width;
if (c_filter_multiplier != 0) {
final_filter_multiplier = c_filter_multiplier;
h_stride = c_stride;
w_stride = c_stride;
}
if (c_filter_multiplier == 0 || c_filter == -1) {
if (data_layout != DataLayout::NHWC) {
KernelDepthwiseConvInputGradNCHW<T, c_filter, fuse_relu_before_conv>(
input_data,
output_grad_data,
filter_data,
batch_size,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
input_grad_data);
} else {
KernelDepthwiseConvInputGradNHWC<T, fuse_relu_before_conv>(
input_data,
output_grad_data,
filter_data,
batch_size,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
input_grad_data);
}
} else {
if (data_layout != DataLayout::NHWC) {
KernelDepthwiseConvInputGradCFilterNCHW<T,
c_filter,
c_filter_multiplier,
fuse_relu_before_conv>(
input_data,
output_grad_data,
filter_data,
batch_size,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
c_filter_multiplier,
filter_height,
filter_width,
c_stride,
c_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
input_grad_data);
} else {
KernelDepthwiseConvInputGradCFilterNHWC<T,
c_filter,
c_filter_multiplier,
fuse_relu_before_conv>(
input_data,
output_grad_data,
filter_data,
batch_size,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
c_filter_multiplier,
filter_height,
filter_width,
c_stride,
c_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
input_grad_data);
}
}
}
// Cuda kernel to compute the depthwise convolution backprop w.r.t. filter.
template <typename T, bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvFilterGradNCHW(
const T* output_grad_data,
const T* input_data,
const int num,
const int output_channels,
const int output_height,
const int output_width,
const int input_channels,
const int input_height,
const int input_width,
const int filter_multiplier,
const int filter_height,
const int filter_width,
const int stride_height,
const int stride_width,
const int padding_height,
const int padding_width,
const int dilate_height,
const int dilate_width,
T* filter_grad_data) {
T f_grad(0);
const bool loop_batch =
static_cast<int64_t>(output_height) * output_width >= WARP_SIZE;
int kw_id = blockIdx.x;
int kh_id = blockIdx.y;
int oc_id = blockIdx.z;
int ic_id = oc_id / filter_multiplier;
int64_t idx =
((static_cast<int64_t>(blockIdx.z) * static_cast<int64_t>(gridDim.y)) +
static_cast<int64_t>(blockIdx.y)) *
static_cast<int64_t>(gridDim.x) +
static_cast<int64_t>(blockIdx.x);
const int64_t ohw = static_cast<int64_t>(output_height) * output_width;
const int64_t onhw = static_cast<int64_t>(num) * ohw;
const int64_t h_offset =
static_cast<int64_t>(kh_id) * dilate_height - padding_height;
const int64_t w_offset =
static_cast<int64_t>(kw_id) * dilate_width - padding_width;
if (loop_batch) {
for (int og_w = threadIdx.x; og_w < output_width; og_w += blockDim.x) {
for (int bid = 0; bid < num; ++bid) {
for (int og_h = threadIdx.y; og_h < output_height; og_h += blockDim.y) {
int64_t i_h = static_cast<int64_t>(og_h) * stride_height + h_offset;
int64_t i_w = static_cast<int64_t>(og_w) * stride_width + w_offset;
if (i_w >= 0 && i_w < input_width && i_h >= 0 && i_h < input_height) {
int64_t input_offset =
((static_cast<int64_t>(bid) * input_channels + ic_id) *
input_height +
i_h) *
input_width +
i_w;
int64_t output_grad_offset =
((static_cast<int64_t>(bid) * output_channels + oc_id) *
output_height +
og_h) *
output_width +
og_w;
if (fuse_relu_before_conv) {
f_grad +=
output_grad_data[output_grad_offset] *
static_cast<T>(
max(0.0f, static_cast<double>(input_data[input_offset])));
} else {
f_grad += output_grad_data[output_grad_offset] *
input_data[input_offset];
}
}
}
}
}
} else {
for (int64_t id = threadIdx.x; id < onhw; id += blockDim.x) {
int64_t bid = id / ohw;
int64_t og_hw = id - bid * ohw;
int64_t og_h = og_hw / output_width;
int64_t og_w = og_hw - og_h * output_width;
int64_t i_h = static_cast<int64_t>(og_h) * stride_height + h_offset;
int64_t i_w = static_cast<int64_t>(og_w) * stride_width + w_offset;
if (i_w >= 0 && i_w < input_width && i_h >= 0 && i_h < input_height) {
int64_t input_offset =
((static_cast<int64_t>(bid) * input_channels + ic_id) *
input_height +
i_h) *
input_width +
i_w;
int64_t output_grad_offset =
(static_cast<int64_t>(bid) * output_channels + oc_id) * ohw + og_hw;
if (fuse_relu_before_conv) {
f_grad += output_grad_data[output_grad_offset] *
static_cast<T>(max(
0.0f, static_cast<double>(input_data[input_offset])));
} else {
f_grad +=
output_grad_data[output_grad_offset] * input_data[input_offset];
}
}
}
}
T val = BlockReduceSum<T>(f_grad);
if (threadIdx.x == 0 && threadIdx.y == 0) {
filter_grad_data[idx] = val;
}
}
template <typename T,
typename index_t,
typename std::enable_if_t<std::is_same_v<phi::float16, T>>* = nullptr>
__device__ __forceinline__ void NoReturnAtomicAdd(T* tensor,
index_t index,
const index_t numel,
T value) {
#if (defined(PADDLE_WITH_HIP) || \
(defined(__CUDA_ARCH__) && (__CUDA_ARCH__ < 700)))
CudaAtomicAdd(tensor + index, value);
#else
// Check if 32 bit aligned
__half* target_addr = reinterpret_cast<__half*>(tensor + index);
bool low_byte =
(reinterpret_cast<std::uintptr_t>(target_addr) % sizeof(__half2) == 0);
if (low_byte && index < (numel - 1)) {
__half2 value2;
value2.x = value.to_half();
value2.y = __int2half_rz(0);
atomicAdd(reinterpret_cast<__half2*>(target_addr), value2);
} else if (!low_byte && index > 0) {
__half2 value2;
value2.x = __int2half_rz(0);
value2.y = value.to_half();
atomicAdd(reinterpret_cast<__half2*>(target_addr - 1), value2);
} else {
atomicAdd(reinterpret_cast<__half*>(tensor) + index, value.to_half());
}
#endif
}
template <
typename T,
typename index_t,
typename std::enable_if_t<std::is_same_v<phi::bfloat16, T>>* = nullptr>
__device__ __forceinline__ void NoReturnAtomicAdd(T* tensor,
index_t index,
const index_t numel,
T value) {
#if (defined(PADDLE_WITH_HIP) || \
(defined(__CUDA_ARCH__) && (__CUDA_ARCH__ < 800)))
CudaAtomicAdd(tensor + index, value);
#else
// Check if 32 bit aligned
__nv_bfloat16* target_addr = reinterpret_cast<__nv_bfloat16*>(tensor + index);
bool low_byte =
(reinterpret_cast<std::uintptr_t>(target_addr) % sizeof(__nv_bfloat16) ==
0);
if (low_byte && index < (numel - 1)) {
__nv_bfloat162 value2;
value2.x = value.to_nv_bfloat16();
value2.y = __int2bfloat16_rz(0);
atomicAdd(reinterpret_cast<__nv_bfloat162*>(target_addr), value2);
} else if (!low_byte && index > 0) {
__nv_bfloat162 value2;
value2.x = __int2bfloat16_rz(0);
value2.y = value.to_nv_bfloat16();
atomicAdd(reinterpret_cast<__nv_bfloat162*>(target_addr - 1), value2);
} else {
atomicAdd(reinterpret_cast<__nv_bfloat16*>(tensor) + index,
value.to_nv_bfloat16());
}
#endif
}
template <
typename T,
typename index_t,
typename std::enable_if_t<!std::is_same_v<phi::float16, T> &&
!std::is_same_v<phi::bfloat16, T>>* = nullptr>
__device__ __forceinline__ void NoReturnAtomicAdd(T* tensor,
index_t index,
const index_t numel,
T value) {
CudaAtomicAdd(tensor + index, value);
}
template <typename T, bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvFilterGradNHWC(
const T* output_grad_data,
const T* input_data,
const int num,
const int output_channels,
const int output_height,
const int output_width,
const int input_channels,
const int input_height,
const int input_width,
const int filter_multiplier,
const int filter_height,
const int filter_width,
const int stride_height,
const int stride_width,
const int padding_height,
const int padding_width,
const int dilate_height,
const int dilate_width,
T* filter_grad_data) {
int bid = blockIdx.z;
int image_h = blockIdx.y;
int kernel_iw = blockIdx.x % filter_width;
int kernel_ih = blockIdx.x / filter_width;
for (int kernel_id = threadIdx.x; kernel_id < output_channels;
kernel_id += blockDim.x) {
T s(0);
int64_t gbid =
(static_cast<int64_t>(kernel_id) * filter_height + kernel_ih) *
filter_width +
kernel_iw;
for (int image_w = threadIdx.y; image_w < output_width;
image_w += blockDim.y) {
int64_t kernel_h =
static_cast<int64_t>(kernel_ih) * dilate_height - padding_height;
int64_t kernel_w =
static_cast<int64_t>(kernel_iw) * dilate_width - padding_width;
int64_t image_hk =
static_cast<int64_t>(image_h) * stride_height + kernel_h;
int64_t image_wk =
static_cast<int64_t>(image_w) * stride_width + kernel_w;
if (image_hk < 0 || image_hk >= input_height) continue;
if (image_wk < 0 || image_wk >= input_width) continue;
int64_t input_id =
((static_cast<int64_t>(bid) * input_height + image_hk) * input_width +
image_wk) *
input_channels +
kernel_id / filter_multiplier;
int64_t output_id =
((static_cast<int64_t>(bid) * output_height + image_h) *
output_width +
image_w) *
output_channels +
kernel_id;
if (fuse_relu_before_conv) {
s += output_grad_data[output_id] *
static_cast<T>(
max(0.0f, static_cast<double>(input_data[input_id])));
} else {
s += output_grad_data[output_id] * input_data[input_id];
}
}
const int64_t numel =
static_cast<int64_t>(output_channels) * filter_width * filter_height;
NoReturnAtomicAdd(filter_grad_data, gbid, numel, s);
}
}
template <typename T, int c_filter, bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvFilterGradCFilterNHWC(
const T* output_grad_data,
const T* input_data,
const int num,
const int output_channels,
const int output_height,
const int output_width,
const int input_channels,
const int input_height,
const int input_width,
const int filter_multiplier,
const int filter_height,
const int filter_width,
const int stride_height,
const int stride_width,
const int padding_height,
const int padding_width,
const int dilate_height,
const int dilate_width,
T* filter_grad_data) {
const int bid = blockIdx.z;
int64_t image_h = static_cast<int64_t>(blockIdx.x) * dilate_height +
static_cast<int64_t>(blockIdx.y);
if (image_h >= output_height) {
return;
}
const int kWeightSize =
static_cast<int>(static_cast<int64_t>(c_filter) * c_filter);
T r_weight[kWeightSize];
const int64_t wi_size =
(static_cast<int64_t>(output_width) + dilate_width - 1) / dilate_width;
for (int kernel_id = threadIdx.x; kernel_id < output_channels;
kernel_id += blockDim.x) {
for (int i = 0; i < kWeightSize; ++i) {
r_weight[i] = 0;
}
for (int64_t i = threadIdx.y; i < wi_size * dilate_width; i += blockDim.y) {
const int64_t i_dw = i / wi_size;
const int64_t i_wi = i - i_dw * wi_size;
const int64_t image_w = i_wi * dilate_width + i_dw;
if (image_w >= output_width) {
continue;
}
for (int kernel_ih = 0; kernel_ih < c_filter; ++kernel_ih) {
for (int kernel_iw = 0; kernel_iw < c_filter; ++kernel_iw) {
int64_t kernel_h =
static_cast<int64_t>(kernel_ih) * dilate_height - padding_height;
int64_t kernel_w =
static_cast<int64_t>(kernel_iw) * dilate_width - padding_width;
int64_t image_hk =
static_cast<int64_t>(image_h) * stride_height + kernel_h;
int64_t image_wk =
static_cast<int64_t>(image_w) * stride_width + kernel_w;
if (image_hk < 0 || image_hk >= input_height) continue;
if (image_wk < 0 || image_wk >= input_width) continue;
int64_t input_id =
((static_cast<int64_t>(bid) * input_height + image_hk) *
input_width +
image_wk) *
input_channels +
kernel_id / filter_multiplier;
int64_t output_id =
((static_cast<int64_t>(bid) * output_height + image_h) *
output_width +
image_w) *
output_channels +
kernel_id;
T s(0);
if (fuse_relu_before_conv) {
s = output_grad_data[output_id] *
static_cast<T>(
max(0.0f, static_cast<double>(input_data[input_id])));
} else {
s = output_grad_data[output_id] * input_data[input_id];
}
r_weight[static_cast<int64_t>(kernel_ih) * c_filter + kernel_iw] += s;
}
}
}
const int64_t numel = static_cast<int64_t>(output_channels) * kWeightSize;
for (int i = 0; i < kWeightSize; ++i) {
NoReturnAtomicAdd(filter_grad_data,
static_cast<int64_t>(i) * output_channels + kernel_id,
numel,
r_weight[i]);
}
}
}
template <typename T, int c_filter, bool fuse_relu_before_conv>
__device__ __inline__ void KernelDepthwiseConvFilterGradCFilterSmallChannelNHWC(
const T* output_grad_data,
const T* input_data,
const int num,
const int output_channels,
const int output_height,
const int output_width,
const int input_channels,
const int input_height,
const int input_width,
const int filter_multiplier,
const int filter_height,
const int filter_width,
const int stride_height,
const int stride_width,
const int padding_height,
const int padding_width,
const int dilate_height,
const int dilate_width,
T* filter_grad_data) {
const int bid = blockIdx.y;
const int64_t idx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
const int64_t numel = static_cast<int64_t>(output_channels) *
static_cast<int64_t>(c_filter) * c_filter;
if (idx >= numel) {
return;
}
const int64_t tmp = idx / output_channels;
const int64_t kernel_id = idx - tmp * output_channels;
const int64_t kernel_ih = tmp / c_filter;
const int64_t kernel_iw = tmp - kernel_ih * c_filter;
const int64_t h_offset =
static_cast<int64_t>(kernel_ih) * dilate_height - padding_height;
const int64_t w_offset =
static_cast<int64_t>(kernel_iw) * dilate_width - padding_width;
T s(0);
for (int og_h = 0; og_h < output_height; ++og_h) {
for (int og_w = 0; og_w < output_width; ++og_w) {
int64_t image_hk = static_cast<int64_t>(og_h) * stride_height + h_offset;
int64_t image_wk = static_cast<int64_t>(og_w) * stride_width + w_offset;
if (image_hk >= 0 && image_hk < input_height && image_wk >= 0 &&
image_wk < input_width) {
int64_t input_id =
((static_cast<int64_t>(bid) * input_height + image_hk) *
input_width +
image_wk) *
input_channels +
kernel_id / filter_multiplier;
int64_t output_id =
((static_cast<int64_t>(bid) * output_height + og_h) * output_width +
og_w) *
output_channels +
kernel_id;
if (fuse_relu_before_conv) {
s += output_grad_data[output_id] *
static_cast<T>(
max(0.0f, static_cast<double>(input_data[input_id])));
} else {
s += output_grad_data[output_id] * input_data[input_id];
}
}
}
}
NoReturnAtomicAdd(filter_grad_data, idx, numel, s);
}
template <typename T,
int c_filter_multiplier,
int c_stride,
int c_filter,
DataLayout data_layout,
bool fuse_relu_before_conv>
__global__ void KernelDepthwiseConvFilterGradSp(const T* output_grad_data,
const T* input_data,
const int num,
const int output_channels,
const int output_height,
const int output_width,
const int input_channels,
const int input_height,
const int input_width,
const int filter_multiplier,
const int filter_height,
const int filter_width,
const int stride_height,
const int stride_width,
const int padding_height,
const int padding_width,
const int dilate_height,
const int dilate_width,
T* filter_grad_data) {
int final_filter_multiplier = filter_multiplier;
int h_stride = stride_height;
int w_stride = stride_width;
if (c_filter_multiplier != 0) {
final_filter_multiplier = c_filter_multiplier;
h_stride = c_stride;
w_stride = c_stride;
}
if (c_filter_multiplier == 0 || c_filter == -1) {
if (data_layout != DataLayout::NHWC) {
KernelDepthwiseConvFilterGradNCHW<T, fuse_relu_before_conv>(
output_grad_data,
input_data,
num,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
filter_grad_data);
} else {
KernelDepthwiseConvFilterGradNHWC<T, fuse_relu_before_conv>(
output_grad_data,
input_data,
num,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
filter_grad_data);
}
} else {
if (data_layout != DataLayout::NHWC) {
KernelDepthwiseConvFilterGradNCHW<T, fuse_relu_before_conv>(
output_grad_data,
input_data,
num,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
filter_grad_data);
} else {
if (output_channels < SMALL_THRESHOLD) {
KernelDepthwiseConvFilterGradCFilterSmallChannelNHWC<
T,
c_filter,
fuse_relu_before_conv>(output_grad_data,
input_data,
num,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
filter_grad_data);
} else {
KernelDepthwiseConvFilterGradCFilterNHWC<T,
c_filter,
fuse_relu_before_conv>(
output_grad_data,
input_data,
num,
output_channels,
output_height,
output_width,
input_channels,
input_height,
input_width,
final_filter_multiplier,
filter_height,
filter_width,
h_stride,
w_stride,
padding_height,
padding_width,
dilate_height,
dilate_width,
filter_grad_data);
}
}
}
}
/*
* All tensors are in NCHW format.
* Ksize, strides, paddings are two elements. These two elements represent
* height and width, respectively.
*/
template <class T, bool fuse_relu_before_conv>
class DepthwiseConvFunctor<GPUContext, T, fuse_relu_before_conv> {
public:
void operator()(const GPUContext& dev_ctx,
const DenseTensor& input,
const DenseTensor& filter,
const std::vector<int>& strides,
const std::vector<int>& paddings,
const std::vector<int>& dilations,
DenseTensor* output,
const DataLayout data_layout = DataLayout::NCHW) {
const int64_t batch_size = input.dims()[0];
const int64_t input_channels_64 =
(data_layout != DataLayout::NHWC ? input.dims()[1] : input.dims()[3]);
const int64_t input_height_64 =
(data_layout != DataLayout::NHWC ? input.dims()[2] : input.dims()[1]);
const int64_t input_width_64 =
(data_layout != DataLayout::NHWC ? input.dims()[3] : input.dims()[2]);
const int64_t output_channels_64 =
(data_layout != DataLayout::NHWC ? output->dims()[1]
: output->dims()[3]);
const int64_t output_height_64 =
(data_layout != DataLayout::NHWC ? output->dims()[2]
: output->dims()[1]);
const int64_t output_width_64 =
(data_layout != DataLayout::NHWC ? output->dims()[3]
: output->dims()[2]);
const int64_t ksize_height_64 = filter.dims()[2];
const int64_t ksize_width_64 = filter.dims()[3];
PADDLE_ENFORCE_LE_INT_MAX(batch_size, "depthwise conv batch size");
PADDLE_ENFORCE_LE_INT_MAX(input_channels_64,
"depthwise conv input channels");
PADDLE_ENFORCE_LE_INT_MAX(input_height_64, "depthwise conv input height");
PADDLE_ENFORCE_LE_INT_MAX(input_width_64, "depthwise conv input width");
PADDLE_ENFORCE_LE_INT_MAX(output_channels_64,
"depthwise conv output channels");
PADDLE_ENFORCE_LE_INT_MAX(output_height_64, "depthwise conv output height");
PADDLE_ENFORCE_LE_INT_MAX(output_width_64, "depthwise conv output width");
PADDLE_ENFORCE_LE_INT_MAX(ksize_height_64, "depthwise conv kernel height");
PADDLE_ENFORCE_LE_INT_MAX(ksize_width_64, "depthwise conv kernel width");
const int batch_size_int = static_cast<int>(batch_size);
const int input_channels = static_cast<int>(input_channels_64);
const int input_height = static_cast<int>(input_height_64);
const int input_width = static_cast<int>(input_width_64);
const int output_channels = static_cast<int>(output_channels_64);
const int output_height = static_cast<int>(output_height_64);
const int output_width = static_cast<int>(output_width_64);
const int ksize_height = static_cast<int>(ksize_height_64);
const int ksize_width = static_cast<int>(ksize_width_64);
const int stride_height = strides[0];
const int stride_width = strides[1];
const int padding_height = paddings[0];
const int padding_width = paddings[1];
const int dilate_height = dilations[0];
const int dilate_width = dilations[1];
const T* input_data = input.data<T>();
const T* filter_data = filter.data<T>();
T* output_data = dev_ctx.template Alloc<T>(output);
DenseTensor filter_hwc;
if (data_layout == DataLayout::NHWC) {
DDim filter_hwc_dims({filter.dims()[2],
filter.dims()[3],
filter.dims()[0],
filter.dims()[1]});
filter_hwc.Resize(filter_hwc_dims);
dev_ctx.template Alloc<T>(&filter_hwc);
std::vector<int> perm_axis({2, 3, 0, 1});
funcs::TransposeNormal<GPUContext, T> trans;
trans(dev_ctx, filter, &filter_hwc, perm_axis);
filter_data = filter_hwc.data<T>();
}
int thread = 512;
int blocks;
dim3 threads;
dim3 grid;
const auto& max_grid_dim_size = dev_ctx.GetCUDAMaxGridDimSize();
if (data_layout != DataLayout::NHWC) {
if (output_width > 1024 && output_width <= 2048)
thread = (output_width - 1) / 2 + 1;
else if (output_width > 512 && output_width <= 1024)
thread = output_width;
blocks = std::min(std::max(thread / output_width, 1), output_height);
const int thread_x = std::min(output_width, thread);
threads = dim3(thread_x, blocks, 1);
PADDLE_ENFORCE_LE(output_channels,
max_grid_dim_size[0],
common::errors::InvalidArgument(
"depthwise conv grid.x exceeds device limit."));
PADDLE_ENFORCE_LE(batch_size_int,
max_grid_dim_size[1],
common::errors::InvalidArgument(
"depthwise conv grid.y exceeds device limit."));
grid = dim3(output_channels, batch_size_int, 1);
} else {
const int64_t block_y_64 =
((static_cast<int64_t>(output_width) + dilate_width - 1) /
dilate_width) *
dilate_width;
blocks = static_cast<int>(
std::min<int64_t>(std::max(thread / output_channels, 1), block_y_64));
const int thread_x = std::min(output_channels, thread);
const int64_t grid_x =
(static_cast<int64_t>(output_height) + dilate_height - 1) /
dilate_height;
PADDLE_ENFORCE_LE_INT_MAX(grid_x, "depthwise conv CUDA grid x size");
PADDLE_ENFORCE_LE(grid_x,
static_cast<int64_t>(max_grid_dim_size[0]),
common::errors::InvalidArgument(
"depthwise conv grid.x exceeds device limit."));
PADDLE_ENFORCE_LE(dilate_height,
max_grid_dim_size[1],
common::errors::InvalidArgument(
"depthwise conv grid.y exceeds device limit."));
PADDLE_ENFORCE_LE(batch_size,
static_cast<int64_t>(max_grid_dim_size[2]),
common::errors::InvalidArgument(
"depthwise conv grid.z exceeds device limit."));
threads = dim3(thread_x, blocks, 1);
grid = dim3(static_cast<uint32_t>(grid_x),
static_cast<uint32_t>(dilate_height),
static_cast<uint32_t>(batch_size));
}
int filter_multiplier = output_channels / input_channels;
int64_t nums_output = output->numel();
int block_size = 512;
int64_t grid_size_64 = (nums_output + block_size - 1) / block_size;
PADDLE_ENFORCE_LE_INT_MAX(grid_size_64, "grid_size");
int grid_size = static_cast<int>(grid_size_64);
#define check_case(c_filter_multiplier, c_stride, c_filter) \
if (c_filter_multiplier == 0 || \
filter_multiplier == c_filter_multiplier && \
stride_height == stride_width && stride_height == c_stride && \
(ksize_height == ksize_width && ksize_height == c_filter || \
c_filter == -1)) { \
if (c_filter == -1) { \
PADDLE_ENFORCE_LE(grid_size_64, \
static_cast<int64_t>(max_grid_dim_size[0]), \
common::errors::InvalidArgument( \
"depthwise conv grid.x exceeds device limit.")); \
threads.x = block_size; \
grid.x = grid_size; \
threads.y = threads.z = grid.y = grid.z = 1; \
} \
if (data_layout != DataLayout::NHWC) { \
KernelDepthwiseConvSp<T, \
c_filter_multiplier, \
c_stride, \
c_filter, \
DataLayout::NCHW, \
fuse_relu_before_conv> \
<<<grid, threads, 0, dev_ctx.stream()>>>(input_data, \
filter_data, \
batch_size_int, \
output_channels, \
output_height, \
output_width, \
input_channels, \
input_height, \
input_width, \
filter_multiplier, \
ksize_height, \
ksize_width, \
stride_height, \
stride_width, \
padding_height, \
padding_width, \
dilate_height, \
dilate_width, \
output_data); \
} else { \
KernelDepthwiseConvSp<T, \
c_filter_multiplier, \
c_stride, \
c_filter, \
DataLayout::NHWC, \
fuse_relu_before_conv> \
<<<grid, threads, 0, dev_ctx.stream()>>>(input_data, \
filter_data, \
batch_size_int, \
output_channels, \
output_height, \
output_width, \
input_channels, \
input_height, \
input_width, \
filter_multiplier, \
ksize_height, \
ksize_width, \
stride_height, \
stride_width, \
padding_height, \
padding_width, \
dilate_height, \
dilate_width, \
output_data); \
} \
return; \
}
check_case(1, 1, 3);
check_case(1, 1, 5);
check_case(1, 1, -1);
check_case(1, 2, 3);
check_case(1, 2, 5);
check_case(1, 2, -1);
check_case(2, 1, 3);
check_case(2, 1, 5);
check_case(2, 1, -1);
check_case(2, 2, 3);
check_case(2, 2, 5);
check_case(2, 2, -1);
check_case(0, 0, -1);
// NOTE(liangdun): 0,0 for other case
// add other case if needed, e.g. check_case(2^n,1)
#undef check_case
}
};
template <typename T, bool fuse_relu_before_conv>
class DepthwiseConvInputGradFunctor<GPUContext, T, fuse_relu_before_conv> {
public:
void operator()(const GPUContext& dev_ctx,
const DenseTensor& input,
const DenseTensor& filter,
const DenseTensor& output_grad,
const std::vector<int>& strides,
const std::vector<int>& paddings,
const std::vector<int>& dilations,
DenseTensor* input_grad,
const DataLayout data_layout = DataLayout::NCHW) {
const int64_t batch_size = input.dims()[0];
const int64_t input_channels_64 =
(data_layout != DataLayout::NHWC ? input.dims()[1] : input.dims()[3]);
const int64_t input_height_64 =
(data_layout != DataLayout::NHWC ? input.dims()[2] : input.dims()[1]);
const int64_t input_width_64 =
(data_layout != DataLayout::NHWC ? input.dims()[3] : input.dims()[2]);
const int64_t output_channels_64 =
(data_layout != DataLayout::NHWC ? output_grad.dims()[1]
: output_grad.dims()[3]);
const int64_t output_height_64 =
(data_layout != DataLayout::NHWC ? output_grad.dims()[2]
: output_grad.dims()[1]);
const int64_t output_width_64 =
(data_layout != DataLayout::NHWC ? output_grad.dims()[3]
: output_grad.dims()[2]);
const int64_t ksize_height_64 = filter.dims()[2];
const int64_t ksize_width_64 = filter.dims()[3];
PADDLE_ENFORCE_LE_INT_MAX(batch_size,
"depthwise conv input grad batch size");
PADDLE_ENFORCE_LE_INT_MAX(input_channels_64,
"depthwise conv input grad input channels");
PADDLE_ENFORCE_LE_INT_MAX(input_height_64,
"depthwise conv input grad input height");
PADDLE_ENFORCE_LE_INT_MAX(input_width_64,
"depthwise conv input grad input width");
PADDLE_ENFORCE_LE_INT_MAX(output_channels_64,
"depthwise conv input grad output channels");
PADDLE_ENFORCE_LE_INT_MAX(output_height_64,
"depthwise conv input grad output height");
PADDLE_ENFORCE_LE_INT_MAX(output_width_64,
"depthwise conv input grad output width");
PADDLE_ENFORCE_LE_INT_MAX(ksize_height_64,
"depthwise conv input grad kernel height");
PADDLE_ENFORCE_LE_INT_MAX(ksize_width_64,
"depthwise conv input grad kernel width");
const int batch_size_int = static_cast<int>(batch_size);
const int input_channels = static_cast<int>(input_channels_64);
const int input_height = static_cast<int>(input_height_64);
const int input_width = static_cast<int>(input_width_64);
const int output_channels = static_cast<int>(output_channels_64);
const int output_height = static_cast<int>(output_height_64);
const int output_width = static_cast<int>(output_width_64);
const int ksize_height = static_cast<int>(ksize_height_64);
const int ksize_width = static_cast<int>(ksize_width_64);
const int stride_height = strides[0];
const int stride_width = strides[1];
const int padding_height = paddings[0];
const int padding_width = paddings[1];
const int dilate_height = dilations[0];
const int dilate_width = dilations[1];
const T* input_data = input.data<T>();
const T* filter_data = filter.data<T>();
const T* output_grad_data = output_grad.data<T>();
T* input_grad_data = dev_ctx.template Alloc<T>(input_grad);
DenseTensor filter_hwc;
if (data_layout == DataLayout::NHWC) {
DDim filter_hwc_dims({filter.dims()[2],
filter.dims()[3],
filter.dims()[0],
filter.dims()[1]});
filter_hwc.Resize(filter_hwc_dims);
dev_ctx.template Alloc<T>(&filter_hwc);
std::vector<int> perm_axis({2, 3, 0, 1});
funcs::TransposeNormal<GPUContext, T> trans;
trans(dev_ctx, filter, &filter_hwc, perm_axis);
filter_data = filter_hwc.data<T>();
}
int thread = 512;
int blocks;
dim3 threads;
dim3 grid;
const auto& max_grid_dim_size = dev_ctx.GetCUDAMaxGridDimSize();
if (data_layout != DataLayout::NHWC) {
if (input_width > 1024 && input_width <= 2048) {
thread = (input_width - 1) / 2 + 1;
} else if (input_width > 512 && input_width <= 1024) {
thread = input_width;
}
blocks = std::min(std::max(thread / input_width, 1), input_height);
const int thread_x = std::min(input_width, thread);
threads = dim3(thread_x, blocks, 1);
PADDLE_ENFORCE_LE(
input_channels,
max_grid_dim_size[0],
common::errors::InvalidArgument(
"depthwise conv input grad grid.x exceeds device limit."));
PADDLE_ENFORCE_LE(
batch_size_int,
max_grid_dim_size[1],
common::errors::InvalidArgument(
"depthwise conv input grad grid.y exceeds device limit."));
grid = dim3(input_channels, batch_size_int, 1);
} else {
const int64_t block_y_64 =
((static_cast<int64_t>(input_width) + dilate_width - 1) /
dilate_width) *
dilate_width;
blocks = static_cast<int>(
std::min<int64_t>(std::max(thread / input_channels, 1), block_y_64));
const int thread_x = std::min(input_channels, thread);
const int64_t grid_x =
(static_cast<int64_t>(input_height) + dilate_height - 1) /
dilate_height;
PADDLE_ENFORCE_LE_INT_MAX(grid_x,
"depthwise conv input grad CUDA grid x size");
PADDLE_ENFORCE_LE(
grid_x,
static_cast<int64_t>(max_grid_dim_size[0]),
common::errors::InvalidArgument(
"depthwise conv input grad grid.x exceeds device limit."));
PADDLE_ENFORCE_LE(
dilate_height,
max_grid_dim_size[1],
common::errors::InvalidArgument(
"depthwise conv input grad grid.y exceeds device limit."));
PADDLE_ENFORCE_LE(
batch_size,
static_cast<int64_t>(max_grid_dim_size[2]),
common::errors::InvalidArgument(
"depthwise conv input grad grid.z exceeds device limit."));
threads = dim3(thread_x, blocks, 1);
grid = dim3(static_cast<uint32_t>(grid_x),
static_cast<uint32_t>(dilate_height),
static_cast<uint32_t>(batch_size));
}
int filter_multiplier = output_channels / input_channels;
int64_t nums_input = input_grad->numel();
int block_size = 512;
int64_t grid_size_64 = (nums_input + block_size - 1) / block_size;
PADDLE_ENFORCE_LE_INT_MAX(grid_size_64, "grid_size");
int grid_size = static_cast<int>(grid_size_64);
#define check_case(c_filter_multiplier, c_stride, c_filter) \
if (c_filter_multiplier == 0 || \
filter_multiplier == c_filter_multiplier && \
stride_height == stride_width && stride_height == c_stride && \
(ksize_height == ksize_width && ksize_height == c_filter || \
c_filter == -1)) { \
if (data_layout != DataLayout::NHWC) { \
if (c_filter == -1) { \
PADDLE_ENFORCE_LE( \
grid_size_64, \
static_cast<int64_t>(max_grid_dim_size[0]), \
common::errors::InvalidArgument( \
"depthwise conv input grad grid.x exceeds device limit.")); \
threads.x = block_size; \
grid.x = grid_size; \
threads.y = threads.z = grid.y = grid.z = 1; \
} \
KernelDepthwiseConvInputGradSp<T, \
c_filter_multiplier, \
c_stride, \
c_filter, \
DataLayout::NCHW, \
fuse_relu_before_conv> \
<<<grid, threads, 0, dev_ctx.stream()>>>(input_data, \
output_grad_data, \
filter_data, \
batch_size_int, \
output_channels, \
output_height, \
output_width, \
input_channels, \
input_height, \
input_width, \
filter_multiplier, \
ksize_height, \
ksize_width, \
stride_height, \
stride_width, \
padding_height, \
padding_width, \
dilate_height, \
dilate_width, \
input_grad_data); \
} else { \
KernelDepthwiseConvInputGradSp<T, \
c_filter_multiplier, \
c_stride, \
c_filter, \
DataLayout::NHWC, \
fuse_relu_before_conv> \
<<<grid, threads, 0, dev_ctx.stream()>>>(input_data, \
output_grad_data, \
filter_data, \
batch_size_int, \
output_channels, \
output_height, \
output_width, \
input_channels, \
input_height, \
input_width, \
filter_multiplier, \
ksize_height, \
ksize_width, \
stride_height, \
stride_width, \
padding_height, \
padding_width, \
dilate_height, \
dilate_width, \
input_grad_data); \
} \
return; \
}
check_case(1, 1, 3);
check_case(1, 1, 5);
check_case(1, 1, -1);
check_case(1, 2, 3);
check_case(1, 2, 5);
check_case(1, 2, -1);
check_case(2, 1, 3);
check_case(2, 1, 5);
check_case(2, 1, -1);
check_case(2, 2, 3);
check_case(2, 2, 5);
check_case(2, 2, -1);
check_case(0, 0, -1);
// NOTE(liangdun): 0,0 for other case
// add other case if needed, e.g. check_case(2^n,1)
#undef check_case
}
};
template <typename T, bool fuse_relu_before_conv>
class DepthwiseConvFilterGradFunctor<GPUContext, T, fuse_relu_before_conv> {
public:
void operator()(const GPUContext& dev_ctx,
const DenseTensor& input,
const DenseTensor& output_grad,
const std::vector<int>& strides,
const std::vector<int>& paddings,
const std::vector<int>& dilations,
DenseTensor* filter_grad,
const DataLayout data_layout = DataLayout::NCHW) {
const int64_t batch_size = input.dims()[0];
const int64_t input_channels_64 =
(data_layout != DataLayout::NHWC ? input.dims()[1] : input.dims()[3]);
const int64_t input_height_64 =
(data_layout != DataLayout::NHWC ? input.dims()[2] : input.dims()[1]);
const int64_t input_width_64 =
(data_layout != DataLayout::NHWC ? input.dims()[3] : input.dims()[2]);
const int64_t output_channels_64 =
(data_layout != DataLayout::NHWC ? output_grad.dims()[1]
: output_grad.dims()[3]);
const int64_t output_height_64 =
(data_layout != DataLayout::NHWC ? output_grad.dims()[2]
: output_grad.dims()[1]);
const int64_t output_width_64 =
(data_layout != DataLayout::NHWC ? output_grad.dims()[3]
: output_grad.dims()[2]);
const int64_t ksize_height_64 = filter_grad->dims()[2];
const int64_t ksize_width_64 = filter_grad->dims()[3];
PADDLE_ENFORCE_LE_INT_MAX(batch_size,
"depthwise conv filter grad batch size");
PADDLE_ENFORCE_LE_INT_MAX(input_channels_64,
"depthwise conv filter grad input channels");
PADDLE_ENFORCE_LE_INT_MAX(input_height_64,
"depthwise conv filter grad input height");
PADDLE_ENFORCE_LE_INT_MAX(input_width_64,
"depthwise conv filter grad input width");
PADDLE_ENFORCE_LE_INT_MAX(output_channels_64,
"depthwise conv filter grad output channels");
PADDLE_ENFORCE_LE_INT_MAX(output_height_64,
"depthwise conv filter grad output height");
PADDLE_ENFORCE_LE_INT_MAX(output_width_64,
"depthwise conv filter grad output width");
PADDLE_ENFORCE_LE_INT_MAX(ksize_height_64,
"depthwise conv filter grad kernel height");
PADDLE_ENFORCE_LE_INT_MAX(ksize_width_64,
"depthwise conv filter grad kernel width");
const int batch_size_int = static_cast<int>(batch_size);
const int input_channels = static_cast<int>(input_channels_64);
const int input_height = static_cast<int>(input_height_64);
const int input_width = static_cast<int>(input_width_64);
const int output_channels = static_cast<int>(output_channels_64);
const int output_height = static_cast<int>(output_height_64);
const int output_width = static_cast<int>(output_width_64);
const int ksize_height = static_cast<int>(ksize_height_64);
const int ksize_width = static_cast<int>(ksize_width_64);
const int stride_height = strides[0];
const int stride_width = strides[1];
const int padding_height = paddings[0];
const int padding_width = paddings[1];
const int dilate_height = dilations[0];
const int dilate_width = dilations[1];
const T* input_data = input.data<T>();
const T* output_grad_data = output_grad.data<T>();
T* filter_grad_data = dev_ctx.template Alloc<T>(filter_grad);
int block_size = 512;
int blocks;
dim3 threads;
dim3 grid;
const auto& max_grid_dim_size = dev_ctx.GetCUDAMaxGridDimSize();
if (data_layout != DataLayout::NHWC) {
if (output_width > 1024 && output_width <= 2048) {
block_size = (output_width - 1) / 2 + 1;
} else if (output_width > 512 && output_width <= 1024) {
block_size = output_width;
}
blocks = std::min(std::max(block_size / output_width, 1), output_height);
PADDLE_ENFORCE_LE(
ksize_width,
max_grid_dim_size[0],
common::errors::InvalidArgument(
"depthwise conv filter grad grid.x exceeds device limit."));
PADDLE_ENFORCE_LE(
ksize_height,
max_grid_dim_size[1],
common::errors::InvalidArgument(
"depthwise conv filter grad grid.y exceeds device limit."));
PADDLE_ENFORCE_LE(
output_channels,
max_grid_dim_size[2],
common::errors::InvalidArgument(
"depthwise conv filter grad grid.z exceeds device limit."));
grid = dim3(ksize_width, ksize_height, output_channels);
threads = dim3(std::min(output_width, block_size), blocks, 1);
const int64_t output_hw =
static_cast<int64_t>(output_height) * output_width;
if (output_hw < WARP_SIZE) {
const int64_t block_x_64 = batch_size * output_hw;
const int block_x =
static_cast<int>(std::min<int64_t>(block_size, block_x_64));
threads = dim3(block_x);
}
} else {
// Large block size may cause atomic dependence, reduce block size here.
block_size = 256;
const int64_t block_y_64 =
((static_cast<int64_t>(output_width) + dilate_width - 1) /
dilate_width) *
dilate_width;
blocks = static_cast<int>(std::min<int64_t>(
std::max(block_size / output_channels, 1), block_y_64));
const int thread_x = std::min(output_channels, block_size);
const int64_t grid_x =
(static_cast<int64_t>(output_height) + dilate_height - 1) /
dilate_height;
PADDLE_ENFORCE_LE_INT_MAX(grid_x,
"depthwise conv filter grad CUDA grid x size");
PADDLE_ENFORCE_LE(
grid_x,
static_cast<int64_t>(max_grid_dim_size[0]),
common::errors::InvalidArgument(
"depthwise conv filter grad grid.x exceeds device limit."));
PADDLE_ENFORCE_LE(
dilate_height,
max_grid_dim_size[1],
common::errors::InvalidArgument(
"depthwise conv filter grad grid.y exceeds device limit."));
PADDLE_ENFORCE_LE(
batch_size_int,
max_grid_dim_size[2],
common::errors::InvalidArgument(
"depthwise conv filter grad grid.z exceeds device limit."));
const int grid_x_int = static_cast<int>(grid_x);
grid = dim3(grid_x_int, dilate_height, batch_size_int);
threads = dim3(thread_x, blocks, 1);
if (output_channels < SMALL_THRESHOLD) {
const int64_t hwc_size =
static_cast<int64_t>(ksize_height) * ksize_width * output_channels;
const int64_t blocks_64 =
(hwc_size + static_cast<int64_t>(block_size) - 1) / block_size;
PADDLE_ENFORCE_LE_INT_MAX(blocks_64,
"CUDA launch grid filter_hwc_size");
PADDLE_ENFORCE_LE(
blocks_64,
static_cast<int64_t>(max_grid_dim_size[0]),
common::errors::InvalidArgument(
"depthwise conv filter grad grid.x exceeds device limit."));
PADDLE_ENFORCE_LE(
batch_size,
static_cast<int64_t>(max_grid_dim_size[1]),
common::errors::InvalidArgument(
"depthwise conv filter grad grid.y exceeds device limit."));
grid = dim3(static_cast<int>(blocks_64), batch_size, 1);
threads = dim3(static_cast<int>(
std::min(static_cast<int64_t>(block_size), hwc_size)));
}
}
int filter_multiplier = output_channels / input_channels;
#define check_case(c_filter_multiplier, c_stride, c_filter) \
if (c_filter_multiplier == 0 || \
filter_multiplier == c_filter_multiplier && \
stride_height == stride_width && stride_height == c_stride && \
(ksize_height == ksize_width && ksize_height == c_filter || \
c_filter == -1)) { \
if (data_layout != DataLayout::NHWC) { \
KernelDepthwiseConvFilterGradSp<T, \
c_filter_multiplier, \
c_stride, \
c_filter, \
DataLayout::NCHW, \
fuse_relu_before_conv> \
<<<grid, threads, 0, dev_ctx.stream()>>>(output_grad_data, \
input_data, \
batch_size_int, \
output_channels, \
output_height, \
output_width, \
input_channels, \
input_height, \
input_width, \
filter_multiplier, \
ksize_height, \
ksize_width, \
stride_height, \
stride_width, \
padding_height, \
padding_width, \
dilate_height, \
dilate_width, \
filter_grad_data); \
} else { \
DenseTensor filter_grad_hwc; \
if (c_filter != -1) { \
DDim filter_grad_hwc_dims({filter_grad->dims()[2], \
filter_grad->dims()[3], \
filter_grad->dims()[0], \
filter_grad->dims()[1]}); \
filter_grad_hwc.Resize(filter_grad_hwc_dims); \
dev_ctx.template Alloc<T>(&filter_grad_hwc); \
funcs::SetConstant<GPUContext, T> set_zero; \
set_zero(dev_ctx, &filter_grad_hwc, static_cast<T>(0)); \
filter_grad_data = filter_grad_hwc.data<T>(); \
} else { \
block_size = 512; \
if (output_channels > 1024 && output_channels <= 2048) { \
block_size = (output_channels - 1) / 2 + 1; \
} else if (output_channels > 512 && output_channels <= 1024) { \
block_size = output_channels; \
} \
blocks = \
std::min(std::max(block_size / output_channels, 1), output_width); \
\
const int64_t ksize_area = \
static_cast<int64_t>(ksize_width) * ksize_height; \
PADDLE_ENFORCE_LE_INT_MAX(ksize_area, "depthwise conv filter size"); \
PADDLE_ENFORCE_LE( \
ksize_area, \
static_cast<int64_t>(max_grid_dim_size[0]), \
common::errors::InvalidArgument( \
"depthwise conv filter grad grid.x exceeds device limit.")); \
PADDLE_ENFORCE_LE( \
output_height, \
max_grid_dim_size[1], \
common::errors::InvalidArgument( \
"depthwise conv filter grad grid.y exceeds device limit.")); \
PADDLE_ENFORCE_LE( \
batch_size, \
static_cast<int64_t>(max_grid_dim_size[2]), \
common::errors::InvalidArgument( \
"depthwise conv filter grad grid.z exceeds device limit.")); \
grid = dim3(static_cast<uint32_t>(ksize_area), \
static_cast<uint32_t>(output_height), \
static_cast<uint32_t>(batch_size)); \
threads = dim3(std::min(output_channels, block_size), blocks, 1); \
} \
KernelDepthwiseConvFilterGradSp<T, \
c_filter_multiplier, \
c_stride, \
c_filter, \
DataLayout::NHWC, \
fuse_relu_before_conv> \
<<<grid, threads, 0, dev_ctx.stream()>>>(output_grad_data, \
input_data, \
batch_size_int, \
output_channels, \
output_height, \
output_width, \
input_channels, \
input_height, \
input_width, \
filter_multiplier, \
ksize_height, \
ksize_width, \
stride_height, \
stride_width, \
padding_height, \
padding_width, \
dilate_height, \
dilate_width, \
filter_grad_data); \
if (c_filter != -1) { \
std::vector<int> perm_axis({2, 3, 0, 1}); \
funcs::TransposeNormal<GPUContext, T> trans; \
trans(dev_ctx, filter_grad_hwc, filter_grad, perm_axis); \
} \
} \
return; \
}
check_case(1, 1, 3);
check_case(1, 1, 5);
check_case(1, 1, -1);
check_case(1, 2, 3);
check_case(1, 2, 5);
check_case(1, 2, -1);
check_case(2, 1, 3);
check_case(2, 1, 5);
check_case(2, 1, -1);
check_case(2, 2, 3);
check_case(2, 2, 5);
check_case(2, 2, -1);
check_case(0, 0, -1);
#undef check_case
}
};
template class DepthwiseConvFunctor<GPUContext, float, false>;
template class DepthwiseConvFunctor<GPUContext, double, false>;
template class DepthwiseConvFunctor<GPUContext, phi::float16, false>;
template class DepthwiseConvInputGradFunctor<GPUContext, float, false>;
template class DepthwiseConvInputGradFunctor<GPUContext, double, false>;
template class DepthwiseConvInputGradFunctor<GPUContext, phi::float16, false>;
template class DepthwiseConvFilterGradFunctor<GPUContext, float, false>;
template class DepthwiseConvFilterGradFunctor<GPUContext, double, false>;
template class DepthwiseConvFilterGradFunctor<GPUContext, phi::float16, false>;
template class DepthwiseConvFunctor<GPUContext, float, true>;
template class DepthwiseConvFunctor<GPUContext, double, true>;
template class DepthwiseConvFunctor<GPUContext, phi::float16, true>;
template class DepthwiseConvInputGradFunctor<GPUContext, float, true>;
template class DepthwiseConvInputGradFunctor<GPUContext, double, true>;
template class DepthwiseConvInputGradFunctor<GPUContext, phi::float16, true>;
template class DepthwiseConvFilterGradFunctor<GPUContext, float, true>;
template class DepthwiseConvFilterGradFunctor<GPUContext, double, true>;
template class DepthwiseConvFilterGradFunctor<GPUContext, phi::float16, true>;
} // namespace phi::math