chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
if(WITH_GPU OR WITH_ROCM)
# fusion_group
if(NOT APPLE AND NOT WIN32)
paddle_test(test_fusion_group_op SRCS fusion_group_op_test.cc)
endif()
if(NOT WITH_ROCM)
nv_test(
test_fused_residual_dropout_bias
SRCS fused_residual_dropout_bias_test.cu
DEPS tensor op_registry dropout_op generated_op phi common)
nv_test(
test_fused_dropout_act_bias
SRCS fused_dropout_act_bias_test.cu
DEPS tensor op_registry dropout_op generated_op phi common)
nv_test(
test_fused_layernorm_residual_dropout_bias
SRCS fused_layernorm_residual_dropout_bias_test.cu
DEPS tensor
op_registry
dropout_op
generated_op
phi
common
${CINN_DEPS})
nv_test(
test_cudnn_norm_conv
SRCS cudnn_norm_conv_test.cc
DEPS generated_op tensor op_registry phi common)
paddle_test(test_cudnn_bn_add_relu SRCS cudnn_bn_add_relu_test.cc)
endif()
endif()
@@ -0,0 +1,947 @@
/* Copyright (c) 2021 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 <random>
#include <vector>
#include "gtest/gtest.h"
#include "paddle/common/flags.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/framework/tensor_util.h"
#include "paddle/phi/common/float16.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/fusion/gpu/cudnn_bn_stats_finalize.cu.h"
#include "paddle/phi/kernels/fusion/gpu/cudnn_scale_bias_add_relu.cu.h"
COMMON_DECLARE_bool(cudnn_batchnorm_spatial_persistent);
namespace framework = paddle::framework;
namespace platform = paddle::platform;
template <typename T>
void InitRandomTensor(const std::vector<int64_t> &dims,
phi::DenseTensor *cpu_out) {
T *cpu_out_ptr =
cpu_out->mutable_data<T>(common::make_ddim(dims), phi::CPUPlace());
std::default_random_engine random(0);
std::uniform_real_distribution<float> dis(-1.0, 1.0);
for (int i = 0; i < cpu_out->numel(); ++i) {
cpu_out_ptr[i] = static_cast<T>(dis(random));
}
}
template <typename T>
void InitConstantTensor(const std::vector<int64_t> &dims,
T value,
phi::DenseTensor *cpu_out) {
T *cpu_out_ptr =
cpu_out->mutable_data<T>(common::make_ddim(dims), phi::CPUPlace());
for (int i = 0; i < cpu_out->numel(); ++i) {
cpu_out_ptr[i] = value;
}
}
template <typename T>
void CheckOutput(std::string name,
const phi::DenseTensor &cpu_res,
const phi::DenseTensor &cpu_base,
float diff,
bool is_relative_atol = false) {
if (cpu_res.dims().size() == cpu_base.dims().size()) {
EXPECT_EQ(cpu_res.dims(), cpu_base.dims());
} else {
EXPECT_EQ(cpu_res.numel(), cpu_base.numel());
}
const T *cpu_res_ptr = cpu_res.data<T>();
const T *cpu_base_ptr = cpu_base.data<T>();
float max_diff = 0;
int index = 0;
for (int i = 0; i < cpu_res.numel(); ++i) {
float cur_diff;
if (is_relative_atol) {
cur_diff = static_cast<float>(
std::abs((cpu_res_ptr[i] - cpu_base_ptr[i]) / cpu_base_ptr[i]));
EXPECT_LT(static_cast<float>(std::abs((cpu_res_ptr[i] - cpu_base_ptr[i]) /
cpu_base_ptr[i])),
diff);
} else {
cur_diff = static_cast<float>(std::abs(cpu_res_ptr[i] - cpu_base_ptr[i]));
EXPECT_LT(static_cast<float>(std::abs(cpu_res_ptr[i] - cpu_base_ptr[i])),
diff);
}
if (cur_diff > max_diff) {
max_diff = cur_diff;
index = i;
}
}
std::string error_type = is_relative_atol ? "relative" : "absolute";
LOG(INFO) << "[" << name << "] The dims is [" << cpu_res.dims()
<< "], maximum " << error_type << " error is " << max_diff << ": "
<< cpu_res_ptr[index] << " vs " << cpu_base_ptr[index];
}
template <typename T>
void ComputeSumAndSquareSum(const phi::DenseTensor &cpu_x,
phi::DenseTensor *cpu_sum,
phi::DenseTensor *cpu_sum_of_square) {
// x is in NHWC format.
const auto &dims = cpu_x.dims();
int64_t c = dims[3];
const T *cpu_x_ptr = cpu_x.data<T>();
float *cpu_sum_ptr =
cpu_sum->mutable_data<float>({1, 1, 1, c}, phi::CPUPlace());
float *cpu_sum_square_ptr =
cpu_sum_of_square->mutable_data<float>({1, 1, 1, c}, phi::CPUPlace());
for (int j = 0; j < c; ++j) {
float tmp_sum = 0.0f;
float tmp_sum_of_squares = 0.0f;
for (int i = 0; i < cpu_x.numel() / c; ++i) {
float tmp_x = static_cast<float>(cpu_x_ptr[i * c + j]);
tmp_sum += tmp_x;
tmp_sum_of_squares += tmp_x * tmp_x;
}
cpu_sum_ptr[j] = tmp_sum;
cpu_sum_square_ptr[j] = tmp_sum_of_squares;
}
}
template <typename T>
void ComputeInplaceAdd(const phi::DenseTensor &cpu_x, phi::DenseTensor *cpu_y) {
EXPECT_EQ(cpu_x.dims(), cpu_y->dims());
const T *cpu_x_ptr = cpu_x.data<T>();
T *cpu_y_ptr = cpu_y->data<T>();
for (int64_t i = 0; i < cpu_x.numel(); ++i) {
cpu_y_ptr[i] += cpu_x_ptr[i];
}
}
template <typename T>
void ComputeInplaceRelu(phi::DenseTensor *cpu_x) {
T *cpu_x_ptr = cpu_x->data<T>();
for (int64_t i = 0; i < cpu_x->numel(); ++i) {
cpu_x_ptr[i] =
cpu_x_ptr[i] > static_cast<T>(0) ? cpu_x_ptr[i] : static_cast<T>(0);
}
}
void ComputeBatchNormForward(const phi::GPUContext &ctx,
const phi::DenseTensor &cpu_x,
const phi::DenseTensor &cpu_scale,
const phi::DenseTensor &cpu_bias,
phi::DenseTensor *cpu_mean,
phi::DenseTensor *cpu_var,
phi::DenseTensor *cpu_saved_mean,
phi::DenseTensor *cpu_saved_var,
phi::DenseTensor *cpu_y,
phi::DenseTensor *saved_reserve_space) {
framework::Scope scope;
auto *x = scope.Var("X")->GetMutable<phi::DenseTensor>();
auto *scale = scope.Var("Scale")->GetMutable<phi::DenseTensor>();
auto *bias = scope.Var("Bias")->GetMutable<phi::DenseTensor>();
auto *mean = scope.Var("Mean")->GetMutable<phi::DenseTensor>();
auto *var = scope.Var("Variance")->GetMutable<phi::DenseTensor>();
auto *y = scope.Var("Y")->GetMutable<phi::DenseTensor>();
auto *saved_mean = scope.Var("SavedMean")->GetMutable<phi::DenseTensor>();
auto *saved_var = scope.Var("SavedVariance")->GetMutable<phi::DenseTensor>();
auto *reserve_space =
scope.Var("ReserveSpace")->GetMutable<phi::DenseTensor>();
auto place = ctx.GetPlace();
paddle::framework::TensorCopySync(cpu_x, place, x);
paddle::framework::TensorCopySync(cpu_scale, place, scale);
paddle::framework::TensorCopySync(cpu_bias, place, bias);
paddle::framework::TensorCopySync(*cpu_mean, place, mean);
paddle::framework::TensorCopySync(*cpu_var, place, var);
int64_t channels = x->dims()[3];
scale->Resize({channels});
bias->Resize({channels});
mean->Resize({channels});
var->Resize({channels});
framework::AttributeMap attrs;
std::string data_layout = "NHWC";
attrs.insert({"data_layout", data_layout});
auto op =
framework::OpRegistry::CreateOp("batch_norm",
{{"X", {"X"}},
{"Scale", {"Scale"}},
{"Bias", {"Bias"}},
{"Mean", {"Mean"}},
{"Variance", {"Variance"}}},
{{"Y", {"Y"}},
{"MeanOut", {"Mean"}},
{"VarianceOut", {"Variance"}},
{"SavedMean", {"SavedMean"}},
{"SavedVariance", {"SavedVariance"}},
{"ReserveSpace", {"ReserveSpace"}}},
attrs);
op->Run(scope, ctx.GetPlace());
paddle::framework::TensorCopySync(*y, phi::CPUPlace(), cpu_y);
paddle::framework::TensorCopySync(*mean, phi::CPUPlace(), cpu_mean);
paddle::framework::TensorCopySync(*var, phi::CPUPlace(), cpu_var);
paddle::framework::TensorCopySync(
*saved_mean, phi::CPUPlace(), cpu_saved_mean);
paddle::framework::TensorCopySync(*saved_var, phi::CPUPlace(), cpu_saved_var);
// reserved_space will stay on GPU and used in grad op.
saved_reserve_space->ShareDataWith(*reserve_space);
}
void ComputeFusedBNAddReluForward(const phi::GPUContext &ctx,
const phi::DenseTensor &cpu_x,
const phi::DenseTensor &cpu_z,
const phi::DenseTensor &cpu_scale,
const phi::DenseTensor &cpu_bias,
phi::DenseTensor *cpu_mean,
phi::DenseTensor *cpu_var,
phi::DenseTensor *cpu_saved_mean,
phi::DenseTensor *cpu_saved_var,
phi::DenseTensor *cpu_y,
phi::DenseTensor *saved_reserve_space) {
framework::Scope scope;
auto *x = scope.Var("X")->GetMutable<phi::DenseTensor>();
auto *z = scope.Var("Z")->GetMutable<phi::DenseTensor>();
auto *scale = scope.Var("Scale")->GetMutable<phi::DenseTensor>();
auto *bias = scope.Var("Bias")->GetMutable<phi::DenseTensor>();
auto *mean = scope.Var("Mean")->GetMutable<phi::DenseTensor>();
auto *var = scope.Var("Variance")->GetMutable<phi::DenseTensor>();
auto *y = scope.Var("Y")->GetMutable<phi::DenseTensor>();
auto *saved_mean = scope.Var("SavedMean")->GetMutable<phi::DenseTensor>();
auto *saved_var = scope.Var("SavedVariance")->GetMutable<phi::DenseTensor>();
auto *reserve_space =
scope.Var("ReserveSpace")->GetMutable<phi::DenseTensor>();
auto place = ctx.GetPlace();
paddle::framework::TensorCopySync(cpu_x, place, x);
paddle::framework::TensorCopySync(cpu_z, place, z);
paddle::framework::TensorCopySync(cpu_scale, place, scale);
paddle::framework::TensorCopySync(cpu_bias, place, bias);
paddle::framework::TensorCopySync(*cpu_mean, place, mean);
paddle::framework::TensorCopySync(*cpu_var, place, var);
int64_t channels = x->dims()[3];
scale->Resize({channels});
bias->Resize({channels});
mean->Resize({channels});
var->Resize({channels});
framework::AttributeMap attrs;
auto op =
framework::OpRegistry::CreateOp("fused_bn_add_activation",
{{"X", {"X"}},
{"Z", {"Z"}},
{"Scale", {"Scale"}},
{"Bias", {"Bias"}},
{"Mean", {"Mean"}},
{"Variance", {"Variance"}}},
{{"Y", {"Y"}},
{"MeanOut", {"Mean"}},
{"VarianceOut", {"Variance"}},
{"SavedMean", {"SavedMean"}},
{"SavedVariance", {"SavedVariance"}},
{"ReserveSpace", {"ReserveSpace"}}},
attrs);
op->Run(scope, ctx.GetPlace());
paddle::framework::TensorCopySync(*y, phi::CPUPlace(), cpu_y);
paddle::framework::TensorCopySync(*mean, phi::CPUPlace(), cpu_mean);
paddle::framework::TensorCopySync(*var, phi::CPUPlace(), cpu_var);
paddle::framework::TensorCopySync(
*saved_mean, phi::CPUPlace(), cpu_saved_mean);
paddle::framework::TensorCopySync(*saved_var, phi::CPUPlace(), cpu_saved_var);
// reserved_space will stay on GPU and used in grad op.
saved_reserve_space->ShareDataWith(*reserve_space);
}
void ComputeFusedBNAddReluBackward(const phi::GPUContext &ctx,
const phi::DenseTensor &cpu_dy,
const phi::DenseTensor &cpu_x,
const phi::DenseTensor &cpu_scale,
const phi::DenseTensor &cpu_bias,
const phi::DenseTensor &cpu_saved_mean,
const phi::DenseTensor &cpu_saved_var,
const phi::DenseTensor &cpu_y,
const phi::DenseTensor &saved_reserve_space,
phi::DenseTensor *cpu_dx,
phi::DenseTensor *cpu_dz,
phi::DenseTensor *cpu_dscale,
phi::DenseTensor *cpu_dbias) {
framework::Scope scope;
auto *x = scope.Var("X")->GetMutable<phi::DenseTensor>();
auto *y = scope.Var("Y")->GetMutable<phi::DenseTensor>();
auto *dy = scope.Var("Y@GRAD")->GetMutable<phi::DenseTensor>();
auto *scale = scope.Var("Scale")->GetMutable<phi::DenseTensor>();
auto *bias = scope.Var("Bias")->GetMutable<phi::DenseTensor>();
auto *saved_mean = scope.Var("SavedMean")->GetMutable<phi::DenseTensor>();
auto *saved_var = scope.Var("SavedVariance")->GetMutable<phi::DenseTensor>();
auto *reserve_space =
scope.Var("ReserveSpace")->GetMutable<phi::DenseTensor>();
auto *dx = scope.Var("X@GRAD")->GetMutable<phi::DenseTensor>();
auto *dz = scope.Var("Z@GRAD")->GetMutable<phi::DenseTensor>();
auto *dscale = scope.Var("Scale@GRAD")->GetMutable<phi::DenseTensor>();
auto *dbias = scope.Var("Bias@GRAD")->GetMutable<phi::DenseTensor>();
auto place = ctx.GetPlace();
paddle::framework::TensorCopySync(cpu_x, place, x);
paddle::framework::TensorCopySync(cpu_y, place, y);
paddle::framework::TensorCopySync(cpu_dy, place, dy);
paddle::framework::TensorCopySync(cpu_scale, place, scale);
paddle::framework::TensorCopySync(cpu_bias, place, bias);
paddle::framework::TensorCopySync(cpu_saved_mean, place, saved_mean);
paddle::framework::TensorCopySync(cpu_saved_var, place, saved_var);
reserve_space->ShareDataWith(saved_reserve_space);
int64_t channels = x->dims()[3];
scale->Resize({channels});
bias->Resize({channels});
saved_mean->Resize({channels});
saved_var->Resize({channels});
framework::AttributeMap attrs;
float momentum = 0.9;
float epsilon = 1e-5;
std::string act_type = "relu";
attrs.insert({"momentum", momentum});
attrs.insert({"epsilon", epsilon});
attrs.insert({"act_type", act_type});
auto op =
framework::OpRegistry::CreateOp("fused_bn_add_activation_grad",
{{"X", {"X"}},
{"Y", {"Y"}},
{"Y@GRAD", {"Y@GRAD"}},
{"Scale", {"Scale"}},
{"Bias", {"Bias"}},
{"SavedMean", {"SavedMean"}},
{"SavedVariance", {"SavedVariance"}},
{"ReserveSpace", {"ReserveSpace"}}},
{{"X@GRAD", {"X@GRAD"}},
{"Z@GRAD", {"Z@GRAD"}},
{"Scale@GRAD", {"Scale@GRAD"}},
{"Bias@GRAD", {"Bias@GRAD"}}},
attrs);
op->Run(scope, ctx.GetPlace());
paddle::framework::TensorCopySync(*dx, phi::CPUPlace(), cpu_dx);
paddle::framework::TensorCopySync(*dz, phi::CPUPlace(), cpu_dz);
paddle::framework::TensorCopySync(*dscale, phi::CPUPlace(), cpu_dscale);
paddle::framework::TensorCopySync(*dbias, phi::CPUPlace(), cpu_dbias);
}
template <typename T>
class CudnnBNAddReluTester {
public:
CudnnBNAddReluTester(int batch_size,
int height,
int width,
int channels,
std::string act_type,
bool fuse_add,
bool has_shortcut) {
batch_size_ = batch_size;
height_ = height;
width_ = width;
channels_ = channels;
ele_count_ = batch_size_ * height_ * width_;
act_type_ = act_type;
fuse_add_ = fuse_add;
has_shortcut_ = has_shortcut;
SetUp();
}
~CudnnBNAddReluTester() = default;
void CheckForward(float diff, bool is_relative_atol = false) {
LOG(INFO) << "[CheckForward, diff=" << diff
<< ", is_relative_atol=" << is_relative_atol
<< "] act_type=" << act_type_ << ", fuse_add=" << fuse_add_
<< ", has_shortcut=" << has_shortcut_;
phi::GPUContext *ctx = static_cast<phi::GPUContext *>(
phi::DeviceContextPool::Instance().Get(phi::GPUPlace(0)));
auto select = [&](phi::DenseTensor *in) {
return has_shortcut_ ? in : nullptr;
};
phi::DenseTensor cpu_mean_base_x;
phi::DenseTensor cpu_var_base_x;
phi::DenseTensor cpu_mean_base_z;
phi::DenseTensor cpu_var_base_z;
if (!has_shortcut_ && fuse_add_ && (act_type_ == "relu")) {
BaselineForwardFusedBNAddRelu(*ctx,
&cpu_mean_base_x,
&cpu_var_base_x,
&cpu_saved_mean_base_x_,
&cpu_saved_var_base_x_,
&cpu_y_base_,
&saved_reserve_space_x_);
} else {
BaselineForward(*ctx,
&cpu_mean_base_x,
&cpu_var_base_x,
&cpu_saved_mean_base_x_,
&cpu_saved_var_base_x_,
&cpu_y_base_,
&saved_reserve_space_x_,
select(&cpu_mean_base_z),
select(&cpu_var_base_z),
select(&cpu_saved_mean_base_z_),
select(&cpu_saved_var_base_z_),
select(&saved_reserve_space_z_));
}
phi::DenseTensor cpu_mean_x;
phi::DenseTensor cpu_var_x;
phi::DenseTensor cpu_y;
phi::DenseTensor cpu_mean_z;
phi::DenseTensor cpu_var_z;
FusedForward(*ctx,
&cpu_mean_x,
&cpu_var_x,
&cpu_saved_mean_x_,
&cpu_saved_var_x_,
&cpu_y,
&cpu_bitmask_,
select(&cpu_mean_z),
select(&cpu_var_z),
select(&cpu_saved_mean_z_),
select(&cpu_saved_var_z_));
CheckOutput<float>(
"Mean", cpu_mean_x, cpu_mean_base_x, diff, is_relative_atol);
CheckOutput<float>(
"Variance", cpu_var_x, cpu_var_base_x, diff, is_relative_atol);
CheckOutput<float>("SavedMean",
cpu_saved_mean_x_,
cpu_saved_mean_base_x_,
diff,
is_relative_atol);
CheckOutput<float>("SavedVariance",
cpu_saved_var_x_,
cpu_saved_var_base_x_,
diff,
is_relative_atol);
if (has_shortcut_) {
CheckOutput<float>(
"MeanZ", cpu_mean_z, cpu_mean_base_z, diff, is_relative_atol);
CheckOutput<float>(
"VarianceZ", cpu_var_z, cpu_var_base_z, diff, is_relative_atol);
CheckOutput<float>("SavedMeanZ",
cpu_saved_mean_z_,
cpu_saved_mean_base_z_,
diff,
is_relative_atol);
CheckOutput<float>("SavedVarianceZ",
cpu_saved_var_z_,
cpu_saved_var_base_z_,
diff,
is_relative_atol);
}
CheckOutput<T>("Y", cpu_y, cpu_y_base_, diff, is_relative_atol);
}
void CheckBackward(float diff, bool is_relative_atol = false) {
phi::GPUContext *ctx = static_cast<phi::GPUContext *>(
phi::DeviceContextPool::Instance().Get(phi::GPUPlace(0)));
phi::DenseTensor cpu_dx_base;
phi::DenseTensor cpu_dz_base;
phi::DenseTensor cpu_dscale_base;
phi::DenseTensor cpu_dbias_base;
BaselineBackwardFusedBNAddRelu(
*ctx, &cpu_dx_base, &cpu_dz_base, &cpu_dscale_base, &cpu_dbias_base);
phi::DenseTensor cpu_dx;
phi::DenseTensor cpu_dz;
phi::DenseTensor cpu_dscale;
phi::DenseTensor cpu_dbias;
FusedBackward(*ctx, &cpu_dx, &cpu_dz, &cpu_dscale, &cpu_dbias);
CheckOutput<T>("DX", cpu_dx, cpu_dx_base, diff, is_relative_atol);
CheckOutput<T>("DZ", cpu_dz, cpu_dz_base, diff, is_relative_atol);
CheckOutput<float>(
"DScale", cpu_dscale, cpu_dscale_base, diff, is_relative_atol);
CheckOutput<float>(
"DBias", cpu_dbias, cpu_dbias_base, diff, is_relative_atol);
}
private:
void SetUp() {
InitRandomTensor<T>({batch_size_, height_, width_, channels_}, &cpu_x_);
InitRandomTensor<float>({channels_}, &cpu_bn_scale_x_);
InitRandomTensor<float>({channels_}, &cpu_bn_bias_x_);
if (has_shortcut_) {
InitRandomTensor<T>({batch_size_, height_, width_, channels_}, &cpu_z_);
InitRandomTensor<float>({channels_}, &cpu_bn_scale_z_);
InitRandomTensor<float>({channels_}, &cpu_bn_bias_z_);
} else {
if (fuse_add_) {
InitRandomTensor<T>({batch_size_, height_, width_, channels_}, &cpu_z_);
}
}
InitRandomTensor<T>({batch_size_, height_, width_, channels_}, &cpu_dy_);
}
void InitMeanVar(phi::DenseTensor *cpu_mean,
phi::DenseTensor *cpu_var,
phi::DenseTensor *cpu_saved_mean,
phi::DenseTensor *cpu_saved_var) {
InitConstantTensor<float>({channels_}, static_cast<float>(0.0f), cpu_mean);
InitConstantTensor<float>({channels_}, static_cast<float>(1.0f), cpu_var);
InitConstantTensor<float>(
{channels_}, static_cast<float>(0.0f), cpu_saved_mean);
InitConstantTensor<float>(
{channels_}, static_cast<float>(0.0f), cpu_saved_var);
}
void BaselineForward(const phi::GPUContext &ctx,
phi::DenseTensor *cpu_mean_x,
phi::DenseTensor *cpu_var_x,
phi::DenseTensor *cpu_saved_mean_x,
phi::DenseTensor *cpu_saved_var_x,
phi::DenseTensor *cpu_y,
phi::DenseTensor *saved_reserve_space_x,
phi::DenseTensor *cpu_mean_z = nullptr,
phi::DenseTensor *cpu_var_z = nullptr,
phi::DenseTensor *cpu_saved_mean_z = nullptr,
phi::DenseTensor *cpu_saved_var_z = nullptr,
phi::DenseTensor *saved_reserve_space_z = nullptr) {
InitMeanVar(cpu_mean_x, cpu_var_x, cpu_saved_mean_x, cpu_saved_var_x);
ComputeBatchNormForward(ctx,
cpu_x_,
cpu_bn_scale_x_,
cpu_bn_bias_x_,
cpu_mean_x,
cpu_var_x,
cpu_saved_mean_x,
cpu_saved_var_x,
cpu_y,
saved_reserve_space_x);
if (has_shortcut_) {
phi::DenseTensor cpu_z_out;
InitMeanVar(cpu_mean_z, cpu_var_z, cpu_saved_mean_z, cpu_saved_var_z);
ComputeBatchNormForward(ctx,
cpu_z_,
cpu_bn_scale_z_,
cpu_bn_bias_z_,
cpu_mean_z,
cpu_var_z,
cpu_saved_mean_z,
cpu_saved_var_z,
&cpu_z_out,
saved_reserve_space_z);
ComputeInplaceAdd<T>(cpu_z_out, cpu_y);
} else {
if (fuse_add_) {
ComputeInplaceAdd<T>(cpu_z_, cpu_y);
}
}
if (act_type_ == "relu") {
ComputeInplaceRelu<T>(cpu_y);
}
}
void BaselineForwardFusedBNAddRelu(const phi::GPUContext &ctx,
phi::DenseTensor *cpu_mean,
phi::DenseTensor *cpu_var,
phi::DenseTensor *cpu_saved_mean,
phi::DenseTensor *cpu_saved_var,
phi::DenseTensor *cpu_y,
phi::DenseTensor *saved_reserve_space) {
InitMeanVar(cpu_mean, cpu_var, cpu_saved_mean, cpu_saved_var);
ComputeFusedBNAddReluForward(ctx,
cpu_x_,
cpu_z_,
cpu_bn_scale_x_,
cpu_bn_bias_x_,
cpu_mean,
cpu_var,
cpu_saved_mean,
cpu_saved_var,
cpu_y,
saved_reserve_space);
}
void BaselineBackwardFusedBNAddRelu(const phi::GPUContext &ctx,
phi::DenseTensor *cpu_dx,
phi::DenseTensor *cpu_dz,
phi::DenseTensor *cpu_dscale,
phi::DenseTensor *cpu_dbias) {
ComputeFusedBNAddReluBackward(ctx,
cpu_dy_,
cpu_x_,
cpu_bn_scale_x_,
cpu_bn_bias_x_,
cpu_saved_mean_base_x_,
cpu_saved_var_base_x_,
cpu_y_base_,
saved_reserve_space_x_,
cpu_dx,
cpu_dz,
cpu_dscale,
cpu_dbias);
}
void ComputeFusedBNStatsFinalize(const phi::GPUContext &ctx,
const phi::DenseTensor &cpu_x,
const phi::DenseTensor &cpu_bn_scale,
const phi::DenseTensor &cpu_bn_bias,
phi::DenseTensor *sum,
phi::DenseTensor *sum_of_square,
phi::DenseTensor *bn_scale,
phi::DenseTensor *bn_bias,
phi::DenseTensor *mean,
phi::DenseTensor *var,
phi::DenseTensor *saved_mean,
phi::DenseTensor *saved_var,
phi::DenseTensor *equiv_scale,
phi::DenseTensor *equiv_bias) {
phi::DenseTensor cpu_sum;
phi::DenseTensor cpu_sum_of_square;
ComputeSumAndSquareSum<T>(cpu_x, &cpu_sum, &cpu_sum_of_square);
auto place = ctx.GetPlace();
paddle::framework::TensorCopySync(cpu_sum, place, sum);
paddle::framework::TensorCopySync(cpu_sum_of_square, place, sum_of_square);
paddle::framework::TensorCopySync(cpu_bn_scale, place, bn_scale);
paddle::framework::TensorCopySync(cpu_bn_bias, place, bn_bias);
bn_scale->Resize({1, 1, 1, channels_});
bn_bias->Resize({1, 1, 1, channels_});
// input
mean->Resize({1, 1, 1, channels_});
var->Resize({1, 1, 1, channels_});
// output
equiv_scale->Resize({1, 1, 1, channels_});
equiv_bias->Resize({1, 1, 1, channels_});
saved_mean->Resize({1, 1, 1, channels_});
saved_var->Resize({1, 1, 1, channels_});
auto param_shape = common::vectorize<int>(bn_scale->dims());
phi::fusion::CudnnBNStatsFinalize<T> bn_op(ctx, param_shape);
bn_op.Forward(ctx,
*sum,
*sum_of_square,
*bn_scale,
*bn_bias,
saved_mean,
saved_var,
mean,
var,
equiv_scale,
equiv_bias,
eps_,
momentum_,
ele_count_,
true);
}
// Get forward results of CudnnBNStatsFinalize + CudnnScaleBiasAddRelu
void FusedForward(const phi::GPUContext &ctx,
phi::DenseTensor *cpu_mean_x,
phi::DenseTensor *cpu_var_x,
phi::DenseTensor *cpu_saved_mean_x,
phi::DenseTensor *cpu_saved_var_x,
phi::DenseTensor *cpu_y,
phi::DenseTensor *cpu_bitmask,
phi::DenseTensor *cpu_mean_z = nullptr,
phi::DenseTensor *cpu_var_z = nullptr,
phi::DenseTensor *cpu_saved_mean_z = nullptr,
phi::DenseTensor *cpu_saved_var_z = nullptr) {
phi::DenseTensor x;
phi::DenseTensor sum_x;
phi::DenseTensor sum_of_square_x;
phi::DenseTensor bn_scale_x;
phi::DenseTensor bn_bias_x;
phi::DenseTensor z;
phi::DenseTensor sum_z;
phi::DenseTensor sum_of_square_z;
phi::DenseTensor bn_scale_z;
phi::DenseTensor bn_bias_z;
auto place = ctx.GetPlace();
paddle::framework::TensorCopySync(cpu_x_, place, &x);
if (fuse_add_ || has_shortcut_) {
paddle::framework::TensorCopySync(cpu_z_, place, &z);
}
phi::DenseTensor mean_x;
phi::DenseTensor var_x;
phi::DenseTensor saved_mean_x;
phi::DenseTensor saved_var_x;
phi::DenseTensor equiv_scale_x;
phi::DenseTensor equiv_bias_x;
phi::DenseTensor mean_z;
phi::DenseTensor var_z;
phi::DenseTensor saved_mean_z;
phi::DenseTensor saved_var_z;
phi::DenseTensor equiv_scale_z;
phi::DenseTensor equiv_bias_z;
phi::DenseTensor y;
phi::DenseTensor bitmask;
InitMeanVar(cpu_mean_x, cpu_var_x, cpu_saved_mean_x, cpu_saved_var_x);
paddle::framework::TensorCopySync(*cpu_mean_x, place, &mean_x);
paddle::framework::TensorCopySync(*cpu_var_x, place, &var_x);
if (has_shortcut_) {
InitMeanVar(cpu_mean_z, cpu_var_z, cpu_saved_mean_z, cpu_saved_var_z);
paddle::framework::TensorCopySync(*cpu_mean_z, place, &mean_z);
paddle::framework::TensorCopySync(*cpu_var_z, place, &var_z);
}
// 1. BN Stats Finalize
ComputeFusedBNStatsFinalize(ctx,
cpu_x_,
cpu_bn_scale_x_,
cpu_bn_bias_x_,
&sum_x,
&sum_of_square_x,
&bn_scale_x,
&bn_bias_x,
&mean_x,
&var_x,
&saved_mean_x,
&saved_var_x,
&equiv_scale_x,
&equiv_bias_x);
if (has_shortcut_) {
ComputeFusedBNStatsFinalize(ctx,
cpu_z_,
cpu_bn_scale_z_,
cpu_bn_bias_z_,
&sum_z,
&sum_of_square_z,
&bn_scale_z,
&bn_bias_z,
&mean_z,
&var_z,
&saved_mean_z,
&saved_var_z,
&equiv_scale_z,
&equiv_bias_z);
}
y.Resize(common::make_ddim({batch_size_, height_, width_, channels_}));
int c = channels_;
int64_t nhw = ele_count_;
int32_t c_int32_elems = ((c + 63) & ~63) / 32;
int32_t nhw_int32_elems = (static_cast<int32_t>(nhw) + 31) & ~31;
bitmask.Resize(common::make_ddim({nhw_int32_elems, c_int32_elems, 1}));
auto data_shape = common::vectorize<int>(x.dims());
auto param_shape = common::vectorize<int>(bn_scale_x.dims());
auto bitmask_shape = common::vectorize<int>(bitmask.dims());
// 2. Scale Bias + Relu
phi::fusion::CudnnScaleBiasAddRelu<T> sbar_op(ctx,
act_type_,
fuse_add_,
has_shortcut_,
data_shape,
param_shape,
bitmask_shape);
sbar_op.Forward(ctx,
x,
equiv_scale_x,
equiv_bias_x,
&z,
&equiv_scale_z,
&equiv_bias_z,
&y,
&bitmask);
paddle::framework::TensorCopySync(mean_x, phi::CPUPlace(), cpu_mean_x);
paddle::framework::TensorCopySync(var_x, phi::CPUPlace(), cpu_var_x);
paddle::framework::TensorCopySync(
saved_mean_x, phi::CPUPlace(), cpu_saved_mean_x);
paddle::framework::TensorCopySync(
saved_var_x, phi::CPUPlace(), cpu_saved_var_x);
if (has_shortcut_) {
paddle::framework::TensorCopySync(mean_z, phi::CPUPlace(), cpu_mean_z);
paddle::framework::TensorCopySync(var_z, phi::CPUPlace(), cpu_var_z);
paddle::framework::TensorCopySync(
saved_mean_z, phi::CPUPlace(), cpu_saved_mean_z);
paddle::framework::TensorCopySync(
saved_var_z, phi::CPUPlace(), cpu_saved_var_z);
}
paddle::framework::TensorCopySync(y, phi::CPUPlace(), cpu_y);
paddle::framework::TensorCopySync(bitmask, phi::CPUPlace(), cpu_bitmask);
}
// Get backward results of CudnnBNStatsFinalize + CudnnScaleBiasAddRelu
void FusedBackward(const phi::GPUContext &ctx,
phi::DenseTensor *cpu_dx,
phi::DenseTensor *cpu_dz,
phi::DenseTensor *cpu_dscale,
phi::DenseTensor *cpu_dbias) {
phi::DenseTensor dy;
phi::DenseTensor x;
phi::DenseTensor bn_scale;
phi::DenseTensor bn_bias;
phi::DenseTensor saved_mean;
phi::DenseTensor saved_var;
phi::DenseTensor bitmask;
phi::DenseTensor dx;
phi::DenseTensor dz;
phi::DenseTensor dscale;
phi::DenseTensor dbias;
auto place = ctx.GetPlace();
paddle::framework::TensorCopySync(cpu_dy_, place, &dy);
paddle::framework::TensorCopySync(cpu_x_, place, &x);
paddle::framework::TensorCopySync(cpu_bn_scale_x_, place, &bn_scale);
paddle::framework::TensorCopySync(cpu_bn_bias_x_, place, &bn_bias);
paddle::framework::TensorCopySync(cpu_saved_mean_x_, place, &saved_mean);
paddle::framework::TensorCopySync(cpu_saved_var_x_, place, &saved_var);
paddle::framework::TensorCopySync(cpu_bitmask_, place, &bitmask);
bn_scale.Resize({1, 1, 1, channels_});
bn_bias.Resize({1, 1, 1, channels_});
saved_mean.Resize({1, 1, 1, channels_});
saved_var.Resize({1, 1, 1, channels_});
dx.Resize(common::make_ddim({batch_size_, height_, width_, channels_}));
dz.Resize(common::make_ddim({batch_size_, height_, width_, channels_}));
dscale.Resize(common::make_ddim({1, 1, 1, channels_}));
dbias.Resize(common::make_ddim({1, 1, 1, channels_}));
auto data_shape = common::vectorize<int>(x.dims());
auto param_shape = common::vectorize<int>(bn_scale.dims());
auto bitmask_shape = common::vectorize<int>(bitmask.dims());
std::string act_type = "relu";
phi::fusion::CudnnScaleBiasAddRelu<T> sbar_op(
ctx, act_type, true, false, data_shape, param_shape, bitmask_shape);
sbar_op.Backward(ctx,
dy,
x,
bn_scale,
bn_bias,
saved_mean,
saved_var,
&bitmask,
&dx,
&dz,
&dscale,
&dbias,
eps_);
paddle::framework::TensorCopySync(dx, phi::CPUPlace(), cpu_dx);
paddle::framework::TensorCopySync(dz, phi::CPUPlace(), cpu_dz);
paddle::framework::TensorCopySync(dscale, phi::CPUPlace(), cpu_dscale);
paddle::framework::TensorCopySync(dbias, phi::CPUPlace(), cpu_dbias);
}
private:
int batch_size_;
int height_;
int width_;
int channels_;
int ele_count_;
std::string act_type_;
bool fuse_add_;
bool has_shortcut_;
// Forward input
phi::DenseTensor cpu_x_;
phi::DenseTensor cpu_bn_scale_x_;
phi::DenseTensor cpu_bn_bias_x_;
phi::DenseTensor cpu_z_;
phi::DenseTensor cpu_bn_scale_z_;
phi::DenseTensor cpu_bn_bias_z_;
// Backward input
phi::DenseTensor cpu_dy_;
phi::DenseTensor cpu_bitmask_;
phi::DenseTensor cpu_saved_mean_x_;
phi::DenseTensor cpu_saved_var_x_;
phi::DenseTensor cpu_saved_mean_z_;
phi::DenseTensor cpu_saved_var_z_;
phi::DenseTensor cpu_saved_mean_base_x_;
phi::DenseTensor cpu_saved_var_base_x_;
phi::DenseTensor saved_reserve_space_x_;
phi::DenseTensor cpu_saved_mean_base_z_;
phi::DenseTensor cpu_saved_var_base_z_;
phi::DenseTensor saved_reserve_space_z_;
phi::DenseTensor cpu_y_base_;
double eps_ = 1e-5;
float momentum_ = 0.9;
};
TEST(CudnnBNAddReluFp16, BNAdd) {
int batch_size = 4;
int height = 8;
int width = 8;
int channels = 64;
std::string act_type = "";
bool has_shortcut = false;
FLAGS_cudnn_batchnorm_spatial_persistent = true;
for (auto fuse_add : {false, true}) {
CudnnBNAddReluTester<phi::dtype::float16> test(
batch_size, height, width, channels, act_type, fuse_add, has_shortcut);
test.CheckForward(2e-3);
}
}
TEST(CudnnBNAddReluFp16, BNAddRelu) {
int batch_size = 4;
int height = 8;
int width = 8;
int channels = 64;
std::string act_type = "relu";
bool has_shortcut = false;
FLAGS_cudnn_batchnorm_spatial_persistent = true;
for (auto fuse_add : {false, true}) {
CudnnBNAddReluTester<phi::dtype::float16> test(
batch_size, height, width, channels, act_type, fuse_add, has_shortcut);
test.CheckForward(2e-3);
if (fuse_add) {
test.CheckBackward(2e-4);
}
}
}
TEST(CudnnBNAddReluFp16, HasShortcut) {
int batch_size = 4;
int height = 8;
int width = 8;
int channels = 64;
std::string act_type = "";
bool fuse_add = false;
bool has_shortcut = true;
FLAGS_cudnn_batchnorm_spatial_persistent = true;
CudnnBNAddReluTester<phi::dtype::float16> test(
batch_size, height, width, channels, act_type, fuse_add, has_shortcut);
test.CheckForward(5e-3);
}
@@ -0,0 +1,538 @@
/* Copyright (c) 2021 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 <random>
#include <vector>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/framework/tensor_util.h"
#include "paddle/phi/common/float16.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/fusion/gpu/cudnn_norm_conv.cu.h"
namespace framework = paddle::framework;
namespace platform = paddle::platform;
USE_OP_ITSELF(conv2d);
USE_OP_ITSELF(conv2d_grad);
PD_DECLARE_KERNEL(conv2d, GPUDNN, ALL_LAYOUT);
PD_DECLARE_KERNEL(conv2d_grad, GPUDNN, ALL_LAYOUT);
template <typename T>
void InitRandomTensor(const std::vector<int64_t> &dims,
phi::DenseTensor *cpu_out) {
T *cpu_out_ptr =
cpu_out->mutable_data<T>(common::make_ddim(dims), phi::CPUPlace());
std::default_random_engine random(0);
std::uniform_real_distribution<float> dis(0.0, 1.0);
for (int i = 0; i < cpu_out->numel(); ++i) {
cpu_out_ptr[i] = static_cast<T>(dis(random));
}
}
template <typename T>
void TransposeNchwToNhwc(const phi::DenseTensor &cpu_in,
phi::DenseTensor *cpu_out) {
const auto &in_dims = cpu_in.dims();
EXPECT_EQ(cpu_in.dims().size(), 4);
const T *cpu_in_ptr = cpu_in.data<T>();
T *cpu_out_ptr = cpu_out->mutable_data<T>(
{in_dims[0], in_dims[2], in_dims[3], in_dims[1]}, phi::CPUPlace());
int64_t n = in_dims[0];
int64_t c = in_dims[1];
int64_t hw = in_dims[2] * in_dims[3];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < hw; ++j) {
for (int k = 0; k < c; ++k) {
int dst_idx = i * hw * c + j * c + k; // NOLINT
int src_idx = i * c * hw + k * hw + j; // NOLINT
cpu_out_ptr[dst_idx] = cpu_in_ptr[src_idx];
}
}
}
}
template <typename T>
void CheckOutput(const phi::DenseTensor &cpu_res,
const phi::DenseTensor &cpu_base,
float diff,
bool is_relative_atol = false) {
EXPECT_EQ(cpu_res.dims(), cpu_base.dims());
const T *cpu_res_ptr = cpu_res.data<T>();
const T *cpu_base_ptr = cpu_base.data<T>();
for (int i = 0; i < cpu_res.numel(); ++i) {
if (is_relative_atol) {
EXPECT_LT(static_cast<float>(std::abs((cpu_res_ptr[i] - cpu_base_ptr[i]) /
cpu_base_ptr[i])),
diff);
} else {
EXPECT_LT(static_cast<float>(std::abs(cpu_res_ptr[i] - cpu_base_ptr[i])),
diff);
}
}
}
// Use Paddle conv2d op results as baseline
void ComputeConv2DForward(const phi::GPUContext &ctx,
const phi::DenseTensor &cpu_input,
const phi::DenseTensor &cpu_filter,
phi::DenseTensor *cpu_output,
int stride,
int padding) {
framework::Scope scope;
auto *input = scope.Var("Input")->GetMutable<phi::DenseTensor>();
auto *filter = scope.Var("Filter")->GetMutable<phi::DenseTensor>();
auto *output = scope.Var("Output")->GetMutable<phi::DenseTensor>();
auto place = ctx.GetPlace();
paddle::framework::TensorCopySync(cpu_input, place, input);
paddle::framework::TensorCopySync(cpu_filter, place, filter);
framework::AttributeMap attrs;
bool use_cudnn = true;
std::string data_format = "NHWC";
std::vector<int> strides = {stride, stride};
std::vector<int> paddings = {padding, padding};
attrs.insert({"strides", strides});
attrs.insert({"paddings", paddings});
attrs.insert({"use_cudnn", use_cudnn});
attrs.insert({"data_format", data_format});
auto op = framework::OpRegistry::CreateOp(
"conv2d",
{{"Input", {"Input"}}, {"Filter", {"Filter"}}},
{{"Output", {"Output"}}},
attrs);
op->Run(scope, ctx.GetPlace());
paddle::framework::TensorCopySync(*output, phi::CPUPlace(), cpu_output);
}
// Use Paddle conv2d_grad op results as baseline
void ComputeConv2DBackward(const phi::GPUContext &ctx,
const phi::DenseTensor &cpu_input,
const phi::DenseTensor &cpu_filter,
const phi::DenseTensor &cpu_output_grad,
phi::DenseTensor *cpu_input_grad,
phi::DenseTensor *cpu_filter_grad,
int stride,
int padding,
int dilation) {
framework::Scope scope;
auto *input = scope.Var("Input")->GetMutable<phi::DenseTensor>();
auto *filter = scope.Var("Filter")->GetMutable<phi::DenseTensor>();
auto *output_grad = scope.Var("Output@GRAD")->GetMutable<phi::DenseTensor>();
auto *input_grad = scope.Var("Input@GRAD")->GetMutable<phi::DenseTensor>();
auto *filter_grad = scope.Var("Filter@GRAD")->GetMutable<phi::DenseTensor>();
auto place = ctx.GetPlace();
paddle::framework::TensorCopySync(cpu_input, place, input);
paddle::framework::TensorCopySync(cpu_filter, place, filter);
paddle::framework::TensorCopySync(cpu_output_grad, place, output_grad);
framework::AttributeMap attrs;
bool use_cudnn = true;
std::string data_format = "NHWC";
std::string padding_algorithm = "EXPLICIT";
std::vector<int> strides = {stride, stride};
std::vector<int> paddings = {padding, padding};
std::vector<int> dilations = {dilation, dilation};
int groups = 1;
bool exhaustive_search = false;
bool use_addto = false;
attrs.insert({"use_cudnn", use_cudnn});
attrs.insert({"data_format", data_format});
attrs.insert({"padding_algorithm", padding_algorithm});
attrs.insert({"strides", strides});
attrs.insert({"paddings", paddings});
attrs.insert({"dilations", dilations});
attrs.insert({"groups", groups});
attrs.insert({"exhaustive_search", exhaustive_search});
attrs.insert({"use_addto", use_addto});
attrs.insert({"workspace_size_MB", 512});
auto op = framework::OpRegistry::CreateOp(
"conv2d_grad",
{{"Input", {"Input"}},
{"Filter", {"Filter"}},
{"Output@GRAD", {"Output@GRAD"}}},
{{"Input@GRAD", {"Input@GRAD"}}, {"Filter@GRAD", {"Filter@GRAD"}}},
attrs);
op->Run(scope, ctx.GetPlace());
paddle::framework::TensorCopySync(
*input_grad, phi::CPUPlace(), cpu_input_grad);
paddle::framework::TensorCopySync(
*filter_grad, phi::CPUPlace(), cpu_filter_grad);
}
template <typename T>
void ComputeSumAndSquareSum(const phi::DenseTensor &cpu_out,
phi::DenseTensor *cpu_sum,
phi::DenseTensor *cpu_sum_of_square) {
const auto &dims = cpu_out.dims();
int64_t c = dims[3];
const T *cpu_out_ptr = cpu_out.data<T>();
float *cpu_sum_ptr =
cpu_sum->mutable_data<float>({1, 1, 1, c}, phi::CPUPlace());
float *cpu_sum_square_ptr =
cpu_sum_of_square->mutable_data<float>({1, 1, 1, c}, phi::CPUPlace());
for (int j = 0; j < c; ++j) {
float tmp_sum = 0.0f;
float tmp_sum_of_squares = 0.0f;
for (int i = 0; i < cpu_out.numel() / c; ++i) {
float tmp_out = static_cast<float>(cpu_out_ptr[i * c + j]);
tmp_sum += tmp_out;
tmp_sum_of_squares += tmp_out * tmp_out;
}
cpu_sum_ptr[j] = tmp_sum;
cpu_sum_square_ptr[j] = tmp_sum_of_squares;
}
}
template <typename T>
class CudnnNormConvolutionTester {
public:
CudnnNormConvolutionTester(int batch_size,
int height,
int width,
int input_channels,
int output_channels,
int kernel_size,
int stride) {
batch_size_ = batch_size;
height_ = height;
width_ = width;
input_channels_ = input_channels;
output_channels_ = output_channels;
kernel_size_ = kernel_size;
stride_ = stride;
padding_ = (kernel_size_ - 1) / 2;
out_height_ = (height_ + 2 * padding_ - kernel_size_) / stride_ + 1;
out_width_ = (width_ + 2 * padding_ - kernel_size_) / stride_ + 1;
SetUp();
}
~CudnnNormConvolutionTester() = default;
void CheckForward(float diff, bool is_relative_atol = false) {
phi::GPUContext *ctx = static_cast<phi::GPUContext *>(
phi::DeviceContextPool::Instance().Get(phi::GPUPlace(0)));
phi::DenseTensor cpu_output_base;
phi::DenseTensor cpu_sum_base;
phi::DenseTensor cpu_sum_of_square_base;
BaselineForward(
*ctx, &cpu_output_base, &cpu_sum_base, &cpu_sum_of_square_base);
phi::DenseTensor cpu_output;
phi::DenseTensor cpu_sum;
phi::DenseTensor cpu_sum_of_square;
FusedForward(*ctx, &cpu_output, &cpu_sum, &cpu_sum_of_square);
// Check forward correctness between baseline and results of normconv.
CheckOutput<T>(cpu_output, cpu_output_base, diff, is_relative_atol);
CheckOutput<float>(cpu_sum, cpu_sum_base, diff, is_relative_atol);
CheckOutput<float>(
cpu_sum_of_square, cpu_sum_of_square_base, diff, is_relative_atol);
}
void CheckBackward(float diff, bool is_relative_atol = false) {
phi::GPUContext *ctx = static_cast<phi::GPUContext *>(
phi::DeviceContextPool::Instance().Get(phi::GPUPlace(0)));
phi::DenseTensor cpu_input_grad_base;
phi::DenseTensor cpu_filter_nchw_grad_base;
phi::DenseTensor cpu_filter_nhwc_grad_base;
BaselineBackward(*ctx, &cpu_input_grad_base, &cpu_filter_nchw_grad_base);
TransposeNchwToNhwc<T>(cpu_filter_nchw_grad_base,
&cpu_filter_nhwc_grad_base);
phi::DenseTensor cpu_input_grad;
phi::DenseTensor cpu_filter_nhwc_grad;
FusedBackward(*ctx, &cpu_input_grad, &cpu_filter_nhwc_grad);
// Check backward correctness between baseline and results of normconv.
CheckOutput<T>(cpu_input_grad, cpu_input_grad_base, diff, is_relative_atol);
CheckOutput<T>(cpu_filter_nhwc_grad,
cpu_filter_nhwc_grad_base,
diff,
is_relative_atol);
}
private:
void SetUp() {
InitRandomTensor<T>({batch_size_, height_, width_, input_channels_},
&cpu_input_);
InitRandomTensor<T>(
{output_channels_, input_channels_, kernel_size_, kernel_size_},
&cpu_filter_nchw_);
// transpoes for filter, NCHW -> NHWC
TransposeNchwToNhwc<T>(cpu_filter_nchw_, &cpu_filter_nhwc_);
InitRandomTensor<T>(
{batch_size_, out_height_, out_width_, output_channels_},
&cpu_output_grad_);
}
void BaselineForward(const phi::GPUContext &ctx,
phi::DenseTensor *cpu_output_base,
phi::DenseTensor *cpu_sum_base,
phi::DenseTensor *cpu_sum_of_square_base) {
ComputeConv2DForward(
ctx, cpu_input_, cpu_filter_nchw_, cpu_output_base, stride_, padding_);
ComputeSumAndSquareSum<T>(
*cpu_output_base, cpu_sum_base, cpu_sum_of_square_base);
}
void BaselineBackward(const phi::GPUContext &ctx,
phi::DenseTensor *cpu_input_grad_base,
phi::DenseTensor *cpu_filter_grad_base) {
ComputeConv2DBackward(ctx,
cpu_input_,
cpu_filter_nchw_,
cpu_output_grad_,
cpu_input_grad_base,
cpu_filter_grad_base,
stride_,
padding_,
dilation_);
}
// get forward results of cudnn_norm_conv
void FusedForward(const phi::GPUContext &ctx,
phi::DenseTensor *cpu_output,
phi::DenseTensor *cpu_sum,
phi::DenseTensor *cpu_sum_of_square) {
phi::DenseTensor input;
phi::DenseTensor filter_nhwc;
phi::DenseTensor output;
phi::DenseTensor sum;
phi::DenseTensor sum_of_square;
auto place = ctx.GetPlace();
paddle::framework::TensorCopySync(cpu_input_, place, &input);
paddle::framework::TensorCopySync(cpu_filter_nhwc_, place, &filter_nhwc);
output.Resize(common::make_ddim(
{batch_size_, out_height_, out_width_, output_channels_}));
sum.Resize(common::make_ddim({1, 1, 1, output_channels_}));
sum_of_square.Resize(common::make_ddim({1, 1, 1, output_channels_}));
auto input_shape = common::vectorize<int>(input.dims());
auto filter_shape = common::vectorize<int>(filter_nhwc.dims());
auto output_shape = common::vectorize<int>(output.dims());
phi::fusion::CudnnNormConvolution<T> conv_op(ctx,
input_shape,
filter_shape,
output_shape,
padding_,
stride_,
dilation_,
group_);
conv_op.Forward(ctx, input, filter_nhwc, &output, &sum, &sum_of_square);
paddle::framework::TensorCopySync(output, phi::CPUPlace(), cpu_output);
paddle::framework::TensorCopySync(sum, phi::CPUPlace(), cpu_sum);
paddle::framework::TensorCopySync(
sum_of_square, phi::CPUPlace(), cpu_sum_of_square);
}
void FusedBackward(const phi::GPUContext &ctx,
phi::DenseTensor *cpu_input_grad,
phi::DenseTensor *cpu_filter_grad) {
phi::DenseTensor input;
phi::DenseTensor filter_nhwc;
phi::DenseTensor output_grad;
phi::DenseTensor input_grad;
phi::DenseTensor filter_grad;
auto place = ctx.GetPlace();
paddle::framework::TensorCopySync(cpu_input_, place, &input);
paddle::framework::TensorCopySync(cpu_filter_nhwc_, place, &filter_nhwc);
paddle::framework::TensorCopySync(cpu_output_grad_, place, &output_grad);
input_grad.Resize(input.dims());
filter_grad.Resize(filter_nhwc.dims());
auto input_shape = common::vectorize<int>(input.dims());
auto filter_shape = common::vectorize<int>(filter_nhwc.dims());
auto output_shape = common::vectorize<int>(output_grad.dims());
phi::fusion::CudnnNormConvolutionGrad<T> conv_grad_op(ctx,
input_shape,
filter_shape,
output_shape,
padding_,
stride_,
dilation_,
group_);
conv_grad_op.Backward(
ctx, input, filter_nhwc, output_grad, &input_grad, &filter_grad);
paddle::framework::TensorCopySync(
input_grad, phi::CPUPlace(), cpu_input_grad);
paddle::framework::TensorCopySync(
filter_grad, phi::CPUPlace(), cpu_filter_grad);
}
private:
int batch_size_;
int height_;
int width_;
int out_height_;
int out_width_;
int input_channels_;
int output_channels_;
int kernel_size_;
int stride_;
int padding_;
const int dilation_ = 1;
const int group_ = 1;
// Forward input
phi::DenseTensor cpu_input_;
phi::DenseTensor cpu_filter_nchw_;
phi::DenseTensor cpu_filter_nhwc_;
// Backward input
phi::DenseTensor cpu_output_grad_;
};
// test for fp16, kernel = 1, output_channels = input_channels
TEST(CudnnNormConvFp16, K1S1) {
int batch_size = 4;
int height = 56;
int width = 56;
int input_channels = 32;
int output_channels = 32;
int kernel_size = 1;
int stride = 1;
CudnnNormConvolutionTester<phi::dtype::float16> test(batch_size,
height,
width,
input_channels,
output_channels,
kernel_size,
stride);
phi::GPUContext *ctx = static_cast<phi::GPUContext *>(
phi::DeviceContextPool::Instance().Get(phi::GPUPlace(0)));
if (ctx->GetComputeCapability() < 70 || ctx->GetComputeCapability() >= 90) {
ASSERT_THROW(test.CheckForward(1e-3, true),
paddle::platform::EnforceNotMet);
ASSERT_THROW(test.CheckBackward(1e-3, true),
paddle::platform::EnforceNotMet);
} else {
ASSERT_NO_THROW(test.CheckForward(1e-3, true));
ASSERT_NO_THROW(test.CheckBackward(1e-3, true));
}
}
// test for fp16, kernel = 3, output_channels = input_channels
TEST(CudnnNormConvFp16, K3S1) {
int batch_size = 4;
int height = 56;
int width = 56;
int input_channels = 32;
int output_channels = 32;
int kernel_size = 3;
int stride = 1;
CudnnNormConvolutionTester<phi::dtype::float16> test(batch_size,
height,
width,
input_channels,
output_channels,
kernel_size,
stride);
phi::GPUContext *ctx = static_cast<phi::GPUContext *>(
phi::DeviceContextPool::Instance().Get(phi::GPUPlace(0)));
if (ctx->GetComputeCapability() < 70 || ctx->GetComputeCapability() >= 90) {
ASSERT_THROW(test.CheckForward(1e-3, true),
paddle::platform::EnforceNotMet);
ASSERT_THROW(test.CheckBackward(1e-3, true),
paddle::platform::EnforceNotMet);
} else {
ASSERT_NO_THROW(test.CheckForward(1e-3, true));
ASSERT_NO_THROW(test.CheckBackward(1e-3, true));
}
}
// test for fp16, kernel = 1, output_channels = input_channels * 4
TEST(CudnnNormConvFp16, K1S1O4) {
int batch_size = 4;
int height = 56;
int width = 56;
int input_channels = 32;
int output_channels = 128;
int kernel_size = 1;
int stride = 1;
CudnnNormConvolutionTester<phi::dtype::float16> test(batch_size,
height,
width,
input_channels,
output_channels,
kernel_size,
stride);
phi::GPUContext *ctx = static_cast<phi::GPUContext *>(
phi::DeviceContextPool::Instance().Get(phi::GPUPlace(0)));
if (ctx->GetComputeCapability() < 70 || ctx->GetComputeCapability() >= 90) {
ASSERT_THROW(test.CheckForward(1e-3, true),
paddle::platform::EnforceNotMet);
ASSERT_THROW(test.CheckBackward(1e-3, true),
paddle::platform::EnforceNotMet);
} else {
ASSERT_NO_THROW(test.CheckForward(1e-3, true));
ASSERT_NO_THROW(test.CheckBackward(1e-3, true));
}
}
// test for fp16, kernel = 1, stride = 2, output_channels = input_channels * 4
TEST(CudnnNormConvFp16, K1S2O4) {
int batch_size = 4;
int height = 8;
int width = 8;
int input_channels = 32;
int output_channels = 128;
int kernel_size = 1;
int stride = 2;
CudnnNormConvolutionTester<phi::dtype::float16> test(batch_size,
height,
width,
input_channels,
output_channels,
kernel_size,
stride);
phi::GPUContext *ctx = static_cast<phi::GPUContext *>(
phi::DeviceContextPool::Instance().Get(phi::GPUPlace(0)));
if (ctx->GetComputeCapability() <= 70 || ctx->GetComputeCapability() >= 90) {
ASSERT_THROW(test.CheckForward(1e-3, true),
paddle::platform::EnforceNotMet);
ASSERT_THROW(test.CheckBackward(1e-3), paddle::platform::EnforceNotMet);
} else {
ASSERT_NO_THROW(test.CheckForward(1e-3, true));
ASSERT_NO_THROW(test.CheckBackward(1e-3));
}
}
@@ -0,0 +1,403 @@
/* Copyright (c) 2021 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 <time.h>
#include <random>
#include <vector>
#include "paddle/phi/common/amp_type_traits.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/functors.h"
#include "paddle/phi/kernels/fusion/gpu/fused_dropout_act_bias.h"
#include "test/cpp/fluid/fused/fused_dropout_test.h"
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
PD_DECLARE_KERNEL(dropout, GPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(dropout_grad, GPU, ALL_LAYOUT);
#endif
namespace framework = paddle::framework;
namespace platform = paddle::platform;
/**
* @brief the unittest of fused_dropout_act_bias
* 1. random input data
* 2. add bias, call activation, call paddle dropout, and get the base result
* 3. call FusedDropoutActBias function get fused result
* 4. compare the base result and fused result
*/
template <typename T, typename Functor, typename GradFunctor>
struct TestFusedDropoutActBias {
uint32_t rows;
uint32_t cols;
uint64_t seed;
float dropout_prob;
bool is_upscale_in_train;
bool is_test; // default false, Set to true for inference only
bool has_bias = true;
phi::DenseTensor src, bias, out, mask;
phi::DenseTensor dsrc, dbias;
std::vector<T> src_vec, bias_vec, out_vec, mask_vec;
std::vector<T> correct_out, correct_dsrc, correct_dbias;
std::vector<uint8_t> correct_mask;
phi::GPUPlace place;
phi::GPUContext *ctx;
TestFusedDropoutActBias() {
rows = 32;
cols = 32;
seed = 0;
dropout_prob = 0.0;
is_upscale_in_train = false;
is_test = false;
has_bias = true;
phi::DeviceContextPool &pool = phi::DeviceContextPool::Instance();
auto devicectx = pool.Get(place);
ctx = reinterpret_cast<phi::GPUContext *>(devicectx);
}
TestFusedDropoutActBias(int rows_,
int cols_,
uint64_t seed_ = 0,
float dropout_prob_ = 0.0,
bool is_upscale_in_train_ = false,
bool is_test_ = false) {
rows = rows_;
cols = cols_;
seed = seed_;
dropout_prob = dropout_prob_;
is_upscale_in_train = is_upscale_in_train_;
is_test = is_test_;
has_bias = true;
phi::DeviceContextPool &pool = phi::DeviceContextPool::Instance();
auto devicectx = pool.Get(place);
ctx = reinterpret_cast<phi::GPUContext *>(devicectx);
}
~TestFusedDropoutActBias() = default;
void SetUp() {
const int n = rows * cols;
correct_out.resize(n);
correct_mask.resize(n);
correct_dsrc.resize(n);
correct_dbias.resize(cols);
src_vec.resize(n);
bias_vec.resize(cols);
std::default_random_engine random(time(NULL));
std::uniform_real_distribution<float> dis(0.0, 1.0);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
src_vec[i * cols + j] = static_cast<T>(dis(random));
if (i == 0) bias_vec[j] = dis(random);
}
}
framework::TensorFromVector<T>(src_vec, *ctx, &src);
src.Resize({rows, cols});
if (has_bias) {
framework::TensorFromVector<T>(bias_vec, *ctx, &bias);
bias.Resize({cols});
}
{
out.mutable_data<T>({rows, cols}, place);
mask.mutable_data<uint8_t>({rows, cols}, place);
dsrc.mutable_data<T>({rows, cols}, place);
if (has_bias) {
dbias.mutable_data<T>({cols}, place);
}
}
}
void BaseForward() {
std::vector<T> out1(rows * cols);
Functor act;
if (has_bias) {
// add bias and call activation
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
const T tmp = src_vec[i * cols + j] + bias_vec[j];
out1[i * cols + j] = act(tmp);
}
}
// call dropout
Dropout<T>(out1,
src.dims(),
&correct_out,
&correct_mask,
*ctx,
seed,
dropout_prob,
is_upscale_in_train,
is_test);
} else {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
const T tmp = src_vec[i * cols + j];
out1[i * cols + j] = act(tmp);
}
}
Dropout<T>(out1,
src.dims(),
&correct_out,
&correct_mask,
*ctx,
seed,
dropout_prob,
is_upscale_in_train,
is_test);
}
ctx->Wait();
}
void BaseBackward() {
std::vector<T> _out(rows * cols);
// call dropout_grad
DropoutGrad<T>(&_out,
src.dims(),
correct_out,
correct_mask,
*ctx,
dropout_prob,
is_upscale_in_train);
// calculate dbias
memset(&correct_dbias[0], 0, cols * sizeof(T));
GradFunctor act_grad;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
T args[2];
args[0] = _out[i * cols + j];
if (has_bias) {
args[1] = src_vec[i * cols + j] + bias_vec[j];
} else {
args[1] = src_vec[i * cols + j];
}
T val = args[0] * act_grad.UseOut(args[1]);
correct_dsrc[i * cols + j] = val;
}
}
if (has_bias) {
// reduce_sum: keep the same calculate order as the GPU
ReduceSum<T>(correct_dsrc, &correct_dbias, rows, cols);
}
}
void FusedForward() {
const int VecSize = MAX_CACHE_BYTES / sizeof(T);
auto config =
phi::fusion::Get1DBlocksAnd2DGrids(*ctx,
static_cast<uint64_t>(rows),
static_cast<uint64_t>(cols),
VecSize);
const int increment = ((cols - 1) / (config.thread_per_block.x *
config.block_per_grid.x * VecSize) +
1) *
VecSize;
T *bias_ptr = nullptr;
if (has_bias) {
bias_ptr = bias.data<T>();
}
Functor act;
phi::fusion::LaunchDropoutActBias<T, uint8_t, Functor>(act,
seed,
rows,
cols,
increment,
dropout_prob,
is_upscale_in_train,
is_test,
src.data<T>(),
bias_ptr,
out.data<T>(),
mask.data<uint8_t>(),
*ctx);
ctx->Wait();
}
void FusedBackward() {
if (is_test) return;
T *bias_ptr = nullptr;
T *dbias_ptr = nullptr;
if (has_bias) {
dbias_ptr = dbias.data<T>();
bias_ptr = bias.data<T>();
}
GradFunctor act_grad;
phi::fusion::LaunchDropoutActBiasGrad<T, uint8_t, GradFunctor>(
act_grad,
out.data<T>(),
mask.data<uint8_t>(),
src.data<T>(),
bias_ptr,
dropout_prob,
is_upscale_in_train,
rows,
cols,
dsrc.data<T>(),
dbias_ptr,
*ctx);
}
void Run() {
SetUp();
BaseForward();
FusedForward();
BaseBackward();
FusedBackward();
}
void CheckOut(const T diff) {
const int n = rows * cols;
std::vector<T> _out(n);
std::vector<uint8_t> _mask(n);
framework::TensorToVector(out, *ctx, &_out);
if (!is_test) {
framework::TensorToVector<uint8_t>(mask, *ctx, &_mask);
}
ctx->Wait();
for (int i = 0; i < n; i++) {
EXPECT_LT(std::abs(_out[i] - correct_out[i]), diff);
if (!is_test) EXPECT_EQ(_mask[i], correct_mask[i]);
}
}
void CheckGrad(const T diff) {
if (is_test) return;
const int n = rows * cols;
std::vector<T> _dsrc(n);
framework::TensorToVector(dsrc, *ctx, &_dsrc);
for (int i = 0; i < n; i++) {
EXPECT_LT(std::abs(_dsrc[i] - correct_dsrc[i]), diff);
}
if (has_bias) {
std::vector<T> _dbias(cols);
framework::TensorToVector(dbias, *ctx, &_dbias);
ctx->Wait();
for (int i = 0; i < cols; i++) {
EXPECT_LT(std::abs(_dbias[i] - correct_dbias[i]), diff);
}
}
}
};
// test the shape , bias, activation
template <typename T, typename Functor, typename GradFunctor>
static void BaseTest(const bool is_fp16 = false) {
const int rows = 16;
std::vector<int> cols_list = {16, 17};
bool has_bias[2] = {true, false};
T default_diff = !is_fp16 ? static_cast<T>(1e-5) : static_cast<T>(1e-1);
for (auto cols : {16, 17}) {
for (auto has_bias : {true, false}) {
TestFusedDropoutActBias<T, Functor, GradFunctor> test(rows, cols);
test.has_bias = has_bias;
test.Run();
test.CheckOut(default_diff);
test.CheckGrad(default_diff);
}
}
}
TEST(FusedDropout, GPUFusedDorpoutActBias) {
BaseTest<float,
phi::funcs::ReluFunctor<float>,
phi::funcs::ReluGradFunctor<float>>();
BaseTest<float,
phi::fusion::LayerNormParamTypeGeluFunctor<float>,
phi::fusion::GeluGradFunctor<float>>();
}
TEST(FusedDropout, GPUFusedDropoutActBiasDouble) {
BaseTest<double,
phi::funcs::ReluFunctor<double>,
phi::funcs::ReluGradFunctor<double>>();
BaseTest<double,
phi::fusion::LayerNormParamTypeGeluFunctor<double>,
phi::fusion::GeluGradFunctor<double>>();
}
// test fp16, For inference, check_grad is not required. ref: test_dropout_op.py
TEST(FusedDropout, GPUFusedDropoutActBiasFp16) {
using fp16 = phi::dtype::float16;
BaseTest<fp16,
phi::funcs::ReluFunctor<fp16>,
phi::funcs::ReluGradFunctor<fp16>>(true);
}
TEST(FusedDropout, GPUFusedDropoutActBiasIsUpscaleInTrain) {
const int rows = 16;
const int cols = 16;
for (auto is_upscale_in_train : {true, false}) {
TestFusedDropoutActBias<float,
phi::funcs::ReluFunctor<float>,
phi::funcs::ReluGradFunctor<float>>
test(rows, cols, 0, 1.0, is_upscale_in_train, false);
test.Run();
test.CheckOut(static_cast<float>(1e-5));
test.CheckGrad(static_cast<float>(1e-3));
}
}
TEST(FusedDropout, GPUFusedDropoutActBiasIsTest) {
const int rows = 16;
const int cols = 16;
TestFusedDropoutActBias<float,
phi::funcs::ReluFunctor<float>,
phi::funcs::ReluGradFunctor<float>>
test(rows, cols, 0, 0.35, true, true);
test.Run();
test.CheckOut(static_cast<float>(1e-5));
test.CheckGrad(static_cast<float>(1e-3));
}
TEST(FusedDropout, GPUFusedDropoutActBiasSeed) {
const int rows = 16;
const int cols = 16;
TestFusedDropoutActBias<float,
phi::funcs::ReluFunctor<float>,
phi::funcs::ReluGradFunctor<float>>
test(rows, cols, 125, 0.0, false, false);
test.Run();
test.CheckOut(static_cast<float>(1e-5));
test.CheckGrad(static_cast<float>(1e-3));
}
TEST(FusedDropout, GPUFusedDropoutActBiasLargeShape) {
const int rows = 256;
const int cols = 4096;
TestFusedDropoutActBias<float,
phi::funcs::ReluFunctor<float>,
phi::funcs::ReluGradFunctor<float>>
test(rows, cols);
test.Run();
test.CheckOut(static_cast<float>(1e-5));
test.CheckGrad(static_cast<float>(1e-3));
}
+223
View File
@@ -0,0 +1,223 @@
/* Copyright (c) 2021 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 <random>
#include <vector>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/framework/tensor_util.h"
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/core/memory/memory.h"
#include "paddle/phi/kernels/funcs/layer_norm_impl.cu.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/layer_norm_kernel.h"
#include "paddle/utils/string/printf.h"
namespace framework = paddle::framework;
namespace platform = paddle::platform;
namespace memory = paddle::memory;
USE_OP_ITSELF(dropout);
template <typename T>
using CudnnDataType = phi::backends::gpu::CudnnDataType<T>;
template <typename T>
using LayerNormParamType = typename CudnnDataType<T>::BatchNormParamType;
/**
* @brief call paddle dropout op
*/
template <typename T>
void Dropout(const std::vector<T> &x,
const phi::DDim &x_dim,
std::vector<T> *out,
std::vector<uint8_t> *mask,
const phi::GPUContext &ctx,
uint64_t seed,
float dropout_prob,
bool is_upscale_in_train,
bool is_test) {
framework::Scope scope;
auto var_x = scope.Var("X");
auto tensor_x = var_x->GetMutable<phi::DenseTensor>();
framework::TensorFromVector(x, ctx, tensor_x);
tensor_x->Resize(x_dim);
auto var_out = scope.Var("Out");
auto tensor_out = var_out->GetMutable<phi::DenseTensor>();
auto var_mask = scope.Var("Mask");
auto tensor_mask = var_mask->GetMutable<phi::DenseTensor>();
framework::AttributeMap attrs;
attrs.insert({"fix_seed", 1});
attrs.insert({"seed", static_cast<int>(seed)});
attrs.insert({"dropout_prob", dropout_prob});
if (is_upscale_in_train) {
attrs.insert({"dropout_implementation", std::string("upscale_in_train")});
}
if (is_test) {
attrs.insert({"is_test", true});
}
auto op = framework::OpRegistry::CreateOp(
"dropout", {{"X", {"X"}}}, {{"Out", {"Out"}}, {"Mask", {"Mask"}}}, attrs);
op->Run(scope, ctx.GetPlace());
framework::TensorToVector<T>(*tensor_out, ctx, out);
if (!is_test) {
framework::TensorToVector<uint8_t>(*tensor_mask, ctx, mask);
}
ctx.Wait();
}
/**
* @brief call paddle dropout_grad op
*/
template <typename T>
void DropoutGrad(std::vector<T> *dx,
const phi::DDim &x_dim,
const std::vector<T> &dout,
const std::vector<uint8_t> &mask,
const phi::GPUContext &ctx,
float dropout_prob,
bool is_upscale_in_train) {
framework::Scope scope;
const size_t n = x_dim[0] * x_dim[1];
auto var_out = scope.Var("DOut");
auto tensor_out = var_out->GetMutable<phi::DenseTensor>();
framework::TensorFromVector(dout, ctx, tensor_out);
tensor_out->Resize(x_dim);
auto var_mask = scope.Var("Mask");
auto tensor_mask = var_mask->GetMutable<phi::DenseTensor>();
framework::TensorFromVector(mask, ctx, tensor_mask);
tensor_mask->Resize(x_dim);
auto var_dx = scope.Var("DX");
auto tensor_dx = var_dx->GetMutable<phi::DenseTensor>();
framework::AttributeMap attrs;
attrs.insert({"dropout_prob", dropout_prob});
attrs.insert({"is_test", false});
if (is_upscale_in_train) {
attrs.insert({"dropout_implementation", std::string("upscale_in_train")});
} else {
attrs.insert({"dropout_implementation", std::string("downgrade_in_infer")});
}
auto op = framework::OpRegistry::CreateOp(
"dropout_grad",
{{"Out@GRAD", {"DOut"}}, {"Mask", {"Mask"}}},
{{"X@GRAD", {"DX"}}},
attrs);
op->Run(scope, ctx.GetPlace());
framework::TensorToVector(*tensor_dx, ctx, dx);
ctx.Wait();
}
/**
* @brief call paddle layer_norm op
*/
template <typename T>
void LayerNorm(const std::vector<LayerNormParamType<T>> &scale,
const std::vector<LayerNormParamType<T>> &bias,
const std::vector<T> &x,
std::vector<LayerNormParamType<T>> *means,
std::vector<LayerNormParamType<T>> *vars,
std::vector<T> *y,
const double epsilon,
const int rows,
const int cols,
const phi::GPUContext &ctx) {
framework::Scope scope;
auto place = ctx.GetPlace();
paddle::optional<phi::DenseTensor> scale_opt;
if (scale.size() > 0) {
auto var_scale = scope.Var("Scale");
auto tensor_scale = var_scale->GetMutable<phi::DenseTensor>();
framework::TensorFromVector(scale, ctx, tensor_scale);
tensor_scale->Resize({cols});
scale_opt = *tensor_scale;
}
paddle::optional<phi::DenseTensor> bias_opt;
if (bias.size() > 0) {
auto var_bias = scope.Var("Bias");
auto tensor_bias = var_bias->GetMutable<phi::DenseTensor>();
framework::TensorFromVector(bias, ctx, tensor_bias);
tensor_bias->Resize({cols});
bias_opt = *tensor_bias;
}
auto var_x = scope.Var("X");
auto tensor_x = var_x->GetMutable<phi::DenseTensor>();
framework::TensorFromVector(x, ctx, tensor_x);
tensor_x->Resize({rows, cols});
auto var_y = scope.Var("Y");
auto tensor_y = var_y->GetMutable<phi::DenseTensor>();
tensor_y->Resize({rows, cols});
auto var_mean = scope.Var("Mean");
auto tensor_mean = var_mean->GetMutable<phi::DenseTensor>();
tensor_mean->Resize({rows});
auto var_variance = scope.Var("Variance");
auto tensor_variance = var_variance->GetMutable<phi::DenseTensor>();
tensor_variance->Resize({rows});
ctx.Wait();
phi::LayerNormKernel<T>(static_cast<const phi::GPUContext &>(ctx),
*tensor_x,
scale_opt,
bias_opt,
1e-5,
1,
tensor_y,
tensor_mean,
tensor_variance);
framework::TensorToVector(*tensor_y, ctx, y);
framework::TensorToVector(*tensor_mean, ctx, means);
framework::TensorToVector(*tensor_variance, ctx, vars);
ctx.Wait();
}
template <typename T>
inline void ReduceSum(const std::vector<T> &dout,
std::vector<T> *dbias,
const int rows,
const int cols) {
for (int j = 0; j < cols; j++) {
std::vector<T> tmp_dbias(rows);
for (int i = 0; i < rows; i++) {
tmp_dbias[i] = dout[i * cols + j];
}
int tmp_rows = rows / 2;
while (tmp_rows) {
for (int i = 0; i < tmp_rows; i++) {
tmp_dbias[i] += tmp_dbias[i + tmp_rows];
}
tmp_rows /= 2;
}
(*dbias)[j] = tmp_dbias[0];
}
}
@@ -0,0 +1,385 @@
/* Copyright (c) 2021 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 <time.h>
#include <random>
#include <vector>
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/fusion/gpu/fused_dropout_helper.h"
#include "test/cpp/fluid/fused/fused_dropout_test.h"
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
PD_DECLARE_KERNEL(dropout, GPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(dropout_grad, GPU, ALL_LAYOUT);
#endif
/**
* @brief The unit test of fused_layernorm_residual_dropout_bias
*/
template <typename T>
struct TestFusedLayernormResidualDropoutBias {
uint32_t rows;
uint32_t cols;
uint64_t seed;
float dropout_prob, epsilon;
bool is_upscale_in_train;
bool is_test; // default false, Set to true for inference only
bool has_bias = true;
bool has_scale = true;
bool has_layernorm_bias = true;
phi::DenseTensor src, residual, bias, out, mask, scale, layernorm_bias,
layernorm_out, means, vars;
phi::DenseTensor dsrc, dbias;
std::vector<T> src_vec, residual_vec, bias_vec;
std::vector<phi::funcs::LayerNormParamType<T>> means_vec, vars_vec, scale_vec,
layernorm_bias_vec;
std::vector<T> correct_out, correct_dsrc, correct_dbias,
correct_layernorm_out;
std::vector<phi::funcs::LayerNormParamType<T>> correct_means, correct_vars;
std::vector<uint8_t> correct_mask;
phi::GPUPlace place;
phi::GPUContext *ctx;
TestFusedLayernormResidualDropoutBias() {
rows = 32;
cols = 32;
seed = 0;
dropout_prob = 0.0;
is_upscale_in_train = false;
is_test = false;
has_bias = true;
has_scale = true;
has_layernorm_bias = true;
epsilon = 0.00001f;
phi::DeviceContextPool &pool = phi::DeviceContextPool::Instance();
auto devicectx = pool.Get(place);
ctx = reinterpret_cast<phi::GPUContext *>(devicectx);
}
TestFusedLayernormResidualDropoutBias(int _rows,
int _cols,
uint64_t _seed = 0,
float _dropout_prob = 0.0,
float _epsilon = 0.00001f,
bool _is_upscale_in_train = false,
bool _is_test = false,
bool _has_bias = true) {
rows = _rows;
cols = _cols;
seed = _seed;
dropout_prob = _dropout_prob;
epsilon = _epsilon;
is_upscale_in_train = _is_upscale_in_train;
is_test = _is_test;
has_bias = _has_bias;
has_scale = true;
has_layernorm_bias = true;
phi::DeviceContextPool &pool = phi::DeviceContextPool::Instance();
auto devicectx = pool.Get(place);
ctx = reinterpret_cast<phi::GPUContext *>(devicectx);
}
~TestFusedLayernormResidualDropoutBias() = default;
void SetUp() {
using U = phi::funcs::LayerNormParamType<T>;
const int n = rows * cols;
correct_out.resize(n);
correct_mask.resize(n);
correct_dsrc.resize(n);
correct_dbias.resize(cols);
correct_means.resize(rows);
correct_vars.resize(rows);
correct_layernorm_out.resize(n);
src_vec.resize(n);
residual_vec.resize(n);
if (has_bias) {
bias_vec.resize(cols);
}
if (has_scale) {
scale_vec.resize(cols);
}
if (has_layernorm_bias) {
layernorm_bias_vec.resize(cols);
}
std::default_random_engine random(time(NULL));
std::uniform_real_distribution<float> dis(0.0, 1.0);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
src_vec[i * cols + j] = static_cast<T>(dis(random));
residual_vec[i * cols + j] = static_cast<T>(dis(random));
if (i == 0) {
if (has_bias) {
bias_vec[j] = static_cast<T>(dis(random));
}
if (has_scale) {
scale_vec[j] = static_cast<U>(dis(random));
}
if (has_layernorm_bias) {
layernorm_bias_vec[j] = static_cast<U>(dis(random));
}
}
}
}
framework::TensorFromVector<T>(src_vec, *ctx, &src);
src.Resize({rows, cols});
framework::TensorFromVector<T>(residual_vec, *ctx, &residual);
residual.Resize({rows, cols});
if (has_bias) {
framework::TensorFromVector<T>(bias_vec, *ctx, &bias);
bias.Resize({cols});
}
if (has_scale) {
framework::TensorFromVector<U>(scale_vec, *ctx, &scale);
scale.Resize({cols});
}
if (has_layernorm_bias) {
framework::TensorFromVector<U>(layernorm_bias_vec, *ctx, &layernorm_bias);
layernorm_bias.Resize({cols});
}
{
out.Resize({rows, cols});
out.mutable_data<T>(place);
mask.Resize({rows, cols});
mask.mutable_data<uint8_t>(place);
means.Resize({rows});
means.mutable_data<U>(place);
vars.Resize({rows});
vars.mutable_data<U>(place);
layernorm_out.Resize({rows, cols});
layernorm_out.mutable_data<T>(place);
dsrc.Resize({rows, cols});
dsrc.mutable_data<T>(place);
if (has_bias) {
dbias.Resize({cols});
dbias.mutable_data<T>(place);
}
}
}
void BaseForward() {
using U = phi::funcs::LayerNormParamType<T>;
std::vector<T> out1(rows * cols), out2(rows * cols);
if (has_bias) {
// add bias
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
out1[i * cols + j] = src_vec[i * cols + j] + bias_vec[j];
}
}
// call dropout
Dropout<T>(out1,
src.dims(),
&out2,
&correct_mask,
*ctx,
seed,
dropout_prob,
is_upscale_in_train,
is_test);
} else {
Dropout<T>(src_vec,
src.dims(),
&out2,
&correct_mask,
*ctx,
seed,
dropout_prob,
is_upscale_in_train,
is_test);
}
// add residual
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
correct_out[i * cols + j] =
residual_vec[i * cols + j] + out2[i * cols + j];
}
}
LayerNorm<T>(scale_vec,
layernorm_bias_vec,
correct_out,
&correct_means,
&correct_vars,
&correct_layernorm_out,
epsilon,
rows,
cols,
*ctx);
ctx->Wait();
}
void FusedForward() {
using U = phi::funcs::LayerNormParamType<T>;
int VecSize = MAX_CACHE_BYTES / sizeof(T);
if (cols % 4 != 0) {
VecSize = 1;
}
int threads = phi::funcs::GetDesiredBlockDim(cols / VecSize);
const int increment = ((cols - 1) / (threads * VecSize) + 1) * VecSize;
T *bias_ptr = nullptr;
U *scale_ptr = nullptr;
U *layernorm_bias_ptr = nullptr;
if (has_bias) {
bias_ptr = bias.data<T>();
}
if (has_scale) {
scale_ptr = scale.data<U>();
}
if (has_layernorm_bias) {
layernorm_bias_ptr = layernorm_bias.data<U>();
}
phi::fusion::LaunchLayernormResidualDropoutBias<T, uint8_t, U, false>(
rows,
cols,
increment,
seed,
dropout_prob,
epsilon,
is_upscale_in_train,
is_test,
src.data<T>(),
residual.data<T>(),
bias_ptr,
scale_ptr,
layernorm_bias_ptr,
mask.data<uint8_t>(),
out.data<T>(),
layernorm_out.data<T>(),
means.data<U>(),
vars.data<U>(),
*ctx);
ctx->Wait();
}
void Run() {
SetUp();
BaseForward();
FusedForward();
}
void CheckOut(const T diff) {
using U = phi::funcs::LayerNormParamType<T>;
const int n = rows * cols;
std::vector<T> _out(n), _layernorm_out(n);
std::vector<U> _means(rows), _vars(cols);
std::vector<uint8_t> _mask(n);
framework::TensorToVector(out, *ctx, &_out);
framework::TensorToVector(layernorm_out, *ctx, &_layernorm_out);
framework::TensorToVector(means, *ctx, &_means);
framework::TensorToVector(vars, *ctx, &_vars);
if (!is_test && dropout_prob != 0.0f) {
framework::TensorToVector(mask, *ctx, &_mask);
}
ctx->Wait();
for (int i = 0; i < n; i++) {
EXPECT_LT(std::abs(_out[i] - correct_out[i]), diff);
EXPECT_LT(std::abs(_layernorm_out[i] - correct_layernorm_out[i]), diff);
if (!is_test && dropout_prob != 0.0f) {
EXPECT_EQ(_mask[i], correct_mask[i]);
}
}
for (int i = 0; i < rows; i++) {
EXPECT_LT(std::abs(_means[i] - correct_means[i]), static_cast<U>(diff));
EXPECT_LT(std::abs(_vars[i] - correct_vars[i]), static_cast<U>(diff));
}
}
};
template <typename T>
static void BaseTest(const bool is_fp16 = false) {
const int rows = 16;
T default_diff = !is_fp16 ? static_cast<T>(1e-4) : static_cast<T>(1e-2);
for (auto cols : {16, 17}) {
for (auto has_bias : {true, false}) {
for (auto has_scale : {true, false}) {
for (auto has_layernorm_bias : {true, false}) {
TestFusedLayernormResidualDropoutBias<T> test(rows, cols);
test.has_bias = has_bias;
test.has_scale = has_scale;
test.has_layernorm_bias = has_layernorm_bias;
test.Run();
test.CheckOut(default_diff);
}
}
}
}
}
TEST(FusedDropout, GPUFusedLayernormResidualDropoutBias) { BaseTest<float>(); }
TEST(FusedDropout, GPUFusedLayernormResidualDropoutBiasDouble) {
BaseTest<double>();
}
TEST(FusedDropout, GPUFusedLayernormResidualDropoutBiasFp16) {
BaseTest<phi::dtype::float16>(true);
}
TEST(FusedDropout, GPUFusedLayernormResidualDropoutBiasIsUpscaleInTrain) {
const int rows = 16;
const int cols = 16;
for (auto is_upscale_in_train : {true, false}) {
TestFusedLayernormResidualDropoutBias<float> test(
rows, cols, 0, 1.0, 0.00001f, is_upscale_in_train, false);
test.Run();
test.CheckOut(static_cast<float>(1e-4));
}
}
TEST(FusedDropout, GPUFusedLayernormResidualDropoutBiasIsTest) {
const int rows = 16;
const int cols = 16;
TestFusedLayernormResidualDropoutBias<float> test(
rows, cols, 0, 0.35, 0.00001f, true, true);
test.Run();
test.CheckOut(static_cast<float>(1e-4));
}
TEST(FusedDropout, GPUFusedLayernormResidualDropoutBiasSeed) {
const int rows = 16;
const int cols = 16;
TestFusedLayernormResidualDropoutBias<float> test(
rows, cols, 125, 0.0, 0.00001f, false, false);
test.Run();
test.CheckOut(static_cast<float>(1e-4));
}
TEST(FusedDropout, GPUFusedLayernormResidualDropoutLargeShape) {
const int rows = 512;
const int cols = 512;
TestFusedLayernormResidualDropoutBias<float> test(rows, cols);
test.Run();
test.CheckOut(static_cast<float>(1e-4));
}
TEST(FusedDropout, GPUFusedLayernormResidualDropoutFp16MLperf) {
const int rows = 512;
const int cols = 1024;
TestFusedLayernormResidualDropoutBias<phi::dtype::float16> test(
rows, cols, 0, 0, 0.00001f, false, false, false);
test.Run();
test.CheckOut(static_cast<phi::dtype::float16>(1e-2));
}
@@ -0,0 +1,396 @@
/* Copyright (c) 2021 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 <time.h>
#include <random>
#include <vector>
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/fusion/gpu/fused_residual_dropout_bias.h"
#include "test/cpp/fluid/fused/fused_dropout_test.h"
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
PD_DECLARE_KERNEL(dropout, GPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(dropout_grad, GPU, ALL_LAYOUT);
#endif
namespace framework = paddle::framework;
namespace platform = paddle::platform;
bool CheckEqual(float value, float ref) { return std::abs(value - ref) < 1e-5; }
/**
* @brief the unittest of FusedResidualDropoutBias
* 1. random input data
* 2. add bias, call paddle dropout op, add residual, and get the base result
* 3. call FusedResidualDropoutBias function get fused result
* 4. compare the base result and fused result
*/
template <typename T>
struct FusedResidualDropoutBiasTester {
uint32_t rows;
uint32_t cols;
uint64_t seed;
float dropout_prob;
bool is_upscale_in_train;
bool is_test; // default false, Set to true for inference only
bool has_bias = true;
bool add_residual = true;
phi::DenseTensor src, residual, bias, out, mask;
phi::DenseTensor dsrc, dbias;
std::vector<T> src_vec, residual_vec, bias_vec;
std::vector<T> correct_out, correct_dsrc, correct_dbias;
std::vector<uint8_t> correct_mask;
phi::GPUPlace place;
phi::GPUContext *ctx;
FusedResidualDropoutBiasTester() {
rows = 32;
cols = 32;
seed = 0;
dropout_prob = 0.0;
is_upscale_in_train = false;
is_test = false;
phi::DeviceContextPool &pool = phi::DeviceContextPool::Instance();
auto device_ctx = pool.Get(place);
ctx = reinterpret_cast<phi::GPUContext *>(device_ctx);
}
FusedResidualDropoutBiasTester(int rows,
int cols,
uint64_t seed = 0,
float dropout_prob = 0.0,
bool is_upscale_in_train = false,
bool is_test = false)
: rows(rows),
cols(cols),
seed(seed),
dropout_prob(dropout_prob),
is_upscale_in_train(is_upscale_in_train),
is_test(is_test) {
phi::DeviceContextPool &pool = phi::DeviceContextPool::Instance();
auto device_ctx = pool.Get(place);
ctx = reinterpret_cast<phi::GPUContext *>(device_ctx);
}
void SetUp() {
const int n = rows * cols;
correct_out.resize(n);
correct_mask.resize(n);
correct_dsrc.resize(n);
correct_dbias.resize(cols);
src_vec.resize(n);
if (add_residual) {
residual_vec.resize(n);
}
bias_vec.resize(cols);
std::default_random_engine random(time(NULL));
std::uniform_real_distribution<float> dis(0.0, 1.0);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
src_vec[i * cols + j] = static_cast<T>(dis(random));
if (add_residual) {
residual_vec[i * cols + j] = static_cast<T>(dis(random));
}
if (i == 0) {
bias_vec[j] = dis(random);
}
}
}
framework::TensorFromVector<T>(src_vec, *ctx, &src);
src.Resize({rows, cols});
if (add_residual) {
framework::TensorFromVector<T>(residual_vec, *ctx, &residual);
residual.Resize({rows, cols});
}
if (has_bias) {
framework::TensorFromVector<T>(bias_vec, *ctx, &bias);
bias.Resize({cols});
}
out.mutable_data<T>({rows, cols}, place);
mask.mutable_data<uint8_t>({rows, cols}, place);
dsrc.mutable_data<T>({rows, cols}, place);
if (has_bias) {
dbias.mutable_data<T>({cols}, place);
}
}
void BaseForward() {
if (has_bias) {
// add bias
std::vector<T> bias_out(rows * cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
bias_out[i * cols + j] = src_vec[i * cols + j] + bias_vec[j];
}
}
// call dropout
Dropout<T>(bias_out,
src.dims(),
&correct_out,
&correct_mask,
*ctx,
seed,
dropout_prob,
is_upscale_in_train,
is_test);
} else {
Dropout<T>(src_vec,
src.dims(),
&correct_out,
&correct_mask,
*ctx,
seed,
dropout_prob,
is_upscale_in_train,
is_test);
}
ctx->Wait();
PADDLE_ENFORCE_GPU_SUCCESS(platform::GpuGetLastError());
if (add_residual) {
// add residual
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
int idx = i * cols + j;
correct_out[idx] = residual_vec[idx] + correct_out[idx];
}
}
}
}
void BaseBackward() {
DropoutGrad<T>(&correct_dsrc,
src.dims(),
correct_out,
correct_mask,
*ctx,
dropout_prob,
is_upscale_in_train);
// calc dbias
memset(&correct_dbias[0], 0, cols * sizeof(T));
if (has_bias) {
ReduceSum<T>(correct_out, &correct_dbias, rows, cols);
}
}
void FusedForward() {
const int VecSize = MAX_CACHE_BYTES / sizeof(T);
auto config =
phi::fusion::Get1DBlocksAnd2DGrids(*ctx,
static_cast<uint64_t>(rows),
static_cast<uint64_t>(cols),
VecSize);
const int increment = ((cols - 1) / (config.thread_per_block.x *
config.block_per_grid.x * VecSize) +
1) *
VecSize;
T *bias_ptr = has_bias ? bias.data<T>() : nullptr;
T *residual_ptr = add_residual ? residual.data<T>() : nullptr;
phi::fusion::LaunchResidualDropoutBias<T, uint8_t>(rows,
cols,
increment,
seed,
dropout_prob,
is_test,
is_upscale_in_train,
src.data<T>(),
residual_ptr,
bias_ptr,
mask.data<uint8_t>(),
out.data<T>(),
*ctx);
ctx->Wait();
PADDLE_ENFORCE_GPU_SUCCESS(platform::GpuGetLastError());
}
void FusedBackward() {
if (is_test) {
return;
}
T *bias_ptr = has_bias ? dbias.data<T>() : nullptr;
phi::fusion::LaunchResidualDropoutBiasGrad<T, uint8_t>(out.data<T>(),
mask.data<uint8_t>(),
dropout_prob,
is_upscale_in_train,
rows,
cols,
dsrc.data<T>(),
bias_ptr,
*ctx);
}
void Run() {
SetUp();
BaseForward();
FusedForward();
BaseBackward();
FusedBackward();
}
void CheckOut(const T diff) {
const int n = rows * cols;
std::vector<T> fused_out(n);
std::vector<uint8_t> fused_mask(n);
framework::TensorToVector(out, *ctx, &fused_out);
if (!is_test && dropout_prob != 0.0f) {
framework::TensorToVector<uint8_t>(mask, *ctx, &fused_mask);
}
ctx->Wait();
for (int i = 0; i < n; i++) {
EXPECT_LT(std::abs(fused_out[i] - correct_out[i]), diff);
if (!is_test && dropout_prob != 0.0f) {
EXPECT_EQ(fused_mask[i], correct_mask[i]);
}
}
}
void CheckGrad(const T diff) {
if (is_test) {
return;
}
const int n = rows * cols;
std::vector<T> _dsrc(n);
framework::TensorToVector(dsrc, *ctx, &_dsrc);
for (int i = 0; i < n; i++) {
EXPECT_LT(std::abs(_dsrc[i] - correct_dsrc[i]), diff);
}
if (has_bias) {
std::vector<T> _dbias(cols);
framework::TensorToVector(dbias, *ctx, &_dbias);
ctx->Wait();
for (int i = 0; i < cols; i++) {
EXPECT_LT(std::abs(_dbias[i] - correct_dbias[i]), diff);
}
}
}
};
// test the shape and bias
template <typename T>
static void BaseTest() {
const int rows = 16;
T max_diff = static_cast<T>(0);
if (std::is_same<T, phi::dtype::float16>::value) {
max_diff = static_cast<T>(1e-1);
} else {
max_diff = static_cast<T>(1e-5);
}
for (auto cols : {16, 17}) {
for (auto has_bias : {true, false}) {
FusedResidualDropoutBiasTester<T> test(rows, cols);
test.has_bias = has_bias;
test.Run();
test.CheckOut(max_diff);
test.CheckGrad(max_diff);
}
}
}
TEST(FusedDropout, GPUFusedResidualDropoutBias) { BaseTest<float>(); }
TEST(FusedDropout, GPUFusedResidualDropoutBiasDouble) { BaseTest<double>(); }
TEST(FusedDropout, GPUFusedResidualDropoutBiasFp16) {
BaseTest<phi::dtype::float16>();
}
TEST(FusedDropout, GPUFusedResidualDropoutBiasIsUpscaleInTrain) {
const int rows = 16;
const int cols = 16;
for (auto is_upscale_in_train : {true, false}) {
FusedResidualDropoutBiasTester<float> test(
rows, cols, 0, 1.0, is_upscale_in_train, false);
test.Run();
test.CheckOut(static_cast<float>(1e-5));
test.CheckGrad(static_cast<float>(1e-5));
}
}
TEST(FusedDropout, GPUFusedResidualDropoutBiasIsTest) {
const int rows = 16;
const int cols = 16;
FusedResidualDropoutBiasTester<float> test(rows, cols, 0, 0.35, true, true);
test.Run();
test.CheckOut(static_cast<float>(1e-5));
test.CheckGrad(static_cast<float>(1e-5));
}
TEST(FusedDropout, GPUFusedResidualDropoutBiasSeed) {
const int rows = 16;
const int cols = 16;
FusedResidualDropoutBiasTester<float> test(
rows, cols, 125, 0.0, false, false);
test.Run();
test.CheckOut(static_cast<float>(1e-5));
test.CheckGrad(static_cast<float>(1e-5));
}
TEST(FusedDropout, NoResidual) {
const int rows = 16;
const int cols = 16;
for (float p : {0.0f, 0.5f, 1.0f}) {
FusedResidualDropoutBiasTester<float> test(rows, cols, 0, p, false, false);
test.add_residual = false;
test.Run();
// For a non 0 or 1 dropout_prob, just test whether it can run successly.
if (CheckEqual(p, 0.0f) || CheckEqual(p, 1.0f)) {
test.CheckOut(static_cast<float>(1e-5));
test.CheckGrad(static_cast<float>(1e-5));
}
}
}
TEST(FusedDropout, GPUFusedResidualDropoutBiasLargeShape) {
const int rows = 256;
const int cols = 4096;
FusedResidualDropoutBiasTester<float> test(rows, cols);
test.Run();
test.CheckOut(static_cast<float>(1e-5));
test.CheckGrad(static_cast<float>(1e-3));
}
TEST(FusedDropout, GPUFusedResidualDropoutBiasLargeShapeFp16) {
// Used to test that `cudaErrorLaunchOutOfResources` will not occur
int rows = 1;
int cols = 12288;
if (std::getenv("_rows") != nullptr) {
rows = atoi(std::getenv("_rows"));
}
if (std::getenv("_cols") != nullptr) {
cols = atoi(std::getenv("_cols"));
}
FusedResidualDropoutBiasTester<phi::dtype::float16> test(
rows, cols, 0, 0.0, true, true);
test.Run();
test.CheckOut(static_cast<phi::dtype::float16>(1e-1));
test.CheckGrad(static_cast<phi::dtype::float16>(1e-1));
}
@@ -0,0 +1,237 @@
/* Copyright (c) 2019 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 "gtest/gtest.h"
#include "paddle/fluid/framework/op_desc.h"
#include "paddle/fluid/framework/op_proto_maker.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/platform/init.h"
#include "paddle/phi/backends/device_code.h"
namespace paddle {
namespace operators {
using CPUKernelFunc = std::function<void(size_t n, std::vector<void*> args)>;
template <typename T>
phi::DenseTensor* CreateTensor(framework::Scope* scope,
const phi::Place& place,
const std::string& name,
const std::vector<int64_t>& shape) {
auto* var = scope->Var(name);
auto* tensor = var->GetMutable<phi::DenseTensor>();
if (!shape.empty()) {
tensor->mutable_data<T>(common::make_ddim(shape), place);
}
return tensor;
}
template <typename T>
void SetupRandomCPUTensor(phi::DenseTensor* tensor,
const std::vector<int64_t>& shape) {
static unsigned int seed = 100;
std::mt19937 rng(seed++);
std::uniform_real_distribution<double> uniform_dist(0, 1);
T* ptr = tensor->mutable_data<T>(common::make_ddim(shape), phi::CPUPlace());
for (int64_t i = 0; i < tensor->numel(); ++i) {
ptr[i] = static_cast<T>(uniform_dist(rng)) - static_cast<T>(0.5);
}
}
framework::OpDesc* CreateFusionGroupOp(
framework::ProgramDesc* program,
const std::vector<std::string>& input_names,
const std::vector<std::vector<int64_t>>& input_shapes,
const std::vector<std::string>& output_names,
int type,
std::string func_name) {
EXPECT_EQ(input_names.size(), input_shapes.size());
std::vector<int> input_dtypes(input_names.size(),
framework::proto::VarType::FP32);
std::vector<int> output_dtypes(output_names.size(),
framework::proto::VarType::FP32);
for (size_t i = 0; i < input_names.size(); ++i) {
auto* var = program->MutableBlock(0)->Var(input_names[i]);
var->SetType(framework::proto::VarType::DENSE_TENSOR);
var->SetDataType(framework::proto::VarType::FP32);
var->SetShape(input_shapes[i]);
}
for (const auto& output_name : output_names) {
auto* var = program->MutableBlock(0)->Var(output_name);
var->SetType(framework::proto::VarType::DENSE_TENSOR);
var->SetDataType(framework::proto::VarType::FP32);
}
auto* op = program->MutableBlock(0)->AppendOp();
op->SetType("fusion_group");
op->SetInput("Inputs", input_names);
op->SetOutput("Outs", output_names);
op->SetAttr("inputs_dtype", input_dtypes);
op->SetAttr("outs_dtype", output_dtypes);
op->SetAttr("type", type);
op->SetAttr("func_name", func_name);
op->SetAttr(framework::OpProtoAndCheckerMaker::OpRoleAttrName(),
static_cast<int>(framework::OpRole::kForward));
return op;
}
void PrepareDeviceCode(phi::Place place,
std::string func_name,
std::string cuda_kernel_str) {
phi::DeviceCodePool& pool = phi::DeviceCodePool::Init({place});
std::unique_ptr<phi::DeviceCode> code(
new phi::GPUDeviceCode(place, func_name, cuda_kernel_str));
code->Compile();
pool.Set(std::move(code));
}
void CheckOutputs(framework::Scope* scope,
const std::vector<std::string>& output_names,
std::vector<phi::DenseTensor>* cpu_tensors,
size_t num_inputs,
CPUKernelFunc cpu_kernel_func) {
std::vector<phi::DenseTensor> cpu_outputs;
cpu_outputs.resize(output_names.size());
for (size_t j = 0; j < output_names.size(); ++j) {
auto* var = scope->Var(output_names[j]);
const auto& dev_tensor = var->Get<phi::DenseTensor>();
paddle::framework::TensorCopySync(
dev_tensor, phi::CPUPlace(), &(cpu_outputs[j]));
cpu_tensors->at(num_inputs + j)
.mutable_data<float>(dev_tensor.dims(), phi::CPUPlace());
}
size_t n = cpu_tensors->at(0).numel();
std::vector<void*> args;
for (auto& cpu_tensor : *cpu_tensors) {
args.push_back(cpu_tensor.data<float>());
}
cpu_kernel_func(n, args);
for (size_t j = 0; j < output_names.size(); ++j) {
auto* dev_ptr = cpu_outputs[j].data<float>();
auto* cpu_ptr = cpu_tensors->at(num_inputs + j).data<float>();
int64_t length = cpu_outputs[j].numel();
LOG(INFO) << "Check the " << j << "th output...";
for (int64_t i = 0; i < length; ++i) {
EXPECT_NEAR(dev_ptr[i], cpu_ptr[i], 1.E-05);
}
}
}
void TestMain(const std::vector<std::string>& input_names,
const std::vector<std::vector<int64_t>>& input_shapes,
const std::vector<std::string>& output_names,
int type,
std::string func_name,
std::string cuda_kernel_str,
CPUKernelFunc cpu_kernel_func) {
// Compile the device code
paddle::framework::InitDevices({0});
phi::GPUPlace place = phi::GPUPlace(0);
PrepareDeviceCode(place, func_name, cuda_kernel_str);
// Create a ProgramDesc that has a fusion_group_op.
framework::ProgramDesc program;
framework::OpDesc* op_desc = CreateFusionGroupOp(
&program, input_names, input_shapes, output_names, type, func_name);
auto fusion_group_op = framework::OpRegistry::CreateOp(*op_desc);
framework::Scope scope;
// Prepare input tensors.
std::vector<phi::DenseTensor> cpu_tensors;
cpu_tensors.resize(input_names.size() + output_names.size());
for (size_t i = 0; i < input_names.size(); ++i) {
SetupRandomCPUTensor<float>(&(cpu_tensors[i]), input_shapes[i]);
phi::DenseTensor* dev_tensor =
CreateTensor<float>(&scope, place, input_names[i], input_shapes[i]);
paddle::framework::TensorCopySync(cpu_tensors[i], place, dev_tensor);
}
// Create output tensors.
std::vector<int64_t> empty_shape;
for (const auto& output_name : output_names) {
CreateTensor<float>(&scope, place, output_name, empty_shape);
}
fusion_group_op->Run(scope, place);
auto* dev_ctx = phi::DeviceContextPool::Instance().Get(place);
dev_ctx->Wait();
// Check the output.
CheckOutputs(
&scope, output_names, &cpu_tensors, input_names.size(), cpu_kernel_func);
}
TEST(FusionGroupOp, elementwise) {
if (!phi::dynload::HasNVRTC() || !phi::dynload::HasCUDADriver()) {
return;
}
// z = relu(x + y)
std::vector<std::string> input_names = {"x", "y"};
std::vector<std::string> output_names = {"z"};
std::vector<std::vector<int64_t>> input_shapes = {{256, 256}, {256, 256}};
constexpr auto kernel = R"(
static inline __device__ float relu(float x) {
return x * (x > 0);
}
extern "C" __global__
void elementwise_cuda_kernel_0(size_t n, float *x, float* y, float* z) {
for (size_t tid = blockIdx.x * blockDim.x + threadIdx.x; tid < n;
tid += blockDim.x * gridDim.x) {
float tmp_0 = x[tid];
float tmp_1 = y[tid];
float tmp_2 = tmp_0 + tmp_1;
float tmp_3 = relu(tmp_2);
z[tid] = tmp_3;
}
})";
auto elementwise_cpu_kernel_0 = [](size_t n,
std::vector<void*> args) -> void {
float* x = static_cast<float*>(args[0]);
float* y = static_cast<float*>(args[1]);
float* z = static_cast<float*>(args[2]);
for (size_t i = 0; i < n; ++i) {
float tmp_0 = x[i];
float tmp_1 = y[i];
float tmp_2 = tmp_0 + tmp_1;
float tmp_3 = tmp_2 > 0 ? tmp_2 : 0;
z[i] = tmp_3;
}
};
TestMain(input_names,
input_shapes,
output_names,
0,
"elementwise_cuda_kernel_0",
kernel,
elementwise_cpu_kernel_0);
}
} // namespace operators
} // namespace paddle
USE_OP_ITSELF(fusion_group);
PD_DECLARE_KERNEL(fusion_group, GPU, ALL_LAYOUT);