#pragma once
#include <cuda_runtime.h>
#include <cstddef>
#include <limits>
#include <new>
#include "common.h"
namespace xgboost::common::cuda_impl {
template <typename T>
struct PinnedAllocPolicy {
using pointer = T*; using const_pointer = const T*; using size_type = std::size_t; using value_type = T;
[[nodiscard]] constexpr size_type max_size() const { return std::numeric_limits<size_type>::max() / sizeof(value_type);
}
[[nodiscard]] pointer allocate(size_type cnt, const_pointer = nullptr) const { if (cnt > this->max_size()) {
throw std::bad_array_new_length{};
}
pointer result(nullptr);
dh::safe_cuda(cudaMallocHost(reinterpret_cast<void**>(&result), cnt * sizeof(value_type)));
return result;
}
void deallocate(pointer p, size_type) { dh::safe_cuda(cudaFreeHost(p)); } };
template <typename T>
struct ManagedAllocPolicy {
using pointer = T*; using const_pointer = const T*; using size_type = std::size_t; using value_type = T;
[[nodiscard]] constexpr size_type max_size() const { return std::numeric_limits<size_type>::max() / sizeof(value_type);
}
[[nodiscard]] pointer allocate(size_type cnt, const_pointer = nullptr) const { if (cnt > this->max_size()) {
throw std::bad_array_new_length{};
}
pointer result(nullptr);
dh::safe_cuda(cudaMallocManaged(reinterpret_cast<void**>(&result), cnt * sizeof(value_type)));
return result;
}
void deallocate(pointer p, size_type) { dh::safe_cuda(cudaFree(p)); } };
template <typename T>
struct SamAllocPolicy {
using pointer = T*; using const_pointer = const T*; using size_type = std::size_t; using value_type = T;
[[nodiscard]] constexpr size_type max_size() const { return std::numeric_limits<size_type>::max() / sizeof(value_type);
}
[[nodiscard]] pointer allocate(size_type cnt, const_pointer = nullptr) const { if (cnt > this->max_size()) {
throw std::bad_array_new_length{};
}
size_type n_bytes = cnt * sizeof(value_type);
pointer result = reinterpret_cast<pointer>(std::malloc(n_bytes));
if (!result) {
throw std::bad_alloc{};
}
dh::safe_cuda(cudaHostRegister(result, n_bytes, cudaHostRegisterDefault));
return result;
}
void deallocate(pointer p, size_type) { dh::safe_cuda(cudaHostUnregister(p));
std::free(p);
}
};
template <typename T, template <typename> typename Policy>
class CudaHostAllocatorImpl : public Policy<T> {
public:
using typename Policy<T>::value_type;
using typename Policy<T>::pointer;
using typename Policy<T>::const_pointer;
using typename Policy<T>::size_type;
using reference = value_type&; using const_reference = const value_type&;
using difference_type = std::ptrdiff_t;
template <typename U>
struct rebind { using other = CudaHostAllocatorImpl<U, Policy>; };
CudaHostAllocatorImpl() = default;
~CudaHostAllocatorImpl() = default;
CudaHostAllocatorImpl(CudaHostAllocatorImpl const&) = default;
CudaHostAllocatorImpl& operator=(CudaHostAllocatorImpl const& that) = default;
CudaHostAllocatorImpl& operator=(CudaHostAllocatorImpl&& that) = default;
template <typename U>
CudaHostAllocatorImpl(CudaHostAllocatorImpl<U, Policy> const&) {}
pointer address(reference r) { return &r; } const_pointer address(const_reference r) { return &r; }
bool operator==(CudaHostAllocatorImpl const&) const { return true; }
bool operator!=(CudaHostAllocatorImpl const& x) const { return !operator==(x); }
};
template <typename T>
using PinnedAllocator = CudaHostAllocatorImpl<T, PinnedAllocPolicy>;
template <typename T>
using ManagedAllocator = CudaHostAllocatorImpl<T, ManagedAllocPolicy>;
template <typename T>
using SamAllocator = CudaHostAllocatorImpl<T, SamAllocPolicy>;
}