Skip to content

Commit

Permalink
Make any_resource emplacable (#2425)
Browse files Browse the repository at this point in the history
* Rename `async_any_resource` to `any_async_resource`

* Add a way of constructing an `any_{async_}resource` from a set of arguments and a tag type

---------

Co-authored-by: Allison Piper <[email protected]>
  • Loading branch information
miscco and alliepiper committed Sep 19, 2024
1 parent 2fe09c8 commit 8b2bf13
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ template <class _Tp, class... _Properties>
class uninitialized_async_buffer
{
private:
using __async_resource = ::cuda::experimental::mr::async_any_resource<_Properties...>;
using __async_resource = ::cuda::experimental::mr::any_async_resource<_Properties...>;
__async_resource __mr_;
::cuda::stream_ref __stream_ = {};
size_t __count_ = 0;
Expand Down Expand Up @@ -204,7 +204,7 @@ public:
}

//! @rst
//! Returns a \c const reference to the :ref:`any_async_resource <cudax-memory-resource-async-any-resource>`
//! Returns a \c const reference to the :ref:`any_async_resource <cudax-memory-resource-any-async-resource>`
//! that holds the memory resource used to allocate the buffer
//! @endrst
_CCCL_NODISCARD const __async_resource& get_resource() const noexcept
Expand Down
94 changes: 86 additions & 8 deletions cudax/include/cuda/experimental/__memory_resource/any_resource.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,15 @@
#include <cuda/std/__concepts/__concept_macros.h>
#include <cuda/std/__concepts/_One_of.h>
#include <cuda/std/__concepts/all_of.h>
#include <cuda/std/__new_>
#include <cuda/std/__type_traits/is_constructible.h>
#include <cuda/std/__type_traits/is_nothrow_constructible.h>
#include <cuda/std/__type_traits/is_same.h>
#include <cuda/std/__type_traits/remove_cvref.h>
#include <cuda/std/__type_traits/type_set.h>
#include <cuda/std/__utility/exchange.h>
#include <cuda/std/__utility/forward.h>
#include <cuda/std/__utility/in_place.h>

namespace cuda::experimental::mr
{
Expand Down Expand Up @@ -77,19 +81,25 @@ private:

using __vtable = _CUDA_VMR::_Filtered_vtable<_Properties...>;

//! @brief Validates that a set of \c _OtherProperties... is a superset of \c _Properties... .
template <class... _OtherProperties>
static constexpr bool __properties_match =
_CUDA_VSTD::__type_set_contains<_CUDA_VSTD::__make_type_set<_OtherProperties...>, _Properties...>;

//! @brief Validates that a passed in \c _Resource satisfies the \c resource or \c async_resource concept respectively
//! as well as all properties in \c _Properties... .
template <class _Resource>
static constexpr bool __valid_resource =
_Alloc_type == _CUDA_VMR::_AllocType::_Async
? _CUDA_VMR::async_resource_with<_Resource, _Properties...>
: _CUDA_VMR::resource_with<_Resource, _Properties...>;

public:
//! @brief Constructs a \c basic_any_resource from a type that satisfies the \c resource or \c async_resource
//! concept as well as all properties.
//! @param __res The resource to be wrapped within the \c basic_any_resource.
_LIBCUDACXX_TEMPLATE(class _Resource, class __resource_t = _CUDA_VSTD::remove_cvref_t<_Resource>)
_LIBCUDACXX_REQUIRES(
(!__is_basic_any_resource<_Resource>) _LIBCUDACXX_AND(_CUDA_VMR::resource_with<__resource_t, _Properties...>)
_LIBCUDACXX_AND(_Alloc_type != _CUDA_VMR::_AllocType::_Async
|| (_CUDA_VMR::async_resource_with<__resource_t, _Properties...>) ))
_LIBCUDACXX_REQUIRES((!__is_basic_any_resource<_Resource>) _LIBCUDACXX_AND __valid_resource<__resource_t>)
basic_any_resource(_Resource&& __res) noexcept
: _CUDA_VMR::_Resource_base<_Alloc_type, _CUDA_VMR::_WrapperType::_Owning>(
nullptr, &_CUDA_VMR::__alloc_vtable<_Alloc_type, _CUDA_VMR::_WrapperType::_Owning, __resource_t>)
Expand All @@ -105,8 +115,31 @@ public:
}
}

//! @brief Constructs a \c basic_any_resource wrapping an object of type \c _Resource that
//! is constructed from \c __args... . \c _Resource must satisfy the \c resource or \c async_resource
//! concept, and it must provide all properties in \c _Properties... .
//! @param __args The arguments used to construct the instance of \c _Resource to be wrapped within the
//! \c basic_any_resource.
_LIBCUDACXX_TEMPLATE(class _Resource, class... _Args)
_LIBCUDACXX_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_constructible, _Resource, _Args...)
_LIBCUDACXX_AND __valid_resource<_Resource>)
basic_any_resource(_CUDA_VSTD::in_place_type_t<_Resource>, _Args&&... __args) noexcept
: _CUDA_VMR::_Resource_base<_Alloc_type, _CUDA_VMR::_WrapperType::_Owning>(
nullptr, &_CUDA_VMR::__alloc_vtable<_Alloc_type, _CUDA_VMR::_WrapperType::_Owning, _Resource>)
, __vtable(__vtable::template _Create<_Resource>())
{
if constexpr (_CUDA_VMR::_IsSmall<_Resource>())
{
::new (static_cast<void*>(this->__object.__buf_)) _Resource(_CUDA_VSTD::forward<_Args>(__args)...);
}
else
{
this->__object.__ptr_ = new _Resource(_CUDA_VSTD::forward<_Args>(__args)...);
}
}

//! @brief Conversion from a \c basic_any_resource with the same set of properties but in a different order.
//! This constructor also handles conversion from \c async_any_resource to \c any_resource
//! This constructor also handles conversion from \c any_async_resource to \c any_resource
//! @param __other The other \c basic_any_resource.
_LIBCUDACXX_TEMPLATE(_CUDA_VMR::_AllocType _OtherAllocType, class... _OtherProperties)
_LIBCUDACXX_REQUIRES(
Expand Down Expand Up @@ -260,19 +293,64 @@ template <class... _Properties>
using any_resource = basic_any_resource<_CUDA_VMR::_AllocType::_Default, _Properties...>;

//! @rst
//! .. _cudax-memory-resource-async-any-resource:
//! .. _cudax-memory-resource-any-async-resource:
//!
//! Type erased wrapper around an `async_resource`
//! -----------------------------------------------
//!
//! ``async_any_resource`` wraps any given :ref:`async resource <libcudacxx-extended-api-memory-resources-resource>`
//! ``any_async_resource`` wraps any given :ref:`async resource <libcudacxx-extended-api-memory-resources-resource>`
//! that satisfies the required properties. It owns the contained resource, taking care of construction / destruction.
//! This makes it especially suited for use in e.g. container types that need to ensure that the lifetime of the
//! container exceeds the lifetime of the memory resource used to allocate the storage
//!
//! @endrst
template <class... _Properties>
using async_any_resource = basic_any_resource<_CUDA_VMR::_AllocType::_Async, _Properties...>;
using any_async_resource = basic_any_resource<_CUDA_VMR::_AllocType::_Async, _Properties...>;

//! @rst
//! .. _cudax-memory-resource-make-any-resource:
//!
//! Factory function for `any_resource` objects
//! -------------------------------------------
//!
//! ``make_any_resource`` constructs an :ref:`any_resource <cudax-memory-resource-any-resource>` object that wraps a
//! newly constructed instance of the given resource type. The resource type must satisfy the ``cuda::mr::resource``
//! concept and provide all of the properties specified in the template parameter pack.
//!
//! @param __args The arguments used to construct the instance of the resource type.
//!
//! @endrst
template <class _Resource, class... _Properties, class... _Args>
auto make_any_resource(_Args&&... __args) -> any_resource<_Properties...>
{
static_assert(_CUDA_VMR::resource<_Resource>, "_Resource does not satisfy the cuda::mr::resource concept");
static_assert(_CUDA_VMR::resource_with<_Resource, _Properties...>,
"Resource does not satisfy the required properties");
return any_resource<_Properties...>{_CUDA_VSTD::in_place_type<_Resource>, _CUDA_VSTD::forward<_Args>(__args)...};
}

//! @rst
//! .. _cudax-memory-resource-make-any-async-resource:
//!
//! Factory function for `any_async_resource` objects
//! -------------------------------------------------
//!
//! ``make_any_async_resource`` constructs an :ref:`any_async_resource <cudax-memory-resource-any-async-resource>`
//! object that wraps a newly constructed instance of the given resource type. The resource type must satisfy the
//! ``cuda::mr::async_resource`` concept and provide all of the properties specified in the template parameter pack.
//!
//! @param __args The arguments used to construct the instance of the resource type.
//!
//! @endrst
template <class _Resource, class... _Properties, class... _Args>
auto make_any_async_resource(_Args&&... __args) -> any_async_resource<_Properties...>
{
static_assert(_CUDA_VMR::async_resource<_Resource>,
"_Resource does not satisfy the cuda::mr::async_resource concept");
static_assert(_CUDA_VMR::async_resource_with<_Resource, _Properties...>,
"Resource does not satisfy the required properties");
return any_async_resource<_Properties...>{_CUDA_VSTD::in_place_type<_Resource>, _CUDA_VSTD::forward<_Args>(__args)...};
}

} // namespace cuda::experimental::mr

Expand Down
1 change: 1 addition & 0 deletions cudax/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ foreach(cn_target IN LISTS cudax_TARGETS)
)

cudax_add_catch2_test(test_target memory_resource ${cn_target}
memory_resource/any_async_resource.cu
memory_resource/any_resource.cu
memory_resource/async_memory_pool.cu
memory_resource/async_memory_resource.cu
Expand Down
178 changes: 178 additions & 0 deletions cudax/test/memory_resource/any_async_resource.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDA Experimental in CUDA C++ Core Libraries,
// 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.
//
//===----------------------------------------------------------------------===//

#include <cuda/experimental/memory_resource.cuh>

#include "test_resource.h"
#include <catch2/catch.hpp>
#include <testing.cuh>

TEMPLATE_TEST_CASE_METHOD(test_fixture, "any_async_resource", "[container][resource]", big_resource, small_resource)
{
using TestResource = TestType;
constexpr bool is_big = sizeof(TestResource) > sizeof(cuda::mr::_AnyResourceStorage);

SECTION("construct and destruct")
{
Counts expected{};
CHECK(this->counts == expected);
{
cudax::mr::any_async_resource<> mr{TestResource{42, this}};
expected.new_count += is_big;
++expected.object_count;
++expected.move_count;
CHECK(this->counts == expected);
}
expected.delete_count += is_big;
--expected.object_count;
CHECK(this->counts == expected);
}

// Reset the counters:
this->counts = Counts();

SECTION("copy and move")
{
Counts expected{};
CHECK(this->counts == expected);
{
cudax::mr::any_async_resource<> mr{TestResource{42, this}};
expected.new_count += is_big;
++expected.object_count;
++expected.move_count;
CHECK(this->counts == expected);

auto mr2 = mr;
expected.new_count += is_big;
++expected.copy_count;
++expected.object_count;
CHECK(this->counts == expected);
CHECK(mr == mr2);
++expected.equal_to_count;
CHECK(this->counts == expected);

auto mr3 = std::move(mr);
expected.move_count += !is_big; // for big resources, move is a pointer swap
CHECK(this->counts == expected);
CHECK(mr2 == mr3);
++expected.equal_to_count;
CHECK(this->counts == expected);
}
expected.delete_count += 2 * is_big;
expected.object_count -= 2;
CHECK(this->counts == expected);
}

// Reset the counters:
this->counts = Counts();

SECTION("allocate and deallocate")
{
Counts expected{};
CHECK(this->counts == expected);
{
cudax::mr::any_async_resource<> mr{TestResource{42, this}};
expected.new_count += is_big;
++expected.object_count;
++expected.move_count;
CHECK(this->counts == expected);

void* ptr = mr.allocate(bytes(50), align(8));
CHECK(ptr == this);
++expected.allocate_count;
CHECK(this->counts == expected);

mr.deallocate(ptr, bytes(50), align(8));
++expected.deallocate_count;
CHECK(this->counts == expected);
}
expected.delete_count += is_big;
--expected.object_count;
CHECK(this->counts == expected);
}

// Reset the counters:
this->counts = Counts();

SECTION("allocate_async and deallocate_async")
{
Counts expected{};
CHECK(this->counts == expected);
{
cudax::stream stream{};
cudax::mr::any_async_resource<> mr{TestResource{42, this}};
expected.new_count += is_big;
++expected.object_count;
++expected.move_count;
CHECK(this->counts == expected);

void* ptr = mr.allocate_async(bytes(50), align(8), ::cuda::stream_ref{stream});
CHECK(ptr == this);
++expected.allocate_async_count;
CHECK(this->counts == expected);

mr.deallocate_async(ptr, bytes(50), align(8), ::cuda::stream_ref{stream});
++expected.deallocate_async_count;
CHECK(this->counts == expected);
}
expected.delete_count += is_big;
--expected.object_count;
CHECK(this->counts == expected);
}

// Reset the counters:
this->counts = Counts();

SECTION("conversion to resource_ref")
{
Counts expected{};
{
cudax::mr::any_async_resource<> mr{TestResource{42, this}};
expected.new_count += is_big;
++expected.object_count;
++expected.move_count;
CHECK(this->counts == expected);

cuda::mr::resource_ref<> ref = mr;

CHECK(this->counts == expected);
auto* ptr = ref.allocate(bytes(100), align(8));
CHECK(ptr == this);
++expected.allocate_count;
CHECK(this->counts == expected);
ref.deallocate(ptr, bytes(0), align(0));
++expected.deallocate_count;
CHECK(this->counts == expected);
}
expected.delete_count += is_big;
--expected.object_count;
CHECK(this->counts == expected);
}

// Reset the counters:
this->counts = Counts();

SECTION("make_any_async_resource")
{
Counts expected{};
CHECK(this->counts == expected);
{
cudax::mr::any_async_resource<> mr = cudax::mr::make_any_async_resource<TestResource>(42, this);
expected.new_count += is_big;
++expected.object_count;
CHECK(this->counts == expected);
}
expected.delete_count += is_big;
--expected.object_count;
CHECK(this->counts == expected);
}
// Reset the counters:
this->counts = Counts();
}
17 changes: 17 additions & 0 deletions cudax/test/memory_resource/any_resource.cu
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,21 @@ TEMPLATE_TEST_CASE_METHOD(test_fixture, "any_resource", "[container][resource]",

// Reset the counters:
this->counts = Counts();

SECTION("make_any_resource")
{
Counts expected{};
CHECK(this->counts == expected);
{
cudax::mr::any_resource<> mr = cudax::mr::make_any_resource<TestResource>(42, this);
expected.new_count += is_big;
++expected.object_count;
CHECK(this->counts == expected);
}
expected.delete_count += is_big;
--expected.object_count;
CHECK(this->counts == expected);
}
// Reset the counters:
this->counts = Counts();
}
Loading

0 comments on commit 8b2bf13

Please sign in to comment.