-
Notifications
You must be signed in to change notification settings - Fork 188
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement
cuda::uninitialized_buffer
This `uninitialized_buffer` provides an allocation of `N` elements of type `T` utilitzing a `cuda::mr::resource` to allocate the storage. The buffer takes care of alignment and deallocation of the storage. The user is required to ensure that the lifetime of the memory resource exceeds the lifetime of the buffer.
- Loading branch information
Showing
5 changed files
with
434 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// Part of the CUDA Toolkit, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef _CUDA_BUFFER | ||
#define _CUDA_BUFFER | ||
|
||
#include <cuda/std/detail/__config> | ||
|
||
#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) | ||
# pragma GCC system_header | ||
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) | ||
# pragma clang system_header | ||
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) | ||
# pragma system_header | ||
#endif // no system header | ||
|
||
#include <cuda/__memory_resource/properties.h> | ||
#include <cuda/__memory_resource/resource_ref.h> | ||
#include <cuda/std/__concepts/_One_of.h> | ||
#include <cuda/std/__memory/align.h> | ||
#include <cuda/std/span> | ||
|
||
#if _CCCL_STD_VER >= 2014 && !defined(_CCCL_COMPILER_MSVC_2017) \ | ||
&& defined(LIBCUDACXX_ENABLE_EXPERIMENTAL_MEMORY_RESOURCE) | ||
|
||
//! @file The cuda::uninitialized_buffer class provides a typed buffer allocated from a given memory resource. | ||
|
||
_LIBCUDACXX_BEGIN_NAMESPACE_CUDA | ||
|
||
//! @rst | ||
//! .. _libcudacxx-extended-api-memory-resources-buffer: | ||
//! The `cuda::uninitialized_buffer` memory storage | ||
//! ----------------------------------------------- | ||
//! | ||
//! ``cuda::uninitialized_buffer`` provides a typed buffer allocated from a given `memory resource | ||
//! <libcudacxx-extended-api-memory-resources-resource>`. It handles alignment and release of the allocation. | ||
//! The memory is uninitialized, so that a user needs to ensure elements are properly constructed. | ||
//! In addition to being type safe, ``cuda::uninitialized_buffer`` also takes a set of `properties | ||
//! <libcudacxx-extended-api-memory-resources-properties>` to ensure that e.g. execution space constraints are checked | ||
//! at compile time. However, we can only forward stateless propertie. If a user wants to a a stateful one, then they | ||
//! need to implement ``get_property(const device_buffer&, Property)``. | ||
//! | ||
//! .. note:: | ||
//! | ||
//! ``cuda::uninitialized_buffer`` stores a reference to the provided memory `memory resource | ||
//! <libcudacxx-extended-api-memory-resources-resource>`. It is the users resposibility to ensure the lifetime of the | ||
//! resource exceeds the lifetime of the buffer. | ||
//! | ||
//! @endrst | ||
template <class _Tp, class... _Properties> | ||
class uninitialized_buffer | ||
{ | ||
private: | ||
_CUDA_VMR::resource_ref<_Properties...> __mr_; | ||
size_t __count_ = 0; | ||
void* __buf_ = nullptr; | ||
|
||
//! @brief Determines the allocation size given the alignment and size of @p _Tp | ||
_CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY static constexpr size_t | ||
__get_allocation_size(const size_t __count) noexcept | ||
{ | ||
constexpr size_t __alignment = alignof(_Tp); | ||
return (__count * sizeof(_Tp) + (__alignment - 1)) & ~(__alignment - 1); | ||
} | ||
|
||
//! @brief Determines the properly aligned start of the buffer given the alignment and size of @p _Tp | ||
_CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp* __get_data() const noexcept | ||
{ | ||
constexpr size_t __alignment = alignof(_Tp); | ||
size_t __space = __get_allocation_size(__count_); | ||
void* __ptr = __buf_; | ||
return reinterpret_cast<_Tp*>(_CUDA_VSTD::align(__alignment, __count_ * sizeof(_Tp), __ptr, __space)); | ||
} | ||
|
||
public: | ||
//! @brief Constructs a ``cuda::uninitilaized_buffer`` from a memory resource and a number of elements | ||
//! @param __mr The memory resource to allocate the buffer with. | ||
//! @param __count The desired size of the buffer. | ||
uninitialized_buffer(_CUDA_VMR::resource_ref<_Properties...> __mr, const size_t __count) | ||
: __mr_(__mr) | ||
, __count_(__count) | ||
, __buf_(__mr_.allocate(__get_allocation_size(__count_))) | ||
{} | ||
|
||
uninitialized_buffer(const uninitialized_buffer&) = delete; | ||
uninitialized_buffer& operator=(const uninitialized_buffer&) = delete; | ||
uninitialized_buffer(uninitialized_buffer&& __other) noexcept | ||
: __mr_(__other.__mr_) | ||
, __count_(__other.__count_) | ||
, __buf_(__other.__buf_) | ||
{ | ||
__other.__count_ = 0; | ||
__other.__buf_ = nullptr; | ||
} | ||
uninitialized_buffer& operator=(uninitialized_buffer&& __other) noexcept | ||
{ | ||
if (__buf_) | ||
{ | ||
__mr_.deallocate(__buf_, __get_allocation_size(__count_)); | ||
} | ||
__mr_ = __other.__mr_; | ||
__count_ = __other.__count_; | ||
__buf_ = __other.__buf_; | ||
__other.__count_ = 0; | ||
__other.__buf_ = nullptr; | ||
return *this; | ||
} | ||
|
||
//! @brief Destroys a ``cuda::uninitilaized_buffer`` deallocating the buffer | ||
~uninitialized_buffer() | ||
{ | ||
if (__buf_) | ||
{ | ||
__mr_.deallocate(__buf_, __get_allocation_size(__count_)); | ||
} | ||
} | ||
|
||
//! @brief Returns an aligned pointer to the buffer | ||
_CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp* begin() noexcept | ||
{ | ||
return __get_data(); | ||
} | ||
|
||
//! @brief Returns an aligned pointer to the buffer | ||
_CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp* begin() const noexcept | ||
{ | ||
return __get_data(); | ||
} | ||
|
||
//! @brief Returns an aligned pointer to end of the buffer | ||
_CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp* end() noexcept | ||
{ | ||
return __get_data() + __count_; | ||
} | ||
|
||
//! @brief Returns an aligned pointer to end of the buffer | ||
_CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp* end() const noexcept | ||
{ | ||
return __get_data() + __count_; | ||
} | ||
|
||
//! @brief Returns an aligned pointer to the buffer | ||
_CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp* data() noexcept | ||
{ | ||
return __get_data(); | ||
} | ||
|
||
//! @brief Returns an aligned pointer to the buffer | ||
_CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp* data() const noexcept | ||
{ | ||
return __get_data(); | ||
} | ||
|
||
//! @brief Returns the size of the buffer | ||
_CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr size_t size() noexcept | ||
{ | ||
return __count_; | ||
} | ||
|
||
//! @brief Returns the size of the buffer | ||
_CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr size_t size() const noexcept | ||
{ | ||
return __count_; | ||
} | ||
|
||
//! @rst | ||
//! @brief Forwards the `property <libcudacxx-extended-api-memory-resources-properties>` from the passed resource | ||
//! @endrst | ||
_LIBCUDACXX_TEMPLATE(class _Property) | ||
_LIBCUDACXX_REQUIRES((!property_with_value<_Property>) _LIBCUDACXX_AND _CUDA_VSTD::_One_of<_Property, _Properties...>) | ||
friend constexpr void get_property(const uninitialized_buffer&, _Property) noexcept {} | ||
}; | ||
|
||
template <class _Tp> | ||
using uninitialized_device_buffer = uninitialized_buffer<_Tp, _CUDA_VMR::device_accessible>; | ||
|
||
_LIBCUDACXX_END_NAMESPACE_CUDA | ||
|
||
#endif // _CCCL_STD_VER >= 2014 && !_CCCL_COMPILER_MSVC_2017 && LIBCUDACXX_ENABLE_EXPERIMENTAL_MEMORY_RESOURCE | ||
|
||
#endif //_CUDA_BUFFER |
61 changes: 61 additions & 0 deletions
61
libcudacxx/test/libcudacxx/cuda/uninitialized_buffer/access.pass.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// Part of the libcu++ Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
// UNSUPPORTED: c++03, c++11 | ||
// UNSUPPORTED: msvc-19.16 | ||
// UNSUPPORTED: nvrtc | ||
#define LIBCUDACXX_ENABLE_EXPERIMENTAL_MEMORY_RESOURCE | ||
|
||
#include <cuda/buffer> | ||
#include <cuda/memory_resource> | ||
#include <cuda/std/cassert> | ||
#include <cuda/std/span> | ||
|
||
#include <nv/target> | ||
|
||
struct do_not_construct | ||
{ | ||
do_not_construct() | ||
{ | ||
assert(false); | ||
} | ||
}; | ||
|
||
template <class T> | ||
void test() | ||
{ | ||
using uninitialized_buffer = cuda::uninitialized_buffer<T>; | ||
|
||
cuda::mr::cuda_memory_resource resource{}; | ||
const uninitialized_buffer buf{resource, 42}; | ||
assert(buf.data() != nullptr); | ||
assert(buf.size() == 42); | ||
|
||
assert(buf.begin() == buf.data()); | ||
assert(buf.end() == buf.begin() + buf.size()); | ||
} | ||
|
||
void test() | ||
{ | ||
test<char>(); | ||
test<short>(); | ||
test<int>(); | ||
test<long>(); | ||
test<long long>(); | ||
test<float>(); | ||
test<double>(); | ||
test<do_not_construct>(); | ||
} | ||
|
||
int main(int arg, char** argv) | ||
{ | ||
NV_IF_TARGET(NV_IS_HOST, (test();)); | ||
|
||
return 0; | ||
} |
89 changes: 89 additions & 0 deletions
89
libcudacxx/test/libcudacxx/cuda/uninitialized_buffer/construct.pass.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// Part of the libcu++ Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
// UNSUPPORTED: c++03, c++11 | ||
// UNSUPPORTED: msvc-19.16 | ||
// UNSUPPORTED: nvrtc | ||
#define LIBCUDACXX_ENABLE_EXPERIMENTAL_MEMORY_RESOURCE | ||
|
||
#include <cuda/buffer> | ||
#include <cuda/memory_resource> | ||
#include <cuda/std/cassert> | ||
#include <cuda/std/type_traits> | ||
|
||
#include <nv/target> | ||
|
||
struct do_not_construct | ||
{ | ||
do_not_construct() | ||
{ | ||
assert(false); | ||
} | ||
}; | ||
|
||
template <class T> | ||
void test() | ||
{ | ||
using uninitialized_buffer = cuda::uninitialized_buffer<T>; | ||
static_assert(!cuda::std::is_default_constructible<uninitialized_buffer>::value, ""); | ||
static_assert(!cuda::std::is_copy_constructible<uninitialized_buffer>::value, ""); | ||
static_assert(!cuda::std::is_copy_assignable<uninitialized_buffer>::value, ""); | ||
|
||
cuda::mr::cuda_memory_resource resource{}; | ||
{ | ||
uninitialized_buffer buf{resource, 42}; | ||
assert(buf.data() != nullptr); | ||
assert(buf.size() == 42); | ||
} | ||
{ | ||
uninitialized_buffer input{resource, 42}; | ||
const T* ptr = input.data(); | ||
|
||
uninitialized_buffer from_rvalue{cuda::std::move(input)}; | ||
assert(from_rvalue.data() == ptr); | ||
assert(from_rvalue.size() == 42); | ||
|
||
// Ensure that we properly reset the input buffer | ||
assert(input.data() == nullptr); | ||
assert(input.size() == 0); | ||
} | ||
|
||
{ | ||
uninitialized_buffer input{resource, 42}; | ||
const T* ptr = input.data(); | ||
|
||
uninitialized_buffer assign_rvalue{resource, 1337}; | ||
assign_rvalue = cuda::std::move(input); | ||
assert(assign_rvalue.data() == ptr); | ||
assert(assign_rvalue.size() == 42); | ||
|
||
// Ensure that we properly reset the input buffer | ||
assert(input.data() == nullptr); | ||
assert(input.size() == 0); | ||
} | ||
} | ||
|
||
void test() | ||
{ | ||
test<char>(); | ||
test<short>(); | ||
test<int>(); | ||
test<long>(); | ||
test<long long>(); | ||
test<float>(); | ||
test<double>(); | ||
test<do_not_construct>(); | ||
} | ||
|
||
int main(int arg, char** argv) | ||
{ | ||
NV_IF_TARGET(NV_IS_HOST, (test();)); | ||
|
||
return 0; | ||
} |
35 changes: 35 additions & 0 deletions
35
libcudacxx/test/libcudacxx/cuda/uninitialized_buffer/properties.pass.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// Part of the libcu++ Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
// UNSUPPORTED: c++03, c++11 | ||
// UNSUPPORTED: msvc-19.16 | ||
// UNSUPPORTED: nvrtc | ||
#define LIBCUDACXX_ENABLE_EXPERIMENTAL_MEMORY_RESOURCE | ||
|
||
#include <cuda/buffer> | ||
#include <cuda/memory_resource> | ||
|
||
static_assert( | ||
cuda::has_property<cuda::uninitialized_buffer<int, cuda::mr::device_accessible>, cuda::mr::device_accessible>, ""); | ||
|
||
struct my_property | ||
{ | ||
using value_type = int; | ||
}; | ||
|
||
constexpr int get_property(const cuda::uninitialized_buffer<int, my_property>&, my_property) | ||
{ | ||
return 42; | ||
} | ||
static_assert(cuda::has_property<cuda::uninitialized_buffer<int, my_property>, my_property>, ""); | ||
|
||
int main(int arg, char** argv) | ||
{ | ||
return 0; | ||
} |
Oops, something went wrong.