14#ifndef EIGEN_GPU_SUPPORT_H
15#define EIGEN_GPU_SUPPORT_H
18#include "./InternalHeaderCheck.h"
20#include <cuda_runtime.h>
34enum class GpuOp { NoTrans, Trans, ConjTrans };
41inline void gpu_check_failed(
const char* error,
const char* expression,
const char* file,
int line) {
42 std::fprintf(stderr,
"%s:%d: %s: %s\n", file, line, expression, error);
43#if defined(EIGEN_NO_DEBUG)
46 eigen_assert(
false &&
"GPU runtime or library call failed");
52#ifndef EIGEN_GPU_CHECK_FAILED
53#define EIGEN_GPU_CHECK_FAILED(error, expression, file, line) \
54 ::Eigen::gpu::internal::gpu_check_failed(error, expression, file, line)
57#define EIGEN_CUDA_RUNTIME_CHECK(expr) \
59 const cudaError_t _e = (expr); \
60 if (_e != cudaSuccess) EIGEN_GPU_CHECK_FAILED(cudaGetErrorName(_e), #expr, __FILE__, __LINE__); \
64inline void gpu_check_failed_code(
const char* library,
int status,
const char* expression,
const char* file,
int line) {
66 std::snprintf(error,
sizeof(error),
"%s status %d", library, status);
68 EIGEN_UNUSED_VARIABLE(expression);
69 EIGEN_UNUSED_VARIABLE(file);
70 EIGEN_UNUSED_VARIABLE(line);
71 EIGEN_GPU_CHECK_FAILED(error, expression, file, line);
78inline int to_blas_int(int64_t v) {
79 eigen_assert(v >= 0 && v <=
static_cast<int64_t
>((std::numeric_limits<int>::max)()) &&
80 "dimension exceeds the int range supported by cuBLAS / cuSOLVER");
81 return static_cast<int>(v);
99inline bool device_supports_memory_pools() {
100#ifdef EIGEN_GPU_NO_STREAM_ORDERED_ALLOC
103 static const bool supported = [] {
105 if (cudaGetDevice(&device) != cudaSuccess)
return false;
107 if (cudaDeviceGetAttribute(&v, cudaDevAttrMemoryPoolsSupported, device) != cudaSuccess)
return false;
108 if (v == 0)
return false;
112 cudaMemPool_t pool =
nullptr;
113 if (cudaDeviceGetDefaultMemPool(&pool, device) == cudaSuccess) {
116 unsigned long long threshold = ~0ULL;
117 (void)cudaMemPoolSetAttribute(pool, cudaMemPoolAttrReleaseThreshold, &threshold);
125inline void* device_malloc(
size_t bytes) {
127 if (device_supports_memory_pools()) {
128 EIGEN_CUDA_RUNTIME_CHECK(cudaMallocAsync(&p, bytes,
nullptr));
130 EIGEN_CUDA_RUNTIME_CHECK(cudaMalloc(&p, bytes));
135inline void device_free(
void* p)
noexcept {
137 if (device_supports_memory_pools()) {
138 (void)cudaFreeAsync(p,
nullptr);
144struct CudaFreeDeleter {
149 void operator()(
void* p)
const noexcept {
150 if (p && !borrow) device_free(p);
154struct CudaFreeHostDeleter {
155 void operator()(
void* p)
const noexcept {
156 if (p) (void)cudaFreeHost(p);
161struct CudaStreamDeleter {
163 void operator()(cudaStream_t s)
const noexcept {
164 if (owns && s) (void)cudaStreamDestroy(s);
167using UniqueStream = std::unique_ptr<std::remove_pointer_t<cudaStream_t>, CudaStreamDeleter>;
179template <
size_t SmallBufferThreshold = 256,
size_t MaxPoolSize = 64>
180struct DeviceBufferPool {
181 static constexpr size_t kSmallBufferThreshold = SmallBufferThreshold;
182 static constexpr size_t kMaxPoolSize = MaxPoolSize;
187 cudaEvent_t release_event;
197 enum class State :
signed char { kNotConstructed = 0, kAlive = 1, kDestroyed = 2 };
199 static State& threadState() {
200 thread_local State state = State::kNotConstructed;
204 DeviceBufferPool() { threadState() = State::kAlive; }
206 ~DeviceBufferPool() {
207 for (
const Entry& entry : free_list_) freeBlock(entry.ptr, entry.release_event);
208 for (cudaEvent_t event : spare_events_) (void)cudaEventDestroy(event);
209 threadState() = State::kDestroyed;
214 void* allocate(
size_t bytes) {
215 for (
auto it = free_list_.begin(); it != free_list_.end(); ++it) {
216 if (cudaEventQuery(it->release_event) != cudaSuccess)
break;
217 if (it->bytes >= bytes) {
219 spare_events_.push_back(it->release_event);
220 free_list_.erase(it);
224 return device_malloc(bytes);
228 void deallocate(
void* p,
size_t bytes)
noexcept {
229 if (free_list_.size() >= kMaxPoolSize) {
233 cudaEvent_t release_event = acquireEvent();
234 if (release_event ==
nullptr) {
238 if (cudaEventRecord(release_event,
nullptr) != cudaSuccess) {
239 freeBlock(p, release_event);
242 free_list_.push_back({p, bytes, release_event});
245 static DeviceBufferPool& threadLocal() {
246 thread_local DeviceBufferPool pool;
252 cudaEvent_t acquireEvent() noexcept {
253 if (!spare_events_.empty()) {
254 cudaEvent_t
event = spare_events_.back();
255 spare_events_.pop_back();
258 cudaEvent_t
event =
nullptr;
259 if (cudaEventCreateWithFlags(&event, cudaEventDisableTiming) != cudaSuccess)
return nullptr;
265 static void freeBlock(
void* p, cudaEvent_t release_event)
noexcept {
266 (void)cudaEventDestroy(release_event);
270 std::vector<Entry> free_list_;
272 std::vector<cudaEvent_t> spare_events_;
278struct PooledCudaFreeDeleter {
281 void operator()(
void* p)
const noexcept {
283 if (size > 0 && size <= DeviceBufferPool<>::kSmallBufferThreshold &&
284 DeviceBufferPool<>::threadState() == DeviceBufferPool<>::State::kAlive) {
285 DeviceBufferPool<>::threadLocal().deallocate(p, size);
295 DeviceBuffer() =
default;
297 explicit DeviceBuffer(
size_t bytes) : bytes_(bytes) {
303 const bool pooled = bytes <= DeviceBufferPool<>::kSmallBufferThreshold && !device_supports_memory_pools() &&
304 DeviceBufferPool<>::threadState() != DeviceBufferPool<>::State::kDestroyed;
305 void* p = pooled ? DeviceBufferPool<>::threadLocal().allocate(bytes) : device_malloc(bytes);
306 ptr_ = std::unique_ptr<void, PooledCudaFreeDeleter>(p, PooledCudaFreeDeleter{pooled ? bytes : 0});
313 DeviceBuffer(DeviceBuffer&& o) noexcept : ptr_(std::move(o.ptr_)), bytes_(o.bytes_) { o.bytes_ = 0; }
314 DeviceBuffer& operator=(DeviceBuffer&& o)
noexcept {
316 ptr_ = std::move(o.ptr_);
323 void* get()
const noexcept {
return ptr_.get(); }
324 void* release()
noexcept {
326 return ptr_.release();
328 explicit operator bool()
const noexcept {
return static_cast<bool>(ptr_); }
331 size_t size() const noexcept {
return bytes_; }
336 static DeviceBuffer adopt(
void* p,
size_t bytes)
noexcept {
338 b.ptr_ = std::unique_ptr<void, PooledCudaFreeDeleter>(p, PooledCudaFreeDeleter{});
339 b.bytes_ = p ? bytes : 0;
344 std::unique_ptr<void, PooledCudaFreeDeleter> ptr_;
350class PinnedHostBuffer {
352 PinnedHostBuffer() =
default;
354 explicit PinnedHostBuffer(
size_t bytes) {
357 EIGEN_CUDA_RUNTIME_CHECK(cudaMallocHost(&p, bytes));
362 void* get() const noexcept {
return ptr_.get(); }
363 explicit operator bool() const noexcept {
return static_cast<bool>(ptr_); }
366 std::unique_ptr<void, CudaFreeHostDeleter> ptr_;
373template <
typename Scalar>
374void upload_host_matrix(Scalar* dst, Index dst_outer_stride,
const Scalar* src, Index src_outer_stride, Index rows,
375 Index cols, cudaStream_t stream) {
376 if (rows <= 0 || cols <= 0)
return;
377 eigen_assert(dst_outer_stride >= rows);
378 const size_t column_bytes =
static_cast<size_t>(rows) *
sizeof(Scalar);
379 if (src_outer_stride >= rows) {
380 EIGEN_CUDA_RUNTIME_CHECK(cudaMemcpy2DAsync(dst,
static_cast<size_t>(dst_outer_stride) *
sizeof(Scalar), src,
381 static_cast<size_t>(src_outer_stride) *
sizeof(Scalar), column_bytes,
382 static_cast<size_t>(cols), cudaMemcpyHostToDevice, stream));
384 for (Index col = 0; col < cols; ++col) {
385 EIGEN_CUDA_RUNTIME_CHECK(cudaMemcpyAsync(dst + col * dst_outer_stride, src + col * src_outer_stride, column_bytes,
386 cudaMemcpyHostToDevice, stream));
393template <
typename Scalar>
394struct cuda_data_type;
397struct cuda_data_type<float> {
398 static constexpr cudaDataType_t value = CUDA_R_32F;
401struct cuda_data_type<double> {
402 static constexpr cudaDataType_t value = CUDA_R_64F;
405struct cuda_data_type<std::complex<float>> {
406 static constexpr cudaDataType_t value = CUDA_C_32F;
409struct cuda_data_type<std::complex<double>> {
410 static constexpr cudaDataType_t value = CUDA_C_64F;
Internal RAII owner for an untyped GPU device allocation.
Definition GpuSupport.h:293
size_t size() const noexcept
Definition GpuSupport.h:331
Namespace containing all symbols from the Eigen library.