35 virtual uint8_t *
get() = 0;
37 virtual size_t bytes()
const = 0;
45 virtual std::unique_ptr<DeviceHostSingleAllocationBase>
clone() = 0;
55 std::vector<uint8_t> host_data;
73 host_data.resize(bytes_);
81 device_storage =
nullptr;
84 size_t bytes()
const override {
return bytes_; }
85 uint8_t *
get()
override
88 return host_data.data();
92 return device_storage->raw_ptr();
101 std::copy(host_data.begin(), host_data.begin() +
n, host_dst);
105 device_storage->copy_device_to_host(host_dst,
n);
113 std::copy(host_src, host_src +
n, host_data.begin());
117 device_storage->copy_host_to_device(host_src,
n);
121 std::unique_ptr<DeviceHostSingleAllocationBase>
clone()
override
123 auto ret = std::make_unique<t_self>();
125 return std::move(ret);
126 ret->allocate(bytes_, B_);
129 std::copy(host_data.begin(), host_data.end(), ret->host_data.begin());
133 device_storage->copy_to_device(ret->get(), bytes_);
136 return std::move(ret);
146 template <DeviceBackend B,
typename T,
typename TSize =
int64_t>
149 static_assert(std::is_trivially_copyable_v<T> && std::is_default_constructible_v<T>,
150 "host_device_vector elements must be trivially_copyable and default_constructible");
177 template <
class T,
class Derived>
180 static_assert(std::is_trivially_copyable_v<T> && std::is_default_constructible_v<T>,
181 "data_vector_base elements must be trivially_copyable and default_constructible");
184 T &
operator[](
size_t i) {
return static_cast<Derived *
>(
this)->data()[i]; }
186 const T &
operator[](
size_t i)
const {
return static_cast<const Derived *
>(
this)->data()[i]; }
188 const T &
at(
size_t i)
const
190 auto *dThis =
static_cast<const Derived *
>(
this);
191 DNDS_check_throw_info(dThis->size() > i, std::to_string(i) +
" --- " + std::to_string(dThis->size()));
197 auto *dThis =
static_cast<Derived *
>(
this);
198 DNDS_check_throw_info(dThis->size() > i, std::to_string(i) +
" --- " + std::to_string(dThis->size()));
215 template <
typename T>
218 static_assert(std::is_trivially_copyable_v<T> && std::is_default_constructible_v<T>,
219 "host_device_vector elements must be trivially_copyable and default_constructible");
223 std::unique_ptr<DeviceHostSingleAllocationBase> host_data = std::make_unique<DeviceHostSingleAllocationDirect>();
224 std::unique_ptr<DeviceHostSingleAllocationBase> device_data = std::make_unique<DeviceHostSingleAllocationDirect>();
225 T *host_ptr =
reinterpret_cast<T *
>(host_data->get());
226 T *device_ptr =
reinterpret_cast<T *
>(device_data->get());
229 void sync_device_ptr()
231 device_ptr =
reinterpret_cast<T *
>(device_data->get());
236 host_ptr =
reinterpret_cast<T *
>(host_data->get());
247 template <
class TFill>
250 this->
resize(n, std::forward<TFill>(val));
257 std::copy(
v.begin(),
v.end(), host_ptr);
276 template <
class TFill>
280 std::fill(this->
begin(), this->
end(), std::forward<TFill>(
fill));
286 device_data->allocate(size_ *
sizeof(T), B);
308 std::copy(this->
begin(), this->
end(), ret.begin());
317 device_data->bytes() !=
this->size() *
sizeof(T) ||
318 device_data->device() != backend)
321 device_data->copy_from_host(
reinterpret_cast<uint8_t *
>(this->
data()), this->
size() *
sizeof(T));
343 DNDS_assert(device_data && host_data && device_data->bytes() == host_data->bytes());
344 device_data->copy_to_host(
reinterpret_cast<uint8_t *
>(this->
data()), this->
size() *
sizeof(T));
348 template <DeviceBackend B,
typename TSize =
int64_t>
350 template <DeviceBackend B,
typename TSize =
int64_t>
365 this->size_ =
R.size();
366 this->host_data =
R.host_data->clone();
367 this->sync_host_ptr();
368 this->device_data =
R.device_data->clone();
373 this->sync_device_ptr();
379 this->size_ =
R.size();
380 this->host_data =
R.host_data->clone();
381 this->sync_host_ptr();
382 this->device_data =
R.device_data->clone();
387 this->sync_device_ptr();
397 R.device_data.swap(device_data);
398 std::swap(
R.device_ptr, device_ptr);
399 R.host_data.swap(host_data);
400 std::swap(
R.host_ptr, host_ptr);
401 std::swap(
R.size_, size_);
412 template <
typename T>
415 static_assert(std::is_trivially_copyable_v<T> && std::is_default_constructible_v<T>,
416 "host_device_vector elements must be trivially_copyable and default_constructible");
418 using t_base::t_base;
427 this->t_base::operator=(
v);
446 deviceStorage->copy_host_to_device(
reinterpret_cast<uint8_t *
>(this->data()), this->size() *
sizeof(T));
454 deviceStorage->copy_device_to_host(
reinterpret_cast<uint8_t *
>(this->data()), this->size() *
sizeof(T));
463 template <DeviceBackend B,
typename TSize =
int64_t>
465 template <DeviceBackend B,
typename TSize =
int64_t>
485 this->t_base::operator=(
R);
489 R.deviceStorage->copy_to_device(this->deviceStorage->raw_ptr(),
this->deviceStorage->bytes());
501 R.deviceStorage->copy_to_device(this->deviceStorage->raw_ptr(),
this->deviceStorage->bytes());
520 R.deviceStorage.swap(this->deviceStorage);
#define DNDS_DEVICE_TRIVIAL_COPY_DEFINE(T, T_Self)
#define DNDS_DEVICE_CALLABLE
Device memory abstraction layer with backend-specific storage and factory creation.
Assertion / error-handling macros and supporting helper functions.
#define DNDS_assert_info(expr, info)
Debug-only assertion with an extra std::string info message.
#define DNDS_assert(expr)
Debug-only assertion (compiled out when DNDS_NDEBUG is defined). Prints the expression + file/line + ...
#define DNDS_check_throw_info(expr, info)
Same as DNDS_check_throw but attaches a user-supplied info message to the thrown std::runtime_error.
#define DNDS_HD_assert(cond)
Host-only expansion of DNDS_HD_assert (equivalent to DNDS_assert).
#define DNDS_check_throw(expr)
Runtime check active in both debug and release builds. Throws std::runtime_error if expr evaluates to...
Abstract single-allocation owning byte buffer.
virtual void free()=0
Release the allocation.
virtual size_t bytes() const =0
Allocation size in bytes.
virtual void allocate(size_t bytes, DeviceBackend B=DeviceBackend::Unknown)=0
Allocate bytes on backend B (or on the host when Unknown).
virtual std::unique_ptr< DeviceHostSingleAllocationBase > clone()=0
Deep copy; returns a new allocation containing the same bytes.
virtual void copy_from_host(uint8_t *host_src, size_t n)=0
Copy n bytes from host_src into this allocation.
virtual uint8_t * get()=0
Typed byte pointer to the current allocation.
virtual void copy_to_host(uint8_t *host_dst, size_t n)=0
Copy n bytes from this allocation into host_dst.
virtual DeviceBackend device()=0
Which backend currently owns the allocation.
virtual ~DeviceHostSingleAllocationBase()
DeviceHostSingleAllocationBase()=default
Concrete DeviceHostSingleAllocationBase using std::vector<uint8_t> for host memory and DeviceStorage ...
void copy_from_host(uint8_t *host_src, size_t n) override
Copy n bytes from host_src into this allocation.
uint8_t * get() override
Typed byte pointer to the current allocation.
void free() override
Release the allocation.
void allocate(size_t bytes, DeviceBackend B=DeviceBackend::Unknown) override
Allocate bytes on backend B (or on the host when Unknown).
DeviceBackend device() override
Which backend currently owns the allocation.
void copy_to_host(uint8_t *host_dst, size_t n) override
Copy n bytes from this allocation into host_dst.
~DeviceHostSingleAllocationDirect() override
DeviceHostSingleAllocationDirect()=default
std::unique_ptr< DeviceHostSingleAllocationBase > clone() override
Deep copy; returns a new allocation containing the same bytes.
size_t bytes() const override
Allocation size in bytes.
CRTP base offering operator[] / at on top of a derived's data() and size() accessors....
const T & operator[](size_t i) const
const T & at(size_t i) const
Host + optional device vector of trivially copyable T.
DNDS_HOST void to_device(DeviceBackend backend=DeviceBackend::Host)
DNDS_HOST T * dataDevice()
DNDS_HOST void resize(size_t new_size, TFill &&fill)
t_self & operator=(const t_self &R)
DNDS_HOST void swap(t_self &R) noexcept
DNDS_HOST auto end() const
DNDS_HOST auto cend() const
DNDS_HOST host_device_vector_r1(const std::vector< T > &v)
DNDS_HOST void create_device_data(DeviceBackend B)
DNDS_HOST void clear_device()
DNDS_HOST t_self & operator=(const std::vector< T > &v)
DNDS_HOST auto cbegin() const
host_device_vector_r1(const t_self &R)
DNDS_HOST auto begin() const
DNDS_HOST host_device_vector_r1(size_t n)
t_deviceView< B, TSize > deviceView()
DNDS_HOST void resize(size_t new_size)
DNDS_HOST const T * data() const
vector_DeviceView< B, T, TSize > t_deviceView
DNDS_HOST size_t size() const
DNDS_HOST const T * dataDevice() const
DNDS_HOST host_device_vector_r1()=default
DNDS_HOST host_device_vector_r1(size_t n, TFill &&val)
Non-owning device-callable view {pointer, size} over a typed array.
DNDS_DEVICE_CALLABLE T operator[](TSize i) const
DNDS_DEVICE_CALLABLE TSize size() const
DNDS_DEVICE_CALLABLE T & operator[](TSize i)
the host side operators are provided as implemented
t_supDeviceStorageBase device_storage_create(DeviceBackend backend, size_t n_bytes)
Top-level factory: dispatches to the per-backend factory based on backend. Returns a null unique_ptr ...
DeviceBackend
Enumerates the backends a DeviceStorage / Array can live on.
@ Unknown
Unset / sentinel.
const char * device_backend_name(DeviceBackend B)
Canonical string name for a DeviceBackend (used in log messages).
std::unique_ptr< DeviceStorageBase, std::function< void(DeviceStorageBase *)> > t_supDeviceStorageBase
Owning unique pointer to a DeviceStorageBase with cross-DLL-safe deleter.
t_supDeviceStorageBase null_supDeviceStorageBase()
Null-value helper for t_supDeviceStorageBase.
Legacy std::vector<T> subclass with an optional device mirror.
host_device_vector_r0< T > t_self
t_deviceView< B, TSize > deviceView()
vector_DeviceView< B, T, TSize > t_deviceView
const T * dataDevice() const
void to_device(DeviceBackend backend=DeviceBackend::Host)
DNDS_HOST t_self & operator=(const std::vector< T > &v)
void swap(t_self &R) noexcept
t_supDeviceStorageBase deviceStorage
t_self & operator=(const t_self &R)
host_device_vector_r0(const t_self &R)
DNDS_HOST host_device_vector_r0(const std::vector< T > &v)
Eigen::Matrix< real, 5, 1 > v
Eigen::Vector3d n(1.0, 0.0, 0.0)