326 lines
10 KiB
Plaintext
326 lines
10 KiB
Plaintext
/*!
|
|
* Copyright (c) 2020-2021 IBM Corporation, Microsoft Corporation. All rights reserved.
|
|
* Copyright (c) 2020-2026 Microsoft Corporation. All rights reserved.
|
|
* Copyright (c) 2020-2026 The LightGBM developers. All rights reserved.
|
|
* Licensed under the MIT License. See LICENSE file in the project root for license information.
|
|
*/
|
|
|
|
#ifndef LIGHTGBM_CUDA_CUDA_UTILS_H_
|
|
#define LIGHTGBM_CUDA_CUDA_UTILS_H_
|
|
|
|
#ifdef USE_CUDA
|
|
|
|
#if defined(USE_ROCM)
|
|
#include <LightGBM/cuda/cuda_rocm_interop.h>
|
|
#include <rccl/rccl.h>
|
|
#else
|
|
#include <cuda.h>
|
|
#include <cuda_runtime.h>
|
|
#include <nccl.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
|
|
#include <LightGBM/utils/log.h>
|
|
#include <LightGBM/meta.h>
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <cmath>
|
|
|
|
namespace LightGBM {
|
|
|
|
typedef unsigned long long atomic_add_long_t;
|
|
|
|
#define CUDASUCCESS_OR_FATAL(ans) { gpuAssert((ans), __FILE__, __LINE__); }
|
|
inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort = true) {
|
|
if (code != cudaSuccess) {
|
|
LightGBM::Log::Fatal("[CUDA] %s %s %d\n", cudaGetErrorString(code), file, line);
|
|
if (abort) exit(code);
|
|
}
|
|
}
|
|
|
|
#define CUDASUCCESS_OR_FATAL_OUTER(ans) { gpuAssert((ans), file, line); }
|
|
|
|
#define NCCLCHECK(cmd) do { \
|
|
ncclResult_t r = cmd; \
|
|
if (r!= ncclSuccess) { \
|
|
printf("Failed, NCCL error %s:%d '%s'\n", \
|
|
__FILE__,__LINE__,ncclGetErrorString(r)); \
|
|
exit(EXIT_FAILURE); \
|
|
} \
|
|
} while(0)
|
|
|
|
void SetCUDADevice(int gpu_device_id, const char* file, int line);
|
|
|
|
int GetCUDADevice(const char* file, int line);
|
|
|
|
template <typename T>
|
|
void AllocateCUDAMemory(T** out_ptr, size_t size, const char* file, const int line) {
|
|
void* tmp_ptr = nullptr;
|
|
CUDASUCCESS_OR_FATAL_OUTER(cudaMalloc(&tmp_ptr, size * sizeof(T)));
|
|
*out_ptr = reinterpret_cast<T*>(tmp_ptr);
|
|
}
|
|
|
|
template <typename T>
|
|
void CopyFromHostToCUDADevice(T* dst_ptr, const T* src_ptr, size_t size, const char* file, const int line) {
|
|
void* void_dst_ptr = reinterpret_cast<void*>(dst_ptr);
|
|
const void* void_src_ptr = reinterpret_cast<const void*>(src_ptr);
|
|
size_t size_in_bytes = size * sizeof(T);
|
|
CUDASUCCESS_OR_FATAL_OUTER(cudaMemcpy(void_dst_ptr, void_src_ptr, size_in_bytes, cudaMemcpyHostToDevice));
|
|
}
|
|
|
|
template <typename T>
|
|
void InitCUDAMemoryFromHostMemory(T** dst_ptr, const T* src_ptr, size_t size, const char* file, const int line) {
|
|
AllocateCUDAMemory<T>(dst_ptr, size, file, line);
|
|
CopyFromHostToCUDADevice<T>(*dst_ptr, src_ptr, size, file, line);
|
|
}
|
|
|
|
template <typename T>
|
|
void CopyFromCUDADeviceToHost(T* dst_ptr, const T* src_ptr, size_t size, const char* file, const int line) {
|
|
void* void_dst_ptr = reinterpret_cast<void*>(dst_ptr);
|
|
const void* void_src_ptr = reinterpret_cast<const void*>(src_ptr);
|
|
size_t size_in_bytes = size * sizeof(T);
|
|
CUDASUCCESS_OR_FATAL_OUTER(cudaMemcpy(void_dst_ptr, void_src_ptr, size_in_bytes, cudaMemcpyDeviceToHost));
|
|
}
|
|
|
|
template <typename T>
|
|
void CopyFromCUDADeviceToHostAsync(T* dst_ptr, const T* src_ptr, size_t size, cudaStream_t stream, const char* file, const int line) {
|
|
void* void_dst_ptr = reinterpret_cast<void*>(dst_ptr);
|
|
const void* void_src_ptr = reinterpret_cast<const void*>(src_ptr);
|
|
size_t size_in_bytes = size * sizeof(T);
|
|
CUDASUCCESS_OR_FATAL_OUTER(cudaMemcpyAsync(void_dst_ptr, void_src_ptr, size_in_bytes, cudaMemcpyDeviceToHost, stream));
|
|
}
|
|
|
|
template <typename T>
|
|
void CopyFromCUDADeviceToCUDADevice(T* dst_ptr, const T* src_ptr, size_t size, const char* file, const int line) {
|
|
void* void_dst_ptr = reinterpret_cast<void*>(dst_ptr);
|
|
const void* void_src_ptr = reinterpret_cast<const void*>(src_ptr);
|
|
size_t size_in_bytes = size * sizeof(T);
|
|
CUDASUCCESS_OR_FATAL_OUTER(cudaMemcpy(void_dst_ptr, void_src_ptr, size_in_bytes, cudaMemcpyDeviceToDevice));
|
|
}
|
|
|
|
template <typename T>
|
|
void CopyFromCUDADeviceToCUDADeviceAsync(T* dst_ptr, const T* src_ptr, size_t size, const char* file, const int line) {
|
|
void* void_dst_ptr = reinterpret_cast<void*>(dst_ptr);
|
|
const void* void_src_ptr = reinterpret_cast<const void*>(src_ptr);
|
|
size_t size_in_bytes = size * sizeof(T);
|
|
CUDASUCCESS_OR_FATAL_OUTER(cudaMemcpyAsync(void_dst_ptr, void_src_ptr, size_in_bytes, cudaMemcpyDeviceToDevice));
|
|
}
|
|
|
|
void SynchronizeCUDADevice(const char* file, const int line);
|
|
|
|
void SynchronizeCUDAStream(cudaStream_t cuda_stream, const char* file, const int line);
|
|
|
|
template <typename T>
|
|
void SetCUDAMemory(T* dst_ptr, int value, size_t size, const char* file, const int line) {
|
|
CUDASUCCESS_OR_FATAL_OUTER(cudaMemset(reinterpret_cast<void*>(dst_ptr), value, size * sizeof(T)));
|
|
SynchronizeCUDADevice(file, line);
|
|
}
|
|
|
|
template <typename T>
|
|
void DeallocateCUDAMemory(T** ptr, const char* file, const int line) {
|
|
if (*ptr != nullptr) {
|
|
CUDASUCCESS_OR_FATAL_OUTER(cudaFree(reinterpret_cast<void*>(*ptr)));
|
|
*ptr = nullptr;
|
|
}
|
|
}
|
|
|
|
void PrintLastCUDAError();
|
|
|
|
template <typename T>
|
|
class CUDAVector {
|
|
public:
|
|
CUDAVector() {
|
|
size_ = 0;
|
|
data_ = nullptr;
|
|
}
|
|
|
|
explicit CUDAVector(size_t size) {
|
|
size_ = size;
|
|
AllocateCUDAMemory<T>(&data_, size_, __FILE__, __LINE__);
|
|
}
|
|
|
|
void Resize(size_t size) {
|
|
if (size == size_) {
|
|
return;
|
|
}
|
|
if (size == 0) {
|
|
Clear();
|
|
return;
|
|
}
|
|
T* new_data = nullptr;
|
|
AllocateCUDAMemory<T>(&new_data, size, __FILE__, __LINE__);
|
|
if (size_ > 0 && data_ != nullptr) {
|
|
const size_t size_for_old_content = std::min<size_t>(size_, size);
|
|
CopyFromCUDADeviceToCUDADevice<T>(new_data, data_, size_for_old_content, __FILE__, __LINE__);
|
|
}
|
|
DeallocateCUDAMemory<T>(&data_, __FILE__, __LINE__);
|
|
data_ = new_data;
|
|
size_ = size;
|
|
}
|
|
|
|
void InitFromHostVector(const std::vector<T>& host_vector) {
|
|
Resize(host_vector.size());
|
|
CopyFromHostToCUDADevice(data_, host_vector.data(), host_vector.size(), __FILE__, __LINE__);
|
|
}
|
|
|
|
void InitFromHostMemory(const T* host_memory, size_t len) {
|
|
Resize(len);
|
|
CopyFromHostToCUDADevice(data_, host_memory, len, __FILE__, __LINE__);
|
|
}
|
|
|
|
void Clear() {
|
|
if (size_ > 0 && data_ != nullptr) {
|
|
DeallocateCUDAMemory<T>(&data_, __FILE__, __LINE__);
|
|
}
|
|
size_ = 0;
|
|
}
|
|
|
|
void PushBack(const T* values, size_t len) {
|
|
T* new_data = nullptr;
|
|
AllocateCUDAMemory<T>(&new_data, size_ + len, __FILE__, __LINE__);
|
|
if (size_ > 0 && data_ != nullptr) {
|
|
CopyFromCUDADeviceToCUDADevice<T>(new_data, data_, size_, __FILE__, __LINE__);
|
|
}
|
|
CopyFromCUDADeviceToCUDADevice<T>(new_data + size_, values, len, __FILE__, __LINE__);
|
|
DeallocateCUDAMemory<T>(&data_, __FILE__, __LINE__);
|
|
size_ += len;
|
|
data_ = new_data;
|
|
}
|
|
|
|
size_t Size() const {
|
|
return size_;
|
|
}
|
|
|
|
~CUDAVector() {
|
|
DeallocateCUDAMemory<T>(&data_, __FILE__, __LINE__);
|
|
}
|
|
|
|
std::vector<T> ToHost() {
|
|
std::vector<T> host_vector(size_);
|
|
if (size_ > 0 && data_ != nullptr) {
|
|
CopyFromCUDADeviceToHost(host_vector.data(), data_, size_, __FILE__, __LINE__);
|
|
}
|
|
return host_vector;
|
|
}
|
|
|
|
T* RawData() const {
|
|
return data_;
|
|
}
|
|
|
|
void SetValue(int value) {
|
|
SetCUDAMemory<T>(data_, value, size_, __FILE__, __LINE__);
|
|
}
|
|
|
|
const T* RawDataReadOnly() const {
|
|
return data_;
|
|
}
|
|
|
|
T* MoveTo() {
|
|
size_ = 0;
|
|
T* old_data = data_;
|
|
data_ = nullptr;
|
|
return old_data;
|
|
}
|
|
|
|
template <typename OTHER_T>
|
|
void MoveFrom(CUDAVector<OTHER_T>& other, size_t new_size) {
|
|
data_ = reinterpret_cast<T*>(other.MoveTo());
|
|
size_ = new_size;
|
|
}
|
|
|
|
private:
|
|
T* data_;
|
|
size_t size_;
|
|
};
|
|
|
|
template <typename T>
|
|
static __device__ T SafeLog(T x) {
|
|
if (x > 0) {
|
|
return std::log(x);
|
|
} else {
|
|
return -INFINITY;
|
|
}
|
|
}
|
|
|
|
class NCCLInfo {
|
|
public:
|
|
NCCLInfo() {
|
|
nccl_communicator_ = nullptr;
|
|
nccl_gpu_rank_ = -1;
|
|
local_gpu_rank_ = -1;
|
|
gpu_device_id_ = -1;
|
|
num_gpu_in_node_ = 0;
|
|
global_num_data_ = 0;
|
|
}
|
|
|
|
virtual void SetNCCLInfo(
|
|
ncclComm_t nccl_communicator,
|
|
int nccl_gpu_rank,
|
|
int local_gpu_rank,
|
|
int gpu_device_id,
|
|
data_size_t global_num_data) {
|
|
nccl_communicator_ = nccl_communicator;
|
|
nccl_gpu_rank_ = nccl_gpu_rank;
|
|
local_gpu_rank_ = local_gpu_rank;
|
|
gpu_device_id_ = gpu_device_id;
|
|
global_num_data_ = global_num_data;
|
|
}
|
|
|
|
protected:
|
|
ncclComm_t nccl_communicator_ = nullptr;
|
|
int nccl_gpu_rank_ = -1;
|
|
int local_gpu_rank_ = -1;
|
|
int gpu_device_id_ = -1;
|
|
int num_gpu_in_node_ = 0;
|
|
data_size_t global_num_data_ = 0;
|
|
};
|
|
|
|
cudaStream_t CUDAStreamCreate();
|
|
|
|
void CUDAStreamDestroy(cudaStream_t cuda_stream);
|
|
|
|
void NCCLGroupStart();
|
|
|
|
void NCCLGroupEnd();
|
|
|
|
template <typename T>
|
|
void NCCLAllReduce(const T* send_buffer, T* recv_buffer, size_t count, ncclDataType_t datatype, ncclRedOp_t op, ncclComm_t comm, cudaStream_t stream) {
|
|
NCCLCHECK(ncclAllReduce(reinterpret_cast<const void*>(send_buffer), reinterpret_cast<void*>(recv_buffer), count, datatype, op, comm, stream));
|
|
}
|
|
|
|
template <typename T>
|
|
void NCCLAllReduce(const T* send_buffer, T* recv_buffer, size_t count, ncclDataType_t datatype, ncclRedOp_t op, ncclComm_t comm) {
|
|
cudaStream_t nccl_stream;
|
|
CUDASUCCESS_OR_FATAL(cudaStreamCreate(&nccl_stream));
|
|
NCCLCHECK(ncclAllReduce(reinterpret_cast<const void*>(send_buffer), reinterpret_cast<void*>(recv_buffer), count, datatype, op, comm, nccl_stream));
|
|
CUDASUCCESS_OR_FATAL(cudaStreamSynchronize(nccl_stream));
|
|
CUDASUCCESS_OR_FATAL(cudaStreamDestroy(nccl_stream));
|
|
}
|
|
|
|
template <typename T>
|
|
T NCCLAllReduce(T send_value, ncclDataType_t datatype, ncclRedOp_t op, ncclComm_t comm, cudaStream_t stream) {
|
|
CUDAVector<T> send_buffer(1);
|
|
CopyFromHostToCUDADevice<T>(send_buffer.RawData(), &send_value, 1, __FILE__, __LINE__);
|
|
NCCLAllReduce<T>(send_buffer.RawDataReadOnly(), send_buffer.RawData(), 1, datatype, op, comm, stream);
|
|
T recv_value = 0;
|
|
CopyFromCUDADeviceToHost<T>(&recv_value, send_buffer.RawDataReadOnly(), 1, __FILE__, __LINE__);
|
|
return recv_value;
|
|
}
|
|
|
|
template <typename T>
|
|
T NCCLAllReduce(T send_value, ncclDataType_t datatype, ncclRedOp_t op, ncclComm_t comm) {
|
|
CUDAVector<T> send_buffer(1);
|
|
CopyFromHostToCUDADevice<T>(send_buffer.RawData(), &send_value, 1, __FILE__, __LINE__);
|
|
NCCLAllReduce<T>(send_buffer.RawDataReadOnly(), send_buffer.RawData(), 1, datatype, op, comm);
|
|
T recv_value = 0;
|
|
CopyFromCUDADeviceToHost<T>(&recv_value, send_buffer.RawDataReadOnly(), 1, __FILE__, __LINE__);
|
|
return recv_value;
|
|
}
|
|
|
|
} // namespace LightGBM
|
|
|
|
#endif // USE_CUDA
|
|
|
|
#endif // LIGHTGBM_CUDA_CUDA_UTILS_H_
|