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

436 lines
17 KiB
C++

// Copyright (c) 2022 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 "paddle/phi/backends/gpu/gpu_dnn.h"
#include "paddle/phi/common/memory_utils.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/dense_tensor.h"
namespace phi {
#ifdef PADDLE_WITH_HIP
using gpuRNNMode_t = miopenRNNMode_t;
using gpuDnnHandle_t = miopenHandle_t;
using gpuDnnDataType_t = miopenDataType_t;
#else
using gpuRNNMode_t = cudnnRNNMode_t;
using gpuDnnHandle_t = cudnnHandle_t;
using gpuDnnDataType_t = cudnnDataType_t;
#endif
class RNNDescriptors {
public:
RNNDescriptors(int seq_length,
int batch_size,
int input_size,
int hidden_size,
int num_layers,
float dropout_prob,
int seed,
int weight_numel,
gpuRNNMode_t mode,
bool is_bidirec,
bool is_test)
: seq_length_(seq_length),
batch_size_(batch_size),
input_size_(input_size),
hidden_size_(hidden_size),
num_layers_(num_layers),
dropout_prob_(dropout_prob),
seed_(seed),
weight_numel_(weight_numel),
mode_(mode),
is_bidirec_(is_bidirec),
is_test_(is_test) {}
template <typename T>
void Create(const gpuDnnHandle_t &handle,
const DeviceContext &dev_ctx,
const std::vector<int> &sequence_length,
size_t *workspace_size,
size_t *reserve_size,
DenseTensor *dropout_state) {
int numDirections = is_bidirec_ ? 2 : 1;
gpuDnnDataType_t cudnn_type = backends::gpu::CudnnDataType<T>::type;
// ------------------- cudnn x, y descriptors ---------------------
std::vector<int> dims_x = {batch_size_, input_size_, 1};
std::vector<int> strides_x = {input_size_, 1, 1};
std::vector<int> dims_y = {batch_size_, hidden_size_ * numDirections, 1};
std::vector<int> strides_y = {hidden_size_ * numDirections, 1, 1};
for (int i = 0; i < seq_length_; ++i) {
x_descs_.emplace_back(x_desc_.descriptor<T>(dims_x, strides_x));
y_descs_.emplace_back(y_desc_.descriptor<T>(dims_y, strides_y));
}
#if CUDNN_VERSION >= 90000
auto seqlen_is_empty = sequence_length.empty();
if (seqlen_is_empty) {
std::vector<int> seqlen_array(batch_size_);
for (int i = 0; i < batch_size_; ++i) {
seqlen_array[i] = seq_length_;
}
x_seq_desc_.descriptor<T>(
seq_length_, batch_size_, input_size_, true, seqlen_array);
y_seq_desc_.descriptor<T>(seq_length_,
batch_size_,
hidden_size_ * numDirections,
true,
seqlen_array);
} else {
x_seq_desc_.descriptor<T>(
seq_length_, batch_size_, input_size_, true, sequence_length);
y_seq_desc_.descriptor<T>(seq_length_,
batch_size_,
hidden_size_ * numDirections,
true,
sequence_length);
}
#elif defined(PADDLE_WITH_CUDA) && CUDNN_VERSION >= 7201
if (!sequence_length.empty()) {
x_seq_desc_.descriptor<T>(
seq_length_, batch_size_, input_size_, true, sequence_length);
y_seq_desc_.descriptor<T>(seq_length_,
batch_size_,
hidden_size_ * numDirections,
true,
sequence_length);
}
#endif
// ------------------- cudnn hx, hy, cx, cy descriptors----------
std::vector<int> dims_hx = {
num_layers_ * numDirections, batch_size_, hidden_size_};
std::vector<int> strides_hx = {hidden_size_ * batch_size_, hidden_size_, 1};
init_h_desc_.descriptor<T>(dims_hx, strides_hx);
init_c_desc_.descriptor<T>(dims_hx, strides_hx);
last_h_desc_.descriptor<T>(dims_hx, strides_hx);
last_c_desc_.descriptor<T>(dims_hx, strides_hx);
// ------------------- cudnn dropout descriptors ---------------------
size_t state_size = 0;
bool is_initialized = dropout_state->initialized();
#ifdef PADDLE_WITH_HIP
if (!is_initialized) {
PADDLE_ENFORCE_GPU_SUCCESS(
dynload::miopenDropoutGetStatesSize(handle, &state_size));
dropout_state->Resize({static_cast<int64_t>(state_size)});
dev_ctx.template Alloc<uint8_t>(dropout_state);
}
dropout_desc_.descriptor(handle, // NOLINT
dev_ctx.GetPlace(),
is_initialized,
dropout_prob_,
dropout_state,
seed_,
state_size);
#else
// Note(lvyongkang): delete `is_initialized` in condition, cause this will
// lead to bug in PIR mode, where rnn op has an input named
// `dropout_state_in`. `dropout_state_in` will share same buffer with
// `dropout_state_out`, this buffer is the dropput_state in kernel. And
// since `dropout_state_in` is input, so this buffer will be initialized in
// kernel.
if (!is_test_) {
PADDLE_ENFORCE_GPU_SUCCESS(
dynload::cudnnDropoutGetStatesSize(handle, &state_size));
dropout_state->Resize({static_cast<int64_t>(state_size)});
dev_ctx.template Alloc<uint8_t>(dropout_state);
}
dropout_desc_.descriptor(handle, // NOLINT
dev_ctx.GetPlace(),
is_initialized,
dropout_prob_,
is_test_ ? nullptr : dropout_state,
seed_,
state_size);
#endif
// ------------------- cudnn rnn descriptors ---------------------
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(dynload::miopenSetRNNDescriptor_V2(
rnn_desc_.desc(),
hidden_size_,
num_layers_,
dropout_desc_.desc(),
miopenRNNlinear,
is_bidirec_ ? miopenRNNbidirection : miopenRNNunidirection,
mode_,
miopenRNNwithBias,
miopenRNNdefault,
cudnn_type));
#elif CUDNN_VERSION >= 90000
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnSetRNNDescriptor_v8(
rnn_desc_.desc(),
CUDNN_RNN_ALGO_STANDARD,
mode_,
CUDNN_RNN_DOUBLE_BIAS,
is_bidirec_ ? CUDNN_BIDIRECTIONAL : CUDNN_UNIDIRECTIONAL,
CUDNN_LINEAR_INPUT,
cudnn_type,
cudnn_type,
CUDNN_DEFAULT_MATH,
input_size_,
hidden_size_,
hidden_size_,
num_layers_,
dropout_desc_.desc(),
seqlen_is_empty ? CUDNN_RNN_PADDED_IO_DISABLED
: CUDNN_RNN_PADDED_IO_ENABLED));
#elif CUDNN_VERSION >= 6000
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnSetRNNDescriptor_v6(
handle,
rnn_desc_.desc(),
hidden_size_,
num_layers_,
dropout_desc_.desc(),
CUDNN_LINEAR_INPUT,
is_bidirec_ ? CUDNN_BIDIRECTIONAL : CUDNN_UNIDIRECTIONAL,
mode_,
CUDNN_RNN_ALGO_STANDARD,
cudnn_type));
#else
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnSetRNNDescriptor(
rnn_desc_.desc(),
hidden_size_,
num_layers_,
dropout_desc_.desc(),
CUDNN_LINEAR_INPUT,
is_bidirec_ ? CUDNN_BIDIRECTIONAL : CUDNN_UNIDIRECTIONAL,
mode_,
cudnn_type));
#endif
#if defined(PADDLE_WITH_CUDA) && CUDNN_VERSION < 90000 && CUDNN_VERSION >= 7201
if (!sequence_length.empty()) {
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnSetRNNPaddingMode(
rnn_desc_.desc(), CUDNN_RNN_PADDED_IO_ENABLED));
}
#endif
// ------------------- cudnn weights_size ---------------------
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(dynload::miopenGetRNNParamsSize(
handle, rnn_desc_.desc(), x_descs_[0], &weights_size_, cudnn_type));
#elif CUDNN_VERSION >= 90000
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnGetRNNWeightSpaceSize(
handle, rnn_desc_.desc(), &weights_size_));
#else
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnGetRNNParamsSize(
handle, rnn_desc_.desc(), x_descs_[0], &weights_size_, cudnn_type));
#endif
PADDLE_ENFORCE_EQ(
weights_size_,
sizeof(T) * weight_numel_,
common::errors::InvalidArgument(
"The cudnn rnn and setting weight size should be same."));
// ------------------- cudnn weight descriptors ---------------------
auto layout = DataLayout::NCHW;
int dim_tmp = weights_size_ / sizeof(T);
std::vector<int> dim_w = {dim_tmp, 1, 1};
weight_desc_.descriptor<T>(layout, dim_w);
// ------------------- cudnn workspace, reserve size ---------------------
#ifdef PADDLE_WITH_HIP
PADDLE_ENFORCE_GPU_SUCCESS(
dynload::miopenGetRNNWorkspaceSize(handle,
rnn_desc_.desc(),
seq_length_,
x_descs_.data(),
workspace_size));
PADDLE_ENFORCE_GPU_SUCCESS(dynload::miopenGetRNNTrainingReserveSize(
handle, rnn_desc_.desc(), seq_length_, x_descs_.data(), reserve_size));
#elif CUDNN_VERSION >= 90000
PADDLE_ENFORCE_GPU_SUCCESS(
dynload::cudnnGetRNNTempSpaceSizes(handle,
rnn_desc_.desc(),
CUDNN_FWD_MODE_TRAINING,
x_seq_desc_.desc(),
workspace_size,
reserve_size));
#else
PADDLE_ENFORCE_GPU_SUCCESS(
dynload::cudnnGetRNNWorkspaceSize(handle,
rnn_desc_.desc(),
seq_length_,
x_descs_.data(),
workspace_size));
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cudnnGetRNNTrainingReserveSize(
handle, rnn_desc_.desc(), seq_length_, x_descs_.data(), reserve_size));
#endif
}
#ifdef PADDLE_WITH_HIP
miopenTensorDescriptor_t *x_descs() { return x_descs_.data(); }
miopenTensorDescriptor_t *y_descs() { return y_descs_.data(); }
miopenTensorDescriptor_t init_h_desc() { return init_h_desc_.desc(); }
miopenTensorDescriptor_t init_c_desc() { return init_c_desc_.desc(); }
miopenTensorDescriptor_t last_h_desc() { return last_h_desc_.desc(); }
miopenTensorDescriptor_t last_c_desc() { return last_c_desc_.desc(); }
miopenRNNDescriptor_t rnn_desc() { return rnn_desc_.desc(); }
miopenDropoutDescriptor_t dropout_desc() { return dropout_desc_.desc(); }
miopenTensorDescriptor_t weight_desc() { return weight_desc_.desc(); }
#else
cudnnTensorDescriptor_t *x_descs() { return x_descs_.data(); }
cudnnTensorDescriptor_t *y_descs() { return y_descs_.data(); }
#if CUDNN_VERSION >= 7201
cudnnRNNDataDescriptor_t x_seq_desc() { return x_seq_desc_.desc(); }
cudnnRNNDataDescriptor_t y_seq_desc() { return y_seq_desc_.desc(); }
#endif
cudnnTensorDescriptor_t init_h_desc() { return init_h_desc_.desc(); }
cudnnTensorDescriptor_t init_c_desc() { return init_c_desc_.desc(); }
cudnnTensorDescriptor_t last_h_desc() { return last_h_desc_.desc(); }
cudnnTensorDescriptor_t last_c_desc() { return last_c_desc_.desc(); }
cudnnRNNDescriptor_t rnn_desc() { return rnn_desc_.desc(); }
cudnnDropoutDescriptor_t dropout_desc() { return dropout_desc_.desc(); }
cudnnFilterDescriptor_t weight_desc() { return weight_desc_.desc(); }
#endif
size_t weights_size() { return weights_size_; }
private:
int seq_length_;
int batch_size_;
int input_size_;
int hidden_size_;
int num_layers_;
float dropout_prob_;
int seed_;
int weight_numel_;
gpuRNNMode_t mode_;
bool is_bidirec_;
bool is_test_;
size_t weights_size_;
#ifdef PADDLE_WITH_HIP
std::vector<miopenTensorDescriptor_t> x_descs_;
std::vector<miopenTensorDescriptor_t> y_descs_;
#else
std::vector<cudnnTensorDescriptor_t> x_descs_;
std::vector<cudnnTensorDescriptor_t> y_descs_;
#endif
backends::gpu::ScopedTensorDescriptor x_desc_;
backends::gpu::ScopedTensorDescriptor y_desc_;
#if defined(PADDLE_WITH_CUDA) && CUDNN_VERSION >= 7201
backends::gpu::ScopedRNNTensorDescriptor x_seq_desc_;
backends::gpu::ScopedRNNTensorDescriptor y_seq_desc_;
#endif
backends::gpu::ScopedTensorDescriptor init_h_desc_;
backends::gpu::ScopedTensorDescriptor init_c_desc_;
backends::gpu::ScopedTensorDescriptor last_h_desc_;
backends::gpu::ScopedTensorDescriptor last_c_desc_;
backends::gpu::ScopedDropoutDescriptor dropout_desc_;
backends::gpu::ScopedFilterDescriptor weight_desc_;
backends::gpu::ScopedRNNDescriptor rnn_desc_;
};
template <typename T, typename Type>
bool IsContinuous(const Type &weight_list) {
bool continuous = true;
for (size_t i = 0; i < weight_list.size() - 1; ++i) {
auto *in_data = weight_list[i]->template data<T>();
auto *in_after_data = weight_list[i + 1]->template data<T>();
auto in_size = weight_list[i]->numel();
bool temp = in_data + in_size == in_after_data;
continuous = continuous && temp;
}
return continuous;
}
template <typename T>
void WeightToTensor(const Place &place UNUSED,
gpuStream_t stream,
const std::vector<const DenseTensor *> &weight_list,
DenseTensor *weight) {
auto weight_data = weight->data<T>();
int weight_offset = 0;
for (size_t i = 0; i < weight_list.size(); ++i) {
const T *in_data = weight_list[i]->data<T>();
auto in_size = weight_list[i]->numel();
memory_utils::Copy(weight->place(),
weight_data + weight_offset,
weight_list[i]->place(),
in_data,
in_size * sizeof(T),
stream);
weight_offset += in_size;
}
}
#ifdef PADDLE_WITH_HIP
template <typename T>
void WeightListToTensor(const Place &place,
gpuStream_t stream,
const std::vector<DenseTensor> &tensor_list,
DenseTensor *weight_whole,
const size_t offset = 0UL) {
size_t weight_offset = offset;
auto weight_data = weight_whole->data<T>();
for (size_t i = 0; i < tensor_list.size(); ++i) {
const T *in_data = tensor_list[i].data<T>();
auto in_size = tensor_list[i].numel();
memory_utils::Copy(weight_whole->place(),
weight_data + weight_offset,
tensor_list[i].place(),
in_data,
in_size * sizeof(T),
stream);
weight_offset += in_size;
}
}
template <typename T>
void WeightToPermutedTensor(const Place &place,
gpuStream_t stream,
std::vector<const DenseTensor *> *weight_list,
DenseTensor *weight_whole,
const gpuRNNMode_t rnn_mode,
const bool is_bidirec) {
if (is_bidirec) {
for (size_t i = 0; i < weight_list->size(); i += 4) {
auto tmp = (*weight_list)[i + 1];
(*weight_list)[i + 1] = (*weight_list)[i + 2];
(*weight_list)[i + 2] = tmp;
}
}
size_t weight_offset = 0;
for (size_t i = 0; i < weight_list->size(); ++i) {
if (rnn_mode == miopenLSTM) {
std::vector<DenseTensor> split_tensor = (*weight_list)[i]->Chunk(4, 0);
WeightListToTensor<T>(
place,
stream,
{split_tensor[0], split_tensor[1], split_tensor[3], split_tensor[2]},
weight_whole,
weight_offset);
} else if (rnn_mode == miopenGRU) {
std::vector<DenseTensor> split_tensor = (*weight_list)[i]->Chunk(3, 0);
WeightListToTensor<T>(place,
stream,
{split_tensor[1], split_tensor[0], split_tensor[2]},
weight_whole,
weight_offset);
} else {
WeightListToTensor<T>(
place, stream, {*(*weight_list)[i]}, weight_whole, weight_offset);
}
weight_offset += (*weight_list)[i]->numel();
}
}
#endif
} // namespace phi