Skip to content

Commit 8b2bf13

Browse files
misccoalliepiper
andauthored
Make any_resource emplacable (#2425)
* 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]>
1 parent 2fe09c8 commit 8b2bf13

File tree

8 files changed

+321
-20
lines changed

8 files changed

+321
-20
lines changed

cudax/include/cuda/experimental/__container/uninitialized_async_buffer.cuh

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ template <class _Tp, class... _Properties>
7272
class uninitialized_async_buffer
7373
{
7474
private:
75-
using __async_resource = ::cuda::experimental::mr::async_any_resource<_Properties...>;
75+
using __async_resource = ::cuda::experimental::mr::any_async_resource<_Properties...>;
7676
__async_resource __mr_;
7777
::cuda::stream_ref __stream_ = {};
7878
size_t __count_ = 0;
@@ -204,7 +204,7 @@ public:
204204
}
205205

206206
//! @rst
207-
//! Returns a \c const reference to the :ref:`any_async_resource <cudax-memory-resource-async-any-resource>`
207+
//! Returns a \c const reference to the :ref:`any_async_resource <cudax-memory-resource-any-async-resource>`
208208
//! that holds the memory resource used to allocate the buffer
209209
//! @endrst
210210
_CCCL_NODISCARD const __async_resource& get_resource() const noexcept

cudax/include/cuda/experimental/__memory_resource/any_resource.cuh

+86-8
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,15 @@
4242
#include <cuda/std/__concepts/__concept_macros.h>
4343
#include <cuda/std/__concepts/_One_of.h>
4444
#include <cuda/std/__concepts/all_of.h>
45+
#include <cuda/std/__new_>
46+
#include <cuda/std/__type_traits/is_constructible.h>
4547
#include <cuda/std/__type_traits/is_nothrow_constructible.h>
4648
#include <cuda/std/__type_traits/is_same.h>
4749
#include <cuda/std/__type_traits/remove_cvref.h>
50+
#include <cuda/std/__type_traits/type_set.h>
4851
#include <cuda/std/__utility/exchange.h>
4952
#include <cuda/std/__utility/forward.h>
53+
#include <cuda/std/__utility/in_place.h>
5054

5155
namespace cuda::experimental::mr
5256
{
@@ -77,19 +81,25 @@ private:
7781

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

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

89+
//! @brief Validates that a passed in \c _Resource satisfies the \c resource or \c async_resource concept respectively
90+
//! as well as all properties in \c _Properties... .
91+
template <class _Resource>
92+
static constexpr bool __valid_resource =
93+
_Alloc_type == _CUDA_VMR::_AllocType::_Async
94+
? _CUDA_VMR::async_resource_with<_Resource, _Properties...>
95+
: _CUDA_VMR::resource_with<_Resource, _Properties...>;
96+
8497
public:
8598
//! @brief Constructs a \c basic_any_resource from a type that satisfies the \c resource or \c async_resource
8699
//! concept as well as all properties.
87100
//! @param __res The resource to be wrapped within the \c basic_any_resource.
88101
_LIBCUDACXX_TEMPLATE(class _Resource, class __resource_t = _CUDA_VSTD::remove_cvref_t<_Resource>)
89-
_LIBCUDACXX_REQUIRES(
90-
(!__is_basic_any_resource<_Resource>) _LIBCUDACXX_AND(_CUDA_VMR::resource_with<__resource_t, _Properties...>)
91-
_LIBCUDACXX_AND(_Alloc_type != _CUDA_VMR::_AllocType::_Async
92-
|| (_CUDA_VMR::async_resource_with<__resource_t, _Properties...>) ))
102+
_LIBCUDACXX_REQUIRES((!__is_basic_any_resource<_Resource>) _LIBCUDACXX_AND __valid_resource<__resource_t>)
93103
basic_any_resource(_Resource&& __res) noexcept
94104
: _CUDA_VMR::_Resource_base<_Alloc_type, _CUDA_VMR::_WrapperType::_Owning>(
95105
nullptr, &_CUDA_VMR::__alloc_vtable<_Alloc_type, _CUDA_VMR::_WrapperType::_Owning, __resource_t>)
@@ -105,8 +115,31 @@ public:
105115
}
106116
}
107117

118+
//! @brief Constructs a \c basic_any_resource wrapping an object of type \c _Resource that
119+
//! is constructed from \c __args... . \c _Resource must satisfy the \c resource or \c async_resource
120+
//! concept, and it must provide all properties in \c _Properties... .
121+
//! @param __args The arguments used to construct the instance of \c _Resource to be wrapped within the
122+
//! \c basic_any_resource.
123+
_LIBCUDACXX_TEMPLATE(class _Resource, class... _Args)
124+
_LIBCUDACXX_REQUIRES(_CCCL_TRAIT(_CUDA_VSTD::is_constructible, _Resource, _Args...)
125+
_LIBCUDACXX_AND __valid_resource<_Resource>)
126+
basic_any_resource(_CUDA_VSTD::in_place_type_t<_Resource>, _Args&&... __args) noexcept
127+
: _CUDA_VMR::_Resource_base<_Alloc_type, _CUDA_VMR::_WrapperType::_Owning>(
128+
nullptr, &_CUDA_VMR::__alloc_vtable<_Alloc_type, _CUDA_VMR::_WrapperType::_Owning, _Resource>)
129+
, __vtable(__vtable::template _Create<_Resource>())
130+
{
131+
if constexpr (_CUDA_VMR::_IsSmall<_Resource>())
132+
{
133+
::new (static_cast<void*>(this->__object.__buf_)) _Resource(_CUDA_VSTD::forward<_Args>(__args)...);
134+
}
135+
else
136+
{
137+
this->__object.__ptr_ = new _Resource(_CUDA_VSTD::forward<_Args>(__args)...);
138+
}
139+
}
140+
108141
//! @brief Conversion from a \c basic_any_resource with the same set of properties but in a different order.
109-
//! This constructor also handles conversion from \c async_any_resource to \c any_resource
142+
//! This constructor also handles conversion from \c any_async_resource to \c any_resource
110143
//! @param __other The other \c basic_any_resource.
111144
_LIBCUDACXX_TEMPLATE(_CUDA_VMR::_AllocType _OtherAllocType, class... _OtherProperties)
112145
_LIBCUDACXX_REQUIRES(
@@ -260,19 +293,64 @@ template <class... _Properties>
260293
using any_resource = basic_any_resource<_CUDA_VMR::_AllocType::_Default, _Properties...>;
261294

262295
//! @rst
263-
//! .. _cudax-memory-resource-async-any-resource:
296+
//! .. _cudax-memory-resource-any-async-resource:
264297
//!
265298
//! Type erased wrapper around an `async_resource`
266299
//! -----------------------------------------------
267300
//!
268-
//! ``async_any_resource`` wraps any given :ref:`async resource <libcudacxx-extended-api-memory-resources-resource>`
301+
//! ``any_async_resource`` wraps any given :ref:`async resource <libcudacxx-extended-api-memory-resources-resource>`
269302
//! that satisfies the required properties. It owns the contained resource, taking care of construction / destruction.
270303
//! This makes it especially suited for use in e.g. container types that need to ensure that the lifetime of the
271304
//! container exceeds the lifetime of the memory resource used to allocate the storage
272305
//!
273306
//! @endrst
274307
template <class... _Properties>
275-
using async_any_resource = basic_any_resource<_CUDA_VMR::_AllocType::_Async, _Properties...>;
308+
using any_async_resource = basic_any_resource<_CUDA_VMR::_AllocType::_Async, _Properties...>;
309+
310+
//! @rst
311+
//! .. _cudax-memory-resource-make-any-resource:
312+
//!
313+
//! Factory function for `any_resource` objects
314+
//! -------------------------------------------
315+
//!
316+
//! ``make_any_resource`` constructs an :ref:`any_resource <cudax-memory-resource-any-resource>` object that wraps a
317+
//! newly constructed instance of the given resource type. The resource type must satisfy the ``cuda::mr::resource``
318+
//! concept and provide all of the properties specified in the template parameter pack.
319+
//!
320+
//! @param __args The arguments used to construct the instance of the resource type.
321+
//!
322+
//! @endrst
323+
template <class _Resource, class... _Properties, class... _Args>
324+
auto make_any_resource(_Args&&... __args) -> any_resource<_Properties...>
325+
{
326+
static_assert(_CUDA_VMR::resource<_Resource>, "_Resource does not satisfy the cuda::mr::resource concept");
327+
static_assert(_CUDA_VMR::resource_with<_Resource, _Properties...>,
328+
"Resource does not satisfy the required properties");
329+
return any_resource<_Properties...>{_CUDA_VSTD::in_place_type<_Resource>, _CUDA_VSTD::forward<_Args>(__args)...};
330+
}
331+
332+
//! @rst
333+
//! .. _cudax-memory-resource-make-any-async-resource:
334+
//!
335+
//! Factory function for `any_async_resource` objects
336+
//! -------------------------------------------------
337+
//!
338+
//! ``make_any_async_resource`` constructs an :ref:`any_async_resource <cudax-memory-resource-any-async-resource>`
339+
//! object that wraps a newly constructed instance of the given resource type. The resource type must satisfy the
340+
//! ``cuda::mr::async_resource`` concept and provide all of the properties specified in the template parameter pack.
341+
//!
342+
//! @param __args The arguments used to construct the instance of the resource type.
343+
//!
344+
//! @endrst
345+
template <class _Resource, class... _Properties, class... _Args>
346+
auto make_any_async_resource(_Args&&... __args) -> any_async_resource<_Properties...>
347+
{
348+
static_assert(_CUDA_VMR::async_resource<_Resource>,
349+
"_Resource does not satisfy the cuda::mr::async_resource concept");
350+
static_assert(_CUDA_VMR::async_resource_with<_Resource, _Properties...>,
351+
"Resource does not satisfy the required properties");
352+
return any_async_resource<_Properties...>{_CUDA_VSTD::in_place_type<_Resource>, _CUDA_VSTD::forward<_Args>(__args)...};
353+
}
276354

277355
} // namespace cuda::experimental::mr
278356

cudax/test/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ foreach(cn_target IN LISTS cudax_TARGETS)
9292
)
9393

9494
cudax_add_catch2_test(test_target memory_resource ${cn_target}
95+
memory_resource/any_async_resource.cu
9596
memory_resource/any_resource.cu
9697
memory_resource/async_memory_pool.cu
9798
memory_resource/async_memory_resource.cu
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of CUDA Experimental in CUDA C++ Core Libraries,
4+
// under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
#include <cuda/experimental/memory_resource.cuh>
12+
13+
#include "test_resource.h"
14+
#include <catch2/catch.hpp>
15+
#include <testing.cuh>
16+
17+
TEMPLATE_TEST_CASE_METHOD(test_fixture, "any_async_resource", "[container][resource]", big_resource, small_resource)
18+
{
19+
using TestResource = TestType;
20+
constexpr bool is_big = sizeof(TestResource) > sizeof(cuda::mr::_AnyResourceStorage);
21+
22+
SECTION("construct and destruct")
23+
{
24+
Counts expected{};
25+
CHECK(this->counts == expected);
26+
{
27+
cudax::mr::any_async_resource<> mr{TestResource{42, this}};
28+
expected.new_count += is_big;
29+
++expected.object_count;
30+
++expected.move_count;
31+
CHECK(this->counts == expected);
32+
}
33+
expected.delete_count += is_big;
34+
--expected.object_count;
35+
CHECK(this->counts == expected);
36+
}
37+
38+
// Reset the counters:
39+
this->counts = Counts();
40+
41+
SECTION("copy and move")
42+
{
43+
Counts expected{};
44+
CHECK(this->counts == expected);
45+
{
46+
cudax::mr::any_async_resource<> mr{TestResource{42, this}};
47+
expected.new_count += is_big;
48+
++expected.object_count;
49+
++expected.move_count;
50+
CHECK(this->counts == expected);
51+
52+
auto mr2 = mr;
53+
expected.new_count += is_big;
54+
++expected.copy_count;
55+
++expected.object_count;
56+
CHECK(this->counts == expected);
57+
CHECK(mr == mr2);
58+
++expected.equal_to_count;
59+
CHECK(this->counts == expected);
60+
61+
auto mr3 = std::move(mr);
62+
expected.move_count += !is_big; // for big resources, move is a pointer swap
63+
CHECK(this->counts == expected);
64+
CHECK(mr2 == mr3);
65+
++expected.equal_to_count;
66+
CHECK(this->counts == expected);
67+
}
68+
expected.delete_count += 2 * is_big;
69+
expected.object_count -= 2;
70+
CHECK(this->counts == expected);
71+
}
72+
73+
// Reset the counters:
74+
this->counts = Counts();
75+
76+
SECTION("allocate and deallocate")
77+
{
78+
Counts expected{};
79+
CHECK(this->counts == expected);
80+
{
81+
cudax::mr::any_async_resource<> mr{TestResource{42, this}};
82+
expected.new_count += is_big;
83+
++expected.object_count;
84+
++expected.move_count;
85+
CHECK(this->counts == expected);
86+
87+
void* ptr = mr.allocate(bytes(50), align(8));
88+
CHECK(ptr == this);
89+
++expected.allocate_count;
90+
CHECK(this->counts == expected);
91+
92+
mr.deallocate(ptr, bytes(50), align(8));
93+
++expected.deallocate_count;
94+
CHECK(this->counts == expected);
95+
}
96+
expected.delete_count += is_big;
97+
--expected.object_count;
98+
CHECK(this->counts == expected);
99+
}
100+
101+
// Reset the counters:
102+
this->counts = Counts();
103+
104+
SECTION("allocate_async and deallocate_async")
105+
{
106+
Counts expected{};
107+
CHECK(this->counts == expected);
108+
{
109+
cudax::stream stream{};
110+
cudax::mr::any_async_resource<> mr{TestResource{42, this}};
111+
expected.new_count += is_big;
112+
++expected.object_count;
113+
++expected.move_count;
114+
CHECK(this->counts == expected);
115+
116+
void* ptr = mr.allocate_async(bytes(50), align(8), ::cuda::stream_ref{stream});
117+
CHECK(ptr == this);
118+
++expected.allocate_async_count;
119+
CHECK(this->counts == expected);
120+
121+
mr.deallocate_async(ptr, bytes(50), align(8), ::cuda::stream_ref{stream});
122+
++expected.deallocate_async_count;
123+
CHECK(this->counts == expected);
124+
}
125+
expected.delete_count += is_big;
126+
--expected.object_count;
127+
CHECK(this->counts == expected);
128+
}
129+
130+
// Reset the counters:
131+
this->counts = Counts();
132+
133+
SECTION("conversion to resource_ref")
134+
{
135+
Counts expected{};
136+
{
137+
cudax::mr::any_async_resource<> mr{TestResource{42, this}};
138+
expected.new_count += is_big;
139+
++expected.object_count;
140+
++expected.move_count;
141+
CHECK(this->counts == expected);
142+
143+
cuda::mr::resource_ref<> ref = mr;
144+
145+
CHECK(this->counts == expected);
146+
auto* ptr = ref.allocate(bytes(100), align(8));
147+
CHECK(ptr == this);
148+
++expected.allocate_count;
149+
CHECK(this->counts == expected);
150+
ref.deallocate(ptr, bytes(0), align(0));
151+
++expected.deallocate_count;
152+
CHECK(this->counts == expected);
153+
}
154+
expected.delete_count += is_big;
155+
--expected.object_count;
156+
CHECK(this->counts == expected);
157+
}
158+
159+
// Reset the counters:
160+
this->counts = Counts();
161+
162+
SECTION("make_any_async_resource")
163+
{
164+
Counts expected{};
165+
CHECK(this->counts == expected);
166+
{
167+
cudax::mr::any_async_resource<> mr = cudax::mr::make_any_async_resource<TestResource>(42, this);
168+
expected.new_count += is_big;
169+
++expected.object_count;
170+
CHECK(this->counts == expected);
171+
}
172+
expected.delete_count += is_big;
173+
--expected.object_count;
174+
CHECK(this->counts == expected);
175+
}
176+
// Reset the counters:
177+
this->counts = Counts();
178+
}

cudax/test/memory_resource/any_resource.cu

+17
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,21 @@ TEMPLATE_TEST_CASE_METHOD(test_fixture, "any_resource", "[container][resource]",
129129

130130
// Reset the counters:
131131
this->counts = Counts();
132+
133+
SECTION("make_any_resource")
134+
{
135+
Counts expected{};
136+
CHECK(this->counts == expected);
137+
{
138+
cudax::mr::any_resource<> mr = cudax::mr::make_any_resource<TestResource>(42, this);
139+
expected.new_count += is_big;
140+
++expected.object_count;
141+
CHECK(this->counts == expected);
142+
}
143+
expected.delete_count += is_big;
144+
--expected.object_count;
145+
CHECK(this->counts == expected);
146+
}
147+
// Reset the counters:
148+
this->counts = Counts();
132149
}

0 commit comments

Comments
 (0)