chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1 @@
|
||||
add_subdirectory(cuda)
|
||||
@@ -0,0 +1,15 @@
|
||||
collect_srcs(backends_srcs SRCS cudnn_workspace_helper.cc)
|
||||
|
||||
if(WITH_GPU)
|
||||
if(WIN32)
|
||||
nv_library(
|
||||
gpu_event_timer
|
||||
SRCS gpu_event_timer.cc
|
||||
DEPS glog phi)
|
||||
else()
|
||||
nv_library(
|
||||
gpu_event_timer
|
||||
SRCS gpu_event_timer.cc
|
||||
DEPS phi_core glog)
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,182 @@
|
||||
/* 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/common/bfloat16.h"
|
||||
#include "paddle/phi/common/complex.h"
|
||||
#include "paddle/phi/common/float16.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
#define FULL_WARP_MASK 0xFFFFFFFF
|
||||
#define CREATE_SHFL_MASK(mask, predicate) \
|
||||
mask = __ballot_sync(FULL_WARP_MASK, (predicate))
|
||||
|
||||
#define CUDA_LAUNCH_KERNEL_BASE(dim, ...) \
|
||||
case (dim): { \
|
||||
constexpr auto kPowerOfTwoDim = (dim); \
|
||||
__VA_ARGS__; \
|
||||
} break
|
||||
|
||||
#define CUDA_LAUNCH_KERNEL_HELPER(...) \
|
||||
CUDA_LAUNCH_KERNEL_BASE(1024, ##__VA_ARGS__); \
|
||||
CUDA_LAUNCH_KERNEL_BASE(512, ##__VA_ARGS__); \
|
||||
CUDA_LAUNCH_KERNEL_BASE(256, ##__VA_ARGS__); \
|
||||
CUDA_LAUNCH_KERNEL_BASE(128, ##__VA_ARGS__); \
|
||||
CUDA_LAUNCH_KERNEL_BASE(64, ##__VA_ARGS__); \
|
||||
CUDA_LAUNCH_KERNEL_BASE(32, ##__VA_ARGS__);
|
||||
|
||||
template <typename T>
|
||||
__forceinline__ __device__ T
|
||||
CudaShuffleDownSync(unsigned mask, T val, int delta, int width = warpSize) {
|
||||
return __shfl_down_sync(mask, val, static_cast<unsigned>(delta), width);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__forceinline__ __device__ T CudaShuffleXorSync(unsigned mask,
|
||||
T val,
|
||||
int width = warpSize) {
|
||||
return __shfl_xor_sync(mask, val, width);
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::float16 CudaShuffleDownSync(
|
||||
unsigned mask, phi::dtype::float16 val, int delta, int width) {
|
||||
return phi::dtype::float16(__shfl_down_sync(
|
||||
mask, val.to_half(), static_cast<unsigned>(delta), width));
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::bfloat16 CudaShuffleDownSync(
|
||||
unsigned mask, phi::dtype::bfloat16 val, int delta, int width) {
|
||||
#if defined(PADDLE_CUDA_BF16)
|
||||
return phi::dtype::bfloat16(__shfl_down_sync(
|
||||
mask, val.to_nv_bfloat16(), static_cast<unsigned>(delta), width));
|
||||
#else
|
||||
PADDLE_ENFORCE(
|
||||
false, "__shfl_down_sync with bfloat16 is not supported on cuda <= 11.");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::complex<float> CudaShuffleDownSync(
|
||||
unsigned mask, phi::dtype::complex<float> val, int delta, int width) {
|
||||
float real = static_cast<float>(__shfl_down_sync(
|
||||
mask, static_cast<float>(val.real), static_cast<unsigned>(delta), width));
|
||||
float imag = static_cast<float>(__shfl_down_sync(
|
||||
mask, static_cast<float>(val.imag), static_cast<unsigned>(delta), width));
|
||||
return phi::dtype::complex<float>(real, imag);
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::complex<double> CudaShuffleDownSync(
|
||||
unsigned mask, phi::dtype::complex<double> val, int delta, int width) {
|
||||
double real =
|
||||
static_cast<double>(__shfl_down_sync(mask,
|
||||
static_cast<double>(val.real),
|
||||
static_cast<unsigned>(delta),
|
||||
width));
|
||||
double imag =
|
||||
static_cast<double>(__shfl_down_sync(mask,
|
||||
static_cast<double>(val.imag),
|
||||
static_cast<unsigned>(delta),
|
||||
width));
|
||||
return phi::dtype::complex<double>(real, imag);
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::float16 CudaShuffleXorSync(
|
||||
unsigned mask, phi::dtype::float16 val, int width) {
|
||||
return phi::dtype::float16(__shfl_xor_sync(mask, val.to_half(), width));
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::bfloat16 CudaShuffleXorSync(
|
||||
unsigned mask, phi::dtype::bfloat16 val, int width) {
|
||||
return phi::dtype::bfloat16(
|
||||
__shfl_xor_sync(mask, val.to_nv_bfloat16(), width));
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::complex<float> CudaShuffleXorSync(
|
||||
unsigned mask, phi::dtype::complex<float> val, int width) {
|
||||
float real = static_cast<float>(
|
||||
__shfl_xor_sync(mask, static_cast<float>(val.real), width));
|
||||
float imag = static_cast<float>(
|
||||
__shfl_xor_sync(mask, static_cast<float>(val.imag), width));
|
||||
return phi::dtype::complex<float>(real, imag);
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::complex<double> CudaShuffleXorSync(
|
||||
unsigned mask, phi::dtype::complex<double> val, int width) {
|
||||
double real = static_cast<double>(
|
||||
__shfl_xor_sync(mask, static_cast<double>(val.real), width));
|
||||
double imag = static_cast<double>(
|
||||
__shfl_xor_sync(mask, static_cast<double>(val.imag), width));
|
||||
return phi::dtype::complex<double>(real, imag);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__forceinline__ __device__ T
|
||||
CudaShuffleSync(unsigned mask, T val, int src_line, int width = 32) {
|
||||
return __shfl_sync(mask, val, src_line, width);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HOSTDEVICE T Infinity() {
|
||||
return INFINITY;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__device__ T reduceSum(T val, int tid, int len) {
|
||||
// NOTE(zcd): The warp size should be taken from the
|
||||
// parameters of the GPU but not specified as 32 simply.
|
||||
// To make the reduceSum more efficiently,
|
||||
// I use Warp-Level Parallelism and assume the Warp size
|
||||
// is 32 which may be different for different GPU,
|
||||
// but most card's warp size is 32.
|
||||
const int warpSize = 32;
|
||||
__shared__ T shm[warpSize];
|
||||
unsigned mask = 0u;
|
||||
CREATE_SHFL_MASK(mask, tid < len);
|
||||
|
||||
for (int offset = warpSize / 2; offset > 0; offset /= 2)
|
||||
val += phi::backends::gpu::CudaShuffleDownSync(mask, val, offset);
|
||||
|
||||
if (tid < warpSize) shm[tid] = 0;
|
||||
__syncthreads();
|
||||
|
||||
if (tid % warpSize == 0) {
|
||||
shm[tid / warpSize] = val;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
CREATE_SHFL_MASK(mask, tid < warpSize);
|
||||
|
||||
if (tid < warpSize) {
|
||||
val = shm[tid];
|
||||
for (int offset = warpSize / 2; offset > 0; offset /= 2)
|
||||
val += phi::backends::gpu::CudaShuffleDownSync(mask, val, offset);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,477 @@
|
||||
// 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.
|
||||
|
||||
#include "paddle/phi/backends/gpu/cuda/cuda_graph.h"
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
|
||||
COMMON_DECLARE_bool(use_cuda_malloc_async_allocator);
|
||||
COMMON_DECLARE_bool(auto_free_cudagraph_allocations_on_launch);
|
||||
|
||||
namespace phi::backends::gpu {
|
||||
|
||||
std::unique_ptr<CUDAGraph> CUDAGraph::capturing_graph_{nullptr};
|
||||
paddle::optional<std::thread::id> CUDAGraph::capturing_thread_id_{paddle::none};
|
||||
std::vector<std::function<void()>> CUDAGraph::cudagraph_pre_capture_callbacks_;
|
||||
|
||||
static std::vector<cudaGraphNode_t> ToposortCUDAGraph(cudaGraph_t graph) {
|
||||
size_t num_nodes;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaGraphGetNodes(graph, nullptr, &num_nodes));
|
||||
std::vector<cudaGraphNode_t> nodes(num_nodes);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaGraphGetNodes(graph, nodes.data(), &num_nodes));
|
||||
|
||||
size_t num_edges;
|
||||
#if CUDA_VERSION < 13000
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaGraphGetEdges(graph, nullptr, nullptr, &num_edges));
|
||||
std::vector<cudaGraphNode_t> from(num_edges), to(num_edges);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaGraphGetEdges(graph, from.data(), to.data(), &num_edges));
|
||||
#else
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaGraphGetEdges(graph, nullptr, nullptr, nullptr, &num_edges));
|
||||
std::vector<cudaGraphNode_t> from(num_edges), to(num_edges);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaGraphGetEdges(graph, from.data(), to.data(), nullptr, &num_edges));
|
||||
#endif
|
||||
|
||||
std::unordered_map<cudaGraphNode_t, std::unordered_set<cudaGraphNode_t>>
|
||||
in_edges, out_edges;
|
||||
for (auto node : nodes) {
|
||||
in_edges[node];
|
||||
out_edges[node];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_edges; ++i) {
|
||||
in_edges[to[i]].insert(from[i]);
|
||||
out_edges[from[i]].insert(to[i]);
|
||||
}
|
||||
|
||||
std::queue<cudaGraphNode_t> q;
|
||||
for (const auto &pair : in_edges) {
|
||||
if (pair.second.empty()) {
|
||||
q.push(pair.first);
|
||||
}
|
||||
}
|
||||
|
||||
nodes.clear();
|
||||
while (!q.empty()) {
|
||||
auto cur = q.front();
|
||||
q.pop();
|
||||
nodes.push_back(cur);
|
||||
|
||||
for (auto out_node : out_edges.at(cur)) {
|
||||
auto &in_nodes = in_edges.at(out_node);
|
||||
in_nodes.erase(cur);
|
||||
if (in_nodes.empty()) {
|
||||
q.push(out_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
PADDLE_ENFORCE_EQ(
|
||||
nodes.size(),
|
||||
num_nodes,
|
||||
common::errors::InvalidArgument("Toposort error, this may be a bug."));
|
||||
return nodes;
|
||||
}
|
||||
|
||||
CUDAGraphID CUDAGraph::UniqueID() {
|
||||
static std::atomic<CUDAGraphID> id;
|
||||
return id.fetch_add(1);
|
||||
}
|
||||
|
||||
int64_t CUDAGraph::UniqueMemoryPoolID() {
|
||||
static std::atomic<int64_t> id(CUDAGraph::kDefaultPoolID + 1);
|
||||
return id.fetch_add(1);
|
||||
}
|
||||
|
||||
void CUDAGraph::Reset() {
|
||||
if (is_reset_) return;
|
||||
for (auto graph : graphs_) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaGraphDestroy(graph));
|
||||
}
|
||||
graphs_.clear();
|
||||
for (auto exec_graph : exec_graphs_) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaGraphExecDestroy(exec_graph));
|
||||
}
|
||||
exec_graphs_.clear();
|
||||
// callback should be called in reverse order because the latter added
|
||||
// callback may rely on the former added callback.
|
||||
for (auto iter = cudagraph_post_reset_callbacks_.rbegin();
|
||||
iter != cudagraph_post_reset_callbacks_.rend();
|
||||
++iter) {
|
||||
(*iter)(*this);
|
||||
}
|
||||
cudagraph_post_reset_callbacks_.clear();
|
||||
is_reset_ = true;
|
||||
}
|
||||
|
||||
void CUDAGraph::Replay() {
|
||||
is_replayed_ = true;
|
||||
PADDLE_ENFORCE_EQ(is_reset_,
|
||||
false,
|
||||
common::errors::PermissionDenied(
|
||||
"Cannot replay the CUDA Graph after reset is called."));
|
||||
size_t n = exec_graphs_.size();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (!is_first_run_) {
|
||||
for (auto &hook : cudagraph_pre_replay_callbacks_[i]) {
|
||||
hook(exec_graphs_[i]);
|
||||
}
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaGraphLaunch(exec_graphs_[i], stream_));
|
||||
}
|
||||
is_first_run_ = false;
|
||||
}
|
||||
|
||||
void CUDAGraph::BeginSegmentCapture() {
|
||||
ThrowErrorIfNotSupportCUDAGraph();
|
||||
PADDLE_ENFORCE_EQ(IsCapturing(),
|
||||
true,
|
||||
common::errors::PermissionDenied(
|
||||
"BeginSegmentCapture should be called when CUDA "
|
||||
"Graph is capturing."));
|
||||
if (IsThreadLocalCapturing()) {
|
||||
PADDLE_ENFORCE_EQ(IsThisThreadCapturing(),
|
||||
true,
|
||||
common::errors::PermissionDenied(
|
||||
"When capturing CUDA Graph in the thread local mode, "
|
||||
"you cannot begin segmented capturing in the thread "
|
||||
"which is not the one that starts the capturing."));
|
||||
}
|
||||
|
||||
for (auto &hook : cudagraph_pre_capture_callbacks_) {
|
||||
hook();
|
||||
}
|
||||
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaStreamBeginCapture(
|
||||
capturing_graph_->stream_, capturing_graph_->capture_mode_));
|
||||
PADDLE_ENFORCE_EQ(IsValidCapturing(),
|
||||
true,
|
||||
common::errors::PermissionDenied(
|
||||
"CUDA Graph should not be invalidated."));
|
||||
VLOG(10) << "Begin to capture CUDA Graph with ID " << capturing_graph_->id_
|
||||
<< ", segment id " << capturing_graph_->graphs_.size()
|
||||
<< ", memory pool id " << capturing_graph_->pool_id_;
|
||||
}
|
||||
|
||||
void CUDAGraph::BeginCapture(phi::GPUPlace place,
|
||||
cudaStream_t stream,
|
||||
cudaStreamCaptureMode mode,
|
||||
bool enable_replace) {
|
||||
ThrowErrorIfNotSupportCUDAGraph();
|
||||
PADDLE_ENFORCE_EQ(IsCapturing(),
|
||||
false,
|
||||
common::errors::PermissionDenied(
|
||||
"CUDA Graph can only captured one by one."));
|
||||
PADDLE_ENFORCE_NOT_NULL(
|
||||
stream,
|
||||
common::errors::PermissionDenied(
|
||||
"CUDA Graph cannot be captured in default CUDA stream 0."));
|
||||
capturing_graph_.reset(new CUDAGraph(enable_replace));
|
||||
capturing_graph_->place_ = place;
|
||||
capturing_graph_->stream_ = stream;
|
||||
capturing_graph_->capture_mode_ = mode;
|
||||
if (mode == cudaStreamCaptureModeThreadLocal) {
|
||||
capturing_thread_id_ = std::this_thread::get_id();
|
||||
VLOG(10) << "Capturing CUDA Graph in thread local mode, thread id: "
|
||||
<< capturing_thread_id_;
|
||||
}
|
||||
BeginSegmentCapture();
|
||||
}
|
||||
|
||||
inline void sync_streams(gpuStream_t to_record, gpuStream_t to_wait) {
|
||||
if (to_record == to_wait) return;
|
||||
cudaEvent_t event = nullptr;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaEventCreateWithFlags(&event, cudaEventDisableTiming));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventRecord(event, to_record));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaStreamWaitEvent(to_wait, event));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventDestroy(event));
|
||||
}
|
||||
|
||||
void CUDAGraph::EndSegmentCapture() {
|
||||
ThrowErrorIfNotSupportCUDAGraph();
|
||||
PADDLE_ENFORCE_EQ(
|
||||
IsCapturing(),
|
||||
true,
|
||||
common::errors::PermissionDenied("No CUDA Graph is capturing."));
|
||||
|
||||
for (const auto &stream : capturing_graph_->streams_to_join_) {
|
||||
VLOG(10) << "Joining steam when the capture is going to end stream ="
|
||||
<< stream;
|
||||
sync_streams(stream, capturing_graph_->stream_);
|
||||
}
|
||||
capturing_graph_->streams_to_join_.clear();
|
||||
|
||||
cudaGraph_t graph;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaStreamEndCapture(capturing_graph_->stream_, &graph));
|
||||
auto num_nodes = static_cast<size_t>(-1);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaGraphGetNodes(graph, nullptr, &num_nodes));
|
||||
if (num_nodes == 0) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaGraphDestroy(graph));
|
||||
VLOG(10) << "Skip empty CUDA Graph with ID " << capturing_graph_->id_
|
||||
<< ", segment id " << capturing_graph_->graphs_.size()
|
||||
<< ", memory pool id " << capturing_graph_->pool_id_;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &cudagraph_post_capture_callback :
|
||||
capturing_graph_->cudagraph_post_capture_callbacks_) {
|
||||
cudagraph_post_capture_callback();
|
||||
}
|
||||
capturing_graph_->cudagraph_post_capture_callbacks_.clear();
|
||||
|
||||
capturing_graph_->cudagraph_pre_replay_callbacks_.emplace_back(
|
||||
CUDAGraphNodeLauncher::Instance().GetParameterSettersForExecGraph(graph));
|
||||
|
||||
cudaGraphExec_t exec_graph;
|
||||
if (FLAGS_use_cuda_malloc_async_allocator &&
|
||||
FLAGS_auto_free_cudagraph_allocations_on_launch) {
|
||||
#if CUDA_VERSION >= 11040
|
||||
VLOG(1) << "cudaGraphInstantiateFlagAutoFreeOnLaunch is enabled!";
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaGraphInstantiateWithFlags(
|
||||
&exec_graph, graph, cudaGraphInstantiateFlagAutoFreeOnLaunch));
|
||||
#else
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"The cudaGraphInstantiateFlagAutoFreeOnLaunch is only supported when "
|
||||
"CUDA version >= 11.4.0"));
|
||||
#endif
|
||||
} else {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaGraphInstantiate(&exec_graph, graph, nullptr, nullptr, 0));
|
||||
}
|
||||
VLOG(10) << "End to capture CUDA Graph with ID " << capturing_graph_->id_
|
||||
<< ", segment id " << capturing_graph_->graphs_.size()
|
||||
<< ", memory pool id " << capturing_graph_->pool_id_;
|
||||
capturing_graph_->graphs_.emplace_back(graph);
|
||||
capturing_graph_->exec_graphs_.emplace_back(exec_graph);
|
||||
if (capturing_graph_->enable_replace_) {
|
||||
capturing_graph_->CacheKernelNodeInfos(capturing_graph_->graphs_.size() -
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CUDAGraph> CUDAGraph::EndCapture() {
|
||||
EndSegmentCapture();
|
||||
capturing_thread_id_ = paddle::none;
|
||||
return std::move(capturing_graph_);
|
||||
}
|
||||
|
||||
bool CUDAGraph::IsValidCapturing() {
|
||||
if (!IsCapturing()) return false;
|
||||
cudaStreamCaptureStatus status;
|
||||
CUDAGraphID id;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaStreamGetCaptureInfo(capturing_graph_->stream_, &status, &id));
|
||||
return status == cudaStreamCaptureStatusActive;
|
||||
}
|
||||
|
||||
static std::string ConcatPath(const std::string &dirname,
|
||||
const std::string &filename) {
|
||||
#ifdef _WIN32
|
||||
const std::array<char, 3> kFileSep = {"\\"};
|
||||
#else
|
||||
const std::array<char, 2> kFileSep = {"/"};
|
||||
#endif
|
||||
if (!dirname.empty() && dirname.back() == kFileSep[0]) {
|
||||
return dirname + filename;
|
||||
} else {
|
||||
return dirname + kFileSep.data() + filename;
|
||||
}
|
||||
}
|
||||
|
||||
void CUDAGraph::PrintToDotFiles(const std::string &dirname,
|
||||
unsigned int flags) {
|
||||
ThrowErrorIfNotSupportCUDAGraph();
|
||||
#if CUDA_VERSION >= 11030
|
||||
for (size_t i = 0; i < graphs_.size(); ++i) {
|
||||
auto filename =
|
||||
ConcatPath(dirname, "segment_" + std::to_string(i) + ".dot");
|
||||
VLOG(10) << "Save the " << i << "-th segment of graph " << id_ << " to "
|
||||
<< filename;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaGraphDebugDotPrint(graphs_[i], filename.c_str(), flags));
|
||||
}
|
||||
#else
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"The print_to_dot_files() method is only supported when CUDA version >= "
|
||||
"11.3."));
|
||||
#endif
|
||||
}
|
||||
|
||||
void CUDAGraph::CacheKernelNodeInfos(size_t segment_idx) {
|
||||
auto &graph = graphs_[segment_idx];
|
||||
size_t numNodes = 0;
|
||||
cudaGraphGetNodes(graph, nullptr, &numNodes);
|
||||
std::vector<cudaGraphNode_t> nodes(numNodes);
|
||||
cudaGraphGetNodes(graph, nodes.data(), &numNodes);
|
||||
|
||||
std::vector<KernelNodeInfo> kernel_nodes;
|
||||
for (auto &node : nodes) {
|
||||
cudaGraphNodeType type;
|
||||
cudaGraphNodeGetType(node, &type);
|
||||
if (type == cudaGraphNodeTypeKernel) {
|
||||
KernelNodeInfo info;
|
||||
info.node = node;
|
||||
memset(&info.params, 0, sizeof(info.params));
|
||||
// Use Driver API to get params (works for both Runtime and Driver API
|
||||
// kernels, unlike Runtime API which may return invalid kernelParams
|
||||
// for JIT kernels such as DeepGEMM)
|
||||
dynload::cuGraphKernelNodeGetParams(static_cast<CUgraphNode>(node),
|
||||
&info.params);
|
||||
info.param_infos =
|
||||
GetKernelParamInfos(static_cast<CUfunction>(info.params.func));
|
||||
kernel_nodes.emplace_back(std::move(info));
|
||||
}
|
||||
}
|
||||
VLOG(4) << "Cached " << kernel_nodes.size() << " kernel nodes for segment "
|
||||
<< segment_idx;
|
||||
cached_kernel_nodes_.emplace_back(std::move(kernel_nodes));
|
||||
}
|
||||
|
||||
void CUDAGraph::ReplaceInputPtrs(const std::vector<void *> &old_ptrs,
|
||||
const std::vector<void *> &new_ptrs) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
enable_replace_,
|
||||
true,
|
||||
common::errors::PermissionDenied(
|
||||
"ReplaceInputPtrs requires enable_replace to be set to true "
|
||||
"when creating CUDAGraph."));
|
||||
#if CUDA_VERSION >= 12040
|
||||
for (size_t i = 0; i < cached_kernel_nodes_.size(); ++i) {
|
||||
for (auto &kernel_info : cached_kernel_nodes_[i]) {
|
||||
auto ¶ms = kernel_info.params;
|
||||
bool modified = false;
|
||||
|
||||
for (size_t k = 0; k < kernel_info.param_infos.size(); k++) {
|
||||
size_t param_size = kernel_info.param_infos[k].size;
|
||||
char *param_base = reinterpret_cast<char *>(params.kernelParams[k]);
|
||||
|
||||
for (size_t offset = 0; offset + sizeof(void *) <= param_size;
|
||||
offset += sizeof(void *)) {
|
||||
void *actual_val = *(reinterpret_cast<void **>(param_base + offset));
|
||||
for (size_t j = 0; j < old_ptrs.size(); j++) {
|
||||
if (old_ptrs[j] == actual_val) {
|
||||
VLOG(4) << "cuda func " << params.func << " match old ptr "
|
||||
<< actual_val << " at param " << k << " offset " << offset
|
||||
<< ", replace with " << new_ptrs[j];
|
||||
*(reinterpret_cast<void **>(param_base + offset)) = new_ptrs[j];
|
||||
modified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modified) {
|
||||
dynload::cuGraphExecKernelNodeSetParams(
|
||||
static_cast<CUgraphExec>(exec_graphs_[i]),
|
||||
static_cast<CUgraphNode>(kernel_info.node),
|
||||
¶ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<CUDAGraph::KernelParamInfo> CUDAGraph::GetKernelParamInfos(
|
||||
CUfunction func) {
|
||||
std::vector<KernelParamInfo> infos;
|
||||
#if CUDA_VERSION >= 12040
|
||||
size_t paramOffset, paramSize;
|
||||
int k = 0;
|
||||
|
||||
while (dynload::cuFuncGetParamInfo(func, k, ¶mOffset, ¶mSize) ==
|
||||
CUDA_SUCCESS) {
|
||||
infos.push_back({paramOffset, paramSize});
|
||||
VLOG(4) << "[GetKernelParamInfos] func " << func << " param[" << k
|
||||
<< "] offset=" << paramOffset << " size=" << paramSize;
|
||||
k++;
|
||||
}
|
||||
#endif
|
||||
return infos;
|
||||
}
|
||||
|
||||
void CUDAGraphNodeLauncher::KernelNodeLaunch(
|
||||
parameterSetter_t parameterSetter, gpuKernelCallback_t cudaKernelCallback) {
|
||||
if (UNLIKELY(phi::backends::gpu::CUDAGraph::IsThisThreadCapturing())) {
|
||||
unsigned int id = GenerateIdentifier();
|
||||
auto cudaFunc = cudaKernelCallback(id);
|
||||
|
||||
parameterSetters[cudaFunc][id] = parameterSetter;
|
||||
VLOG(10) << "[KernelNodeLaunch] Launch kernel with cudaFunc = " << cudaFunc
|
||||
<< " id = " << id;
|
||||
} else {
|
||||
cudaKernelCallback(0);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<cudaGraphExecuterSetter_t>
|
||||
CUDAGraphNodeLauncher::GetParameterSettersForExecGraph(cudaGraph_t graph) {
|
||||
size_t num_nodes;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaGraphGetNodes(graph, nullptr, &num_nodes));
|
||||
std::vector<cudaGraphNode_t> nodes(num_nodes);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaGraphGetNodes(graph, nodes.data(), &num_nodes));
|
||||
|
||||
std::vector<std::function<void(cudaGraphExec_t)>> hooks;
|
||||
for (auto node : nodes) {
|
||||
CUgraphNode cuNode = node;
|
||||
CUgraphNodeType pType;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cuGraphNodeGetType(cuNode, &pType));
|
||||
if (pType == CU_GRAPH_NODE_TYPE_KERNEL) {
|
||||
CUDA_KERNEL_NODE_PARAMS cuParams;
|
||||
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
dynload::cuGraphKernelNodeGetParams(cuNode, &cuParams));
|
||||
gpuKernelParams kernel_params(cuParams.kernelParams);
|
||||
auto kernel =
|
||||
parameterSetters.find(static_cast<cudaFunction_t>(cuParams.func));
|
||||
VLOG(10) << "[GetParameterSettersForExecGraph] cuParams.func = "
|
||||
<< cuParams.func;
|
||||
// There exists a parameter setter
|
||||
if (kernel != parameterSetters.end()) {
|
||||
auto launchSequence = kernel->second;
|
||||
unsigned int id = kernel_params.As<int>(0);
|
||||
|
||||
VLOG(10) << "[GetParameterSettersForExecGraph] Find launch kernel id = "
|
||||
<< id;
|
||||
auto parameterSetter = launchSequence.find(id);
|
||||
if (parameterSetter != launchSequence.end()) {
|
||||
auto setter = parameterSetter->second;
|
||||
hooks.emplace_back([setter, cuNode, cuParams](
|
||||
cudaGraphExec_t exec_graph) {
|
||||
gpuKernelParams kernel_params(cuParams.kernelParams);
|
||||
setter(kernel_params);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(dynload::cuGraphExecKernelNodeSetParams(
|
||||
static_cast<CUgraphExec>(exec_graph), cuNode, &cuParams));
|
||||
});
|
||||
} else {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Error: does not find launch id"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hooks;
|
||||
}
|
||||
|
||||
} // namespace phi::backends::gpu
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,408 @@
|
||||
// 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 <array>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/backends/context_pool.h"
|
||||
#include "paddle/phi/backends/device_code.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_context.h"
|
||||
#include "paddle/phi/common/memory_utils.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/utils/optional.h"
|
||||
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
class CUDAGraphContextManager {
|
||||
public:
|
||||
using DeviceContextMap =
|
||||
std::map<Place, std::shared_future<std::unique_ptr<DeviceContext>>>;
|
||||
|
||||
static CUDAGraphContextManager &Instance() {
|
||||
static CUDAGraphContextManager *cuda_graph_ctx_manager =
|
||||
new CUDAGraphContextManager;
|
||||
return *cuda_graph_ctx_manager;
|
||||
}
|
||||
|
||||
DeviceContext *Get(int64_t pool_id, const Place &place, int stream_priority) {
|
||||
std::lock_guard<std::mutex> lk(ctx_mtx_);
|
||||
|
||||
DeviceContextMap &ctxs = cuda_graph_ctx_pool_[pool_id];
|
||||
if (ctxs.find(place) == ctxs.end()) {
|
||||
phi::memory_utils::EmplaceDeviceContexts(
|
||||
&ctxs,
|
||||
{place},
|
||||
/*disable_setting_default_stream_for_allocator=*/true,
|
||||
stream_priority);
|
||||
}
|
||||
return ctxs[place].get().get();
|
||||
}
|
||||
|
||||
void RecordCapturingDeviceContext(DeviceContext *dev_ctx) {
|
||||
capturing_ctxs_.insert(dev_ctx);
|
||||
}
|
||||
|
||||
std::set<DeviceContext *> GetAllCapturingDeviceContexts() const {
|
||||
return capturing_ctxs_;
|
||||
}
|
||||
|
||||
void ClearDeviceContextsRecords() { capturing_ctxs_.clear(); }
|
||||
|
||||
private:
|
||||
CUDAGraphContextManager() {}
|
||||
DISABLE_COPY_AND_ASSIGN(CUDAGraphContextManager);
|
||||
|
||||
std::mutex ctx_mtx_;
|
||||
std::unordered_map<int64_t, DeviceContextMap> cuda_graph_ctx_pool_;
|
||||
std::set<DeviceContext *> capturing_ctxs_;
|
||||
};
|
||||
|
||||
class gpuKernelParams {
|
||||
public:
|
||||
explicit gpuKernelParams(void **params) : kernelParams(params) {}
|
||||
|
||||
template <typename T>
|
||||
T &As(size_t idx) const {
|
||||
return *reinterpret_cast<T *>(kernelParams[idx]);
|
||||
}
|
||||
|
||||
void **getParams() const { return kernelParams; }
|
||||
|
||||
private:
|
||||
void **kernelParams;
|
||||
};
|
||||
|
||||
using cudaGraphExecuterSetter_t = std::function<void(cudaGraphExec_t)>;
|
||||
|
||||
// ** class CUDAGraphNodeLauncher
|
||||
//
|
||||
// This class offers a interface for launching CUDA kernels in CUDA Graph, we
|
||||
// utilize the `cudaGraphExecKernelNodeSetParams` function for parameter setup.
|
||||
// Launching kernels via this class ensures proper management.
|
||||
//
|
||||
// NOTE: It's essential that the first parameter for any kernel launched
|
||||
// through this class is an `unsigned int` identifier. This identifier plays a
|
||||
// crucial role in linking the CUDA kernel to its corresponding CUDA graph
|
||||
// node. We tag each kernel launch with a unique identifier to maintain
|
||||
// structured linkage with its CUDA graph node.
|
||||
//
|
||||
// NOTE: This class use a singleton design pattern ensures there's only a
|
||||
// single global instance accessible via the `Instance()` method.
|
||||
class CUDAGraphNodeLauncher {
|
||||
public:
|
||||
// [Parameter Setter Callback]
|
||||
// Sets the kernel's parameters BEFORE activating the CUDA graph. It enables
|
||||
// dynamic determination and setup of kernel arguments.
|
||||
//
|
||||
// parameterSetter_t parameterSetter = [saved_state](gpuKernelParams
|
||||
// ¶m){
|
||||
// // Code to compute and the parameter values from the saved_state
|
||||
// // ...
|
||||
// param.As<type>(idx) = calculated_value;
|
||||
// };
|
||||
using parameterSetter_t = std::function<void(gpuKernelParams &)>;
|
||||
|
||||
// [CUDA Kernel Callback]
|
||||
// Acts as the launcher for the kernel. It accepts an `unsigned int`
|
||||
// identifier and uses it for the kernel launch.
|
||||
// The `cudaGetFuncBySymbol` method can be used to fetch the `cudaFunction_t`
|
||||
// reference of the kernel from the kernel pointer.
|
||||
// gpuKernelCallback_t cudaKernelCallback = [=](unsigned int id) {
|
||||
// // cudaFunction_t is REQUIRED to get here
|
||||
// cudaFunction_t cudaFunc;
|
||||
// PADDLE_ENFORCE_GPU_SUCCESS(cudaGetFuncBySymbol(&cudaFunc, &kernel));
|
||||
//
|
||||
// kernel<<<>>>(id, ...); // Launching the kernel with id
|
||||
// return cudaFunc;
|
||||
// };
|
||||
using gpuKernelCallback_t = std::function<cudaFunction_t(unsigned int)>;
|
||||
|
||||
// [Kernel Launch]
|
||||
// With the callbacks defined and the CUDA function obtained, the kernel can
|
||||
// be launched using the `KernelNodeLaunch` method.
|
||||
void KernelNodeLaunch(parameterSetter_t parameterSetter,
|
||||
gpuKernelCallback_t cudaKernelCallback);
|
||||
|
||||
std::vector<cudaGraphExecuterSetter_t> GetParameterSettersForExecGraph(
|
||||
cudaGraph_t graph);
|
||||
|
||||
parameterSetter_t GetParameterSetter(const gpuKernelParams ¶ms);
|
||||
|
||||
static CUDAGraphNodeLauncher &Instance() {
|
||||
static CUDAGraphNodeLauncher *launcher = new CUDAGraphNodeLauncher;
|
||||
return *launcher;
|
||||
}
|
||||
|
||||
private:
|
||||
CUDAGraphNodeLauncher() : id(0) {}
|
||||
DISABLE_COPY_AND_ASSIGN(CUDAGraphNodeLauncher);
|
||||
|
||||
unsigned int GenerateIdentifier() { return id++; }
|
||||
|
||||
unsigned int id;
|
||||
std::unordered_map<cudaFunction_t, std::map<unsigned int, parameterSetter_t>>
|
||||
parameterSetters;
|
||||
};
|
||||
|
||||
static void ThrowErrorIfNotSupportCUDAGraph() {}
|
||||
|
||||
using CUDAGraphID = unsigned long long; // NOLINT
|
||||
|
||||
// NOTE: Currently, we do not support to capture CUDA graph in parallel
|
||||
// NOTE: Do not use this class directly because it should be used with
|
||||
// the memory pool.
|
||||
class CUDAGraph {
|
||||
DISABLE_COPY_AND_ASSIGN(CUDAGraph);
|
||||
|
||||
// Since the constructor would throw error is CUDA_VERSION < 10010.
|
||||
// The non-static method of CUDAGraph need not check CUDA_VERSION
|
||||
// again.
|
||||
explicit CUDAGraph(bool enable_replace = false)
|
||||
: enable_replace_(enable_replace) {
|
||||
ThrowErrorIfNotSupportCUDAGraph();
|
||||
id_ = UniqueID();
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr int64_t kDefaultPoolID = 0;
|
||||
static constexpr int64_t kInvalidPoolID = -1;
|
||||
|
||||
~CUDAGraph() { Reset(); }
|
||||
|
||||
CUDAGraphID ID() const { return id_; }
|
||||
|
||||
static int64_t SetMemoryPoolID(int64_t pool_id) {
|
||||
auto &pool_id_ = capturing_graph_->pool_id_;
|
||||
PADDLE_ENFORCE_EQ(pool_id_,
|
||||
kInvalidPoolID,
|
||||
common::errors::InvalidArgument(
|
||||
"Cannot reset memory pool id twice, the "
|
||||
"former memory pool id is %d.",
|
||||
pool_id_));
|
||||
if (pool_id <= kInvalidPoolID) {
|
||||
pool_id_ = UniqueMemoryPoolID();
|
||||
} else {
|
||||
PADDLE_ENFORCE_GE(pool_id,
|
||||
kDefaultPoolID,
|
||||
common::errors::InvalidArgument(
|
||||
"Invalid memory pool id %d.", pool_id));
|
||||
pool_id_ = pool_id;
|
||||
}
|
||||
return pool_id_;
|
||||
}
|
||||
|
||||
int64_t PoolID() const { return pool_id_; }
|
||||
|
||||
static int64_t CapturingPoolID() { return capturing_graph_->pool_id_; }
|
||||
|
||||
void Replay();
|
||||
|
||||
void Reset();
|
||||
|
||||
void AddPostResetCallback(
|
||||
std::function<void(paddle::optional<const CUDAGraph &>)> callback) {
|
||||
std::lock_guard<std::mutex> guard(mtx_);
|
||||
cudagraph_post_reset_callbacks_.push_back(std::move(callback));
|
||||
}
|
||||
|
||||
static void AddPreCaptureCallback(std::function<void()> callback) {
|
||||
cudagraph_pre_capture_callbacks_.push_back(std::move(callback));
|
||||
}
|
||||
|
||||
void AddPostCaptureCallback(std::function<void()> callback) {
|
||||
std::lock_guard<std::mutex> guard(mtx_);
|
||||
cudagraph_post_capture_callbacks_.push_back(std::move(callback));
|
||||
}
|
||||
|
||||
void AddJoiningStream(cudaStream_t stream) {
|
||||
streams_to_join_.insert(stream);
|
||||
}
|
||||
|
||||
void PrintToDotFiles(const std::string &dirname, unsigned int flags);
|
||||
|
||||
bool IsReplayed() const { return is_replayed_; }
|
||||
|
||||
static void BeginCapture(phi::GPUPlace place,
|
||||
cudaStream_t stream,
|
||||
gpuStreamCaptureMode mode,
|
||||
bool enable_replace = false);
|
||||
static std::unique_ptr<CUDAGraph> EndCapture();
|
||||
|
||||
static void BeginSegmentCapture();
|
||||
static void EndSegmentCapture();
|
||||
|
||||
static void AddJoiningStreamDuringCapturing(cudaStream_t stream) {
|
||||
capturing_graph_->AddJoiningStream(stream);
|
||||
}
|
||||
|
||||
static void AddPostResetCallbackDuringCapturing(
|
||||
std::function<void(paddle::optional<const CUDAGraph &>)> callback) {
|
||||
capturing_graph_->AddPostResetCallback(std::move(callback));
|
||||
}
|
||||
|
||||
static void AddPostCaptureCallbackDuringCapturing(
|
||||
std::function<void()> callback) {
|
||||
capturing_graph_->AddPostCaptureCallback(std::move(callback));
|
||||
}
|
||||
|
||||
// No need to add CUDA_VERSION macro because capturing_graph_ would
|
||||
// always be nullptr (constructor throws error)
|
||||
static bool IsCapturing() { return capturing_graph_ != nullptr; }
|
||||
|
||||
static CUDAGraphID CapturingID() { return capturing_graph_->id_; }
|
||||
|
||||
static phi::GPUPlace CapturingPlace() { return capturing_graph_->place_; }
|
||||
|
||||
// This API can be used to debug which GPU operation is not
|
||||
// supported during capturing CUDA Graph.
|
||||
static bool IsValidCapturing();
|
||||
|
||||
static bool IsThreadLocalCapturing() {
|
||||
return IsCapturing() &&
|
||||
capturing_graph_->capture_mode_ == cudaStreamCaptureModeThreadLocal;
|
||||
}
|
||||
|
||||
static bool IsThisThreadCapturing() {
|
||||
if (UNLIKELY(IsCapturing())) {
|
||||
return IsThreadLocalCapturing()
|
||||
? capturing_thread_id_.get() == std::this_thread::get_id()
|
||||
: true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
using SetSeedFunc = std::function<bool(gpuKernelParams *, bool)>;
|
||||
static void RecordRandomKernelInfo(SetSeedFunc set_seed_func) {
|
||||
std::lock_guard<std::mutex> guard(capturing_graph_->func_mtx_);
|
||||
capturing_graph_->set_seed_funcs_.emplace_back(std::move(set_seed_func));
|
||||
}
|
||||
|
||||
static int64_t UniqueMemoryPoolID();
|
||||
|
||||
void ReplaceInputPtrs(const std::vector<void *> &old_ptrs,
|
||||
const std::vector<void *> &new_ptrs);
|
||||
|
||||
struct KernelParamInfo {
|
||||
size_t offset;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct KernelNodeInfo {
|
||||
cudaGraphNode_t node;
|
||||
CUDA_KERNEL_NODE_PARAMS params;
|
||||
std::vector<KernelParamInfo> param_infos;
|
||||
};
|
||||
|
||||
private:
|
||||
static CUDAGraphID UniqueID();
|
||||
std::vector<KernelParamInfo> GetKernelParamInfos(CUfunction func);
|
||||
void CacheKernelNodeInfos(size_t segment_idx);
|
||||
|
||||
private:
|
||||
std::vector<cudaGraph_t> graphs_;
|
||||
std::vector<cudaGraphExec_t> exec_graphs_;
|
||||
std::vector<std::vector<KernelNodeInfo>> cached_kernel_nodes_;
|
||||
gpuStreamCaptureMode capture_mode_;
|
||||
cudaStream_t stream_{nullptr};
|
||||
phi::GPUPlace place_;
|
||||
CUDAGraphID id_;
|
||||
int64_t pool_id_{kInvalidPoolID};
|
||||
bool is_reset_{false};
|
||||
bool is_replayed_{false};
|
||||
bool enable_replace_{false};
|
||||
std::mutex mtx_;
|
||||
|
||||
std::vector<SetSeedFunc> set_seed_funcs_;
|
||||
|
||||
std::unordered_set<cudaStream_t> streams_to_join_;
|
||||
|
||||
// Holds callbacks that are triggered after the CUDA graph is reset. These
|
||||
// callbacks are used for operations that need to be performed following the
|
||||
// reset of a CUDA graph.
|
||||
std::vector<std::function<void(paddle::optional<const CUDAGraph &>)>>
|
||||
cudagraph_post_reset_callbacks_;
|
||||
|
||||
static std::vector<std::function<void()>> cudagraph_pre_capture_callbacks_;
|
||||
|
||||
// Contains callbacks that are invoked after the CUDA graph has been captured.
|
||||
// These callbacks are crucial for managing memory allocations related to the
|
||||
// CUDA graph. They ensure that memory blocks not associated with a graph (as
|
||||
// detailed in cuda_malloc_async_allocator) are not erroneously released
|
||||
// during the graph's lifecycle.
|
||||
std::vector<std::function<void()>> cudagraph_post_capture_callbacks_;
|
||||
|
||||
// Maintains a collection of 'pre-hooks' - functions that are executed before
|
||||
// the CUDA graph is replayed. These pre-hooks are essential for setting up
|
||||
// the necessary conditions or states required for the correct execution of
|
||||
// the CUDA graph.
|
||||
std::vector<std::vector<cudaGraphExecuterSetter_t>>
|
||||
cudagraph_pre_replay_callbacks_;
|
||||
|
||||
std::mutex func_mtx_;
|
||||
|
||||
bool is_first_run_{true};
|
||||
|
||||
static paddle::optional<std::thread::id> capturing_thread_id_;
|
||||
static std::unique_ptr<CUDAGraph> capturing_graph_;
|
||||
};
|
||||
|
||||
class CUDAGraphCaptureModeGuard {
|
||||
DISABLE_COPY_AND_ASSIGN(CUDAGraphCaptureModeGuard);
|
||||
|
||||
public:
|
||||
explicit CUDAGraphCaptureModeGuard(
|
||||
gpuStreamCaptureMode mode = cudaStreamCaptureModeRelaxed) {
|
||||
if (UNLIKELY(CUDAGraph::IsCapturing())) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaThreadExchangeStreamCaptureMode(&mode));
|
||||
// After cudaThreadExchangeStreamCaptureMode is called,
|
||||
// the variable "mode" would be set to the old capturing mode.
|
||||
old_mode_ = mode;
|
||||
}
|
||||
}
|
||||
|
||||
~CUDAGraphCaptureModeGuard() PADDLE_MAY_THROW {
|
||||
if (UNLIKELY(CUDAGraph::IsCapturing())) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaThreadExchangeStreamCaptureMode(&old_mode_));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
gpuStreamCaptureMode old_mode_;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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 <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
|
||||
defined(PADDLE_WITH_CUSTOM_DEVICE)
|
||||
#include "paddle/phi/backends/context_pool.h"
|
||||
#if defined(PADDLE_WITH_CUDA)
|
||||
#include "paddle/phi/backends/gpu/cuda/cuda_graph.h"
|
||||
#elif defined(PADDLE_WITH_HIP)
|
||||
#include "paddle/phi/backends/gpu/rocm/hip_graph.h"
|
||||
#elif defined(PADDLE_WITH_CUSTOM_DEVICE)
|
||||
#include "paddle/phi/backends/custom/cuda_graph.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
inline bool IsCUDAGraphCapturing() {
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
|
||||
defined(PADDLE_WITH_CUSTOM_DEVICE)
|
||||
return CUDAGraph::IsCapturing();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Add reset callback if CUDA Graph is capturing.
|
||||
// Otherwise, invoke callback directly.
|
||||
template <typename Callback>
|
||||
inline void AddPostResetCallbackIfCapturingCUDAGraph(Callback &&callback) {
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
|
||||
defined(PADDLE_WITH_CUSTOM_DEVICE)
|
||||
if (UNLIKELY(IsCUDAGraphCapturing())) {
|
||||
return CUDAGraph::AddPostResetCallbackDuringCapturing(
|
||||
std::forward<Callback>(callback));
|
||||
}
|
||||
#endif
|
||||
callback({});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *RestoreHostMemIfCapturingCUDAGraph(T *host_mem, size_t size) {
|
||||
static_assert(std::is_trivial<T>::value, "T must be trivial type");
|
||||
static_assert(!std::is_same<T, void>::value, "T cannot be void");
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
|
||||
defined(PADDLE_WITH_CUSTOM_DEVICE)
|
||||
if (UNLIKELY(IsCUDAGraphCapturing())) {
|
||||
size_t nbytes = size * sizeof(T);
|
||||
// NOTE: Use new[]/delete[] (plain heap) instead of cudaMallocHost /
|
||||
// hipMallocHost here. cudaMallocHost and hipMallocHost are prohibited
|
||||
// operations during CUDA/HIP Graph stream capture on CUDA 12.x / HIP,
|
||||
// returning cudaErrorStreamCaptureUnsupported (error 900). Plain heap
|
||||
// memory is safe to allocate at any time, and the captured
|
||||
// cudaMemcpyAsync node records the host pointer address; the buffer
|
||||
// lifetime is guaranteed by the post-reset callback below.
|
||||
void *new_host_mem = new uint8_t[nbytes];
|
||||
std::memcpy(new_host_mem, host_mem, nbytes);
|
||||
AddPostResetCallbackIfCapturingCUDAGraph(
|
||||
[=](paddle::optional<const CUDAGraph &> graph) {
|
||||
delete[] reinterpret_cast<uint8_t *>(new_host_mem);
|
||||
});
|
||||
return reinterpret_cast<T *>(new_host_mem);
|
||||
}
|
||||
#endif
|
||||
return host_mem;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,105 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
#include <cuda_runtime.h> // NOLINT
|
||||
#include "paddle/phi/common/data_type.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
/*
|
||||
* Summary: Grid stride looping macro in CUDA kernel
|
||||
*
|
||||
* [ Why need this macro? ]
|
||||
*
|
||||
* The original looping in CUDA kernel is:
|
||||
*
|
||||
* `for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \
|
||||
* i += blockDim.x * gridDim.x)`
|
||||
*
|
||||
* This for condition is risky. The value of `blockIdx.x * blockDim.x`
|
||||
* may be large, such as over 1GB, the first iteration is no problem here,
|
||||
* but when `i += blockDim.x * gridDim.x` is executed, the value of i
|
||||
* will greater than INT_MAX and overflow becomes negative value, at
|
||||
* this time, the cycle condition `i < (n)` is still satisfied, so it
|
||||
* will cause illegal access to cuda memory.
|
||||
*
|
||||
* Here is a real example in ERNIE, it will trigger above error.
|
||||
* The related data are:
|
||||
* - blockIdx.x = 2172938
|
||||
* - blockDim.x = 512
|
||||
* - blockIdx.x * blockDim.x = 1112543864
|
||||
* - INT_MAX = 2147483647
|
||||
*
|
||||
* So we polish the for condition as follow, the int64_t __index__ will
|
||||
* prevent overflow in the loop increment.
|
||||
*
|
||||
* Parameters:
|
||||
* - i: loop index
|
||||
* - num: total element numbers
|
||||
*
|
||||
* Examples:
|
||||
* template <typename T>
|
||||
* __global__ void Scale(T* logit_grad, const T* loss_grad, const int num,
|
||||
* const int d, const int remain) {
|
||||
* CUDA_KERNEL_LOOP(index, num) {
|
||||
* int idx_n = index / d;
|
||||
* int idx_remain = index % remain;
|
||||
* logit_grad[index] *= loss_grad[idx_n * remain + idx_remain];
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
#define CUDA_KERNEL_LOOP_TYPE(i, num, index_type) \
|
||||
int64_t __index__ = \
|
||||
static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x; \
|
||||
int64_t __stride__ = static_cast<int64_t>(blockDim.x) * gridDim.x; \
|
||||
for (index_type i = __index__; __index__ < (num); \
|
||||
__index__ += __stride__, i = __index__)
|
||||
|
||||
template <typename T>
|
||||
cudaDataType_t ToCudaDataType() {
|
||||
if (std::is_same<T, float>::value) {
|
||||
return CUDA_R_32F;
|
||||
} else if (std::is_same<T, double>::value) {
|
||||
return CUDA_R_64F;
|
||||
} else if (std::is_same<T, phi::dtype::float16>::value) {
|
||||
return CUDA_R_16F;
|
||||
#if CUDA_VERSION >= 11000
|
||||
} else if (std::is_same<T, phi::dtype::bfloat16>::value) {
|
||||
return CUDA_R_16BF;
|
||||
#endif
|
||||
#if CUDA_VERSION >= 11060
|
||||
} else if (std::is_same<T, int8_t>::value) {
|
||||
return CUDA_R_8I;
|
||||
} else if (std::is_same<T, int32_t>::value) {
|
||||
return CUDA_R_32I;
|
||||
#endif
|
||||
} else {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"DataType %s is unsupported for CUDA.",
|
||||
DataTypeToString(phi::CppTypeToDataType<T>::Type())));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
#endif
|
||||
@@ -0,0 +1,365 @@
|
||||
// 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.
|
||||
#include <mutex>
|
||||
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
static std::once_flag g_device_props_size_init_flag;
|
||||
static std::vector<std::unique_ptr<std::once_flag>> g_device_props_init_flags;
|
||||
static std::vector<phi::gpuDeviceProp> g_device_props;
|
||||
|
||||
namespace phi::backends::gpu {
|
||||
|
||||
#ifndef PADDLE_WITH_CUSTOM_DEVICE
|
||||
int DnnVersion() {
|
||||
if (!dynload::HasCUDNN()) return -1;
|
||||
return dynload::cudnnGetVersion(); // NOLINT
|
||||
}
|
||||
#endif
|
||||
|
||||
static int GetGPUDeviceCountImpl() {
|
||||
int driverVersion = 0;
|
||||
cudaError_t status = cudaDriverGetVersion(&driverVersion);
|
||||
|
||||
if (!(status == gpuSuccess && driverVersion != 0)) {
|
||||
// No GPU driver
|
||||
VLOG(2) << "GPU Driver Version can't be detected. No GPU driver!";
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto *cuda_visible_devices = std::getenv("CUDA_VISIBLE_DEVICES");
|
||||
|
||||
if (cuda_visible_devices != nullptr) {
|
||||
std::string cuda_visible_devices_str(cuda_visible_devices);
|
||||
if (!cuda_visible_devices_str.empty()) {
|
||||
cuda_visible_devices_str.erase(
|
||||
0, cuda_visible_devices_str.find_first_not_of('\''));
|
||||
cuda_visible_devices_str.erase(
|
||||
cuda_visible_devices_str.find_last_not_of('\'') + 1);
|
||||
cuda_visible_devices_str.erase(
|
||||
0, cuda_visible_devices_str.find_first_not_of('\"'));
|
||||
cuda_visible_devices_str.erase(
|
||||
cuda_visible_devices_str.find_last_not_of('\"') + 1);
|
||||
}
|
||||
if (std::all_of(cuda_visible_devices_str.begin(),
|
||||
cuda_visible_devices_str.end(),
|
||||
[](char ch) { return ch == ' '; })) {
|
||||
VLOG(2) << "CUDA_VISIBLE_DEVICES is set to be "
|
||||
"empty. No GPU detected.";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int count;
|
||||
status = cudaGetDeviceCount(&count);
|
||||
if (status != cudaSuccess) {
|
||||
VLOG(2) << "You have gpu driver and cuda installed, but the machine not "
|
||||
"has any gpu card.";
|
||||
count = 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int GetGPUDeviceCount() {
|
||||
// cache the count
|
||||
static auto dev_cnt = GetGPUDeviceCountImpl();
|
||||
return dev_cnt;
|
||||
}
|
||||
|
||||
int GetGPUComputeCapability(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int major, minor;
|
||||
auto major_error_code =
|
||||
cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, id);
|
||||
auto minor_error_code =
|
||||
cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, id);
|
||||
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(major_error_code);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(minor_error_code);
|
||||
return major * 10 + minor;
|
||||
}
|
||||
|
||||
int GetGPURuntimeVersion(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int runtime_version = 0;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaRuntimeGetVersion(&runtime_version));
|
||||
return runtime_version;
|
||||
}
|
||||
|
||||
int GetGPUDriverVersion(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int driver_version = 0;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaDriverGetVersion(&driver_version));
|
||||
return driver_version;
|
||||
}
|
||||
|
||||
bool TensorCoreAvailable() {
|
||||
int device = GetCurrentDeviceId();
|
||||
int driver_version = GetGPUComputeCapability(device);
|
||||
return driver_version >= 70;
|
||||
}
|
||||
|
||||
int GetGPUMultiProcessors(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int count;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaDeviceGetAttribute(&count, cudaDevAttrMultiProcessorCount, id));
|
||||
return count;
|
||||
}
|
||||
|
||||
int GetGPUMaxThreadsPerMultiProcessor(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int count;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaDeviceGetAttribute(
|
||||
&count, cudaDevAttrMaxThreadsPerMultiProcessor, id));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int GetGPUMaxThreadsPerBlock(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int count;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaDeviceGetAttribute(&count, cudaDevAttrMaxThreadsPerBlock, id));
|
||||
return count;
|
||||
}
|
||||
|
||||
int GetCurrentDeviceId() {
|
||||
int device_id;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaGetDevice(&device_id));
|
||||
return device_id;
|
||||
}
|
||||
|
||||
std::array<unsigned int, 3> GetGpuMaxGridDimSize(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
std::array<unsigned int, 3> ret = {};
|
||||
int size;
|
||||
auto error_code_x = cudaDeviceGetAttribute(&size, cudaDevAttrMaxGridDimX, id);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(error_code_x);
|
||||
ret[0] = size;
|
||||
|
||||
auto error_code_y = cudaDeviceGetAttribute(&size, cudaDevAttrMaxGridDimY, id);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(error_code_y);
|
||||
ret[1] = size;
|
||||
|
||||
auto error_code_z = cudaDeviceGetAttribute(&size, cudaDevAttrMaxGridDimZ, id);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(error_code_z);
|
||||
ret[2] = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::pair<int, int> GetGpuStreamPriorityRange() {
|
||||
int least_priority, greatest_priority;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaDeviceGetStreamPriorityRange(&least_priority, &greatest_priority));
|
||||
return std::make_pair(least_priority, greatest_priority);
|
||||
}
|
||||
|
||||
const gpuDeviceProp &GetDeviceProperties(int id) {
|
||||
std::call_once(g_device_props_size_init_flag, [&] {
|
||||
int gpu_num = 0;
|
||||
gpu_num = GetGPUDeviceCount();
|
||||
g_device_props_init_flags.resize(gpu_num);
|
||||
g_device_props.resize(gpu_num);
|
||||
for (int i = 0; i < gpu_num; ++i) {
|
||||
g_device_props_init_flags[i] = std::make_unique<std::once_flag>();
|
||||
}
|
||||
});
|
||||
|
||||
if (id == -1) {
|
||||
id = GetCurrentDeviceId();
|
||||
}
|
||||
|
||||
if (id < 0 || id >= static_cast<int>(g_device_props.size())) {
|
||||
PADDLE_THROW(common::errors::OutOfRange(
|
||||
"The device id %d is out of range [0, %d), where %d is the number of "
|
||||
"devices on this machine. Because the device id should be greater than "
|
||||
"or equal to zero and smaller than the number of gpus. Please input "
|
||||
"appropriate device again!",
|
||||
id,
|
||||
static_cast<int>(g_device_props.size()),
|
||||
static_cast<int>(g_device_props.size())));
|
||||
}
|
||||
|
||||
std::call_once(*(g_device_props_init_flags[id]), [&] {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaGetDeviceProperties(&g_device_props[id], id));
|
||||
});
|
||||
|
||||
return g_device_props[id];
|
||||
}
|
||||
|
||||
void SetDeviceId(int id) {
|
||||
static thread_local bool first_call = true;
|
||||
if (first_call) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
|
||||
PADDLE_RETRY_CUDA_SUCCESS(cudaSetDevice(id));
|
||||
VLOG(4) << "SetDeviceId " << id;
|
||||
first_call = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int prev_id;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaGetDevice(&prev_id));
|
||||
if (prev_id != id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
|
||||
PADDLE_RETRY_CUDA_SUCCESS(cudaSetDevice(id));
|
||||
VLOG(4) << "SetDeviceId " << id;
|
||||
}
|
||||
}
|
||||
|
||||
void GpuMemcpyAsync(void *dst,
|
||||
const void *src,
|
||||
size_t count,
|
||||
gpuMemcpyKind kind,
|
||||
gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaMemcpyAsync(dst, src, count, kind, stream));
|
||||
}
|
||||
|
||||
void GpuMemcpySync(void *dst,
|
||||
const void *src,
|
||||
size_t count,
|
||||
gpuMemcpyKind kind) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaMemcpy(dst, src, count, kind));
|
||||
}
|
||||
|
||||
void GpuMemcpyPeerAsync(void *dst,
|
||||
int dst_device,
|
||||
const void *src,
|
||||
int src_device,
|
||||
size_t count,
|
||||
gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaMemcpyPeerAsync(dst, dst_device, src, src_device, count, stream));
|
||||
}
|
||||
|
||||
void GpuMemcpyPeerSync(
|
||||
void *dst, int dst_device, const void *src, int src_device, size_t count) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaMemcpyPeer(dst, dst_device, src, src_device, count));
|
||||
}
|
||||
|
||||
void GpuMemsetAsync(void *dst, int value, size_t count, gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaMemsetAsync(dst, value, count, stream));
|
||||
}
|
||||
|
||||
void GpuStreamSync(gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaStreamSynchronize(stream));
|
||||
}
|
||||
|
||||
void GpuDestroyStream(gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaStreamDestroy(stream));
|
||||
}
|
||||
|
||||
void GpuDeviceSync() { PADDLE_ENFORCE_GPU_SUCCESS(cudaDeviceSynchronize()); }
|
||||
|
||||
gpuError_t GpuGetLastError() { return cudaGetLastError(); }
|
||||
|
||||
// See
|
||||
// https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#um-requirements
|
||||
// for more detail about managed memory requirements
|
||||
bool IsGPUManagedMemorySupported(int dev_id) {
|
||||
PADDLE_ENFORCE_LT(dev_id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
dev_id,
|
||||
GetGPUDeviceCount()));
|
||||
#if defined(__linux__) || defined(_WIN32)
|
||||
int ManagedMemoryAttr;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaDeviceGetAttribute(
|
||||
&ManagedMemoryAttr, cudaDevAttrManagedMemory, dev_id));
|
||||
return ManagedMemoryAttr != 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsGPUManagedMemoryOversubscriptionSupported(int dev_id) {
|
||||
PADDLE_ENFORCE_LT(dev_id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
dev_id,
|
||||
GetGPUDeviceCount()));
|
||||
#ifdef __linux__
|
||||
return IsGPUManagedMemorySupported(dev_id) &&
|
||||
GetGPUComputeCapability(dev_id) >= 60;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace phi::backends::gpu
|
||||
@@ -0,0 +1,313 @@
|
||||
// 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 <algorithm>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/backends/gpu/cuda/cudnn_helper.h"
|
||||
#include "paddle/phi/core/utils/data_type.h"
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
template <typename T>
|
||||
inline std::vector<T> TransformDimOrder(const std::vector<T>& dims) {
|
||||
std::vector<T> transformed_dims(dims.begin(), dims.end());
|
||||
if (dims.size() < 4) {
|
||||
return transformed_dims;
|
||||
}
|
||||
T H, W, D, C;
|
||||
if (dims.size() == 4) {
|
||||
H = dims[1];
|
||||
W = dims[2];
|
||||
C = dims[3];
|
||||
transformed_dims[1] = C;
|
||||
transformed_dims[2] = H;
|
||||
transformed_dims[3] = W;
|
||||
} else {
|
||||
D = dims[1];
|
||||
H = dims[2];
|
||||
W = dims[3];
|
||||
C = dims[4];
|
||||
transformed_dims[1] = C;
|
||||
transformed_dims[2] = D;
|
||||
transformed_dims[3] = H;
|
||||
transformed_dims[4] = W;
|
||||
}
|
||||
return transformed_dims;
|
||||
}
|
||||
|
||||
inline cudnnDataType_t ToCudnnDataType(const DataType& t) {
|
||||
cudnnDataType_t type = CUDNN_DATA_FLOAT;
|
||||
switch (t) {
|
||||
case DataType::FLOAT16:
|
||||
type = CUDNN_DATA_HALF;
|
||||
break;
|
||||
case DataType::FLOAT32:
|
||||
type = CUDNN_DATA_FLOAT;
|
||||
break;
|
||||
case DataType::FLOAT64:
|
||||
type = CUDNN_DATA_DOUBLE;
|
||||
break;
|
||||
#if CUDNN_VERSION_MIN(8, 6, 0) && CUDA_VERSION >= 11080
|
||||
case DataType::FLOAT8_E4M3FN:
|
||||
type = CUDNN_DATA_FP8_E4M3;
|
||||
break;
|
||||
case DataType::FLOAT8_E5M2:
|
||||
type = CUDNN_DATA_FP8_E5M2;
|
||||
break;
|
||||
#endif
|
||||
#if CUDNN_VERSION_MIN(8, 1, 0)
|
||||
case DataType::BFLOAT16:
|
||||
type = CUDNN_DATA_BFLOAT16;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
class ActivationDescriptor {
|
||||
public:
|
||||
using T = cudnnActivationStruct;
|
||||
struct Deleter {
|
||||
void operator()(T* t) {
|
||||
if (t != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyActivationDescriptor(t));
|
||||
t = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
ActivationDescriptor() {
|
||||
T* raw_ptr;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateActivationDescriptor(&raw_ptr));
|
||||
desc_.reset(raw_ptr);
|
||||
}
|
||||
template <typename T>
|
||||
void set(cudnnActivationMode_t mode, const T& coef) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetActivationDescriptor(
|
||||
desc_.get(), mode, CUDNN_NOT_PROPAGATE_NAN, static_cast<double>(coef)));
|
||||
}
|
||||
|
||||
T* desc() { return desc_.get(); }
|
||||
T* desc() const { return desc_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<T, Deleter> desc_;
|
||||
};
|
||||
|
||||
class TensorDescriptor {
|
||||
public:
|
||||
using T = cudnnTensorStruct;
|
||||
struct Deleter {
|
||||
void operator()(T* t) {
|
||||
if (t != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyTensorDescriptor(t));
|
||||
t = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
TensorDescriptor() {
|
||||
T* raw_ptr;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateTensorDescriptor(&raw_ptr));
|
||||
desc_.reset(raw_ptr);
|
||||
}
|
||||
T* desc() { return desc_.get(); }
|
||||
T* desc() const { return desc_.get(); }
|
||||
void set(const DenseTensor& tensor, const int groups = 1) {
|
||||
auto dims = common::vectorize<int>(tensor.dims());
|
||||
std::vector<int> strides(dims.size());
|
||||
strides[dims.size() - 1] = 1;
|
||||
for (int i = dims.size() - 2; i >= 0; i--) {
|
||||
strides[i] = dims[i + 1] * strides[i + 1];
|
||||
}
|
||||
std::vector<int> dims_with_group(dims.begin(), dims.end());
|
||||
if (groups > 1) {
|
||||
dims_with_group[1] = dims_with_group[1] / groups;
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetTensorNdDescriptor(
|
||||
desc_.get(),
|
||||
ToCudnnDataType(tensor.dtype()),
|
||||
dims_with_group.size(),
|
||||
dims_with_group.data(),
|
||||
strides.data()));
|
||||
}
|
||||
|
||||
void set(const std::vector<int>& dims,
|
||||
const cudnnTensorFormat_t format,
|
||||
const cudnnDataType_t dtype) {
|
||||
std::vector<int> transformed_dims;
|
||||
if (format == CUDNN_TENSOR_NHWC) {
|
||||
transformed_dims = TransformDimOrder(dims);
|
||||
} else {
|
||||
transformed_dims = dims;
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetTensorNdDescriptorEx(desc_.get(),
|
||||
format,
|
||||
dtype,
|
||||
transformed_dims.size(),
|
||||
transformed_dims.data()));
|
||||
}
|
||||
|
||||
void set(const DenseTensor& tensor, const cudnnTensorFormat_t format) {
|
||||
auto dims = common::vectorize<int>(tensor.dims());
|
||||
auto dtype = ToCudnnDataType(tensor.dtype());
|
||||
set(dims, format, dtype);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<T, Deleter> desc_;
|
||||
};
|
||||
|
||||
class FilterDescriptor {
|
||||
public:
|
||||
using T = cudnnFilterStruct;
|
||||
struct Deleter {
|
||||
void operator()(T* t) {
|
||||
if (t != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyFilterDescriptor(t));
|
||||
t = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
FilterDescriptor() {
|
||||
T* raw_ptr;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateFilterDescriptor(&raw_ptr));
|
||||
desc_.reset(raw_ptr);
|
||||
}
|
||||
T* desc() { return desc_.get(); }
|
||||
T* desc() const { return desc_.get(); }
|
||||
|
||||
void set(const std::vector<int>& dims,
|
||||
const cudnnTensorFormat_t format,
|
||||
const cudnnDataType_t dtype,
|
||||
const int groups = 1) {
|
||||
std::vector<int> transformed_dims;
|
||||
if (format == CUDNN_TENSOR_NHWC) {
|
||||
transformed_dims = TransformDimOrder(dims);
|
||||
} else {
|
||||
transformed_dims = dims;
|
||||
}
|
||||
if (groups > 1) {
|
||||
transformed_dims[1] = transformed_dims[1] / groups;
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetFilterNdDescriptor(desc_.get(),
|
||||
dtype,
|
||||
format,
|
||||
transformed_dims.size(),
|
||||
transformed_dims.data()));
|
||||
}
|
||||
|
||||
void set(const DenseTensor& tensor,
|
||||
const cudnnTensorFormat_t format,
|
||||
const int groups = 1) {
|
||||
auto dims = common::vectorize<int>(tensor.dims());
|
||||
auto dtype = ToCudnnDataType(tensor.dtype());
|
||||
set(dims, format, dtype, groups);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<T, Deleter> desc_;
|
||||
};
|
||||
|
||||
class ConvolutionDescriptor {
|
||||
public:
|
||||
using T = cudnnConvolutionStruct;
|
||||
struct Deleter {
|
||||
void operator()(T* t) {
|
||||
if (t != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyConvolutionDescriptor(t));
|
||||
t = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
ConvolutionDescriptor() {
|
||||
T* raw_ptr;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateConvolutionDescriptor(&raw_ptr));
|
||||
desc_.reset(raw_ptr);
|
||||
}
|
||||
T* desc() { return desc_.get(); }
|
||||
T* desc() const { return desc_.get(); }
|
||||
|
||||
void set(cudnnDataType_t dtype,
|
||||
const std::vector<int>& pads,
|
||||
const std::vector<int>& strides,
|
||||
const std::vector<int>& dilations,
|
||||
bool allow_tf32,
|
||||
const int groups = 1) {
|
||||
allow_tf32_ = allow_tf32;
|
||||
cudnnDataType_t compute_type =
|
||||
(dtype == CUDNN_DATA_DOUBLE) ? CUDNN_DATA_DOUBLE : CUDNN_DATA_FLOAT;
|
||||
T* desc = desc_.get();
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetConvolutionNdDescriptor(desc,
|
||||
pads.size(),
|
||||
pads.data(),
|
||||
strides.data(),
|
||||
dilations.data(),
|
||||
CUDNN_CROSS_CORRELATION,
|
||||
compute_type));
|
||||
#if CUDNN_VERSION_MIN(7, 0, 1)
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetConvolutionGroupCount(desc, groups));
|
||||
#if CUDA_VERSION >= 9000 && CUDNN_VERSION_MIN(7, 0, 1)
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetConvolutionMathType(desc, CUDNN_DEFAULT_MATH));
|
||||
if (dtype == CUDNN_DATA_HALF) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetConvolutionMathType(
|
||||
desc, CUDNN_TENSOR_OP_MATH));
|
||||
#if CUDA_VERSION >= 11000
|
||||
#if CUDNN_VERSION_MIN(8, 1, 0)
|
||||
} else if (dtype == CUDNN_DATA_BFLOAT16) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetConvolutionMathType(
|
||||
desc, CUDNN_TENSOR_OP_MATH));
|
||||
#endif // CUDNN_VERSION_MIN(8,1,0)
|
||||
} else if (dtype == CUDNN_DATA_FLOAT && !allow_tf32) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetConvolutionMathType(desc, CUDNN_FMA_MATH));
|
||||
#endif // CUDA_VERSION >= 11000
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool allow_tf32_;
|
||||
|
||||
private:
|
||||
std::unique_ptr<T, Deleter> desc_;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,676 @@
|
||||
/* 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 <string>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/common/flags.h"
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/backends/dynload/cudnn.h"
|
||||
#include "paddle/phi/common/bfloat16.h"
|
||||
#include "paddle/phi/common/float16.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
COMMON_DECLARE_bool(cudnn_deterministic);
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
#define CUDNN_VERSION_COMPUTE(major, minor, patch) \
|
||||
((major) <= 8 ? (major)*1000 + (minor)*100 + (patch) \
|
||||
: (major)*10000 + (minor)*100 + (patch))
|
||||
|
||||
#define CUDNN_VERSION_MIN(major, minor, patch) \
|
||||
(CUDNN_VERSION >= CUDNN_VERSION_COMPUTE(major, minor, patch))
|
||||
|
||||
enum class PoolingMode {
|
||||
kMaximum,
|
||||
kMaximumDeterministic,
|
||||
kAverageExclusive,
|
||||
kAverageInclusive,
|
||||
};
|
||||
|
||||
enum class ActivationMode {
|
||||
kNone, // activation identity
|
||||
kSigmoid,
|
||||
kRelu,
|
||||
kRelu6,
|
||||
kReluX,
|
||||
kTanh,
|
||||
kBandPass,
|
||||
};
|
||||
|
||||
inline cudnnPoolingMode_t GetPoolingMode(const PoolingMode& mode) {
|
||||
switch (mode) {
|
||||
case PoolingMode::kMaximumDeterministic:
|
||||
return CUDNN_POOLING_MAX_DETERMINISTIC;
|
||||
case PoolingMode::kAverageExclusive:
|
||||
return CUDNN_POOLING_AVERAGE_COUNT_EXCLUDE_PADDING;
|
||||
case PoolingMode::kAverageInclusive:
|
||||
return CUDNN_POOLING_AVERAGE_COUNT_INCLUDE_PADDING;
|
||||
case PoolingMode::kMaximum:
|
||||
return CUDNN_POOLING_MAX;
|
||||
default:
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("Unexpected CUDNN pooling mode."));
|
||||
}
|
||||
}
|
||||
|
||||
inline ActivationMode StringToActivationMode(const std::string& str) {
|
||||
if (str == "identity") {
|
||||
return ActivationMode::kNone;
|
||||
} else if (str == "sigmoid") {
|
||||
return ActivationMode::kSigmoid;
|
||||
} else if (str == "relu") {
|
||||
return ActivationMode::kRelu;
|
||||
} else if (str == "relu6") {
|
||||
return ActivationMode::kRelu6;
|
||||
} else if (str == "relux") {
|
||||
return ActivationMode::kReluX;
|
||||
} else if (str == "tanh") {
|
||||
return ActivationMode::kTanh;
|
||||
} else if (str == "bandpass") {
|
||||
return ActivationMode::kBandPass;
|
||||
} else {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Unknown CUDNN activation string: %s.", str));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class CudnnDataType;
|
||||
|
||||
// CUDNN_DATA_FLOAT8 is not valid before cudnn8.6
|
||||
#if CUDNN_VERSION_MIN(8, 6, 0) && CUDA_VERSION >= 11080
|
||||
template <>
|
||||
class CudnnDataType<phi::dtype::float8_e4m3fn> {
|
||||
public:
|
||||
static const cudnnDataType_t type = CUDNN_DATA_FP8_E4M3;
|
||||
using ScalingParamType = const float;
|
||||
using BatchNormParamType = float;
|
||||
static ScalingParamType* kOne() {
|
||||
static ScalingParamType v = 1.0;
|
||||
return &v;
|
||||
}
|
||||
static ScalingParamType* kZero() {
|
||||
static ScalingParamType v = 0.0;
|
||||
return &v;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// CUDNN_DATA_BFLOAT16 is not valid before cudnn8.1
|
||||
#if CUDNN_VERSION_MIN(8, 1, 0)
|
||||
template <>
|
||||
class CudnnDataType<phi::dtype::bfloat16> {
|
||||
public:
|
||||
static const cudnnDataType_t type = CUDNN_DATA_BFLOAT16;
|
||||
using ScalingParamType = const float;
|
||||
using BatchNormParamType = float;
|
||||
static ScalingParamType* kOne() {
|
||||
static ScalingParamType v = 1.0;
|
||||
return &v;
|
||||
}
|
||||
static ScalingParamType* kZero() {
|
||||
static ScalingParamType v = 0.0;
|
||||
return &v;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <>
|
||||
class CudnnDataType<phi::dtype::float16> {
|
||||
public:
|
||||
static const cudnnDataType_t type = CUDNN_DATA_HALF;
|
||||
// The scaling param type is float for HALF and FLOAT tensors
|
||||
using ScalingParamType = const float;
|
||||
using BatchNormParamType = float;
|
||||
static ScalingParamType* kOne() {
|
||||
static ScalingParamType v = 1.0;
|
||||
return &v;
|
||||
}
|
||||
static ScalingParamType* kZero() {
|
||||
static ScalingParamType v = 0.0;
|
||||
return &v;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class CudnnDataType<float> {
|
||||
public:
|
||||
static const cudnnDataType_t type = CUDNN_DATA_FLOAT;
|
||||
using ScalingParamType = const float;
|
||||
using BatchNormParamType = float;
|
||||
static ScalingParamType* kOne() {
|
||||
static ScalingParamType v = 1.0;
|
||||
return &v;
|
||||
}
|
||||
static ScalingParamType* kZero() {
|
||||
static ScalingParamType v = 0.0;
|
||||
return &v;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class CudnnDataType<double> {
|
||||
public:
|
||||
static const cudnnDataType_t type = CUDNN_DATA_DOUBLE;
|
||||
using ScalingParamType = const double;
|
||||
using BatchNormParamType = double;
|
||||
static ScalingParamType* kOne() {
|
||||
static ScalingParamType v = 1.0;
|
||||
return &v;
|
||||
}
|
||||
static ScalingParamType* kZero() {
|
||||
static ScalingParamType v = 0.0;
|
||||
return &v;
|
||||
}
|
||||
};
|
||||
|
||||
inline cudnnTensorFormat_t GetCudnnTensorFormat(
|
||||
const DataLayout& order) { // Not use
|
||||
switch (order) {
|
||||
case DataLayout::NHWC:
|
||||
return CUDNN_TENSOR_NHWC;
|
||||
case DataLayout::NCHW:
|
||||
return CUDNN_TENSOR_NCHW;
|
||||
case DataLayout::NCDHW:
|
||||
return CUDNN_TENSOR_NCHW; // NOTE: cudnn treat NdTensor as the same
|
||||
case DataLayout::NDHWC:
|
||||
return CUDNN_TENSOR_NHWC; // add, liyamei
|
||||
default:
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"CUDNN has no equivalent dataLayout for input order."));
|
||||
}
|
||||
return CUDNN_TENSOR_NCHW;
|
||||
}
|
||||
|
||||
class ScopedTensorDescriptor {
|
||||
public:
|
||||
ScopedTensorDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateTensorDescriptor(&desc_));
|
||||
}
|
||||
~ScopedTensorDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyTensorDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline cudnnTensorDescriptor_t descriptor(const cudnnTensorFormat_t format,
|
||||
const cudnnDataType_t type,
|
||||
const std::vector<int>& dims,
|
||||
const int groups = 1) {
|
||||
// the format is not used now, will add later
|
||||
std::vector<int> strides(dims.size());
|
||||
strides[dims.size() - 1] = 1;
|
||||
for (int i = dims.size() - 2; i >= 0; i--) {
|
||||
strides[i] = dims[i + 1] * strides[i + 1];
|
||||
}
|
||||
// Update tensor descriptor dims setting if groups > 1
|
||||
// NOTE: Here, Assume using NCHW or NCDHW order
|
||||
std::vector<int> dims_with_group(dims.begin(), dims.end());
|
||||
if (groups > 1) {
|
||||
dims_with_group[1] = dims_with_group[1] / groups;
|
||||
}
|
||||
|
||||
if (dims.size() == 4) {
|
||||
if (format == CUDNN_TENSOR_NCHW) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetTensorNdDescriptor(desc_,
|
||||
type,
|
||||
dims_with_group.size(),
|
||||
dims_with_group.data(),
|
||||
strides.data()));
|
||||
} else { // CUDNN_TENSOR_NHWC
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetTensor4dDescriptor(
|
||||
desc_, format, type, dims[0], dims[3], dims[1], dims[2]));
|
||||
}
|
||||
} else if (dims.size() == 5) {
|
||||
if (format == CUDNN_TENSOR_NCHW) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetTensorNdDescriptor(desc_,
|
||||
type,
|
||||
dims_with_group.size(),
|
||||
dims_with_group.data(),
|
||||
strides.data()));
|
||||
} else { // CUDNN_TENSOR_NHWC
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetTensorNdDescriptorEx(
|
||||
desc_, format, type, dims.size(), dims.data()));
|
||||
}
|
||||
}
|
||||
return desc_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline cudnnTensorDescriptor_t descriptor(const DataLayout& order,
|
||||
const std::vector<int>& dims,
|
||||
const int groups = 1) {
|
||||
return descriptor(
|
||||
GetCudnnTensorFormat(order), CudnnDataType<T>::type, dims, groups);
|
||||
}
|
||||
|
||||
inline cudnnTensorDescriptor_t descriptor(const cudnnDataType_t cudnn_type,
|
||||
const std::vector<int>& dim,
|
||||
const std::vector<int>& stride) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetTensorNdDescriptor(
|
||||
desc_, cudnn_type, dim.size(), dim.data(), stride.data()));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline cudnnTensorDescriptor_t descriptor(const std::vector<int>& dim,
|
||||
const std::vector<int>& stride) {
|
||||
return descriptor(CudnnDataType<T>::type, dim, stride);
|
||||
}
|
||||
|
||||
inline cudnnTensorDescriptor_t desc() { return desc_; }
|
||||
|
||||
private:
|
||||
cudnnTensorDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedTensorDescriptor);
|
||||
};
|
||||
|
||||
class ScopedRNNTensorDescriptor {
|
||||
public:
|
||||
ScopedRNNTensorDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateRNNDataDescriptor(&desc_));
|
||||
}
|
||||
|
||||
~ScopedRNNTensorDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyRNNDataDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline cudnnRNNDataDescriptor_t descriptor(
|
||||
const cudnnDataType_t cudnn_type,
|
||||
int max_seq_length,
|
||||
int batch_size,
|
||||
int input_size,
|
||||
bool time_major,
|
||||
const std::vector<int>& seq_length) {
|
||||
static double padding_fill = 0.0f;
|
||||
cudnnRNNDataLayout_t layout;
|
||||
|
||||
if (time_major) {
|
||||
layout = CUDNN_RNN_DATA_LAYOUT_SEQ_MAJOR_UNPACKED;
|
||||
} else {
|
||||
layout = CUDNN_RNN_DATA_LAYOUT_BATCH_MAJOR_UNPACKED;
|
||||
}
|
||||
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetRNNDataDescriptor(
|
||||
desc_,
|
||||
cudnn_type,
|
||||
layout,
|
||||
max_seq_length,
|
||||
batch_size,
|
||||
input_size,
|
||||
seq_length.data(),
|
||||
static_cast<void*>(&padding_fill)));
|
||||
|
||||
return desc_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline cudnnRNNDataDescriptor_t descriptor(
|
||||
int max_length,
|
||||
int batch_size,
|
||||
int input_size,
|
||||
bool time_major,
|
||||
const std::vector<int>& seq_length) {
|
||||
return descriptor(CudnnDataType<T>::type,
|
||||
max_length,
|
||||
batch_size,
|
||||
input_size,
|
||||
time_major,
|
||||
seq_length);
|
||||
}
|
||||
|
||||
inline cudnnRNNDataDescriptor_t desc() { return desc_; }
|
||||
|
||||
private:
|
||||
cudnnRNNDataDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedRNNTensorDescriptor);
|
||||
};
|
||||
|
||||
class ScopedDropoutDescriptor {
|
||||
public:
|
||||
ScopedDropoutDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateDropoutDescriptor(&desc_));
|
||||
}
|
||||
~ScopedDropoutDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyDropoutDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline cudnnDropoutDescriptor_t descriptor(const cudnnHandle_t& handle,
|
||||
const Place& place UNUSED,
|
||||
bool initialized,
|
||||
float dropout_prob_,
|
||||
DenseTensor* dropout_state_,
|
||||
int seed,
|
||||
size_t state_size) {
|
||||
if (dropout_state_ == nullptr) { // for no dropout or test
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetDropoutDescriptor(desc_,
|
||||
handle,
|
||||
0 /* dropout */,
|
||||
nullptr,
|
||||
0 /* state_size */,
|
||||
0 /* seed */));
|
||||
return desc_;
|
||||
}
|
||||
auto* dropout_state_data = dropout_state_->data<uint8_t>();
|
||||
if (!initialized) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetDropoutDescriptor(
|
||||
desc_, handle, dropout_prob_, dropout_state_data, state_size, seed));
|
||||
} else {
|
||||
auto dropout_state_dims =
|
||||
common::vectorize<int64_t>(dropout_state_->dims());
|
||||
state_size = dropout_state_dims[0];
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnRestoreDropoutDescriptor(
|
||||
desc_, handle, dropout_prob_, dropout_state_data, state_size, 0));
|
||||
}
|
||||
return desc_;
|
||||
}
|
||||
inline cudnnDropoutDescriptor_t desc() { return desc_; }
|
||||
|
||||
private:
|
||||
cudnnDropoutDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedDropoutDescriptor);
|
||||
};
|
||||
|
||||
class ScopedRNNDescriptor {
|
||||
public:
|
||||
ScopedRNNDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnCreateRNNDescriptor(&desc_));
|
||||
}
|
||||
~ScopedRNNDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnDestroyRNNDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline cudnnRNNDescriptor_t desc() { return desc_; }
|
||||
|
||||
private:
|
||||
cudnnRNNDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedRNNDescriptor);
|
||||
};
|
||||
|
||||
class ScopedFilterDescriptor {
|
||||
public:
|
||||
ScopedFilterDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateFilterDescriptor(&desc_));
|
||||
}
|
||||
~ScopedFilterDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyFilterDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline cudnnFilterDescriptor_t descriptor(const cudnnTensorFormat_t format,
|
||||
const cudnnDataType_t type,
|
||||
const std::vector<int>& kernel,
|
||||
const int groups = 1) {
|
||||
// filter layout: MCHW(MCDHW), where M is the number of
|
||||
// output image channels, C is the number of input image channels,
|
||||
// D is the depth of the filter, H is the height of the filter, and W is the
|
||||
// width of the filter.
|
||||
std::vector<int> kernel_with_group(kernel.begin(), kernel.end());
|
||||
if (groups > 1) {
|
||||
kernel_with_group[0] /= groups;
|
||||
// NOTE: input filter(C) of the filter is already asserted to be C/groups.
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetFilterNdDescriptor(desc_,
|
||||
type,
|
||||
format,
|
||||
kernel_with_group.size(),
|
||||
kernel_with_group.data()));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline cudnnFilterDescriptor_t descriptor(const DataLayout& order,
|
||||
const std::vector<int>& kernel,
|
||||
const int groups = 1) {
|
||||
return descriptor(
|
||||
GetCudnnTensorFormat(order), CudnnDataType<T>::type, kernel, groups);
|
||||
}
|
||||
|
||||
inline cudnnFilterDescriptor_t desc() { return desc_; }
|
||||
|
||||
private:
|
||||
cudnnFilterDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedFilterDescriptor);
|
||||
};
|
||||
|
||||
class ScopedConvolutionDescriptor {
|
||||
public:
|
||||
ScopedConvolutionDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateConvolutionDescriptor(&desc_));
|
||||
}
|
||||
~ScopedConvolutionDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyConvolutionDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline cudnnConvolutionDescriptor_t descriptor(
|
||||
cudnnDataType_t type,
|
||||
const std::vector<int>& pads,
|
||||
const std::vector<int>& strides,
|
||||
const std::vector<int>& dilations) {
|
||||
PADDLE_ENFORCE_EQ(pads.size(),
|
||||
strides.size(),
|
||||
common::errors::InvalidArgument(
|
||||
"The size of pads and strides should be equal. But "
|
||||
"received size of pads is %d, size of strides is %d.",
|
||||
pads.size(),
|
||||
strides.size()));
|
||||
PADDLE_ENFORCE_EQ(
|
||||
pads.size(),
|
||||
dilations.size(),
|
||||
common::errors::InvalidArgument(
|
||||
"The size of pads and dilations should be equal. But received size "
|
||||
"of pads is %d, size of dilations is %d.",
|
||||
pads.size(),
|
||||
dilations.size()));
|
||||
|
||||
cudnnDataType_t compute_type =
|
||||
(type == CUDNN_DATA_DOUBLE) ? CUDNN_DATA_DOUBLE : CUDNN_DATA_FLOAT;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetConvolutionNdDescriptor(desc_,
|
||||
pads.size(),
|
||||
pads.data(),
|
||||
strides.data(),
|
||||
dilations.data(),
|
||||
CUDNN_CROSS_CORRELATION,
|
||||
compute_type));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline cudnnConvolutionDescriptor_t descriptor(
|
||||
const std::vector<int>& pads,
|
||||
const std::vector<int>& strides,
|
||||
const std::vector<int>& dilations) {
|
||||
return descriptor(CudnnDataType<T>::type, pads, strides, dilations);
|
||||
}
|
||||
|
||||
private:
|
||||
cudnnConvolutionDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedConvolutionDescriptor);
|
||||
};
|
||||
|
||||
class ScopedPoolingDescriptor {
|
||||
public:
|
||||
ScopedPoolingDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreatePoolingDescriptor(&desc_));
|
||||
}
|
||||
~ScopedPoolingDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyPoolingDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline cudnnPoolingDescriptor_t descriptor(const PoolingMode& mode,
|
||||
const std::vector<int>& kernel,
|
||||
const std::vector<int>& pads,
|
||||
const std::vector<int>& strides) {
|
||||
PADDLE_ENFORCE_EQ(kernel.size(),
|
||||
pads.size(),
|
||||
common::errors::InvalidArgument(
|
||||
"The size of kernel and pads should be equal. But "
|
||||
"received size of kernel is %d, size of pads is %d.",
|
||||
kernel.size(),
|
||||
pads.size()));
|
||||
PADDLE_ENFORCE_EQ(
|
||||
kernel.size(),
|
||||
strides.size(),
|
||||
common::errors::InvalidArgument(
|
||||
"The size of kernel and strides should be equal. But "
|
||||
"received size of kernel is %d, size of strides is %d.",
|
||||
kernel.size(),
|
||||
strides.size()));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetPoolingNdDescriptor(
|
||||
desc_,
|
||||
(GetPoolingMode(mode)),
|
||||
CUDNN_PROPAGATE_NAN, // Always propagate nans.
|
||||
kernel.size(),
|
||||
kernel.data(),
|
||||
pads.data(),
|
||||
strides.data()));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
private:
|
||||
cudnnPoolingDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedPoolingDescriptor);
|
||||
};
|
||||
|
||||
class ScopedSpatialTransformerDescriptor {
|
||||
public:
|
||||
ScopedSpatialTransformerDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateSpatialTransformerDescriptor(&desc_));
|
||||
}
|
||||
~ScopedSpatialTransformerDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroySpatialTransformerDescriptor(desc_));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline cudnnSpatialTransformerDescriptor_t descriptor(const int nbDims,
|
||||
const int dimA[]) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetSpatialTransformerNdDescriptor(
|
||||
desc_,
|
||||
CUDNN_SAMPLER_BILINEAR,
|
||||
CudnnDataType<T>::type,
|
||||
nbDims,
|
||||
dimA));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
private:
|
||||
cudnnSpatialTransformerDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedSpatialTransformerDescriptor);
|
||||
};
|
||||
|
||||
class ScopedActivationDescriptor {
|
||||
public:
|
||||
ScopedActivationDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateActivationDescriptor(&desc_));
|
||||
}
|
||||
~ScopedActivationDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyActivationDescriptor(desc_));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline cudnnActivationDescriptor_t descriptor(
|
||||
const std::string& act, double value_max = static_cast<double>(0.)) {
|
||||
double relu_ceiling = 0.0;
|
||||
ActivationMode activation_mode = StringToActivationMode(act);
|
||||
cudnnActivationMode_t mode;
|
||||
switch (activation_mode) {
|
||||
case ActivationMode::kNone:
|
||||
mode = CUDNN_ACTIVATION_IDENTITY;
|
||||
break;
|
||||
case ActivationMode::kRelu6:
|
||||
relu_ceiling = 6.0;
|
||||
mode = CUDNN_ACTIVATION_CLIPPED_RELU;
|
||||
break;
|
||||
case ActivationMode::kReluX:
|
||||
relu_ceiling = value_max;
|
||||
mode = CUDNN_ACTIVATION_CLIPPED_RELU;
|
||||
break;
|
||||
case ActivationMode::kRelu:
|
||||
mode = CUDNN_ACTIVATION_RELU;
|
||||
break;
|
||||
case ActivationMode::kSigmoid:
|
||||
mode = CUDNN_ACTIVATION_SIGMOID;
|
||||
break;
|
||||
case ActivationMode::kTanh:
|
||||
mode = CUDNN_ACTIVATION_TANH;
|
||||
break;
|
||||
default:
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Unrecognized CUDNN activation mode: %d.",
|
||||
static_cast<int>(activation_mode)));
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnSetActivationDescriptor(
|
||||
desc_, mode, CUDNN_NOT_PROPAGATE_NAN, relu_ceiling));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
private:
|
||||
cudnnActivationDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedActivationDescriptor);
|
||||
};
|
||||
|
||||
class ScopedCTCLossDescriptor {
|
||||
public:
|
||||
ScopedCTCLossDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnCreateCTCLossDescriptor(&desc_));
|
||||
}
|
||||
~ScopedCTCLossDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnDestroyCTCLossDescriptor(desc_));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline cudnnCTCLossDescriptor_t descriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cudnnSetCTCLossDescriptor(desc_, CudnnDataType<T>::type));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
private:
|
||||
cudnnCTCLossDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedCTCLossDescriptor);
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
#include "paddle/phi/backends/gpu/cuda/cudnn_workspace_helper.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
namespace phi::backends::gpu {
|
||||
|
||||
static int GetDefaultConvWorkspaceSizeLimitMBImpl() {
|
||||
const char *env_str = std::getenv("FLAGS_conv_workspace_size_limit");
|
||||
return env_str ? std::stoi(std::string(env_str))
|
||||
: kDefaultConvWorkspaceSizeLimitMB;
|
||||
}
|
||||
|
||||
int GetDefaultConvWorkspaceSizeLimitMB() {
|
||||
static auto workspace_size = GetDefaultConvWorkspaceSizeLimitMBImpl();
|
||||
return workspace_size;
|
||||
}
|
||||
} // namespace phi::backends::gpu
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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/common/macros.h"
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
#define CUDNN_ENFORCE_TENSOR_SIZE_SUPPORTED(tensor) \
|
||||
do { \
|
||||
int64_t largest = (1LL << 31) - 1; \
|
||||
PADDLE_ENFORCE_LE( \
|
||||
tensor.numel(), \
|
||||
largest, \
|
||||
::common::errors::PreconditionNotMet( \
|
||||
"The element size of " #tensor " should be <= INT_MAX(2147483647)" \
|
||||
", but got %lld", \
|
||||
tensor.numel())); \
|
||||
} while (0)
|
||||
|
||||
static constexpr int kDefaultConvWorkspaceSizeLimitMB = 512;
|
||||
|
||||
PADDLE_API int GetDefaultConvWorkspaceSizeLimitMB();
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,145 @@
|
||||
// Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "paddle/phi/backends/gpu/cuda/gpu_event_timer.h"
|
||||
#include <numeric>
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/phi/backends/context_pool.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_context.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi {
|
||||
|
||||
EventPair::EventPair() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventCreate(&start_));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventCreate(&end_));
|
||||
started_ = false;
|
||||
}
|
||||
|
||||
EventPair::~EventPair() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventDestroy(start_));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventDestroy(end_));
|
||||
}
|
||||
|
||||
void EventPair::Start(cudaStream_t stream) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
started_,
|
||||
false,
|
||||
common::errors::InvalidArgument("GPUEventTimer has been started."));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventRecord(start_, stream));
|
||||
started_ = true;
|
||||
}
|
||||
|
||||
void EventPair::Stop(cudaStream_t stream) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
started_,
|
||||
true,
|
||||
common::errors::InvalidArgument("GPUEventTimer has not been started."));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventRecord(end_, stream));
|
||||
started_ = false;
|
||||
}
|
||||
|
||||
void EventPair::Reset() { started_ = false; }
|
||||
|
||||
double EventPair::Elapsed() {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
started_,
|
||||
false,
|
||||
common::errors::InvalidArgument("GPUEventTimer has not been stopped."));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventSynchronize(start_));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventSynchronize(end_));
|
||||
float ms;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaEventElapsedTime(&ms, start_, end_));
|
||||
return ms / 1000.0;
|
||||
}
|
||||
|
||||
GPUEventTimer::GPUEventTimer(phi::GPUPlace place) : length_(0) {
|
||||
auto *dev_ctx = phi::DeviceContextPool::Instance().GetByPlace(place);
|
||||
default_stream_ = dev_ctx->stream();
|
||||
}
|
||||
|
||||
EventPair *GPUEventTimer::GetLatest() {
|
||||
PADDLE_ENFORCE_GT(
|
||||
length_,
|
||||
0,
|
||||
common::errors::InvalidArgument("GPUEventTimer has not been started."));
|
||||
auto &back = events_[length_ - 1];
|
||||
if (back == nullptr) {
|
||||
back.reset(new EventPair());
|
||||
}
|
||||
return back.get();
|
||||
}
|
||||
|
||||
void GPUEventTimer::Start(cudaStream_t stream) {
|
||||
if (length_ == events_.size()) {
|
||||
VLOG(10) << "Expand when length = " << length_;
|
||||
events_.emplace_back();
|
||||
}
|
||||
++length_;
|
||||
GetLatest()->Start(stream);
|
||||
}
|
||||
|
||||
void GPUEventTimer::Stop(cudaStream_t stream) { GetLatest()->Stop(stream); }
|
||||
|
||||
void GPUEventTimer::Start() { Start(default_stream_); }
|
||||
|
||||
void GPUEventTimer::Stop() { Stop(default_stream_); }
|
||||
|
||||
void GPUEventTimer::Reset() {
|
||||
for (size_t i = 0; i < length_; ++i) {
|
||||
events_[i]->Reset();
|
||||
}
|
||||
length_ = 0;
|
||||
}
|
||||
|
||||
double GPUEventTimer::Elapsed(bool reset) {
|
||||
double ret = 0;
|
||||
for (size_t i = 0; i < length_; ++i) {
|
||||
ret += events_[i]->Elapsed();
|
||||
}
|
||||
if (reset) {
|
||||
Reset();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<double> GPUEventTimer::ElapsedList(bool reset) {
|
||||
std::vector<double> values(length_);
|
||||
for (size_t i = 0; i < length_; ++i) {
|
||||
values[i] = events_[i]->Elapsed();
|
||||
}
|
||||
if (reset) {
|
||||
Reset();
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
void GPUEventTimer::PreAlloc(size_t n) {
|
||||
if (events_.size() >= n) return;
|
||||
events_.resize(n);
|
||||
for (auto &pair : events_) {
|
||||
if (pair == nullptr) {
|
||||
pair.reset(new EventPair());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPUEventTimer::ShrinkToFit() { events_.resize(length_); }
|
||||
|
||||
size_t GPUEventTimer::Size() const { return length_; }
|
||||
|
||||
size_t GPUEventTimer::Capacity() const { return events_.size(); }
|
||||
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "cuda_runtime.h" // NOLINT
|
||||
#include "paddle/common/enforce.h"
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace phi {
|
||||
|
||||
class EventPair {
|
||||
DISABLE_COPY_AND_ASSIGN(EventPair);
|
||||
|
||||
public:
|
||||
EventPair();
|
||||
|
||||
~EventPair() PADDLE_MAY_THROW;
|
||||
|
||||
void Start(cudaStream_t stream);
|
||||
|
||||
void Stop(cudaStream_t stream);
|
||||
|
||||
void Reset();
|
||||
|
||||
double Elapsed();
|
||||
|
||||
private:
|
||||
cudaEvent_t start_;
|
||||
cudaEvent_t end_;
|
||||
bool started_;
|
||||
};
|
||||
|
||||
class GPUEventTimer {
|
||||
DISABLE_COPY_AND_ASSIGN(GPUEventTimer);
|
||||
|
||||
public:
|
||||
explicit GPUEventTimer(phi::GPUPlace place);
|
||||
|
||||
void Start(cudaStream_t stream);
|
||||
|
||||
void Stop(cudaStream_t stream);
|
||||
|
||||
void Start();
|
||||
|
||||
void Stop();
|
||||
|
||||
void Reset();
|
||||
|
||||
double Elapsed(bool reset);
|
||||
|
||||
std::vector<double> ElapsedList(bool reset);
|
||||
|
||||
void PreAlloc(size_t n);
|
||||
|
||||
void ShrinkToFit();
|
||||
|
||||
size_t Size() const;
|
||||
|
||||
size_t Capacity() const;
|
||||
|
||||
private:
|
||||
EventPair *GetLatest();
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<EventPair>> events_;
|
||||
size_t length_;
|
||||
cudaStream_t default_stream_;
|
||||
};
|
||||
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,115 @@
|
||||
/* Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
Copyright (c) 2022 NVIDIA Corporation. 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. */
|
||||
|
||||
// Forward-declares CUDA API types used in platform-agnostic wrapper headers.
|
||||
#pragma once
|
||||
|
||||
/// Forward declaration of Eigen types.
|
||||
namespace Eigen {
|
||||
struct GpuDevice;
|
||||
} // namespace Eigen
|
||||
|
||||
/// Forward declaration of CUDA types.
|
||||
|
||||
// Forward declaration of CUDA runtime types.
|
||||
using cudaStream_t = struct CUstream_st *;
|
||||
using cudaEvent_t = struct CUevent_st *;
|
||||
|
||||
// Forward declaration of cuBLAS types.
|
||||
using cublasHandle_t = struct cublasContext *;
|
||||
|
||||
// Forward declaration of cuBLASLt types.
|
||||
using cublasLtHandle_t = struct cublasLtContext *;
|
||||
|
||||
#ifndef PADDLE_WITH_CUSTOM_DEVICE
|
||||
// Forward declaration of cuDNN types.
|
||||
using cudnnHandle_t = struct cudnnContext *;
|
||||
using cudnnTensorDescriptor_t = struct cudnnTensorStruct *;
|
||||
using cudnnConvolutionDescriptor_t = struct cudnnConvolutionStruct *;
|
||||
using cudnnPoolingDescriptor_t = struct cudnnPoolingStruct *;
|
||||
using cudnnFilterDescriptor_t = struct cudnnFilterStruct *;
|
||||
using cudnnLRNDescriptor_t = struct cudnnLRNStruct *;
|
||||
using cudnnActivationDescriptor_t = struct cudnnActivationStruct *;
|
||||
using cudnnSpatialTransformerDescriptor_t =
|
||||
struct cudnnSpatialTransformerStruct *;
|
||||
using cudnnOpTensorDescriptor_t = struct cudnnOpTensorStruct *;
|
||||
using cudnnReduceTensorDescriptor_t = struct cudnnReduceTensorStruct *;
|
||||
using cudnnCTCLossDescriptor_t = struct cudnnCTCLossStruct *;
|
||||
using cudnnTensorTransformDescriptor_t = struct cudnnTensorTransformStruct *;
|
||||
using cudnnDropoutDescriptor_t = struct cudnnDropoutStruct *;
|
||||
using cudnnRNNDescriptor_t = struct cudnnRNNStruct *;
|
||||
using cudnnPersistentRNNPlan_t = struct cudnnPersistentRNNPlan *;
|
||||
using cudnnRNNDataDescriptor_t = struct cudnnRNNDataStruct *;
|
||||
using cudnnAlgorithmDescriptor_t = struct cudnnAlgorithmStruct *;
|
||||
using cudnnAlgorithmPerformance_t = struct cudnnAlgorithmPerformanceStruct *;
|
||||
using cudnnSeqDataDescriptor_t = struct cudnnSeqDataStruct *;
|
||||
using cudnnAttnDescriptor_t = struct cudnnAttnStruct *;
|
||||
using cudnnFusedOpsConstParamPack_t = struct cudnnFusedOpsConstParamStruct *;
|
||||
using cudnnFusedOpsVariantParamPack_t =
|
||||
struct cudnnFusedOpsVariantParamStruct *;
|
||||
using cudnnFusedOpsPlan_t = struct cudnnFusedOpsPlanStruct *;
|
||||
|
||||
// Forward declaration of cuSOLVER types.
|
||||
using cusolverDnHandle_t = struct cusolverDnContext *;
|
||||
|
||||
// Forward declaration of cuSparse types.
|
||||
using cusparseHandle_t = struct cusparseContext *;
|
||||
|
||||
// Forward declaration of NCCL types.
|
||||
using ncclComm_t = struct ncclComm *;
|
||||
|
||||
/// Forward declaration of ROCM types.
|
||||
#include <cstddef>
|
||||
|
||||
using hipDevice_t = int;
|
||||
using hipCtx_t = struct ihipCtx_t *;
|
||||
using hipStream_t = struct ihipStream_t *;
|
||||
using hipEvent_t = struct ihipEvent_t *;
|
||||
|
||||
// Forward declaration of MIOpen types.
|
||||
using miopenHandle_t = struct miopenHandle *;
|
||||
using miopenAcceleratorQueue_t = hipStream_t;
|
||||
using miopenFusionOpDescriptor_t = struct miopenFusionOpDescriptor *;
|
||||
using miopenTensorDescriptor_t = struct miopenTensorDescriptor *;
|
||||
using miopenConvolutionDescriptor_t = struct miopenConvolutionDescriptor *;
|
||||
using miopenPoolingDescriptor_t = struct miopenPoolingDescriptor *;
|
||||
using miopenLRNDescriptor_t = struct miopenLRNDescriptor *;
|
||||
using miopenActivationDescriptor_t = struct miopenActivationDescriptor *;
|
||||
using miopenRNNDescriptor_t = struct miopenRNNDescriptor *;
|
||||
using miopenCTCLossDescriptor_t = struct miopenCTCLossDescriptor *;
|
||||
using miopenDropoutDescriptor_t = struct miopenDropoutDescriptor *;
|
||||
using miopenFusionPlanDescriptor_t = struct miopenFusionPlanDescriptor *;
|
||||
using miopenOperatorDescriptor_t = struct miopenOperatorDescriptor *;
|
||||
using miopenOperatorArgs_t = struct miopenOperatorArgs *;
|
||||
using miopenAllocatorFunction = void *(*)(void *context, size_t sizeBytes);
|
||||
// using miopenDeallocatorFunction = void *(*)(void *context, void *memory);
|
||||
// struct miopenConvAlgoPerf_t;
|
||||
// struct miopenConvSolution_t;
|
||||
|
||||
// Forward declaration of rocBLAS types.
|
||||
using rocblas_handle = struct _rocblas_handle *;
|
||||
|
||||
// Forward declaration of hipblaslt types.
|
||||
using hipblasLtHandle_t = void *;
|
||||
|
||||
// Forward declaration of hipfft types.
|
||||
using hipfftHandle = struct hipfftHandle_t *;
|
||||
|
||||
// Forward declaration of rocSOLVER types.
|
||||
using rocsolver_handle = rocblas_handle;
|
||||
|
||||
// Forward declaration of rocSparse types.
|
||||
using rocsparse_handle = struct _rocsparse_handle *;
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,329 @@
|
||||
/* Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
Copyright (c) 2022 NVIDIA Corporation. 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
|
||||
|
||||
#ifdef PADDLE_WITH_CUSTOM_DEVICE
|
||||
#include "paddle/phi/backends/custom/custom_context.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_helper.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
#else
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
|
||||
defined(PADDLE_WITH_XPU_KP)
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#include "paddle/common/enforce.h"
|
||||
#include "paddle/phi/backends/gpu/forwards.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_decls.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_helper.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
#include "paddle/phi/core/attribute.h"
|
||||
#include "paddle/phi/core/device_context.h"
|
||||
|
||||
namespace phi {
|
||||
|
||||
class CUDAStream;
|
||||
|
||||
class DnnWorkspaceHandle {
|
||||
public:
|
||||
inline DnnWorkspaceHandle(Allocator* allocator, gpuStream_t stream)
|
||||
: allocator_(allocator), stream_(stream) {
|
||||
mtx_ = std::make_unique<std::mutex>();
|
||||
}
|
||||
|
||||
inline void RunFunc(const std::function<void(void*)>& cudnn_func,
|
||||
size_t required_workspace_bytes) {
|
||||
if (required_workspace_bytes > WorkspaceSize()) {
|
||||
ReallocWorkspace(required_workspace_bytes);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(*mtx_);
|
||||
cudnn_func(allocation_ ? allocation_->ptr() : nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Thread which call RunFuncSync() would release gpu memory after
|
||||
* running the function. Currently this function is only used when cudnn
|
||||
* exhaustive searching and callers have to guarantee that the input function
|
||||
* is host blocking */
|
||||
PADDLE_API void RunFuncSync(const std::function<void(void*)>& cudnn_func,
|
||||
size_t required_workspace_bytes,
|
||||
bool use_cached_allocation = true);
|
||||
|
||||
inline size_t WorkspaceSize() {
|
||||
if (allocation_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return allocation_->size();
|
||||
}
|
||||
|
||||
PADDLE_API void ResetWorkspace();
|
||||
|
||||
TEST_API void ReallocWorkspace(size_t required_workspace_bytes);
|
||||
|
||||
DnnWorkspaceHandle(DnnWorkspaceHandle&&) = default;
|
||||
DnnWorkspaceHandle& operator=(DnnWorkspaceHandle&&) = delete;
|
||||
|
||||
private:
|
||||
Allocator::AllocationPtr allocation_{nullptr};
|
||||
Allocator* allocator_{nullptr}; // Not owned
|
||||
gpuStream_t stream_{nullptr}; // Not owned
|
||||
std::unique_ptr<std::mutex> mtx_;
|
||||
};
|
||||
|
||||
class PADDLE_API GPUContext : public DeviceContext,
|
||||
public TypeInfoTraits<DeviceContext, GPUContext> {
|
||||
public:
|
||||
explicit GPUContext(const GPUPlace& place,
|
||||
bool init = true,
|
||||
int stream_priority = 0);
|
||||
|
||||
GPUContext(GPUContext&&);
|
||||
GPUContext& operator=(GPUContext&&);
|
||||
|
||||
virtual ~GPUContext();
|
||||
|
||||
/*! \brief Return place in the device context. */
|
||||
const Place& GetPlace() const override;
|
||||
|
||||
/*! \brief Return gpu stream in the device context. */
|
||||
gpuStream_t stream() const;
|
||||
|
||||
/*! \brief Return CUDAStream in the device context. */
|
||||
CUDAStream* cuda_stream() const;
|
||||
|
||||
/*! \brief Return cudnn handle in the device context. */
|
||||
dnnHandle_t cudnn_handle() const;
|
||||
|
||||
/*! \brief Return cublas handle in the device context. */
|
||||
blasHandle_t cublas_handle() const;
|
||||
|
||||
/*! \brief Return cublasLt handle in the device context. */
|
||||
blasLtHandle_t cublaslt_handle() const;
|
||||
|
||||
/*! \brief Return persistent cublasLt workspace (grow-only, multi-stream
|
||||
* safe). */
|
||||
std::pair<void*, size_t> cublaslt_workspace(size_t required_size) const;
|
||||
|
||||
/*! \brief Return cusolver handle in the device context. */
|
||||
solverHandle_t cusolver_dn_handle() const;
|
||||
|
||||
/*! \brief Return cusparse handle in the device context. */
|
||||
sparseHandle_t cusparse_handle() const;
|
||||
|
||||
/*! \brief Wait for all operations completion in the stream. */
|
||||
void Wait() const override;
|
||||
|
||||
/*! \brief Wait for event in the stream. */
|
||||
void WaitEvent(gpuEvent_t ev) const;
|
||||
|
||||
/*! \brief Check whether tensor core is supported */
|
||||
bool tensor_core_available() const;
|
||||
|
||||
/*! \brief Return compute capability in the device context. */
|
||||
int GetComputeCapability() const;
|
||||
|
||||
/*! \brief Return the max physical thread count in the device context */
|
||||
int GetMaxPhysicalThreadCount() const;
|
||||
|
||||
/*! \brief Return the SM count in the device context */
|
||||
int GetSMCount() const;
|
||||
|
||||
/*! \brief Return the Max thread num of block in the device context */
|
||||
int GetMaxThreadsPerBlock() const;
|
||||
|
||||
/*! \brief Return the max grid dim size in the device context */
|
||||
std::array<unsigned int, 3> GetCUDAMaxGridDimSize() const;
|
||||
|
||||
/*! \brief Return eigen device in the device context. */
|
||||
Eigen::GpuDevice* eigen_device() const;
|
||||
|
||||
/*! \brief Return a cudnn workspace handle to call multiple cudnn
|
||||
* functions without interrupting by other threads.
|
||||
* Once the first cudnn function is called by the handle, a lock
|
||||
* would be acquired to prevent other threads from accessing the
|
||||
* workspace. Once the handle is destructed, the lock would be released.
|
||||
*/
|
||||
// TODO(wilber): The return type is a pointer, to be modified later.
|
||||
DnnWorkspaceHandle cudnn_workspace_handle() const;
|
||||
|
||||
public:
|
||||
/*! \brief Call cublas function safely. */
|
||||
void CublasCall(const std::function<void(blasHandle_t)>&) const;
|
||||
|
||||
/*! \brief Call cublas function with Tensor Core safely. If
|
||||
Tensor Core is not available, use DEFAULT_MATH instead. */
|
||||
void TensorCoreCublasCallIfAvailable(
|
||||
const std::function<void(blasHandle_t)>&) const;
|
||||
|
||||
/*! \brief Call cusparse function safely. */
|
||||
void CusparseCall(const std::function<void(sparseHandle_t)>&) const;
|
||||
|
||||
void RecordEvent(gpuEvent_t ev, const std::function<void()>& callback) const;
|
||||
|
||||
void RecordEvent(gpuEvent_t ev) const;
|
||||
|
||||
void AddStreamCallback(const std::function<void()>& callback) const;
|
||||
|
||||
void WaitStreamCallback() const;
|
||||
|
||||
// Several methods for adapting Dnn-specific attributes
|
||||
bool HasDnnAttr(const std::string& attr_name) const;
|
||||
const Attribute& GetDnnAttr(const std::string& attr_name) const;
|
||||
void SetDnnAttr(const std::string& attr_name, Attribute attr);
|
||||
void ClearDnnAttr();
|
||||
|
||||
static const char* name() { return "GPUContext"; }
|
||||
|
||||
public:
|
||||
/*! \brief Return nccl communicators. */
|
||||
ncclComm_t nccl_comm() const;
|
||||
|
||||
/*! \brief Set nccl communicators. */
|
||||
void set_nccl_comm(ncclComm_t comm);
|
||||
|
||||
// NOTE: External users manage resources. Used in inference scenarios.
|
||||
// The Set interface is for inference only, DeviceContext will mark the
|
||||
// resource as external, and will not delete any resource when destructing.
|
||||
void SetStream(gpuStream_t);
|
||||
|
||||
public:
|
||||
// NOTE: DeviceContext hold resources. Used in training scenarios.
|
||||
// The interface used by the training scene, DeviceContext will initialize
|
||||
// all resources and delete them when destructing.
|
||||
// Note that you must set the Allocator before calling Init function.
|
||||
void Init();
|
||||
|
||||
// TODO(wilber): Why does the GetAllocator interface require a stream
|
||||
// parameter?
|
||||
// The temporary trick method bypasses this problem, and the following
|
||||
// interfaces
|
||||
// need to be deleted later.
|
||||
|
||||
// Note that this is a trick implementation, which can be used to partially
|
||||
// initialize when the SetAllocator interface is not called.
|
||||
void PartialInitWithoutAllocator(int stream_priority = 0);
|
||||
// Note that this is a trick implementation that can be used to initialize
|
||||
// resources that require an Allocator when the SetAllocator interface is
|
||||
// called.
|
||||
void PartialInitWithAllocator();
|
||||
|
||||
// Note that this function is a trick implementation since all 'set' methods
|
||||
// are protected by default.
|
||||
// clear: whether clear the original CUDAStream or not
|
||||
void SetCUDAStream(CUDAStream*, bool clear = true);
|
||||
|
||||
protected:
|
||||
void SetEigenDevice(Eigen::GpuDevice*);
|
||||
void SetEigenDevice(std::function<Eigen::GpuDevice*()>&&);
|
||||
|
||||
void SetBlasHandle(blasHandle_t);
|
||||
void SetBlasHandle(std::function<blasHandle_t()>&&);
|
||||
|
||||
void SetBlasTensorCoreHandle(blasHandle_t);
|
||||
void SetBlasTensorCoreHandle(std::function<blasHandle_t()>&&);
|
||||
|
||||
void SetBlasTF32Handle(blasHandle_t);
|
||||
void SetBlasTF32Handle(std::function<blasHandle_t()>&&);
|
||||
|
||||
void SetBlasLtHandle(blasLtHandle_t);
|
||||
void SetBlasLtHandle(std::function<blasLtHandle_t()>&&);
|
||||
|
||||
void SetDnnHandle(dnnHandle_t);
|
||||
void SetDnnHandle(std::function<dnnHandle_t()>&&);
|
||||
|
||||
void SetSolverHandle(solverHandle_t);
|
||||
void SetSolverHandle(std::function<solverHandle_t()>&&);
|
||||
|
||||
void SetSparseHandle(sparseHandle_t);
|
||||
void SetSparseHandle(std::function<sparseHandle_t()>&&);
|
||||
|
||||
void SetDnnWorkspaceHandle(DnnWorkspaceHandle*);
|
||||
|
||||
void SetComputeCapability(int val);
|
||||
|
||||
void SetMaxThreadsPerMultiProcessor(int val);
|
||||
|
||||
void SetMultiProcessors(int val);
|
||||
|
||||
void SetMaxThreadsPerBlock(int val);
|
||||
|
||||
void SetMaxGridDimSize(const std::array<unsigned int, 3>& val);
|
||||
|
||||
void SetDriverVersion(int val);
|
||||
|
||||
void SetRuntimeVersion(int val);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
// Note: In order to register the kernel of CUDNN, DnnContext is required.
|
||||
// Currently, CUDNN kernel directly uses GPUContext. But if the kernel function
|
||||
// has the same name, this will lead to duplicate instantiations of GPU kernel
|
||||
// and Dnn kernel function, so if we using DnnContext = GPUContext, we
|
||||
// must use different function name for cudnn kernel
|
||||
using GPUDNNContext = GPUContext;
|
||||
|
||||
// KPS (Kernel PrimitiveS API) needs to exist as a kind of backend,
|
||||
// because we want to implement a KPS-based kernel and make it run
|
||||
// on GPU and XPU at the same time, so we need KPSContext when registering
|
||||
// KPS Kernel. Note: XPU and GPU cannot be compiled at the same time!
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
using KPSContext = GPUContext;
|
||||
#endif
|
||||
|
||||
} // namespace phi
|
||||
|
||||
namespace Eigen {
|
||||
struct DefaultDevice;
|
||||
} // namespace Eigen
|
||||
|
||||
namespace phi {
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
// Currently, GPUPinnedContext is only used to data copying.
|
||||
class GPUPinnedContext
|
||||
: public DeviceContext,
|
||||
public phi::TypeInfoTraits<DeviceContext, GPUPinnedContext> {
|
||||
public:
|
||||
PADDLE_API GPUPinnedContext();
|
||||
PADDLE_API explicit GPUPinnedContext(GPUPinnedPlace place);
|
||||
|
||||
const Place& GetPlace() const override;
|
||||
|
||||
Eigen::DefaultDevice* eigen_device() const;
|
||||
|
||||
dnnHandle_t cudnn_handle() const override {
|
||||
PADDLE_THROW(common::errors::Unavailable(
|
||||
"GPUPinnedContext does not support cudnn_handle()."));
|
||||
}
|
||||
|
||||
static const char* name() { return "GPUPinnedContext"; }
|
||||
|
||||
private:
|
||||
GPUPinnedPlace place_;
|
||||
std::unique_ptr<Eigen::DefaultDevice> eigen_device_;
|
||||
};
|
||||
#endif
|
||||
} // namespace phi
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
// Copyright (c) 2022 NVIDIA Corporation. 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/forwards.h"
|
||||
|
||||
namespace phi {
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#define DECLARE_TYPE_FOR_GPU(GPU_TYPE, CUDA_TYPE, ROCM_TYPE) \
|
||||
using GPU_TYPE = ROCM_TYPE;
|
||||
|
||||
#else // PADDLE_WITH_CUDA
|
||||
|
||||
#define DECLARE_TYPE_FOR_GPU(GPU_TYPE, CUDA_TYPE, ROCM_TYPE) \
|
||||
using GPU_TYPE = CUDA_TYPE;
|
||||
#endif
|
||||
|
||||
DECLARE_TYPE_FOR_GPU(gpuStream_t, cudaStream_t, hipStream_t);
|
||||
DECLARE_TYPE_FOR_GPU(gpuEvent_t, cudaEvent_t, hipEvent_t);
|
||||
|
||||
#ifndef PADDLE_WITH_CUSTOM_DEVICE
|
||||
DECLARE_TYPE_FOR_GPU(dnnActivationDescriptor,
|
||||
cudnnActivationStruct,
|
||||
miopenActivationDescriptor);
|
||||
DECLARE_TYPE_FOR_GPU(dnnTensorDescriptor,
|
||||
cudnnTensorStruct,
|
||||
miopenTensorDescriptor);
|
||||
DECLARE_TYPE_FOR_GPU(dnnFilterDescriptor,
|
||||
cudnnFilterStruct,
|
||||
miopenTensorDescriptor);
|
||||
DECLARE_TYPE_FOR_GPU(dnnFilterDescriptor_t,
|
||||
cudnnFilterDescriptor_t,
|
||||
miopenTensorDescriptor_t);
|
||||
DECLARE_TYPE_FOR_GPU(dnnConvolutionDescriptor,
|
||||
cudnnConvolutionStruct,
|
||||
miopenConvolutionDescriptor);
|
||||
DECLARE_TYPE_FOR_GPU(dnnConvolutionDescriptor_t,
|
||||
cudnnConvolutionDescriptor_t,
|
||||
miopenConvolutionDescriptor_t);
|
||||
DECLARE_TYPE_FOR_GPU(dnnPoolingDescriptor_t,
|
||||
cudnnPoolingDescriptor_t,
|
||||
miopenPoolingDescriptor_t);
|
||||
DECLARE_TYPE_FOR_GPU(dnnDropoutDescriptor_t,
|
||||
cudnnDropoutDescriptor_t,
|
||||
miopenDropoutDescriptor_t);
|
||||
|
||||
DECLARE_TYPE_FOR_GPU(blasHandle_t, cublasHandle_t, rocblas_handle);
|
||||
|
||||
DECLARE_TYPE_FOR_GPU(blasLtHandle_t, cublasLtHandle_t, hipblasLtHandle_t);
|
||||
|
||||
DECLARE_TYPE_FOR_GPU(solverHandle_t, cusolverDnHandle_t, rocsolver_handle);
|
||||
|
||||
DECLARE_TYPE_FOR_GPU(sparseHandle_t, cusparseHandle_t, rocsparse_handle);
|
||||
|
||||
#undef DECLARE_TYPE_FOR_GPU
|
||||
|
||||
using CUDAGraphID = unsigned long long; // NOLINT
|
||||
#endif
|
||||
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,24 @@
|
||||
/* 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
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#include "paddle/phi/backends/gpu/rocm/rocm_device_function.h"
|
||||
#else
|
||||
#include "paddle/phi/backends/gpu/cuda/cuda_device_function.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#include "paddle/phi/backends/gpu/rocm/miopen_desc.h"
|
||||
#include "paddle/phi/backends/gpu/rocm/miopen_helper.h"
|
||||
#else // CUDA
|
||||
#include "paddle/phi/backends/gpu/cuda/cudnn_desc.h"
|
||||
#include "paddle/phi/backends/gpu/cuda/cudnn_helper.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#include "paddle/phi/backends/gpu/rocm/rocm_helper.h"
|
||||
#else
|
||||
#include "paddle/phi/backends/gpu/cuda/cuda_helper.h"
|
||||
#endif
|
||||
|
||||
#define CUDA_KERNEL_LOOP(i, num) CUDA_KERNEL_LOOP_TYPE(i, num, int)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. */
|
||||
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
#include "paddle/phi/common/memory_utils.h"
|
||||
|
||||
COMMON_DECLARE_string(selected_gpus);
|
||||
|
||||
namespace phi::backends::gpu {
|
||||
|
||||
static inline std::vector<std::string> Split(std::string const& original,
|
||||
char separator) {
|
||||
std::vector<std::string> results;
|
||||
std::string token;
|
||||
std::istringstream is(original);
|
||||
while (std::getline(is, token, separator)) {
|
||||
if (!token.empty()) {
|
||||
results.push_back(token);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
//! Get a list of device ids from environment variable or use all.
|
||||
std::vector<int> GetSelectedDevices() {
|
||||
// use user specified GPUs in single-node multi-process mode.
|
||||
std::vector<int> devices;
|
||||
if (!FLAGS_selected_gpus.empty()) {
|
||||
auto devices_str = Split(FLAGS_selected_gpus, ',');
|
||||
for (auto const& id : devices_str) {
|
||||
devices.push_back(atoi(id.c_str()));
|
||||
}
|
||||
} else {
|
||||
int count = GetGPUDeviceCount();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
devices.push_back(i);
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
constexpr static float fraction_reserve_gpu_memory = 0.05f;
|
||||
|
||||
size_t GpuAvailableMemToAlloc() {
|
||||
size_t total = 0;
|
||||
size_t available = 0;
|
||||
memory_utils::GpuMemoryUsage(&available, &total);
|
||||
size_t reserving =
|
||||
static_cast<size_t>(fraction_reserve_gpu_memory * available); // NOLINT
|
||||
// If available size is less than minimum chunk size, no usable memory exists
|
||||
size_t available_to_alloc = available - reserving;
|
||||
size_t min_chunk_size = GpuMinChunkSize();
|
||||
if (available_to_alloc < min_chunk_size) {
|
||||
available_to_alloc = 0;
|
||||
}
|
||||
VLOG(10) << "GPU usage " << (available >> 20) << "M/" << (total >> 20)
|
||||
<< "M, " << (available_to_alloc >> 20) << "M available to allocate";
|
||||
return available_to_alloc;
|
||||
}
|
||||
|
||||
size_t GpuMinChunkSize() {
|
||||
// Allow to allocate the minimum chunk size is 256 bytes.
|
||||
return 1 << 8;
|
||||
}
|
||||
|
||||
} // namespace phi::backends::gpu
|
||||
@@ -0,0 +1,148 @@
|
||||
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_types.h"
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
//! Get the version of dnn
|
||||
int DnnVersion();
|
||||
|
||||
//! Get the total number of GPU devices in system.
|
||||
PADDLE_API int GetGPUDeviceCount();
|
||||
|
||||
//! Get the compute capability of the ith GPU (format: major * 10 + minor)
|
||||
int GetGPUComputeCapability(int id);
|
||||
|
||||
//! Get the runtime version of the ith GPU
|
||||
int GetGPURuntimeVersion(int id);
|
||||
|
||||
//! Get the driver version of the ith GPU
|
||||
int GetGPUDriverVersion(int id);
|
||||
|
||||
//! Whether the current device support TensorCore
|
||||
PADDLE_API bool TensorCoreAvailable();
|
||||
|
||||
//! Get the MultiProcessors of the ith GPU.
|
||||
int GetGPUMultiProcessors(int id);
|
||||
|
||||
//! Get the MaxThreads of each MultiProcessor of the ith GPU.
|
||||
int GetGPUMaxThreadsPerMultiProcessor(int id);
|
||||
|
||||
//! Get the MaxThreads of each block of the ith GPU.
|
||||
int GetGPUMaxThreadsPerBlock(int id);
|
||||
|
||||
//! Get the current GPU device id in system.
|
||||
PADDLE_API int GetCurrentDeviceId();
|
||||
|
||||
//! Get the maximum GridDim size for GPU buddy allocator.
|
||||
std::array<unsigned int, 3> GetGpuMaxGridDimSize(int);
|
||||
|
||||
std::pair<int, int> GetGpuStreamPriorityRange();
|
||||
|
||||
//! Get a list of device ids from environment variable or use all.
|
||||
std::vector<int> GetSelectedDevices();
|
||||
|
||||
//! Get the properties of the ith GPU device.
|
||||
PADDLE_API const gpuDeviceProp &GetDeviceProperties(int id);
|
||||
|
||||
//! Set the GPU device id for next execution.
|
||||
PADDLE_API void SetDeviceId(int device_id);
|
||||
|
||||
//! Get the available memory to allocate, which is the size of available gpu
|
||||
//! minus reserving.
|
||||
size_t GpuAvailableMemToAlloc();
|
||||
|
||||
//! Get the minimum chunk size for GPU buddy allocator.
|
||||
size_t GpuMinChunkSize();
|
||||
|
||||
//! Copy memory from address src to dst asynchronously.
|
||||
void GpuMemcpyAsync(void *dst,
|
||||
const void *src,
|
||||
size_t count,
|
||||
gpuMemcpyKind kind,
|
||||
gpuStream_t stream);
|
||||
|
||||
//! Copy memory from address src to dst synchronously.
|
||||
void GpuMemcpySync(void *dst,
|
||||
const void *src,
|
||||
size_t count,
|
||||
gpuMemcpyKind kind);
|
||||
|
||||
//! Copy memory from one device to another device asynchronously.
|
||||
void GpuMemcpyPeerAsync(void *dst,
|
||||
int dst_device,
|
||||
const void *src,
|
||||
int src_device,
|
||||
size_t count,
|
||||
gpuStream_t stream);
|
||||
|
||||
//! Copy memory from one device to another device synchronously.
|
||||
void GpuMemcpyPeerSync(
|
||||
void *dst, int dst_device, const void *src, int src_device, size_t count);
|
||||
|
||||
//! Set memory dst with value count size asynchronously
|
||||
void GpuMemsetAsync(void *dst, int value, size_t count, gpuStream_t stream);
|
||||
|
||||
//! Blocks until stream has completed all operations.
|
||||
PADDLE_API void GpuStreamSync(gpuStream_t stream);
|
||||
|
||||
void GpuDestroyStream(gpuStream_t stream);
|
||||
|
||||
// ! Blocks until device has completed all operations.
|
||||
void GpuDeviceSync();
|
||||
|
||||
gpuError_t GpuGetLastError();
|
||||
|
||||
bool IsGPUManagedMemorySupported(int dev_id);
|
||||
|
||||
bool IsGPUManagedMemoryOversubscriptionSupported(int dev_id);
|
||||
|
||||
class GPUDeviceGuard {
|
||||
public:
|
||||
explicit inline GPUDeviceGuard(int dev_id) {
|
||||
int prev_id = GetCurrentDeviceId();
|
||||
if (prev_id != dev_id) {
|
||||
prev_id_ = prev_id;
|
||||
SetDeviceId(dev_id);
|
||||
}
|
||||
}
|
||||
inline ~GPUDeviceGuard() {
|
||||
if (prev_id_ != -1) {
|
||||
SetDeviceId(prev_id_);
|
||||
}
|
||||
}
|
||||
GPUDeviceGuard(const GPUDeviceGuard &o) = delete;
|
||||
GPUDeviceGuard &operator=(const GPUDeviceGuard &o) = delete;
|
||||
|
||||
private:
|
||||
int prev_id_{-1};
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,247 @@
|
||||
// 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.
|
||||
|
||||
// Used for compute gpu launch parameter config
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
#include <cuda_runtime.h>
|
||||
#else
|
||||
#include <hip/hip_runtime.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_context.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
// CUDA performs better when thread_per_block is between [64, 512]
|
||||
#define PREDEFINED_BLOCK_SIZE 512
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
// Limitation of the setting in one dimension of cuda grid.
|
||||
constexpr int kMultiDimslimit = 65536;
|
||||
|
||||
template <typename T = int64_t>
|
||||
inline T DivUp(T a, T b) {
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
// for round integer value into next highest power of 2.
|
||||
inline int64_t RoundToNextHighPowOfTwo(int64_t n, int64_t min_val = 1) {
|
||||
n--;
|
||||
n |= (n >> 1);
|
||||
n |= (n >> 2);
|
||||
n |= (n >> 4);
|
||||
n |= (n >> 8);
|
||||
n |= (n >> 16);
|
||||
return std::max(min_val, (n + 1));
|
||||
}
|
||||
|
||||
inline int64_t RoundToPowerOfTwo(int64_t n) {
|
||||
constexpr int64_t min_val = 32;
|
||||
int64_t num = RoundToNextHighPowOfTwo(n, min_val);
|
||||
int64_t max_val = 1024;
|
||||
return std::min(max_val, num);
|
||||
}
|
||||
|
||||
#ifdef WITH_NV_JETSON
|
||||
// The number of threads cannot be assigned 1024 in some cases when the device
|
||||
// is nano or tx2 .
|
||||
inline void ChangeThreadNum(const GPUContext& dev_ctx,
|
||||
int* num_thread,
|
||||
int alternative_num_thread = 512) {
|
||||
if (dev_ctx.GetComputeCapability() == 53 ||
|
||||
dev_ctx.GetComputeCapability() == 62) {
|
||||
*num_thread = alternative_num_thread;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct GpuLaunchConfig {
|
||||
public:
|
||||
GpuLaunchConfig() {}
|
||||
|
||||
size_t GetThreadNum() const { return GetBlockSize() * GetGridSize(); }
|
||||
|
||||
size_t GetGridSize() const {
|
||||
return block_per_grid.x * block_per_grid.y * block_per_grid.z;
|
||||
}
|
||||
|
||||
size_t GetBlockSize() const {
|
||||
return thread_per_block.x * thread_per_block.y * thread_per_block.z;
|
||||
}
|
||||
|
||||
int compute_capability = 0;
|
||||
dim3 thread_per_block = dim3(1, 1, 1);
|
||||
dim3 block_per_grid = dim3(1, 1, 1);
|
||||
};
|
||||
|
||||
/* According to NVIDIA, if number of threads per block is 64/128/256/512,
|
||||
* cuda performs better. And number of blocks should be greater (at least
|
||||
* 2x~4x) than number of SMs. Hence, SM count is took into account within
|
||||
* this function to determine the right number of threads per block. */
|
||||
inline GpuLaunchConfig GetGpuLaunchConfig1D(const GPUContext& dev_ctx,
|
||||
int64_t numel,
|
||||
int vec_size = 1) {
|
||||
PADDLE_ENFORCE_GE(numel,
|
||||
0,
|
||||
common::errors::InvalidArgument(
|
||||
"numel is expected to be greater than or equal 0,"
|
||||
" but received %d.",
|
||||
numel));
|
||||
PADDLE_ENFORCE_GE(
|
||||
vec_size,
|
||||
1,
|
||||
common::errors::InvalidArgument(
|
||||
"vec_size is expected greater than 0, but received %d.", vec_size));
|
||||
// Get compute_capability
|
||||
const int capability = dev_ctx.GetComputeCapability();
|
||||
// If thread number per block is 64/128/256/512, cuda performs better.
|
||||
int limit_threads =
|
||||
std::min(PREDEFINED_BLOCK_SIZE, dev_ctx.GetMaxThreadsPerBlock());
|
||||
#ifdef WITH_NV_JETSON
|
||||
if (capability == 53 || capability == 62) {
|
||||
limit_threads = 512;
|
||||
}
|
||||
#endif
|
||||
int threads = limit_threads;
|
||||
int sm_count = dev_ctx.GetSMCount();
|
||||
int64_t active_threads_num = numel / vec_size;
|
||||
if (active_threads_num / (sm_count << 1) < limit_threads) {
|
||||
// Round up threads number into an exponential multiple of 2, while number
|
||||
// of active blocks is about twice of SM, to acquire better performance.
|
||||
threads = RoundToPowerOfTwo(active_threads_num / (sm_count << 1));
|
||||
} else if (active_threads_num / (sm_count << 2) < limit_threads) {
|
||||
// Round up threads number into an exponential multiple of 2, while number
|
||||
// of active blocks is about 4 times of SM, to acquire better performance.
|
||||
threads = RoundToPowerOfTwo(active_threads_num / (sm_count << 2));
|
||||
}
|
||||
// Number of threads per block shall be larger than 64.
|
||||
threads = std::max(64, threads);
|
||||
int64_t blocks = DivUp<int64_t>(DivUp<int64_t>(numel, vec_size), threads);
|
||||
int64_t limit_blocks = dev_ctx.GetCUDAMaxGridDimSize()[0];
|
||||
if (blocks > limit_blocks) {
|
||||
blocks = limit_blocks;
|
||||
}
|
||||
|
||||
GpuLaunchConfig config;
|
||||
config.thread_per_block.x = threads;
|
||||
config.block_per_grid.x = static_cast<uint32_t>(blocks);
|
||||
config.compute_capability = capability;
|
||||
|
||||
VLOG(7) << "Get 1-D launch config: numel=" << numel
|
||||
<< ", vec_size=" << vec_size << ", block_size=" << threads
|
||||
<< ", grid_size=" << blocks << ", limit_blocks=" << limit_blocks
|
||||
<< ", limit_threads=" << limit_threads;
|
||||
return config;
|
||||
}
|
||||
|
||||
inline GpuLaunchConfig GetGpuLaunchConfig2D(const GPUContext& dev_ctx,
|
||||
int64_t x_dim,
|
||||
int64_t y_dim) {
|
||||
PADDLE_ENFORCE_GT(
|
||||
x_dim,
|
||||
0,
|
||||
common::errors::InvalidArgument("x dim number should greater than 0,"
|
||||
" but received value is: %d",
|
||||
x_dim));
|
||||
PADDLE_ENFORCE_GT(
|
||||
y_dim,
|
||||
0,
|
||||
common::errors::InvalidArgument("y dim number should greater than 0,"
|
||||
" but received value is: %d",
|
||||
y_dim));
|
||||
|
||||
const int kThreadsPerBlock = 256;
|
||||
int block_cols = std::min<int64_t>(x_dim, kThreadsPerBlock);
|
||||
int block_rows = std::max(kThreadsPerBlock / block_cols, 1);
|
||||
|
||||
int max_physical_threads = dev_ctx.GetMaxPhysicalThreadCount();
|
||||
const int max_blocks = std::max(max_physical_threads / kThreadsPerBlock, 1);
|
||||
|
||||
GpuLaunchConfig config;
|
||||
// Noticed, block size is not align to 32, if needed do it yourself.
|
||||
config.thread_per_block = dim3(block_cols, block_rows, 1);
|
||||
|
||||
int grid_x = std::min<int64_t>(DivUp<int64_t>(x_dim, block_cols), max_blocks);
|
||||
int grid_y = std::min<int64_t>(max_blocks / grid_x,
|
||||
std::max<int64_t>(y_dim / block_rows, 1));
|
||||
|
||||
config.block_per_grid = dim3(grid_x, grid_y, 1);
|
||||
return config;
|
||||
}
|
||||
|
||||
static inline int GetLastPow2(int n) {
|
||||
n |= (n >> 1);
|
||||
n |= (n >> 2);
|
||||
n |= (n >> 4);
|
||||
n |= (n >> 8);
|
||||
n |= (n >> 16);
|
||||
return std::max(1, n - (n >> 1));
|
||||
}
|
||||
|
||||
inline GpuLaunchConfig GetGpuLaunchConfig3D(const GPUContext& dev_ctx,
|
||||
int num_img,
|
||||
int height,
|
||||
int width) {
|
||||
const int kThreadsPerBlock = 256;
|
||||
int max_threads_per_block = dev_ctx.GetMaxThreadsPerBlock(); // 1024
|
||||
int max_threads = std::min(kThreadsPerBlock, max_threads_per_block);
|
||||
|
||||
int block_x = std::min(GetLastPow2(width), max_threads);
|
||||
int block_y = std::min(GetLastPow2(height), max_threads / block_x);
|
||||
int block_z = std::min(num_img, max_threads / block_x / block_y);
|
||||
|
||||
std::array<unsigned int, 3> max_grid_dim = dev_ctx.GetCUDAMaxGridDimSize();
|
||||
unsigned int grid_x =
|
||||
std::min(max_grid_dim[0], DivUp<unsigned int>(width, block_x));
|
||||
unsigned int grid_y =
|
||||
std::min(max_grid_dim[1], DivUp<unsigned int>(height, block_y));
|
||||
unsigned int grid_z =
|
||||
std::min(max_grid_dim[2], DivUp<unsigned int>(num_img, block_z * 4));
|
||||
|
||||
const int capability = dev_ctx.GetComputeCapability();
|
||||
GpuLaunchConfig config;
|
||||
config.compute_capability = capability;
|
||||
config.thread_per_block = dim3(block_x, block_y, block_z);
|
||||
config.block_per_grid = dim3(grid_x, grid_y, grid_z);
|
||||
return config;
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
void LimitGridDim(const Context& dev_ctx, dim3* grid_dim) {
|
||||
auto max_grid_dim =
|
||||
reinterpret_cast<const GPUContext&>(dev_ctx).GetCUDAMaxGridDimSize();
|
||||
grid_dim->x = grid_dim->x < max_grid_dim[0] ? grid_dim->x : max_grid_dim[0];
|
||||
grid_dim->y = grid_dim->y < max_grid_dim[1] ? grid_dim->y : max_grid_dim[1];
|
||||
grid_dim->z = grid_dim->z < max_grid_dim[2] ? grid_dim->z : max_grid_dim[2];
|
||||
}
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,324 @@
|
||||
// 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.
|
||||
|
||||
#include "paddle/phi/backends/gpu/gpu_resources.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <map>
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_decls.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
#include "paddle/phi/core/allocator.h"
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
#include "paddle/phi/backends/dynload/cublas.h"
|
||||
#include "paddle/phi/backends/dynload/cublasLt.h"
|
||||
#include "paddle/phi/backends/dynload/cudnn.h"
|
||||
#include "paddle/phi/backends/dynload/cusolver.h"
|
||||
#include "paddle/phi/backends/dynload/cusparse.h"
|
||||
#if !defined(__APPLE__) && defined(PADDLE_WITH_NCCL)
|
||||
#include "paddle/phi/backends/dynload/nccl.h"
|
||||
#endif // !defined(__APPLE__) && defined(PADDLE_WITH_NCCL)
|
||||
#endif // PADDLE_WITH_CUDA
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#include "paddle/phi/backends/dynload/hipblasLt.h"
|
||||
#include "paddle/phi/backends/dynload/rocsparse.h"
|
||||
#endif
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "unsupported/Eigen/CXX11/Tensor"
|
||||
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi {
|
||||
|
||||
void InitGpuProperties(Place place,
|
||||
int* compute_capability,
|
||||
int* runtime_version,
|
||||
int* driver_version,
|
||||
int* multi_process,
|
||||
int* max_threads_per_mp,
|
||||
int* max_threads_per_block,
|
||||
std::array<unsigned int, 3>* max_grid_dim_size) {
|
||||
backends::gpu::GPUDeviceGuard guard(place.GetDeviceId());
|
||||
*compute_capability =
|
||||
backends::gpu::GetGPUComputeCapability(place.GetDeviceId());
|
||||
*multi_process = backends::gpu::GetGPUMultiProcessors(place.GetDeviceId());
|
||||
*max_threads_per_mp =
|
||||
backends::gpu::GetGPUMaxThreadsPerMultiProcessor(place.GetDeviceId());
|
||||
*max_grid_dim_size = backends::gpu::GetGpuMaxGridDimSize(place.GetDeviceId());
|
||||
*max_threads_per_block =
|
||||
backends::gpu::GetGPUMaxThreadsPerBlock(place.GetDeviceId());
|
||||
*driver_version = backends::gpu::GetGPUDriverVersion(place.GetDeviceId());
|
||||
*runtime_version = backends::gpu::GetGPURuntimeVersion(place.GetDeviceId());
|
||||
|
||||
const gpuDeviceProp& prop =
|
||||
backends::gpu::GetDeviceProperties(place.GetDeviceId());
|
||||
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
static const std::set<int> compiled_archs{CUDA_REAL_ARCHS};
|
||||
// Make sure compiled cuda arch is as same as runtime cuda arch.
|
||||
if (compiled_archs.find(*compute_capability) == compiled_archs.cend() &&
|
||||
compiled_archs.find(prop.major * 10) == compiled_archs.cend()) {
|
||||
static std::atomic<bool> once_flag(false);
|
||||
if (!once_flag.exchange(true)) {
|
||||
std::string compile_arch_str = "";
|
||||
for (const int32_t& arch : compiled_archs) {
|
||||
compile_arch_str += std::to_string(arch) + " ";
|
||||
}
|
||||
std::map<int, std::string> arch_computing_mapping_table = {
|
||||
{20, "Fermi"},
|
||||
{30, "Kepler"},
|
||||
{35, "Kapler"},
|
||||
{37, "Kepler"},
|
||||
{50, "Maxwell"},
|
||||
{52, "Maxwell"},
|
||||
{60, "Pascal"},
|
||||
{61, "Pascal"},
|
||||
{70, "Volta"},
|
||||
{75, "Turing"},
|
||||
{80, "Ampere"},
|
||||
{86, "Ampere"},
|
||||
{89, "Ada Lovelace"},
|
||||
{90, "Hopper"},
|
||||
{100, "Blackwell"},
|
||||
{120, "Blackwell"},
|
||||
};
|
||||
if (!arch_computing_mapping_table.count(*compute_capability)) {
|
||||
LOG(ERROR) << "Mismatched GPU Architecture: "
|
||||
<< "The installed PaddlePaddle package was compiled for "
|
||||
<< compile_arch_str << ",but your current GPU is "
|
||||
<< *compute_capability
|
||||
<< " Solution: Install the correct wheel package built for "
|
||||
"your GPU "
|
||||
<< "from the official PaddlePaddle website: "
|
||||
<< "https://www.paddlepaddle.org.cn/";
|
||||
|
||||
throw std::runtime_error("Unsupported GPU architecture");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO(wilber): glog may be replaced in the future?
|
||||
LOG_FIRST_N(WARNING, 1) << "Please NOTE: device: "
|
||||
<< static_cast<int>(place.device)
|
||||
<< ", GPU Compute Capability: "
|
||||
<< *compute_capability / 10 << "."
|
||||
<< *compute_capability % 10
|
||||
<< ", Driver API Version: " << *driver_version / 1000
|
||||
<< "." << (*driver_version % 100) / 10
|
||||
<< ", Runtime API Version: "
|
||||
<< *runtime_version / 1000 << "."
|
||||
<< (*runtime_version % 100) / 10;
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
size_t miopen_major, miopen_minor, miopen_patch;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
dynload::miopenGetVersion(&miopen_major, &miopen_minor, &miopen_patch));
|
||||
auto cudnn_dso_ver =
|
||||
(miopen_major * 1000 + miopen_minor * 10 + miopen_patch) / 10;
|
||||
auto compile_miopen_version = MIOPEN_VERSION / 10;
|
||||
if (cudnn_dso_ver < static_cast<size_t>(compile_miopen_version)) {
|
||||
LOG_FIRST_N(WARNING, 1)
|
||||
<< "WARNING: device: " << static_cast<int>(place.device)
|
||||
<< ". The installed Paddle is compiled with MIOPEN "
|
||||
<< compile_miopen_version / 100 << "." << compile_miopen_version % 100
|
||||
<< ", but MIOPEN version in your machine is " << cudnn_dso_ver / 100
|
||||
<< "." << cudnn_dso_ver % 100
|
||||
<< ", which may cause serious incompatible bug. "
|
||||
<< "Please recompile or reinstall Paddle with compatible MIOPEN "
|
||||
"version.";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitStream(gpuStream_t* stream) {
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipStreamCreateWithPriority(stream, hipStreamDefault, 0));
|
||||
#else
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaStreamCreateWithPriority(stream, cudaStreamDefault, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
void DestroyStream(gpuStream_t stream) {
|
||||
if (stream != nullptr) {
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipStreamDestroy(stream));
|
||||
#else
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(cudaStreamDestroy(stream));
|
||||
#endif
|
||||
}
|
||||
stream = nullptr;
|
||||
}
|
||||
|
||||
#ifndef PADDLE_WITH_CUSTOM_DEVICE
|
||||
void InitBlasHandle(blasHandle_t* blas_handle, gpuStream_t stream) {
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
phi::dynload::rocblas_create_handle(blas_handle);
|
||||
phi::dynload::rocblas_set_stream(*blas_handle, stream);
|
||||
#else // PADDLE_WITH_CUDA
|
||||
PADDLE_RETRY_CUDA_SUCCESS(phi::dynload::cublasCreate(blas_handle));
|
||||
PADDLE_RETRY_CUDA_SUCCESS(
|
||||
phi::dynload::cublasSetStream(*blas_handle, stream));
|
||||
#endif // PADDLE_WITH_HIP
|
||||
}
|
||||
|
||||
void DestroyBlasHandle(blasHandle_t handle) {
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
if (handle != nullptr) {
|
||||
phi::dynload::rocblas_destroy_handle(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
#else
|
||||
if (handle != nullptr) {
|
||||
phi::dynload::cublasDestroy(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
#endif // PADDLE_WITH_HIP
|
||||
}
|
||||
|
||||
void InitBlasLtHandle(blasLtHandle_t* blaslt_handle) {
|
||||
#if defined(PADDLE_WITH_CUDA) && CUDA_VERSION >= 11060
|
||||
phi::dynload::cublasLtCreate(blaslt_handle);
|
||||
#elif defined(PADDLE_WITH_HIP)
|
||||
phi::dynload::hipblasLtCreate(blaslt_handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DestroyBlasLtHandle(blasLtHandle_t handle) {
|
||||
#if defined(PADDLE_WITH_CUDA) && CUDA_VERSION >= 11060
|
||||
if (handle != nullptr) {
|
||||
phi::dynload::cublasLtDestroy(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
#elif defined(PADDLE_WITH_HIP)
|
||||
if (handle != nullptr) {
|
||||
phi::dynload::hipblasLtDestroy(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitDnnHandle(dnnHandle_t* handle, gpuStream_t stream, Place place) {
|
||||
if (phi::dynload::HasCUDNN()) {
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
size_t miopen_major, miopen_minor, miopen_patch;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
dynload::miopenGetVersion(&miopen_major, &miopen_minor, &miopen_patch));
|
||||
auto local_miopen_version =
|
||||
(miopen_major * 1000 + miopen_minor * 10 + miopen_patch) / 10;
|
||||
auto compile_miopen_version = MIOPEN_VERSION / 10;
|
||||
if (local_miopen_version < static_cast<size_t>(compile_miopen_version)) {
|
||||
LOG_FIRST_N(WARNING, 1)
|
||||
<< "WARNING: device: " << static_cast<int>(place.device)
|
||||
<< ". The installed Paddle is compiled with MIOPEN "
|
||||
<< compile_miopen_version / 100 << "." << compile_miopen_version % 100
|
||||
<< ", but MIOPEN version in your machine is "
|
||||
<< local_miopen_version / 100 << "." << local_miopen_version % 100
|
||||
<< ", which may cause serious incompatible bug. "
|
||||
<< "Please recompile or reinstall Paddle with compatible MIOPEN "
|
||||
"version.";
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(dynload::miopenCreate(handle));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(dynload::miopenSetStream(*handle, stream));
|
||||
#else
|
||||
auto version = phi::dynload::cudnnGetVersion();
|
||||
auto local_cudnn_major =
|
||||
(version < 9000) ? version / 1000 : version / 10000;
|
||||
auto local_cudnn_minor =
|
||||
(version < 9000) ? (version % 1000) / 100 : (version % 10000) / 100;
|
||||
if (version < static_cast<size_t>(CUDNN_VERSION)) {
|
||||
LOG_FIRST_N(WARNING, 1)
|
||||
<< "WARNING: device: " << static_cast<int>(place.device)
|
||||
<< ". The installed Paddle is compiled with CUDNN " << CUDNN_MAJOR
|
||||
<< "." << CUDNN_MINOR << ", but CUDNN version in your machine is "
|
||||
<< local_cudnn_major << "." << local_cudnn_minor
|
||||
<< ", which may cause serious incompatible bug. "
|
||||
<< "Please recompile or reinstall Paddle with compatible CUDNN "
|
||||
"version.";
|
||||
}
|
||||
PADDLE_RETRY_CUDA_SUCCESS(phi::dynload::cudnnCreate(handle));
|
||||
PADDLE_RETRY_CUDA_SUCCESS(phi::dynload::cudnnSetStream(*handle, stream));
|
||||
#endif
|
||||
} else {
|
||||
*handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyDnnHandle(dnnHandle_t handle) {
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
if (handle != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenDestroy(handle));
|
||||
handle = nullptr;
|
||||
}
|
||||
#else
|
||||
if (handle != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cudnnDestroy(handle));
|
||||
handle = nullptr;
|
||||
}
|
||||
#endif // PADDLE_WITH_HIP
|
||||
}
|
||||
|
||||
void InitSolverHandle(solverHandle_t* handle, gpuStream_t stream) {
|
||||
#if defined(PADDLE_WITH_CUDA)
|
||||
PADDLE_RETRY_CUDA_SUCCESS(phi::dynload::cusolverDnCreate(handle));
|
||||
PADDLE_RETRY_CUDA_SUCCESS(phi::dynload::cusolverDnSetStream(*handle, stream));
|
||||
#elif defined(PADDLE_WITH_HIP)
|
||||
phi::dynload::rocblas_create_handle(handle);
|
||||
phi::dynload::rocblas_set_stream(*handle, stream);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DestroySolverHandle(solverHandle_t solver_handle) {
|
||||
#if defined(PADDLE_WITH_CUDA)
|
||||
if (solver_handle != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cusolverDnDestroy(solver_handle));
|
||||
solver_handle = nullptr;
|
||||
}
|
||||
#elif defined(PADDLE_WITH_HIP)
|
||||
if (solver_handle != nullptr) {
|
||||
phi::dynload::rocblas_destroy_handle(solver_handle);
|
||||
solver_handle = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitSparseHandle(sparseHandle_t* handle, gpuStream_t stream) {
|
||||
#if defined(PADDLE_WITH_CUDA)
|
||||
PADDLE_RETRY_CUDA_SUCCESS(dynload::cusparseCreate(handle));
|
||||
PADDLE_RETRY_CUDA_SUCCESS(dynload::cusparseSetStream(*handle, stream));
|
||||
#elif defined(PADDLE_WITH_HIP)
|
||||
phi::dynload::rocsparse_create_handle(handle);
|
||||
phi::dynload::rocsparse_set_stream(*handle, stream);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DestroySparseHandle(sparseHandle_t handle) {
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
if (handle != nullptr) {
|
||||
PADDLE_RETRY_CUDA_SUCCESS(dynload::cusparseDestroy(handle));
|
||||
handle = nullptr;
|
||||
}
|
||||
#elif defined(PADDLE_WITH_HIP)
|
||||
if (handle != nullptr) {
|
||||
phi::dynload::rocsparse_destroy_handle(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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 <array>
|
||||
|
||||
#include "paddle/phi/backends/gpu/gpu_decls.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
#include "paddle/phi/core/device_context.h"
|
||||
|
||||
namespace phi {
|
||||
|
||||
PADDLE_API void InitGpuProperties(
|
||||
Place place,
|
||||
int* compute_capability,
|
||||
int* runtime_version,
|
||||
int* driver_version,
|
||||
int* multi_process,
|
||||
int* max_threads_per_mp,
|
||||
int* max_threads_per_block,
|
||||
std::array<unsigned int, 3>* max_grid_dim_size);
|
||||
|
||||
PADDLE_API void InitStream(gpuStream_t* stream);
|
||||
PADDLE_API void DestroyStream(gpuStream_t stream);
|
||||
|
||||
#ifndef PADDLE_WITH_CUSTOM_DEVICE
|
||||
PADDLE_API void InitBlasHandle(blasHandle_t* blas_handle, gpuStream_t stream);
|
||||
PADDLE_API void DestroyBlasHandle(blasHandle_t handle);
|
||||
|
||||
PADDLE_API void InitBlasLtHandle(blasLtHandle_t* blaslt_handle);
|
||||
PADDLE_API void DestroyBlasLtHandle(blasLtHandle_t handle);
|
||||
|
||||
PADDLE_API void InitDnnHandle(dnnHandle_t* handle,
|
||||
gpuStream_t stream,
|
||||
Place place);
|
||||
PADDLE_API void DestroyDnnHandle(dnnHandle_t handle);
|
||||
|
||||
PADDLE_API void InitSolverHandle(solverHandle_t* handle, gpuStream_t stream);
|
||||
PADDLE_API void DestroySolverHandle(solverHandle_t solver_handle);
|
||||
|
||||
PADDLE_API void InitSparseHandle(sparseHandle_t* handle, gpuStream_t stream);
|
||||
PADDLE_API void DestroySparseHandle(sparseHandle_t handle);
|
||||
#endif
|
||||
// void InitDnnWorkspace();
|
||||
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,173 @@
|
||||
// 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 "paddle/phi/backends/gpu/forwards.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_decls.h"
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#include "paddle/phi/backends/dynload/miopen.h"
|
||||
#include "paddle/phi/backends/dynload/rocblas.h"
|
||||
#else // PADDLE_WITH_CUDA
|
||||
#ifndef PADDLE_WITH_CUSTOM_DEVICE
|
||||
#include "paddle/phi/backends/dynload/cublas.h"
|
||||
#include "paddle/phi/backends/dynload/cudnn.h"
|
||||
#else
|
||||
#include <cuda_runtime.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
|
||||
// Note(qili93): CUDA Runtime API supported by HIP
|
||||
// https://github.com/ROCm/HIPIFY/blob/master/doc/markdown/CUDA_Runtime_API_functions_supported_by_HIP.md
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#define DECLARE_TYPE_FOR_GPU(GPU_TYPE, CUDA_TYPE, ROCM_TYPE) \
|
||||
using GPU_TYPE = ROCM_TYPE;
|
||||
|
||||
#else // PADDLE_WITH_CUDA
|
||||
|
||||
#define DECLARE_TYPE_FOR_GPU(GPU_TYPE, CUDA_TYPE, ROCM_TYPE) \
|
||||
using GPU_TYPE = CUDA_TYPE;
|
||||
#endif
|
||||
|
||||
DECLARE_TYPE_FOR_GPU(gpuError_t, cudaError_t, hipError_t);
|
||||
DECLARE_TYPE_FOR_GPU(gpuMemcpyKind, cudaMemcpyKind, hipMemcpyKind);
|
||||
DECLARE_TYPE_FOR_GPU(gpuDeviceProp, cudaDeviceProp, hipDeviceProp_t);
|
||||
#ifndef PADDLE_WITH_CUSTOM_DEVICE
|
||||
DECLARE_TYPE_FOR_GPU(dnnDataType_t, cudnnDataType_t, miopenDataType_t);
|
||||
DECLARE_TYPE_FOR_GPU(dnnPoolingMode_t, cudnnPoolingMode_t, miopenPoolingMode_t);
|
||||
DECLARE_TYPE_FOR_GPU(dnnTensorFormat_t,
|
||||
cudnnTensorFormat_t,
|
||||
miopenTensorFormat_t);
|
||||
DECLARE_TYPE_FOR_GPU(dnnActivationMode_t,
|
||||
cudnnActivationMode_t,
|
||||
miopenActivationMode_t);
|
||||
#endif
|
||||
DECLARE_TYPE_FOR_GPU(gpuGraph_t, cudaGraph_t, hipGraph_t);
|
||||
DECLARE_TYPE_FOR_GPU(gpuFunction_t, cudaFunction_t, hipFunction_t);
|
||||
DECLARE_TYPE_FOR_GPU(gpuGraphExec_t, cudaGraphExec_t, hipGraphExec_t);
|
||||
DECLARE_TYPE_FOR_GPU(gpuGraphNode_t, cudaGraphNode_t, hipGraphNode_t);
|
||||
DECLARE_TYPE_FOR_GPU(gpuGraphNodeType, cudaGraphNodeType, hipGraphNodeType);
|
||||
DECLARE_TYPE_FOR_GPU(gpuKernelNodeParams,
|
||||
cudaKernelNodeParams,
|
||||
hipKernelNodeParams);
|
||||
DECLARE_TYPE_FOR_GPU(gpuStreamCaptureMode,
|
||||
cudaStreamCaptureMode,
|
||||
hipStreamCaptureMode);
|
||||
DECLARE_TYPE_FOR_GPU(gpuStreamCaptureStatus,
|
||||
cudaStreamCaptureStatus,
|
||||
hipStreamCaptureStatus);
|
||||
|
||||
#undef DECLARE_TYPE_FOR_GPU
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#define DECLARE_CONSTANT_FOR_GPU(GPU_CV, CUDA_CV, ROCM_CV) \
|
||||
constexpr auto GPU_CV = ROCM_CV;
|
||||
#else // PADDLE_WITH_CUDA
|
||||
#define DECLARE_CONSTANT_FOR_GPU(GPU_CV, CUDA_CV, ROCM_CV) \
|
||||
constexpr auto GPU_CV = CUDA_CV;
|
||||
#endif
|
||||
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuErrorOutOfMemory,
|
||||
cudaErrorMemoryAllocation,
|
||||
hipErrorOutOfMemory);
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuErrorNotReady, cudaErrorNotReady, hipErrorNotReady);
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuSuccess, cudaSuccess, hipSuccess);
|
||||
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuMemcpyHostToDevice,
|
||||
cudaMemcpyKind::cudaMemcpyHostToDevice,
|
||||
hipMemcpyKind::hipMemcpyHostToDevice);
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuMemcpyDeviceToHost,
|
||||
cudaMemcpyKind::cudaMemcpyDeviceToHost,
|
||||
hipMemcpyKind::hipMemcpyDeviceToHost);
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuMemcpyDeviceToDevice,
|
||||
cudaMemcpyKind::cudaMemcpyDeviceToDevice,
|
||||
hipMemcpyKind::hipMemcpyDeviceToDevice);
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuEventDisableTiming,
|
||||
cudaEventDisableTiming,
|
||||
hipEventDisableTiming);
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuStreamNonBlocking,
|
||||
cudaStreamNonBlocking,
|
||||
hipStreamNonBlocking);
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuStreamCaptureModeThreadLocal,
|
||||
cudaStreamCaptureModeThreadLocal,
|
||||
hipStreamCaptureModeThreadLocal);
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuStreamCaptureModeRelaxed,
|
||||
cudaStreamCaptureModeRelaxed,
|
||||
hipStreamCaptureModeRelaxed);
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuStreamCaptureStatusActive,
|
||||
cudaStreamCaptureStatusActive,
|
||||
hipStreamCaptureStatusActive);
|
||||
DECLARE_CONSTANT_FOR_GPU(gpuGraphNodeTypeKernel,
|
||||
cudaGraphNodeTypeKernel,
|
||||
hipGraphNodeTypeKernel);
|
||||
|
||||
#undef DECLARE_CONSTANT_FOR_GPU
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#define DECLARE_FUNCTION_FOR_GPU(GPU_FUNC, CUDA_FUNC, ROCM_FUNC) \
|
||||
const auto GPU_FUNC = ROCM_FUNC;
|
||||
#elif defined(PADDLE_WITH_CUDA) // PADDLE_WITH_CUDA
|
||||
#define DECLARE_FUNCTION_FOR_GPU(GPU_FUNC, CUDA_FUNC, ROCM_FUNC) \
|
||||
const auto GPU_FUNC = CUDA_FUNC;
|
||||
#endif
|
||||
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuGraphGetNodes, cudaGraphGetNodes, hipGraphGetNodes);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuGraphGetEdges, cudaGraphGetEdges, hipGraphGetEdges);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuGraphLaunch, cudaGraphLaunch, hipGraphLaunch);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuGraphDestroy, cudaGraphDestroy, hipGraphDestroy);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuGraphExecDestroy,
|
||||
cudaGraphExecDestroy,
|
||||
hipGraphExecDestroy);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuGraphNodeGetType,
|
||||
cudaGraphNodeGetType,
|
||||
hipGraphNodeGetType);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuGraphExecKernelNodeSetParams,
|
||||
cudaGraphExecKernelNodeSetParams,
|
||||
hipGraphExecKernelNodeSetParams);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuGraphKernelNodeGetParams,
|
||||
cudaGraphKernelNodeGetParams,
|
||||
hipGraphKernelNodeGetParams);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuStreamCreateWithPriority,
|
||||
cudaStreamCreateWithPriority,
|
||||
hipStreamCreateWithPriority);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuStreamBeginCapture,
|
||||
cudaStreamBeginCapture,
|
||||
hipStreamBeginCapture);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuStreamEndCapture,
|
||||
cudaStreamEndCapture,
|
||||
hipStreamEndCapture);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuStreamGetCaptureInfo,
|
||||
cudaStreamGetCaptureInfo,
|
||||
hipStreamGetCaptureInfo);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuEventCreateWithFlags,
|
||||
cudaEventCreateWithFlags,
|
||||
hipEventCreateWithFlags);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuEventRecord, cudaEventRecord, hipEventRecord);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuEventDestroy, cudaEventDestroy, hipEventDestroy);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuEventQuery, cudaEventQuery, hipEventQuery);
|
||||
DECLARE_FUNCTION_FOR_GPU(gpuEventSynchronize,
|
||||
cudaEventSynchronize,
|
||||
hipEventSynchronize);
|
||||
|
||||
#undef DECLARE_FUNCTION_FOR_GPU
|
||||
|
||||
} // namespace phi
|
||||
|
||||
#endif // defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2020 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
|
||||
|
||||
#define EIGEN_USE_GPU
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "unsupported/Eigen/CXX11/Tensor"
|
||||
|
||||
namespace phi {
|
||||
namespace funcs {
|
||||
|
||||
template <typename T, int Size, T DefaultValue>
|
||||
struct DeviceArray {
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const T& operator[](int index) const {
|
||||
return data[index];
|
||||
}
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T& operator[](int index) {
|
||||
return data[index];
|
||||
}
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DeviceArray() {
|
||||
for (int i = 0; i < Size; i++) {
|
||||
data[i] = DefaultValue;
|
||||
}
|
||||
}
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DeviceArray(T a0) {
|
||||
data[0] = a0;
|
||||
for (int i = 1; i < Size; i++) {
|
||||
data[i] = DefaultValue;
|
||||
}
|
||||
}
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DeviceArray(T a0, T a1) {
|
||||
data[0] = a0;
|
||||
data[1] = a1;
|
||||
for (int i = 2; i < Size; i++) {
|
||||
data[i] = DefaultValue;
|
||||
}
|
||||
}
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DeviceArray(T a0, T a1, T a2) {
|
||||
data[0] = a0;
|
||||
data[1] = a1;
|
||||
data[2] = a2;
|
||||
for (int i = 3; i < Size; i++) {
|
||||
data[i] = DefaultValue;
|
||||
}
|
||||
}
|
||||
EIGEN_STRONG_INLINE DeviceArray(const std::array<T, Size>& sa) {
|
||||
for (int i = 0; i < Size; i++) {
|
||||
data[i] = sa[i];
|
||||
}
|
||||
}
|
||||
T data[Size];
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Dim3 : DeviceArray<T, 3, 1> {
|
||||
typedef DeviceArray<T, 3, 1> Base;
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Dim3() : Base() {}
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Dim3(T a0, T a1, T a2)
|
||||
: Base(a0, a1, a2) {}
|
||||
};
|
||||
|
||||
// Flat index with real dimension
|
||||
template <typename IndexType = int>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE IndexType
|
||||
FlatTensorIndex(const Dim3<IndexType>& index, const Dim3<IndexType>& dims) {
|
||||
IndexType flat_index = index[0];
|
||||
#pragma unroll
|
||||
for (int i = 1; i < 3; ++i) {
|
||||
flat_index = flat_index * dims[i] + index[i];
|
||||
}
|
||||
return flat_index;
|
||||
}
|
||||
|
||||
// Convert index to tensor index with dimension.
|
||||
template <typename IndexType = int>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Dim3<IndexType> ConvertTensorIndex(
|
||||
IndexType index, const Dim3<IndexType>& dims) {
|
||||
Dim3<IndexType> tensor_index;
|
||||
#pragma unroll
|
||||
for (int i = 2; i >= 0; --i) {
|
||||
IndexType new_index = index / dims[i];
|
||||
tensor_index[i] = static_cast<int>(index - dims[i] * new_index);
|
||||
index = new_index;
|
||||
}
|
||||
return tensor_index;
|
||||
}
|
||||
|
||||
} // namespace funcs
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,333 @@
|
||||
// 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.
|
||||
|
||||
#include "paddle/phi/backends/gpu/rocm/hip_graph.h"
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
COMMON_DECLARE_bool(use_cuda_malloc_async_allocator);
|
||||
COMMON_DECLARE_bool(auto_free_cudagraph_allocations_on_launch);
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
std::unique_ptr<CUDAGraph> CUDAGraph::capturing_graph_{nullptr};
|
||||
paddle::optional<std::thread::id> CUDAGraph::capturing_thread_id_{paddle::none};
|
||||
|
||||
static std::vector<hipGraphNode_t> ToposortCUDAGraph(hipGraph_t graph) {
|
||||
size_t num_nodes;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphGetNodes(graph, nullptr, &num_nodes));
|
||||
std::vector<hipGraphNode_t> nodes(num_nodes);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphGetNodes(graph, nodes.data(), &num_nodes));
|
||||
|
||||
size_t num_edges;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipGraphGetEdges(graph, nullptr, nullptr, &num_edges));
|
||||
std::vector<hipGraphNode_t> from(num_edges), to(num_edges);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipGraphGetEdges(graph, from.data(), to.data(), &num_edges));
|
||||
|
||||
std::unordered_map<hipGraphNode_t, std::unordered_set<hipGraphNode_t>>
|
||||
in_edges, out_edges;
|
||||
for (auto node : nodes) {
|
||||
in_edges[node];
|
||||
out_edges[node];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_edges; ++i) {
|
||||
in_edges[to[i]].insert(from[i]);
|
||||
out_edges[from[i]].insert(to[i]);
|
||||
}
|
||||
|
||||
std::queue<hipGraphNode_t> q;
|
||||
for (const auto &pair : in_edges) {
|
||||
if (pair.second.empty()) {
|
||||
q.push(pair.first);
|
||||
}
|
||||
}
|
||||
|
||||
nodes.clear();
|
||||
while (!q.empty()) {
|
||||
auto cur = q.front();
|
||||
q.pop();
|
||||
nodes.push_back(cur);
|
||||
|
||||
for (auto out_node : out_edges.at(cur)) {
|
||||
auto &in_nodes = in_edges.at(out_node);
|
||||
in_nodes.erase(cur);
|
||||
if (in_nodes.empty()) {
|
||||
q.push(out_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
PADDLE_ENFORCE_EQ(
|
||||
nodes.size(),
|
||||
num_nodes,
|
||||
common::errors::InvalidArgument("Toposort error, this may be a bug."));
|
||||
return nodes;
|
||||
}
|
||||
|
||||
CUDAGraphID CUDAGraph::UniqueID() {
|
||||
static std::atomic<CUDAGraphID> id;
|
||||
return id.fetch_add(1);
|
||||
}
|
||||
|
||||
int64_t CUDAGraph::UniqueMemoryPoolID() {
|
||||
static std::atomic<int64_t> id(CUDAGraph::kDefaultPoolID + 1);
|
||||
return id.fetch_add(1);
|
||||
}
|
||||
|
||||
void CUDAGraph::Reset() {
|
||||
if (is_reset_) return;
|
||||
for (auto graph : graphs_) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphDestroy(graph));
|
||||
}
|
||||
graphs_.clear();
|
||||
for (auto exec_graph : exec_graphs_) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphExecDestroy(exec_graph));
|
||||
}
|
||||
exec_graphs_.clear();
|
||||
// callback should be called in reverse order because the latter added
|
||||
// callback may rely on the former added callback.
|
||||
for (auto iter = cudagraph_post_reset_callbacks_.rbegin();
|
||||
iter != cudagraph_post_reset_callbacks_.rend();
|
||||
++iter) {
|
||||
(*iter)(*this);
|
||||
}
|
||||
cudagraph_post_reset_callbacks_.clear();
|
||||
is_reset_ = true;
|
||||
}
|
||||
|
||||
void CUDAGraph::Replay() {
|
||||
PADDLE_ENFORCE_EQ(is_reset_,
|
||||
false,
|
||||
common::errors::PermissionDenied(
|
||||
"Cannot replay the CUDA Graph after reset is called."));
|
||||
size_t n = exec_graphs_.size();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (!is_first_run_) {
|
||||
for (auto &hook : cudagraph_pre_replay_callbacks_[i]) {
|
||||
hook(exec_graphs_[i]);
|
||||
}
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphLaunch(exec_graphs_[i], stream_));
|
||||
}
|
||||
is_first_run_ = false;
|
||||
}
|
||||
|
||||
void CUDAGraph::BeginSegmentCapture() {
|
||||
ThrowErrorIfNotSupportCUDAGraph();
|
||||
PADDLE_ENFORCE_EQ(IsCapturing(),
|
||||
true,
|
||||
common::errors::PermissionDenied(
|
||||
"BeginSegmentCapture should be called when CUDA "
|
||||
"Graph is capturing."));
|
||||
if (IsThreadLocalCapturing()) {
|
||||
PADDLE_ENFORCE_EQ(IsThisThreadCapturing(),
|
||||
true,
|
||||
common::errors::PermissionDenied(
|
||||
"When capturing CUDA Graph in the thread local mode, "
|
||||
"you cannot begin segmented capturing in the thread "
|
||||
"which is not the one that starts the capturing."));
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipStreamBeginCapture(
|
||||
capturing_graph_->stream_, capturing_graph_->capture_mode_));
|
||||
PADDLE_ENFORCE_EQ(IsValidCapturing(),
|
||||
true,
|
||||
common::errors::PermissionDenied(
|
||||
"CUDA Graph should not be invalidated."));
|
||||
VLOG(10) << "Begin to capture CUDA Graph with ID " << capturing_graph_->id_
|
||||
<< ", segment id " << capturing_graph_->graphs_.size()
|
||||
<< ", memory pool id " << capturing_graph_->pool_id_;
|
||||
}
|
||||
|
||||
void CUDAGraph::BeginCapture(phi::GPUPlace place,
|
||||
gpuStream_t stream,
|
||||
hipStreamCaptureMode mode,
|
||||
bool enable_replace) {
|
||||
ThrowErrorIfNotSupportCUDAGraph();
|
||||
PADDLE_ENFORCE_EQ(IsCapturing(),
|
||||
false,
|
||||
common::errors::PermissionDenied(
|
||||
"CUDA Graph can only captured one by one."));
|
||||
PADDLE_ENFORCE_NOT_NULL(
|
||||
stream,
|
||||
common::errors::PermissionDenied(
|
||||
"CUDA Graph cannot be captured in default CUDA stream 0."));
|
||||
capturing_graph_.reset(new CUDAGraph(enable_replace));
|
||||
capturing_graph_->place_ = place;
|
||||
capturing_graph_->stream_ = stream;
|
||||
capturing_graph_->capture_mode_ = mode;
|
||||
if (mode == hipStreamCaptureModeThreadLocal) {
|
||||
capturing_thread_id_ = std::this_thread::get_id();
|
||||
VLOG(10) << "Capturing CUDA Graph in thread local mode, thread id: "
|
||||
<< capturing_thread_id_;
|
||||
}
|
||||
BeginSegmentCapture();
|
||||
}
|
||||
|
||||
void CUDAGraph::EndSegmentCapture() {
|
||||
ThrowErrorIfNotSupportCUDAGraph();
|
||||
PADDLE_ENFORCE_EQ(
|
||||
IsCapturing(),
|
||||
true,
|
||||
common::errors::PermissionDenied("No CUDA Graph is capturing."));
|
||||
hipGraph_t graph;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipStreamEndCapture(capturing_graph_->stream_, &graph));
|
||||
auto num_nodes = static_cast<size_t>(-1);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphGetNodes(graph, nullptr, &num_nodes));
|
||||
if (num_nodes == 0) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphDestroy(graph));
|
||||
VLOG(10) << "Skip empty CUDA Graph with ID " << capturing_graph_->id_
|
||||
<< ", segment id " << capturing_graph_->graphs_.size()
|
||||
<< ", memory pool id " << capturing_graph_->pool_id_;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &cudagraph_post_capture_callback :
|
||||
capturing_graph_->cudagraph_post_capture_callbacks_) {
|
||||
cudagraph_post_capture_callback();
|
||||
}
|
||||
capturing_graph_->cudagraph_post_capture_callbacks_.clear();
|
||||
|
||||
capturing_graph_->cudagraph_pre_replay_callbacks_.emplace_back(
|
||||
CUDAGraphNodeLauncher::Instance().GetParameterSettersForExecGraph(graph));
|
||||
|
||||
// if forward graph is registered, this graph is a backward graph
|
||||
// we check whether there is remain blocks that is unreleased by this
|
||||
hipGraphExec_t exec_graph;
|
||||
if (FLAGS_use_cuda_malloc_async_allocator &&
|
||||
FLAGS_auto_free_cudagraph_allocations_on_launch) {
|
||||
VLOG(1) << "hipGraphInstantiateFlagAutoFreeOnLaunch is enabled!";
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphInstantiateWithFlags(
|
||||
&exec_graph, graph, hipGraphInstantiateFlagAutoFreeOnLaunch));
|
||||
} else {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipGraphInstantiate(&exec_graph, graph, nullptr, nullptr, 0));
|
||||
}
|
||||
VLOG(10) << "End to capture CUDA Graph with ID " << capturing_graph_->id_
|
||||
<< ", segment id " << capturing_graph_->graphs_.size()
|
||||
<< ", memory pool id " << capturing_graph_->pool_id_;
|
||||
capturing_graph_->graphs_.emplace_back(graph);
|
||||
capturing_graph_->exec_graphs_.emplace_back(exec_graph);
|
||||
}
|
||||
|
||||
std::unique_ptr<CUDAGraph> CUDAGraph::EndCapture() {
|
||||
EndSegmentCapture();
|
||||
capturing_thread_id_ = paddle::none;
|
||||
return std::move(capturing_graph_);
|
||||
}
|
||||
|
||||
bool CUDAGraph::IsValidCapturing() {
|
||||
if (!IsCapturing()) return false;
|
||||
hipStreamCaptureStatus status;
|
||||
CUDAGraphID id;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipStreamGetCaptureInfo(capturing_graph_->stream_, &status, &id));
|
||||
return status == hipStreamCaptureStatusActive;
|
||||
}
|
||||
|
||||
static std::string ConcatPath(const std::string &dirname,
|
||||
const std::string &filename) {
|
||||
#ifdef _WIN32
|
||||
const std::array<char, 3> kFileSep = {"\\"};
|
||||
#else
|
||||
const std::array<char, 2> kFileSep = {"/"};
|
||||
#endif
|
||||
if (!dirname.empty() && dirname.back() == kFileSep[0]) {
|
||||
return dirname + filename;
|
||||
} else {
|
||||
return dirname + kFileSep.data() + filename;
|
||||
}
|
||||
}
|
||||
|
||||
void CUDAGraph::PrintToDotFiles(const std::string &dirname,
|
||||
unsigned int flags) {
|
||||
ThrowErrorIfNotSupportCUDAGraph();
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"The print_to_dot_files() method is not supported on ROCm/HIP"));
|
||||
}
|
||||
|
||||
void CUDAGraphNodeLauncher::KernelNodeLaunch(
|
||||
parameterSetter_t parameterSetter, gpuKernelCallback_t cudaKernelCallback) {
|
||||
if (UNLIKELY(phi::backends::gpu::CUDAGraph::IsThisThreadCapturing())) {
|
||||
unsigned int id = GenerateIdentifier();
|
||||
auto cudaFunc = cudaKernelCallback(id);
|
||||
|
||||
parameterSetters[cudaFunc][id] = parameterSetter;
|
||||
VLOG(10) << "[KernelNodeLaunch] Launch kernel with cudaFunc = " << cudaFunc
|
||||
<< " id = " << id;
|
||||
} else {
|
||||
cudaKernelCallback(0);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<cudaGraphExecuterSetter_t>
|
||||
CUDAGraphNodeLauncher::GetParameterSettersForExecGraph(hipGraph_t graph) {
|
||||
size_t num_nodes;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphGetNodes(graph, nullptr, &num_nodes));
|
||||
std::vector<hipGraphNode_t> nodes(num_nodes);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphGetNodes(graph, nodes.data(), &num_nodes));
|
||||
|
||||
std::vector<std::function<void(hipGraphExec_t)>> hooks;
|
||||
for (auto node : nodes) {
|
||||
hipGraphNode_t gpuNode = node;
|
||||
hipGraphNodeType pType;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphNodeGetType(gpuNode, &pType));
|
||||
if (pType == hipGraphNodeTypeKernel) {
|
||||
hipKernelNodeParams gpuParams;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
gpuGraphKernelNodeGetParams(gpuNode, &gpuParams));
|
||||
gpuKernelParams kernel_params(gpuParams.kernelParams);
|
||||
auto kernel =
|
||||
parameterSetters.find(static_cast<gpuFunction_t>(gpuParams.func));
|
||||
VLOG(10) << "[GetParameterSettersForExecGraph] gpuParams.func = "
|
||||
<< gpuParams.func;
|
||||
// There exists a parameter setter
|
||||
if (kernel != parameterSetters.end()) {
|
||||
auto launchSequence = kernel->second;
|
||||
unsigned int id = kernel_params.As<int>(0);
|
||||
|
||||
VLOG(10) << "[GetParameterSettersForExecGraph] Find launch kernel id = "
|
||||
<< id;
|
||||
auto parameterSetter = launchSequence.find(id);
|
||||
if (parameterSetter != launchSequence.end()) {
|
||||
auto setter = parameterSetter->second;
|
||||
hooks.emplace_back(
|
||||
[setter, gpuNode, gpuParams](hipGraphExec_t exec_graph) {
|
||||
gpuKernelParams kernel_params(gpuParams.kernelParams);
|
||||
setter(kernel_params);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGraphExecKernelNodeSetParams(
|
||||
exec_graph, gpuNode, &gpuParams));
|
||||
});
|
||||
} else {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Error: does not find launch id"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hooks;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,374 @@
|
||||
// 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 <array>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/backends/context_pool.h"
|
||||
#include "paddle/phi/backends/device_code.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_context.h"
|
||||
#include "paddle/phi/common/memory_utils.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/utils/optional.h"
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
class CUDAGraphContextManager {
|
||||
public:
|
||||
using DeviceContextMap =
|
||||
std::map<Place, std::shared_future<std::unique_ptr<DeviceContext>>>;
|
||||
|
||||
static CUDAGraphContextManager &Instance() {
|
||||
static CUDAGraphContextManager *cuda_graph_ctx_manager =
|
||||
new CUDAGraphContextManager;
|
||||
return *cuda_graph_ctx_manager;
|
||||
}
|
||||
|
||||
DeviceContext *Get(int64_t pool_id, const Place &place, int stream_priority) {
|
||||
std::lock_guard<std::mutex> lk(ctx_mtx_);
|
||||
VLOG(6) << "Get cuda graph device context for " << place;
|
||||
|
||||
DeviceContextMap &ctxs = cuda_graph_ctx_pool_[pool_id];
|
||||
if (ctxs.find(place) == ctxs.end()) {
|
||||
phi::memory_utils::EmplaceDeviceContexts(
|
||||
&ctxs,
|
||||
{place},
|
||||
/*disable_setting_default_stream_for_allocator=*/true,
|
||||
stream_priority);
|
||||
}
|
||||
return ctxs[place].get().get();
|
||||
}
|
||||
|
||||
void RecordCapturingDeviceContext(DeviceContext *dev_ctx) {
|
||||
capturing_ctxs_.insert(dev_ctx);
|
||||
}
|
||||
|
||||
std::set<DeviceContext *> GetAllCapturingDeviceContexts() const {
|
||||
return capturing_ctxs_;
|
||||
}
|
||||
|
||||
void ClearDeviceContextsRecords() { capturing_ctxs_.clear(); }
|
||||
|
||||
private:
|
||||
CUDAGraphContextManager() {}
|
||||
DISABLE_COPY_AND_ASSIGN(CUDAGraphContextManager);
|
||||
|
||||
std::mutex ctx_mtx_;
|
||||
std::unordered_map<int64_t, DeviceContextMap> cuda_graph_ctx_pool_;
|
||||
std::set<DeviceContext *> capturing_ctxs_;
|
||||
};
|
||||
|
||||
class gpuKernelParams {
|
||||
public:
|
||||
explicit gpuKernelParams(void **params) : kernelParams(params) {}
|
||||
|
||||
template <typename T>
|
||||
T &As(size_t idx) const {
|
||||
return *reinterpret_cast<T *>(kernelParams[idx]);
|
||||
}
|
||||
|
||||
void **getParams() const { return kernelParams; }
|
||||
|
||||
private:
|
||||
void **kernelParams;
|
||||
};
|
||||
|
||||
using cudaGraphExecuterSetter_t = std::function<void(hipGraphExec_t)>;
|
||||
|
||||
// ** class CUDAGraphNodeLauncher
|
||||
//
|
||||
// This class offers a interface for launching CUDA kernels in CUDA Graph, we
|
||||
// utilize the `cudaGraphExecKernelNodeSetParams` function for parameter setup.
|
||||
// Launching kernels via this class ensures proper management.
|
||||
//
|
||||
// NOTE: It's essential that the first parameter for any kernel launched
|
||||
// through this class is an `unsigned int` identifier. This identifier plays a
|
||||
// crucial role in linking the CUDA kernel to its corresponding CUDA graph
|
||||
// node. We tag each kernel launch with a unique identifier to maintain
|
||||
// structured linkage with its CUDA graph node.
|
||||
//
|
||||
// NOTE: This class use a singleton design pattern ensures there's only a
|
||||
// single global instance accessible via the `Instance()` method.
|
||||
class CUDAGraphNodeLauncher {
|
||||
public:
|
||||
// [Parameter Setter Callback]
|
||||
// Sets the kernel's parameters BEFORE activating the CUDA graph. It enables
|
||||
// dynamic determination and setup of kernel arguments.
|
||||
//
|
||||
// parameterSetter_t parameterSetter = [saved_state](gpuKernelParams
|
||||
// ¶m){
|
||||
// // Code to compute and the parameter values from the saved_state
|
||||
// // ...
|
||||
// param.As<type>(idx) = calculated_value;
|
||||
// };
|
||||
using parameterSetter_t = std::function<void(gpuKernelParams &)>;
|
||||
|
||||
// [CUDA Kernel Callback]
|
||||
// Acts as the launcher for the kernel. It accepts an `unsigned int`
|
||||
// identifier and uses it for the kernel launch.
|
||||
// The `cudaGetFuncBySymbol` method can be used to fetch the `cudaFunction_t`
|
||||
// reference of the kernel from the kernel pointer.
|
||||
// gpuKernelCallback_t cudaKernelCallback = [=](unsigned int id) {
|
||||
// // cudaFunction_t is REQUIRED to get here
|
||||
// cudaFunction_t cudaFunc;
|
||||
// PADDLE_ENFORCE_GPU_SUCCESS(cudaGetFuncBySymbol(&cudaFunc, &kernel));
|
||||
//
|
||||
// kernel<<<>>>(id, ...); // Launching the kernel with id
|
||||
// return cudaFunc;
|
||||
// };
|
||||
using gpuKernelCallback_t = std::function<hipFunction_t(unsigned int)>;
|
||||
|
||||
// [Kernel Launch]
|
||||
// With the callbacks defined and the CUDA function obtained, the kernel can
|
||||
// be launched using the `KernelNodeLaunch` method.
|
||||
void KernelNodeLaunch(parameterSetter_t parameterSetter,
|
||||
gpuKernelCallback_t cudaKernelCallback);
|
||||
|
||||
std::vector<cudaGraphExecuterSetter_t> GetParameterSettersForExecGraph(
|
||||
hipGraph_t graph);
|
||||
|
||||
parameterSetter_t GetParameterSetter(const gpuKernelParams ¶ms);
|
||||
|
||||
static CUDAGraphNodeLauncher &Instance() {
|
||||
static CUDAGraphNodeLauncher *launcher = new CUDAGraphNodeLauncher;
|
||||
return *launcher;
|
||||
}
|
||||
|
||||
private:
|
||||
CUDAGraphNodeLauncher() : id(0) {}
|
||||
DISABLE_COPY_AND_ASSIGN(CUDAGraphNodeLauncher);
|
||||
|
||||
unsigned int GenerateIdentifier() { return id++; }
|
||||
|
||||
unsigned int id;
|
||||
std::unordered_map<hipFunction_t, std::map<unsigned int, parameterSetter_t>>
|
||||
parameterSetters;
|
||||
};
|
||||
|
||||
static void ThrowErrorIfNotSupportCUDAGraph() {}
|
||||
|
||||
using CUDAGraphID = unsigned long long; // NOLINT
|
||||
|
||||
// NOTE: Currently, we do not support to capture CUDA graph in parallel
|
||||
// NOTE: Do not use this class directly because it should be used with
|
||||
// the memory pool.
|
||||
class CUDAGraph {
|
||||
DISABLE_COPY_AND_ASSIGN(CUDAGraph);
|
||||
|
||||
// Since the constructor would throw error is CUDA_VERSION < 10010.
|
||||
// The non-static method of CUDAGraph need not check CUDA_VERSION
|
||||
// again.
|
||||
explicit CUDAGraph(bool enable_replace = false)
|
||||
: enable_replace_(enable_replace) {
|
||||
ThrowErrorIfNotSupportCUDAGraph();
|
||||
id_ = UniqueID();
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr int64_t kDefaultPoolID = 0;
|
||||
static constexpr int64_t kInvalidPoolID = -1;
|
||||
|
||||
~CUDAGraph() { Reset(); }
|
||||
|
||||
CUDAGraphID ID() const { return id_; }
|
||||
|
||||
static int64_t SetMemoryPoolID(int64_t pool_id) {
|
||||
auto &pool_id_ = capturing_graph_->pool_id_;
|
||||
PADDLE_ENFORCE_EQ(pool_id_,
|
||||
kInvalidPoolID,
|
||||
common::errors::InvalidArgument(
|
||||
"Cannot reset memory pool id twice, the "
|
||||
"former memory pool id is %d.",
|
||||
pool_id_));
|
||||
if (pool_id <= kInvalidPoolID) {
|
||||
pool_id_ = UniqueMemoryPoolID();
|
||||
} else {
|
||||
PADDLE_ENFORCE_GE(pool_id,
|
||||
kDefaultPoolID,
|
||||
common::errors::InvalidArgument(
|
||||
"Invalid memory pool id %d.", pool_id));
|
||||
pool_id_ = pool_id;
|
||||
}
|
||||
return pool_id_;
|
||||
}
|
||||
|
||||
int64_t PoolID() const { return pool_id_; }
|
||||
|
||||
static int64_t CapturingPoolID() { return capturing_graph_->pool_id_; }
|
||||
|
||||
void Replay();
|
||||
|
||||
void Reset();
|
||||
|
||||
void AddPostResetCallback(
|
||||
std::function<void(paddle::optional<const CUDAGraph &>)> callback) {
|
||||
std::lock_guard<std::mutex> guard(mtx_);
|
||||
cudagraph_post_reset_callbacks_.push_back(std::move(callback));
|
||||
}
|
||||
|
||||
void AddPostCaptureCallback(std::function<void()> callback) {
|
||||
std::lock_guard<std::mutex> guard(mtx_);
|
||||
cudagraph_post_capture_callbacks_.push_back(std::move(callback));
|
||||
}
|
||||
|
||||
void PrintToDotFiles(const std::string &dirname, unsigned int flags);
|
||||
|
||||
static void BeginCapture(phi::GPUPlace place,
|
||||
gpuStream_t stream,
|
||||
gpuStreamCaptureMode mode,
|
||||
bool enable_replace = false);
|
||||
static std::unique_ptr<CUDAGraph> EndCapture();
|
||||
|
||||
static void BeginSegmentCapture();
|
||||
static void EndSegmentCapture();
|
||||
|
||||
static void AddPostResetCallbackDuringCapturing(
|
||||
std::function<void(paddle::optional<const CUDAGraph &>)> callback) {
|
||||
capturing_graph_->AddPostResetCallback(std::move(callback));
|
||||
}
|
||||
|
||||
static void AddPostCaptureCallbackDuringCapturing(
|
||||
std::function<void()> callback) {
|
||||
capturing_graph_->AddPostCaptureCallback(std::move(callback));
|
||||
}
|
||||
|
||||
// No need to add CUDA_VERSION macro because capturing_graph_ would
|
||||
// always be nullptr (constructor throws error)
|
||||
static bool IsCapturing() { return capturing_graph_ != nullptr; }
|
||||
|
||||
static CUDAGraphID CapturingID() { return capturing_graph_->id_; }
|
||||
|
||||
static phi::GPUPlace CapturingPlace() { return capturing_graph_->place_; }
|
||||
|
||||
// This API can be used to debug which GPU operation is not
|
||||
// supported during capturing CUDA Graph.
|
||||
static bool IsValidCapturing();
|
||||
|
||||
static bool IsThreadLocalCapturing() {
|
||||
return IsCapturing() &&
|
||||
capturing_graph_->capture_mode_ == hipStreamCaptureModeThreadLocal;
|
||||
}
|
||||
|
||||
static bool IsThisThreadCapturing() {
|
||||
if (UNLIKELY(IsCapturing())) {
|
||||
return IsThreadLocalCapturing()
|
||||
? capturing_thread_id_.get() == std::this_thread::get_id()
|
||||
: true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
using SetSeedFunc = std::function<bool(gpuKernelParams *, bool)>;
|
||||
static void RecordRandomKernelInfo(SetSeedFunc set_seed_func) {
|
||||
std::lock_guard<std::mutex> guard(capturing_graph_->func_mtx_);
|
||||
capturing_graph_->set_seed_funcs_.emplace_back(std::move(set_seed_func));
|
||||
}
|
||||
|
||||
static int64_t UniqueMemoryPoolID();
|
||||
|
||||
private:
|
||||
static CUDAGraphID UniqueID();
|
||||
|
||||
private:
|
||||
std::vector<hipGraph_t> graphs_;
|
||||
std::vector<hipGraphExec_t> exec_graphs_;
|
||||
gpuStreamCaptureMode capture_mode_;
|
||||
gpuStream_t stream_{nullptr};
|
||||
phi::GPUPlace place_;
|
||||
CUDAGraphID id_;
|
||||
int64_t pool_id_{kInvalidPoolID};
|
||||
bool is_reset_{false};
|
||||
bool enable_replace_{false};
|
||||
std::mutex mtx_;
|
||||
|
||||
std::vector<SetSeedFunc> set_seed_funcs_;
|
||||
|
||||
// Holds callbacks that are triggered after the CUDA graph is reset. These
|
||||
// callbacks are used for operations that need to be performed following the
|
||||
// reset of a CUDA graph.
|
||||
std::vector<std::function<void(paddle::optional<const CUDAGraph &>)>>
|
||||
cudagraph_post_reset_callbacks_;
|
||||
|
||||
// Contains callbacks that are invoked after the CUDA graph has been captured.
|
||||
// These callbacks are crucial for managing memory allocations related to the
|
||||
// CUDA graph. They ensure that memory blocks not associated with a graph (as
|
||||
// detailed in cuda_malloc_async_allocator) are not erroneously released
|
||||
// during the graph's lifecycle.
|
||||
std::vector<std::function<void()>> cudagraph_post_capture_callbacks_;
|
||||
|
||||
// Maintains a collection of 'pre-hooks' - functions that are executed before
|
||||
// the CUDA graph is replayed. These pre-hooks are essential for setting up
|
||||
// the necessary conditions or states required for the correct execution of
|
||||
// the CUDA graph.
|
||||
std::vector<std::vector<cudaGraphExecuterSetter_t>>
|
||||
cudagraph_pre_replay_callbacks_;
|
||||
|
||||
std::mutex func_mtx_;
|
||||
|
||||
bool is_first_run_{true};
|
||||
|
||||
static paddle::optional<std::thread::id> capturing_thread_id_;
|
||||
static std::unique_ptr<CUDAGraph> capturing_graph_;
|
||||
};
|
||||
|
||||
class CUDAGraphCaptureModeGuard {
|
||||
DISABLE_COPY_AND_ASSIGN(CUDAGraphCaptureModeGuard);
|
||||
|
||||
public:
|
||||
explicit CUDAGraphCaptureModeGuard(
|
||||
gpuStreamCaptureMode mode = hipStreamCaptureModeRelaxed) {
|
||||
if (UNLIKELY(CUDAGraph::IsCapturing())) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipThreadExchangeStreamCaptureMode(&mode));
|
||||
// After cudaThreadExchangeStreamCaptureMode is called,
|
||||
// the variable "mode" would be set to the old capturing mode.
|
||||
old_mode_ = mode;
|
||||
}
|
||||
}
|
||||
|
||||
~CUDAGraphCaptureModeGuard() PADDLE_MAY_THROW {
|
||||
if (UNLIKELY(CUDAGraph::IsCapturing())) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipThreadExchangeStreamCaptureMode(&old_mode_));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
gpuStreamCaptureMode old_mode_;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,267 @@
|
||||
// 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 <algorithm>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/backends/gpu/rocm/miopen_helper.h"
|
||||
#include "paddle/phi/core/utils/data_type.h"
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
inline std::vector<int> TransformDimOrder(const std::vector<int>& dims) {
|
||||
std::vector<int> transformed_dims(dims.begin(), dims.end());
|
||||
int H, W, D, C;
|
||||
if (dims.size() == 4) {
|
||||
H = dims[1];
|
||||
W = dims[2];
|
||||
C = dims[3];
|
||||
transformed_dims[1] = C;
|
||||
transformed_dims[2] = H;
|
||||
transformed_dims[3] = W;
|
||||
} else {
|
||||
D = dims[1];
|
||||
H = dims[2];
|
||||
W = dims[3];
|
||||
C = dims[4];
|
||||
transformed_dims[1] = C;
|
||||
transformed_dims[2] = D;
|
||||
transformed_dims[3] = H;
|
||||
transformed_dims[4] = W;
|
||||
}
|
||||
return transformed_dims;
|
||||
}
|
||||
|
||||
inline miopenDataType_t ToCudnnDataType(const DataType& t) {
|
||||
miopenDataType_t type = miopenFloat;
|
||||
switch (t) {
|
||||
case DataType::FLOAT16:
|
||||
type = miopenHalf;
|
||||
break;
|
||||
case DataType::FLOAT32:
|
||||
type = miopenFloat;
|
||||
break;
|
||||
case DataType::BFLOAT16:
|
||||
type = miopenBFloat16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
class ActivationDescriptor {
|
||||
public:
|
||||
using T = miopenActivationDescriptor;
|
||||
struct Deleter {
|
||||
void operator()(T* t) {
|
||||
if (t != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyActivationDescriptor(t));
|
||||
t = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
ActivationDescriptor() {
|
||||
T* raw_ptr;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreateActivationDescriptor(&raw_ptr));
|
||||
desc_.reset(raw_ptr);
|
||||
}
|
||||
template <typename T>
|
||||
void set(miopenActivationMode_t mode, const T& coef) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetActivationDescriptor(
|
||||
desc_.get(), mode, static_cast<double>(coef), 0.0, 0.0));
|
||||
}
|
||||
|
||||
T* desc() { return desc_.get(); }
|
||||
T* desc() const { return desc_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<T, Deleter> desc_;
|
||||
};
|
||||
|
||||
class TensorDescriptor {
|
||||
public:
|
||||
using T = miopenTensorDescriptor;
|
||||
struct Deleter {
|
||||
void operator()(T* t) {
|
||||
if (t != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyTensorDescriptor(t));
|
||||
t = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
TensorDescriptor() {
|
||||
T* raw_ptr;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreateTensorDescriptor(&raw_ptr));
|
||||
desc_.reset(raw_ptr);
|
||||
}
|
||||
T* desc() { return desc_.get(); }
|
||||
T* desc() const { return desc_.get(); }
|
||||
|
||||
void set(const DenseTensor& tensor, const int groups = 1) {
|
||||
auto dims = common::vectorize<int>(tensor.dims());
|
||||
std::vector<int> strides(dims.size());
|
||||
strides[dims.size() - 1] = 1;
|
||||
for (int i = dims.size() - 2; i >= 0; i--) {
|
||||
strides[i] = dims[i + 1] * strides[i + 1];
|
||||
}
|
||||
std::vector<int> dims_with_group(dims.begin(), dims.end());
|
||||
if (groups > 1) {
|
||||
dims_with_group[1] = dims_with_group[1] / groups;
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetTensorDescriptor(
|
||||
(miopenTensorDescriptor_t)(desc_.get()),
|
||||
ToCudnnDataType(tensor.dtype()),
|
||||
static_cast<int>(dims_with_group.size()),
|
||||
const_cast<int*>(dims_with_group.data()),
|
||||
const_cast<int*>(strides.data())));
|
||||
}
|
||||
|
||||
void set(const DenseTensor& tensor, const miopenTensorFormat_t format) {
|
||||
const int groups = 1;
|
||||
PADDLE_ENFORCE_EQ(format,
|
||||
MIOPEN_TENSOR_NCHW,
|
||||
common::errors::InvalidArgument(
|
||||
"format should ONLY be NCHW in MIOPEN."));
|
||||
auto dims = common::vectorize<int>(tensor.dims());
|
||||
std::vector<int> strides(dims.size());
|
||||
strides[dims.size() - 1] = 1;
|
||||
for (int i = dims.size() - 2; i >= 0; i--) {
|
||||
strides[i] = dims[i + 1] * strides[i + 1];
|
||||
}
|
||||
std::vector<int> dims_with_group(dims.begin(), dims.end());
|
||||
if (groups > 1) {
|
||||
dims_with_group[1] = dims_with_group[1] / groups;
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetTensorDescriptor(
|
||||
(miopenTensorDescriptor_t)(desc_.get()),
|
||||
ToCudnnDataType(tensor.dtype()),
|
||||
static_cast<int>(dims_with_group.size()),
|
||||
const_cast<int*>(dims_with_group.data()),
|
||||
const_cast<int*>(strides.data())));
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<T, Deleter> desc_;
|
||||
};
|
||||
|
||||
class FilterDescriptor {
|
||||
public:
|
||||
using T = miopenTensorDescriptor;
|
||||
struct Deleter {
|
||||
void operator()(T* t) {
|
||||
if (t != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyTensorDescriptor(t));
|
||||
t = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
FilterDescriptor() {
|
||||
T* raw_ptr;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreateTensorDescriptor(&raw_ptr));
|
||||
desc_.reset(raw_ptr);
|
||||
}
|
||||
T* desc() { return desc_.get(); }
|
||||
T* desc() const { return desc_.get(); }
|
||||
|
||||
void set(const DenseTensor& tensor,
|
||||
const miopenTensorFormat_t format,
|
||||
const int groups = 1) {
|
||||
PADDLE_ENFORCE_EQ(format,
|
||||
MIOPEN_TENSOR_NCHW,
|
||||
common::errors::InvalidArgument(
|
||||
"format should ONLY be NCHW in MIOPEN."));
|
||||
auto dims = common::vectorize<int>(tensor.dims());
|
||||
std::vector<int> strides(dims.size());
|
||||
strides[dims.size() - 1] = 1;
|
||||
for (int i = dims.size() - 2; i >= 0; i--) {
|
||||
strides[i] = dims[i + 1] * strides[i + 1];
|
||||
}
|
||||
std::vector<int> dims_with_group(dims.begin(), dims.end());
|
||||
if (groups > 1) {
|
||||
dims_with_group[1] = dims_with_group[1] / groups;
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetTensorDescriptor(
|
||||
(miopenTensorDescriptor_t)(desc_.get()),
|
||||
ToCudnnDataType(tensor.dtype()),
|
||||
static_cast<int>(dims_with_group.size()),
|
||||
const_cast<int*>(dims_with_group.data()),
|
||||
const_cast<int*>(strides.data())));
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<T, Deleter> desc_;
|
||||
};
|
||||
|
||||
class ConvolutionDescriptor {
|
||||
public:
|
||||
using T = miopenConvolutionDescriptor;
|
||||
struct Deleter {
|
||||
void operator()(T* t) {
|
||||
if (t != nullptr) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyConvolutionDescriptor(t));
|
||||
t = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
ConvolutionDescriptor() {
|
||||
T* raw_ptr;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreateConvolutionDescriptor(&raw_ptr));
|
||||
desc_.reset(raw_ptr);
|
||||
}
|
||||
T* desc() { return desc_.get(); }
|
||||
T* desc() const { return desc_.get(); }
|
||||
|
||||
void set(miopenDataType_t dtype,
|
||||
const std::vector<int>& pads,
|
||||
const std::vector<int>& strides,
|
||||
const std::vector<int>& dilations,
|
||||
bool allow_tf32,
|
||||
const int groups = 1) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenInitConvolutionNdDescriptor(
|
||||
(miopenConvolutionDescriptor_t)desc_.get(),
|
||||
static_cast<int>(pads.size()),
|
||||
const_cast<int*>(pads.data()),
|
||||
const_cast<int*>(strides.data()),
|
||||
const_cast<int*>(dilations.data()),
|
||||
miopenConvolution));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetConvolutionGroupCount(
|
||||
(miopenConvolutionDescriptor_t)desc_.get(), groups));
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<T, Deleter> desc_;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,590 @@
|
||||
/* 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 <string>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/backends/dynload/miopen.h"
|
||||
#include "paddle/phi/common/bfloat16.h"
|
||||
#include "paddle/phi/common/float16.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
// MIOPEN do not have epsilon definition
|
||||
#define CUDNN_BN_MIN_EPSILON 1e-05
|
||||
|
||||
COMMON_DECLARE_bool(cudnn_deterministic);
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
inline const char* miopenGetErrorString(miopenStatus_t status) {
|
||||
switch (status) {
|
||||
case miopenStatusSuccess:
|
||||
return "miopenStatusSuccess";
|
||||
case miopenStatusNotInitialized:
|
||||
return "miopenStatusNotInitialized";
|
||||
case miopenStatusAllocFailed:
|
||||
return "miopenStatusAllocFailed";
|
||||
case miopenStatusBadParm: // typos: disable-line
|
||||
return "miopenStatusBadParm"; // typos: disable-line
|
||||
case miopenStatusInternalError:
|
||||
return "miopenStatusInternalError";
|
||||
case miopenStatusInvalidValue:
|
||||
return "miopenStatusInvalidValue";
|
||||
case miopenStatusUnknownError:
|
||||
return "miopenStatusUnknownError";
|
||||
case miopenStatusNotImplemented:
|
||||
return "miopenStatusNotImplemented";
|
||||
default:
|
||||
return "Unknown miopen error number";
|
||||
}
|
||||
}
|
||||
|
||||
// no use, but will have compiling error if not defined
|
||||
#define CUDNN_VERSION_COMPUTE(major, minor, patch) \
|
||||
((major) <= 8 ? (major)*1000 + (minor)*100 + (patch) \
|
||||
: (major)*10000 + (minor)*100 + (patch))
|
||||
|
||||
#define CUDNN_VERSION_MIN(major, minor, patch) \
|
||||
(CUDNN_VERSION >= CUDNN_VERSION_COMPUTE(major, minor, patch))
|
||||
|
||||
enum class PoolingMode {
|
||||
kMaximum,
|
||||
kMaximumDeterministic,
|
||||
kAverageExclusive,
|
||||
kAverageInclusive,
|
||||
};
|
||||
|
||||
enum class ActivationMode {
|
||||
kNone, // activation identity
|
||||
kSigmoid,
|
||||
kRelu,
|
||||
kRelu6,
|
||||
kReluX,
|
||||
kTanh,
|
||||
kBandPass,
|
||||
};
|
||||
|
||||
inline miopenPoolingMode_t GetPoolingMode(const PoolingMode& mode) {
|
||||
switch (mode) {
|
||||
case PoolingMode::kMaximumDeterministic:
|
||||
return miopenPoolingMax;
|
||||
case PoolingMode::kAverageExclusive:
|
||||
return miopenPoolingAverage;
|
||||
case PoolingMode::kAverageInclusive:
|
||||
return miopenPoolingAverageInclusive;
|
||||
case PoolingMode::kMaximum:
|
||||
return miopenPoolingMax;
|
||||
default:
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("Unexpected MIOPEN pooling mode."));
|
||||
}
|
||||
}
|
||||
|
||||
inline ActivationMode StringToActivationMode(const std::string& str) {
|
||||
if (str == "identity") {
|
||||
return ActivationMode::kNone;
|
||||
} else if (str == "sigmoid") {
|
||||
return ActivationMode::kSigmoid;
|
||||
} else if (str == "relu") {
|
||||
return ActivationMode::kRelu;
|
||||
} else if (str == "relu6") {
|
||||
return ActivationMode::kRelu6;
|
||||
} else if (str == "relux") {
|
||||
return ActivationMode::kReluX;
|
||||
} else if (str == "tanh") {
|
||||
return ActivationMode::kTanh;
|
||||
} else if (str == "bandpass") {
|
||||
return ActivationMode::kBandPass;
|
||||
} else {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Unknown MIOPEN activation string: %s.", str));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class CudnnDataType;
|
||||
|
||||
template <>
|
||||
class CudnnDataType<phi::dtype::float16> {
|
||||
public:
|
||||
static const miopenDataType_t type = miopenHalf;
|
||||
// The scaling param type is float for HALF and FLOAT tensors
|
||||
using ScalingParamType = const float;
|
||||
using BatchNormParamType = float;
|
||||
static ScalingParamType* kOne() {
|
||||
static ScalingParamType v = 1.0;
|
||||
return &v;
|
||||
}
|
||||
static ScalingParamType* kZero() {
|
||||
static ScalingParamType v = 0.0;
|
||||
return &v;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class CudnnDataType<phi::dtype::bfloat16> {
|
||||
public:
|
||||
static const miopenDataType_t type = miopenBFloat16;
|
||||
// The scaling param type is float for HALF and FLOAT tensors
|
||||
using ScalingParamType = const float;
|
||||
using BatchNormParamType = float;
|
||||
static ScalingParamType* kOne() {
|
||||
static ScalingParamType v = 1.0;
|
||||
return &v;
|
||||
}
|
||||
static ScalingParamType* kZero() {
|
||||
static ScalingParamType v = 0.0;
|
||||
return &v;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class CudnnDataType<float> {
|
||||
public:
|
||||
static const miopenDataType_t type = miopenFloat;
|
||||
using ScalingParamType = const float;
|
||||
using BatchNormParamType = float;
|
||||
static ScalingParamType* kOne() {
|
||||
static ScalingParamType v = 1.0;
|
||||
return &v;
|
||||
}
|
||||
static ScalingParamType* kZero() {
|
||||
static ScalingParamType v = 0.0;
|
||||
return &v;
|
||||
}
|
||||
};
|
||||
|
||||
inline miopenTensorFormat_t GetCudnnTensorFormat(const DataLayout& order) {
|
||||
switch (order) {
|
||||
case DataLayout::NHWC:
|
||||
return MIOPEN_TENSOR_NHWC;
|
||||
case DataLayout::NCHW:
|
||||
return MIOPEN_TENSOR_NCHW;
|
||||
case DataLayout::NCDHW:
|
||||
return MIOPEN_TENSOR_NCHW;
|
||||
case DataLayout::NDHWC:
|
||||
return MIOPEN_TENSOR_NHWC;
|
||||
default:
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"MIOPEN has no equivalent dataLayout for input order."));
|
||||
}
|
||||
return MIOPEN_TENSOR_NCHW;
|
||||
}
|
||||
class ScopedTensorDescriptor {
|
||||
public:
|
||||
ScopedTensorDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreateTensorDescriptor(&desc_));
|
||||
}
|
||||
~ScopedTensorDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyTensorDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline miopenTensorDescriptor_t descriptor(const miopenTensorFormat_t format,
|
||||
const miopenDataType_t type,
|
||||
const std::vector<int>& dims,
|
||||
const int groups = 1) {
|
||||
// the format is not used now, will add later
|
||||
std::vector<int> strides(dims.size());
|
||||
strides[dims.size() - 1] = 1;
|
||||
for (int i = dims.size() - 2; i >= 0; i--) {
|
||||
strides[i] = dims[i + 1] * strides[i + 1];
|
||||
}
|
||||
// Update tensor descriptor dims setting if groups > 1
|
||||
// NOTE: Here, Assume using NCHW or NCDHW order
|
||||
std::vector<int> dims_with_group(dims.begin(), dims.end());
|
||||
if (groups > 1) {
|
||||
dims_with_group[1] = dims_with_group[1] / groups;
|
||||
}
|
||||
|
||||
// MIOPEN ONLY support data layout of NCHW
|
||||
PADDLE_ENFORCE_EQ(format,
|
||||
MIOPEN_TENSOR_NCHW,
|
||||
common::errors::InvalidArgument(
|
||||
"format should ONLY be NCHW in MIOPEN."));
|
||||
if (dims.size() == 4) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetTensorDescriptor(
|
||||
desc_,
|
||||
type,
|
||||
dims_with_group.size(),
|
||||
const_cast<int*>(dims_with_group.data()),
|
||||
const_cast<int*>(strides.data())));
|
||||
} else if (dims.size() == 5) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetTensorDescriptor(
|
||||
desc_,
|
||||
type,
|
||||
dims_with_group.size(),
|
||||
const_cast<int*>(dims_with_group.data()),
|
||||
const_cast<int*>(strides.data())));
|
||||
}
|
||||
return desc_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline miopenTensorDescriptor_t descriptor(const DataLayout& order,
|
||||
const std::vector<int>& dims,
|
||||
const int groups = 1) {
|
||||
return descriptor(
|
||||
GetCudnnTensorFormat(order), CudnnDataType<T>::type, dims, groups);
|
||||
}
|
||||
|
||||
inline miopenTensorDescriptor_t descriptor(const miopenDataType_t miopen_type,
|
||||
const std::vector<int>& dim,
|
||||
const std::vector<int>& stride) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetTensorDescriptor(
|
||||
desc_,
|
||||
miopen_type,
|
||||
dim.size(),
|
||||
const_cast<int*>(dim.data()),
|
||||
const_cast<int*>(stride.data())));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline miopenTensorDescriptor_t descriptor(const std::vector<int>& dim,
|
||||
const std::vector<int>& stride) {
|
||||
return descriptor(CudnnDataType<T>::type, dim, stride);
|
||||
}
|
||||
|
||||
inline miopenTensorDescriptor_t desc() { return desc_; }
|
||||
|
||||
private:
|
||||
miopenTensorDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedTensorDescriptor);
|
||||
};
|
||||
|
||||
class ScopedDropoutDescriptor {
|
||||
public:
|
||||
ScopedDropoutDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreateDropoutDescriptor(&desc_));
|
||||
}
|
||||
~ScopedDropoutDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyDropoutDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline miopenDropoutDescriptor_t descriptor(const miopenHandle_t& handle,
|
||||
const Place& place,
|
||||
bool initialized,
|
||||
float dropout_prob_,
|
||||
DenseTensor* dropout_state_,
|
||||
int seed,
|
||||
size_t state_size) {
|
||||
if (dropout_state_ == nullptr) { // for no dropout or test
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenSetDropoutDescriptor(desc_,
|
||||
handle,
|
||||
0 /* dropout */,
|
||||
nullptr,
|
||||
0 /* state_size */,
|
||||
0 /* seed */,
|
||||
false,
|
||||
false,
|
||||
MIOPEN_RNG_PSEUDO_XORWOW));
|
||||
return desc_;
|
||||
}
|
||||
auto* dropout_state_data = dropout_state_->data<uint8_t>();
|
||||
if (!initialized) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenSetDropoutDescriptor(desc_,
|
||||
handle,
|
||||
dropout_prob_,
|
||||
dropout_state_data,
|
||||
state_size,
|
||||
seed,
|
||||
false,
|
||||
false,
|
||||
MIOPEN_RNG_PSEUDO_XORWOW));
|
||||
} else {
|
||||
auto dropout_state_dims = dropout_state_->dims();
|
||||
state_size = dropout_state_dims[0];
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenRestoreDropoutDescriptor(
|
||||
desc_,
|
||||
handle,
|
||||
dropout_prob_,
|
||||
dropout_state_data,
|
||||
state_size,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
MIOPEN_RNG_PSEUDO_XORWOW));
|
||||
}
|
||||
return desc_;
|
||||
}
|
||||
inline miopenDropoutDescriptor_t desc() { return desc_; }
|
||||
|
||||
private:
|
||||
miopenDropoutDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedDropoutDescriptor);
|
||||
};
|
||||
|
||||
class ScopedRNNDescriptor {
|
||||
public:
|
||||
ScopedRNNDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenCreateRNNDescriptor(&desc_));
|
||||
}
|
||||
~ScopedRNNDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenDestroyRNNDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline miopenRNNDescriptor_t desc() { return desc_; }
|
||||
|
||||
private:
|
||||
miopenRNNDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedRNNDescriptor);
|
||||
};
|
||||
|
||||
class ScopedFilterDescriptor {
|
||||
public:
|
||||
ScopedFilterDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreateTensorDescriptor(&desc_));
|
||||
}
|
||||
~ScopedFilterDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyTensorDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline miopenTensorDescriptor_t descriptor(const miopenTensorFormat_t format,
|
||||
const miopenDataType_t type,
|
||||
const std::vector<int>& kernel,
|
||||
const int groups = 1) {
|
||||
// filter layout: MCHW(MCDHW), where M is the number of
|
||||
// output image channels, C is the number of input image channels,
|
||||
// D is the depth of the filter, H is the height of the filter, and W is the
|
||||
// width of the filter.
|
||||
std::vector<int> kernel_with_group(kernel.begin(), kernel.end());
|
||||
if (groups > 1) {
|
||||
kernel_with_group[0] /= groups;
|
||||
// NOTE: input filter(C) of the filter is already asserted to be C/groups.
|
||||
}
|
||||
std::vector<int> stride_dim(kernel_with_group.size());
|
||||
stride_dim[kernel_with_group.size() - 1] = 1;
|
||||
for (int k = kernel_with_group.size() - 2; k >= 0; k--) {
|
||||
stride_dim[k] = stride_dim[k + 1] * kernel_with_group[k + 1];
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetTensorDescriptor(
|
||||
desc_,
|
||||
type,
|
||||
kernel_with_group.size(),
|
||||
const_cast<int*>(kernel_with_group.data()),
|
||||
const_cast<int*>(stride_dim.data())));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline miopenTensorDescriptor_t descriptor(const DataLayout& order,
|
||||
const std::vector<int>& kernel,
|
||||
const int groups = 1) {
|
||||
return descriptor(
|
||||
GetCudnnTensorFormat(order), CudnnDataType<T>::type, kernel, groups);
|
||||
}
|
||||
|
||||
inline miopenTensorDescriptor_t desc() { return desc_; }
|
||||
|
||||
private:
|
||||
miopenTensorDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedFilterDescriptor);
|
||||
};
|
||||
|
||||
class ScopedConvolutionDescriptor {
|
||||
public:
|
||||
ScopedConvolutionDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreateConvolutionDescriptor(&desc_));
|
||||
}
|
||||
~ScopedConvolutionDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyConvolutionDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline miopenConvolutionDescriptor_t descriptor(
|
||||
miopenDataType_t type,
|
||||
const std::vector<int>& pads,
|
||||
const std::vector<int>& strides,
|
||||
const std::vector<int>& dilations) {
|
||||
PADDLE_ENFORCE_EQ(pads.size(),
|
||||
strides.size(),
|
||||
common::errors::InvalidArgument(
|
||||
"The size of pads and strides should be equal. But "
|
||||
"received size of pads is %d, size of strides is %d.",
|
||||
pads.size(),
|
||||
strides.size()));
|
||||
PADDLE_ENFORCE_EQ(
|
||||
pads.size(),
|
||||
dilations.size(),
|
||||
common::errors::InvalidArgument(
|
||||
"The size of pads and dilations should be equal. But received size "
|
||||
"of pads is %d, size of dilations is %d.",
|
||||
pads.size(),
|
||||
dilations.size()));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenInitConvolutionNdDescriptor(
|
||||
desc_,
|
||||
pads.size(),
|
||||
const_cast<int*>(pads.data()),
|
||||
const_cast<int*>(strides.data()),
|
||||
const_cast<int*>(dilations.data()),
|
||||
miopenConvolution));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline miopenConvolutionDescriptor_t descriptor(
|
||||
const std::vector<int>& pads,
|
||||
const std::vector<int>& strides,
|
||||
const std::vector<int>& dilations) {
|
||||
return descriptor(CudnnDataType<T>::type, pads, strides, dilations);
|
||||
}
|
||||
|
||||
private:
|
||||
miopenConvolutionDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedConvolutionDescriptor);
|
||||
};
|
||||
|
||||
class ScopedPoolingDescriptor {
|
||||
public:
|
||||
ScopedPoolingDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreatePoolingDescriptor(&desc_));
|
||||
}
|
||||
~ScopedPoolingDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyPoolingDescriptor(desc_));
|
||||
}
|
||||
|
||||
inline miopenPoolingDescriptor_t descriptor(const PoolingMode& mode,
|
||||
const std::vector<int>& kernel,
|
||||
const std::vector<int>& pads,
|
||||
const std::vector<int>& strides) {
|
||||
PADDLE_ENFORCE_EQ(kernel.size(),
|
||||
pads.size(),
|
||||
common::errors::InvalidArgument(
|
||||
"The size of kernel and pads should be equal. But "
|
||||
"received size of kernel is %d, size of pads is %d.",
|
||||
kernel.size(),
|
||||
pads.size()));
|
||||
PADDLE_ENFORCE_EQ(
|
||||
kernel.size(),
|
||||
strides.size(),
|
||||
common::errors::InvalidArgument(
|
||||
"The size of kernel and strides should be equal. But "
|
||||
"received size of kernel is %d, size of strides is %d.",
|
||||
kernel.size(),
|
||||
strides.size()));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetNdPoolingDescriptor(
|
||||
desc_,
|
||||
GetPoolingMode(mode),
|
||||
kernel.size(),
|
||||
const_cast<int*>(kernel.data()),
|
||||
const_cast<int*>(pads.data()),
|
||||
const_cast<int*>(strides.data())));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
private:
|
||||
miopenPoolingDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedPoolingDescriptor);
|
||||
};
|
||||
|
||||
class ScopedActivationDescriptor {
|
||||
public:
|
||||
ScopedActivationDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreateActivationDescriptor(&desc_));
|
||||
}
|
||||
~ScopedActivationDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyActivationDescriptor(desc_));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline miopenActivationDescriptor_t descriptor(
|
||||
const std::string& act, double value_max = static_cast<double>(0.)) {
|
||||
double relu_ceiling = 0.0;
|
||||
ActivationMode activation_mode = StringToActivationMode(act);
|
||||
miopenActivationMode_t mode;
|
||||
switch (activation_mode) {
|
||||
case ActivationMode::kNone:
|
||||
mode = miopenActivationPASTHRU;
|
||||
break;
|
||||
case ActivationMode::kRelu6:
|
||||
relu_ceiling = 6.0;
|
||||
mode = miopenActivationCLIPPEDRELU;
|
||||
break;
|
||||
case ActivationMode::kReluX:
|
||||
relu_ceiling = value_max;
|
||||
mode = miopenActivationCLIPPEDRELU;
|
||||
break;
|
||||
case ActivationMode::kRelu:
|
||||
mode = miopenActivationRELU;
|
||||
break;
|
||||
case ActivationMode::kSigmoid:
|
||||
mode = miopenActivationLOGISTIC;
|
||||
break;
|
||||
case ActivationMode::kTanh:
|
||||
mode = miopenActivationTANH;
|
||||
break;
|
||||
default:
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Unrecognized MIOPEN activation mode: %d.",
|
||||
static_cast<int>(activation_mode)));
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetActivationDescriptor(
|
||||
desc_, mode, relu_ceiling, 0.0, 0.0));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
private:
|
||||
miopenActivationDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedActivationDescriptor);
|
||||
};
|
||||
|
||||
class ScopedCTCLossDescriptor {
|
||||
public:
|
||||
ScopedCTCLossDescriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenCreateCTCLossDescriptor(&desc_));
|
||||
}
|
||||
~ScopedCTCLossDescriptor() PADDLE_MAY_THROW {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::miopenDestroyCTCLossDescriptor(desc_));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline miopenCTCLossDescriptor_t descriptor() {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::miopenSetCTCLossDescriptor(
|
||||
desc_, CudnnDataType<T>::type, 0, false));
|
||||
return desc_;
|
||||
}
|
||||
|
||||
private:
|
||||
miopenCTCLossDescriptor_t desc_;
|
||||
DISABLE_COPY_AND_ASSIGN(ScopedCTCLossDescriptor);
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,163 @@
|
||||
/* 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/common/bfloat16.h"
|
||||
#include "paddle/phi/common/complex.h"
|
||||
#include "paddle/phi/common/float16.h"
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
#define CREATE_SHFL_MASK(mask, predicate) mask = __ballot((predicate))
|
||||
|
||||
#define CUDA_LAUNCH_KERNEL_BASE(dim, ...) \
|
||||
case (dim): { \
|
||||
constexpr auto kPowerOfTwoDim = (dim); \
|
||||
__VA_ARGS__; \
|
||||
} break
|
||||
|
||||
#define CUDA_LAUNCH_KERNEL_HELPER(...) \
|
||||
CUDA_LAUNCH_KERNEL_BASE(1024, ##__VA_ARGS__); \
|
||||
CUDA_LAUNCH_KERNEL_BASE(512, ##__VA_ARGS__); \
|
||||
CUDA_LAUNCH_KERNEL_BASE(256, ##__VA_ARGS__); \
|
||||
CUDA_LAUNCH_KERNEL_BASE(128, ##__VA_ARGS__); \
|
||||
CUDA_LAUNCH_KERNEL_BASE(64, ##__VA_ARGS__); \
|
||||
CUDA_LAUNCH_KERNEL_BASE(32, ##__VA_ARGS__);
|
||||
|
||||
template <typename T>
|
||||
__forceinline__ __device__ T
|
||||
CudaShuffleDownSync(unsigned mask, T val, int delta, int width = warpSize) {
|
||||
return __shfl_down(val, delta, width);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__forceinline__ __device__ T CudaShuffleXorSync(unsigned mask,
|
||||
T val,
|
||||
int width = warpSize) {
|
||||
return __shfl_xor(val, width);
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::float16 CudaShuffleDownSync(
|
||||
unsigned mask, phi::dtype::float16 val, int delta, int width) {
|
||||
return phi::dtype::float16(__shfl_down(
|
||||
static_cast<float>(val), static_cast<unsigned>(delta), width));
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::bfloat16 CudaShuffleDownSync(
|
||||
unsigned mask, phi::dtype::bfloat16 val, int delta, int width) {
|
||||
return phi::dtype::bfloat16(__shfl_down(
|
||||
static_cast<float>(val), static_cast<unsigned>(delta), width));
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::complex<float> CudaShuffleDownSync(
|
||||
unsigned mask, phi::dtype::complex<float> val, int delta, int width) {
|
||||
float real = __shfl_down(val.real, delta, width);
|
||||
float imag = __shfl_down(val.imag, delta, width);
|
||||
return phi::dtype::complex<float>(real, imag);
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::complex<double> CudaShuffleDownSync(
|
||||
unsigned mask, phi::dtype::complex<double> val, int delta, int width) {
|
||||
double real = __shfl_down(val.real, delta, width);
|
||||
double imag = __shfl_down(val.imag, delta, width);
|
||||
return phi::dtype::complex<double>(real, imag);
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::float16 CudaShuffleXorSync(
|
||||
unsigned mask, phi::dtype::float16 val, int width) {
|
||||
return phi::dtype::float16(__shfl_xor(static_cast<float>(val), width));
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::bfloat16 CudaShuffleXorSync(
|
||||
unsigned mask, phi::dtype::bfloat16 val, int width) {
|
||||
return phi::dtype::bfloat16(__shfl_xor(static_cast<float>(val), width));
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::complex<float> CudaShuffleXorSync(
|
||||
unsigned mask, phi::dtype::complex<float> val, int width) {
|
||||
float real = __shfl_xor(val.real, width);
|
||||
float imag = __shfl_xor(val.imag, width);
|
||||
return phi::dtype::complex<float>(real, imag);
|
||||
}
|
||||
|
||||
template <>
|
||||
__forceinline__ __device__ phi::dtype::complex<double> CudaShuffleXorSync(
|
||||
unsigned mask, phi::dtype::complex<double> val, int width) {
|
||||
double real = __shfl_xor(val.real, width);
|
||||
double imag = __shfl_xor(val.imag, width);
|
||||
return phi::dtype::complex<double>(real, imag);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__forceinline__ __device__ T
|
||||
CudaShuffleSync(unsigned mask, T val, int src_line, int width = 32) {
|
||||
return __shfl(val, src_line, width);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HOSTDEVICE T Infinity() {
|
||||
return INFINITY;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__device__ T reduceSum(T val, int tid, int len) {
|
||||
// NOTE(zcd): The warp size should be taken from the
|
||||
// parameters of the GPU but not specified as 32 simply.
|
||||
// To make the reduceSum more efficiently,
|
||||
// I use Warp-Level Parallelism and assume the Warp size
|
||||
// is 32 which may be different for different GPU,
|
||||
// but most card's warp size is 32.
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
const int warpSize = 64;
|
||||
#else
|
||||
const int warpSize = 32;
|
||||
#endif
|
||||
__shared__ T shm[warpSize];
|
||||
unsigned mask = 0u;
|
||||
CREATE_SHFL_MASK(mask, tid < len);
|
||||
|
||||
for (int offset = warpSize / 2; offset > 0; offset /= 2)
|
||||
val += phi::backends::gpu::CudaShuffleDownSync(mask, val, offset);
|
||||
|
||||
if (tid < warpSize) shm[tid] = 0;
|
||||
__syncthreads();
|
||||
|
||||
if (tid % warpSize == 0) {
|
||||
shm[tid / warpSize] = val;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
CREATE_SHFL_MASK(mask, tid < warpSize);
|
||||
|
||||
if (tid < warpSize) {
|
||||
val = shm[tid];
|
||||
for (int offset = warpSize / 2; offset > 0; offset /= 2)
|
||||
val += phi::backends::gpu::CudaShuffleDownSync(mask, val, offset);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,138 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#include <hip/hip_runtime.h>
|
||||
#include <hip/library_types.h>
|
||||
#include <hipblaslt/hipblaslt.h>
|
||||
|
||||
#include "paddle/phi/common/bfloat16.h"
|
||||
#include "paddle/phi/common/data_type.h"
|
||||
#include "paddle/phi/common/float16.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
#if HIP_VERSION >= 60100000
|
||||
using hipDataType_t = hipDataType;
|
||||
constexpr hipDataType_t HIP_DATATYPE_R_32F = hipDataType::HIP_R_32F;
|
||||
constexpr hipDataType_t HIP_DATATYPE_R_64F = hipDataType::HIP_R_64F;
|
||||
constexpr hipDataType_t HIP_DATATYPE_R_16F = hipDataType::HIP_R_16F;
|
||||
constexpr hipDataType_t HIP_DATATYPE_R_8I = hipDataType::HIP_R_8I;
|
||||
constexpr hipDataType_t HIP_DATATYPE_R_16BF = hipDataType::HIP_R_16BF;
|
||||
constexpr hipDataType_t HIP_DATATYPE_R_32I = hipDataType::HIP_R_32I;
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
/*
|
||||
* Summary: Grid stride looping macro in CUDA kernel
|
||||
*
|
||||
* [ Why need this macro? ]
|
||||
*
|
||||
* The original looping in CUDA kernel is:
|
||||
*
|
||||
* `for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \
|
||||
* i += blockDim.x * gridDim.x)`
|
||||
*
|
||||
* This for condition is risky. The value of `blockIdx.x * blockDim.x`
|
||||
* may be large, such as over 1GB, the first iteration is no problem here,
|
||||
* but when `i += blockDim.x * gridDim.x` is executed, the value of i
|
||||
* will greater than INT_MAX and overflow becomes negative value, at
|
||||
* this time, the cycle condition `i < (n)` is still satisfied, so it
|
||||
* will cause illegal access to cuda memory.
|
||||
*
|
||||
* Here is a real example in ERINE, it will trigger above error.
|
||||
* The related data are:
|
||||
* - blockIdx.x = 2172938
|
||||
* - blockDim.x = 512
|
||||
* - blockIdx.x * blockDim.x = 1112543864
|
||||
* - INT_MAX = 2147483647
|
||||
*
|
||||
* So we polish the for condition as follow, the int64_t __index__ will
|
||||
* prevent overflow in the loop increment.
|
||||
*
|
||||
* Parameters:
|
||||
* - i: loop index
|
||||
* - num: total element numbers
|
||||
*
|
||||
* Examples:
|
||||
* template <typename T>
|
||||
* __global__ void Scale(T* logit_grad, const T* loss_grad, const int num,
|
||||
* const int d, const int remain) {
|
||||
* CUDA_KERNEL_LOOP(index, num) {
|
||||
* int idx_n = index / d;
|
||||
* int idx_remain = index % remain;
|
||||
* logit_grad[index] *= loss_grad[idx_n * remain + idx_remain];
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
#define CUDA_KERNEL_LOOP_TYPE(i, num, index_type) \
|
||||
int64_t __index__ = \
|
||||
static_cast<int64_t>(hipBlockIdx_x) * hipBlockDim_x + hipThreadIdx_x; \
|
||||
int64_t __stride__ = static_cast<int64_t>(hipBlockDim_x) * hipGridDim_x; \
|
||||
for (index_type i = __index__; __index__ < (num); \
|
||||
__index__ += __stride__, i = __index__)
|
||||
|
||||
template <typename T>
|
||||
hipDataType ToHipDataType() {
|
||||
if (std::is_same<T, float>::value) {
|
||||
return HIP_R_32F;
|
||||
} else if (std::is_same<T, double>::value) {
|
||||
return HIP_R_64F;
|
||||
} else if (std::is_same<T, phi::dtype::float16>::value) {
|
||||
return HIP_R_16F;
|
||||
} else if (std::is_same<T, phi::dtype::bfloat16>::value) {
|
||||
return HIP_R_16BF;
|
||||
} else if (std::is_same<T, int8_t>::value) {
|
||||
return HIP_R_8I;
|
||||
} else if (std::is_same<T, int32_t>::value) {
|
||||
return HIP_R_32I;
|
||||
} else {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"DataType %s is unsupported for ROCm.",
|
||||
DataTypeToString(phi::CppTypeToDataType<T>::Type())));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
hipDataType_t ToHipBlasLtDataType() {
|
||||
if (std::is_same<T, float>::value) {
|
||||
return HIP_DATATYPE_R_32F;
|
||||
} else if (std::is_same<T, double>::value) {
|
||||
return HIP_DATATYPE_R_64F;
|
||||
} else if (std::is_same<T, phi::dtype::float16>::value) {
|
||||
return HIP_DATATYPE_R_16F;
|
||||
} else if (std::is_same<T, phi::dtype::bfloat16>::value) {
|
||||
return HIP_DATATYPE_R_16BF;
|
||||
} else if (std::is_same<T, int8_t>::value) {
|
||||
return HIP_DATATYPE_R_8I;
|
||||
} else if (std::is_same<T, int32_t>::value) {
|
||||
return HIP_DATATYPE_R_32I;
|
||||
} else {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"DataType %s is unsupported for ROCm.",
|
||||
DataTypeToString(phi::CppTypeToDataType<T>::Type())));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,360 @@
|
||||
// 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 <array>
|
||||
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
static std::once_flag g_device_props_size_init_flag;
|
||||
static std::vector<std::unique_ptr<std::once_flag>> g_device_props_init_flags;
|
||||
static std::vector<phi::gpuDeviceProp> g_device_props;
|
||||
|
||||
namespace phi {
|
||||
namespace backends {
|
||||
namespace gpu {
|
||||
|
||||
int DnnVersion() {
|
||||
if (!dynload::HasCUDNN()) return -1;
|
||||
size_t version_major, version_minor, version_patch;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(dynload::miopenGetVersion(
|
||||
&version_major, &version_minor, &version_patch));
|
||||
return version_major * 100 + version_minor * 10 + version_patch;
|
||||
}
|
||||
|
||||
static int GetGPUDeviceCountImpl() {
|
||||
int driverVersion = 0;
|
||||
hipError_t status = hipDriverGetVersion(&driverVersion);
|
||||
|
||||
if (!(status == gpuSuccess && driverVersion != 0)) {
|
||||
// No GPU driver
|
||||
VLOG(2) << "GPU Driver Version can't be detected. No GPU driver!";
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto *cuda_visible_devices = std::getenv("HIP_VISIBLE_DEVICES");
|
||||
|
||||
if (cuda_visible_devices != nullptr) {
|
||||
std::string cuda_visible_devices_str(cuda_visible_devices);
|
||||
if (!cuda_visible_devices_str.empty()) {
|
||||
cuda_visible_devices_str.erase(
|
||||
0, cuda_visible_devices_str.find_first_not_of('\''));
|
||||
cuda_visible_devices_str.erase(
|
||||
cuda_visible_devices_str.find_last_not_of('\'') + 1);
|
||||
cuda_visible_devices_str.erase(
|
||||
0, cuda_visible_devices_str.find_first_not_of('\"'));
|
||||
cuda_visible_devices_str.erase(
|
||||
cuda_visible_devices_str.find_last_not_of('\"') + 1);
|
||||
}
|
||||
if (std::all_of(cuda_visible_devices_str.begin(),
|
||||
cuda_visible_devices_str.end(),
|
||||
[](char ch) { return ch == ' '; })) {
|
||||
VLOG(2) << "HIP_VISIBLE_DEVICES is set to be "
|
||||
"empty. No GPU detected.";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int count;
|
||||
status = hipGetDeviceCount(&count);
|
||||
if (status != hipSuccess) {
|
||||
VLOG(2) << "You have gpu driver and rocm installed, but the machine does "
|
||||
"not have any visible gpu card.";
|
||||
count = 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int GetGPUDeviceCount() {
|
||||
// cache the count
|
||||
static auto dev_cnt = GetGPUDeviceCountImpl();
|
||||
return dev_cnt;
|
||||
}
|
||||
|
||||
int GetGPUComputeCapability(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int major, minor;
|
||||
auto major_error_code = hipDeviceGetAttribute(
|
||||
&major, hipDeviceAttributeComputeCapabilityMajor, id);
|
||||
auto minor_error_code = hipDeviceGetAttribute(
|
||||
&minor, hipDeviceAttributeComputeCapabilityMinor, id);
|
||||
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(major_error_code);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(minor_error_code);
|
||||
return major * 100 + minor;
|
||||
}
|
||||
|
||||
int GetGPURuntimeVersion(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int runtime_version = 0;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipRuntimeGetVersion(&runtime_version));
|
||||
return runtime_version;
|
||||
}
|
||||
|
||||
int GetGPUDriverVersion(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int driver_version = 0;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipDriverGetVersion(&driver_version));
|
||||
return driver_version;
|
||||
}
|
||||
|
||||
bool TensorCoreAvailable() { return false; }
|
||||
|
||||
int GetGPUMultiProcessors(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int count;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipDeviceGetAttribute(&count, hipDeviceAttributeMultiprocessorCount, id));
|
||||
return count;
|
||||
}
|
||||
|
||||
int GetGPUMaxThreadsPerMultiProcessor(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int count;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipDeviceGetAttribute(
|
||||
&count, hipDeviceAttributeMaxThreadsPerMultiProcessor, id));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int GetGPUMaxThreadsPerBlock(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
int count;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipDeviceGetAttribute(&count, hipDeviceAttributeMaxThreadsPerBlock, id));
|
||||
return count;
|
||||
}
|
||||
|
||||
int GetCurrentDeviceId() {
|
||||
int device_id;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGetDevice(&device_id));
|
||||
return device_id;
|
||||
}
|
||||
|
||||
std::array<unsigned int, 3> GetGpuMaxGridDimSize(int id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
std::array<unsigned int, 3> ret;
|
||||
int size;
|
||||
auto error_code_x =
|
||||
hipDeviceGetAttribute(&size, hipDeviceAttributeMaxGridDimX, id);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(error_code_x);
|
||||
ret[0] = size;
|
||||
|
||||
auto error_code_y =
|
||||
hipDeviceGetAttribute(&size, hipDeviceAttributeMaxGridDimY, id);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(error_code_y);
|
||||
ret[1] = size;
|
||||
|
||||
auto error_code_z =
|
||||
hipDeviceGetAttribute(&size, hipDeviceAttributeMaxGridDimZ, id);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(error_code_z);
|
||||
ret[2] = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::pair<int, int> GetGpuStreamPriorityRange() {
|
||||
int least_priority, greatest_priority;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipDeviceGetStreamPriorityRange(&least_priority, &greatest_priority));
|
||||
return std::make_pair(least_priority, greatest_priority);
|
||||
}
|
||||
|
||||
const gpuDeviceProp &GetDeviceProperties(int id) {
|
||||
std::call_once(g_device_props_size_init_flag, [&] {
|
||||
int gpu_num = 0;
|
||||
gpu_num = GetGPUDeviceCount();
|
||||
g_device_props_init_flags.resize(gpu_num);
|
||||
g_device_props.resize(gpu_num);
|
||||
for (int i = 0; i < gpu_num; ++i) {
|
||||
g_device_props_init_flags[i] = std::make_unique<std::once_flag>();
|
||||
}
|
||||
});
|
||||
|
||||
if (id == -1) {
|
||||
id = GetCurrentDeviceId();
|
||||
}
|
||||
|
||||
if (id < 0 || id >= static_cast<int>(g_device_props.size())) {
|
||||
PADDLE_THROW(common::errors::OutOfRange(
|
||||
"The device id %d is out of range [0, %d), where %d is the number of "
|
||||
"devices on this machine. Because the device id should be greater than "
|
||||
"or equal to zero and smaller than the number of gpus. Please input "
|
||||
"appropriate device again!",
|
||||
id,
|
||||
static_cast<int>(g_device_props.size()),
|
||||
static_cast<int>(g_device_props.size())));
|
||||
}
|
||||
|
||||
std::call_once(*(g_device_props_init_flags[id]), [&] {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGetDeviceProperties(&g_device_props[id], id));
|
||||
});
|
||||
|
||||
return g_device_props[id];
|
||||
}
|
||||
|
||||
void SetDeviceId(int id) {
|
||||
static thread_local bool first_call = true;
|
||||
if (first_call) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
|
||||
PADDLE_RETRY_CUDA_SUCCESS(hipSetDevice(id));
|
||||
VLOG(4) << "SetDeviceId " << id;
|
||||
first_call = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int prev_id;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipGetDevice(&prev_id));
|
||||
if (prev_id != id) {
|
||||
PADDLE_ENFORCE_LT(id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
id,
|
||||
GetGPUDeviceCount()));
|
||||
|
||||
PADDLE_RETRY_CUDA_SUCCESS(hipSetDevice(id));
|
||||
VLOG(4) << "SetDeviceId " << id;
|
||||
}
|
||||
}
|
||||
|
||||
void GpuMemcpyAsync(void *dst,
|
||||
const void *src,
|
||||
size_t count,
|
||||
gpuMemcpyKind kind,
|
||||
gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipMemcpyAsync(dst, src, count, kind, stream));
|
||||
}
|
||||
|
||||
void GpuMemcpySync(void *dst,
|
||||
const void *src,
|
||||
size_t count,
|
||||
gpuMemcpyKind kind) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipMemcpy(dst, src, count, kind));
|
||||
}
|
||||
|
||||
void GpuMemcpyPeerAsync(void *dst,
|
||||
int dst_device,
|
||||
const void *src,
|
||||
int src_device,
|
||||
size_t count,
|
||||
gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipMemcpyPeerAsync(dst, dst_device, src, src_device, count, stream));
|
||||
}
|
||||
|
||||
void GpuMemcpyPeerSync(
|
||||
void *dst, int dst_device, const void *src, int src_device, size_t count) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
hipMemcpyPeer(dst, dst_device, src, src_device, count));
|
||||
}
|
||||
|
||||
void GpuMemsetAsync(void *dst, int value, size_t count, gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipMemsetAsync(dst, value, count, stream));
|
||||
}
|
||||
|
||||
void GpuStreamSync(gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipStreamSynchronize(stream));
|
||||
}
|
||||
|
||||
void GpuDestroyStream(gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipStreamDestroy(stream));
|
||||
}
|
||||
|
||||
void GpuDeviceSync() { PADDLE_ENFORCE_GPU_SUCCESS(hipDeviceSynchronize()); }
|
||||
|
||||
gpuError_t GpuGetLastError() { return hipGetLastError(); }
|
||||
|
||||
bool IsGPUManagedMemorySupported(int dev_id) {
|
||||
PADDLE_ENFORCE_LT(dev_id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
dev_id,
|
||||
GetGPUDeviceCount()));
|
||||
// TODO(qili93): Hygon DTK (21.04 and 22.04) not support
|
||||
// hipDeviceAttributeManagedMemory, temporary disable by default, to be
|
||||
// verified in next DTK release
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsGPUManagedMemoryOversubscriptionSupported(int dev_id) {
|
||||
PADDLE_ENFORCE_LT(dev_id,
|
||||
GetGPUDeviceCount(),
|
||||
common::errors::InvalidArgument(
|
||||
"Device id must be less than GPU count, "
|
||||
"but received id is: %d. GPU count is: %d.",
|
||||
dev_id,
|
||||
GetGPUDeviceCount()));
|
||||
#ifdef __linux__
|
||||
return IsGPUManagedMemorySupported(dev_id) &&
|
||||
GetGPUComputeCapability(dev_id) >= 60;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace backends
|
||||
} // namespace phi
|
||||
Reference in New Issue
Block a user