1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
#ifndef HeterogeneousCore_AlpakaInterface_interface_MoveToDeviceCache_h
#define HeterogeneousCore_AlpakaInterface_interface_MoveToDeviceCache_h
#include <type_traits>
#include <alpaka/alpaka.hpp>
#include "HeterogeneousCore/AlpakaCore/interface/QueueCache.h"
#include "HeterogeneousCore/AlpakaCore/interface/CopyToDeviceCache.h"
#include "HeterogeneousCore/AlpakaInterface/interface/devices.h"
namespace cms::alpakatools {
namespace detail {
// By default copy the host object with CopyToDevice<T>
//
// Doing with template specialization (rather than
// std::conditional_t and if constexpr) because the
// CopyToDevice<THostObject>::copyAsync() is ill-defined e.g. for
// PortableCollection on host device
template <typename TDevice, typename THostObject>
class MoveToDeviceCacheImpl {
public:
using HostObject = THostObject;
using Impl = CopyToDeviceCacheImpl<TDevice, THostObject>;
using DeviceObject = typename Impl::DeviceObject;
MoveToDeviceCacheImpl(HostObject&& srcObject) : impl_(srcObject) {}
DeviceObject const& get(size_t i) const { return impl_.get(i); }
private:
Impl impl_;
};
// For host device, move the host object instead
template <typename THostObject>
class MoveToDeviceCacheImpl<alpaka_common::DevHost, THostObject> {
public:
using HostObject = THostObject;
using DeviceObject = HostObject;
MoveToDeviceCacheImpl(HostObject&& srcObject) : data_(std::move(srcObject)) {}
DeviceObject const& get(size_t i) const { return data_; }
private:
HostObject data_;
};
} // namespace detail
/**
* This class template implements a cache for data that is moved
* from the host (of type THostObject) to all the devices
* corresponding to the TDevice device type.
*
* The host-side object to be moved is given as an argument to the
* class constructor. The constructor uses the
* CopyToDevice<THostObject> class template to copy the data to the
* devices, and waits for the data copies to finish, i.e. the
* constructor is synchronous wrt. the data copies. The "move" is
* achieved by requiring the constructor argument to be an rvalue
* reference.
*
* Note that the host object type is required to be non-copyable.
* This is to avoid easy mistakes with objects that follow copy
* semantics of std::shared_ptr (that includes Alpaka buffers), that
* would allow the source memory buffer to be used via another copy
* during the asynchronous data copy to the device.
*
* The device-side object corresponding to the THostObject (actual
* type is the return type of CopyToDevice<THostObject>::copyAsync())
* can be obtained with get() member function, that has either the
* queue or device argument.
*/
template <typename TDevice, typename THostObject>
requires alpaka::isDevice<TDevice>
class MoveToDeviceCache {
public:
using Device = TDevice;
using HostObject = THostObject;
using Impl = detail::MoveToDeviceCacheImpl<Device, HostObject>;
using DeviceObject = typename Impl::DeviceObject;
static_assert(not(std::is_copy_constructible_v<HostObject> or std::is_copy_assignable_v<HostObject>),
"The data object to be moved to device must not be copyable.");
MoveToDeviceCache(HostObject&& srcData) : data_(std::move(srcData)) {}
DeviceObject const& get(Device const& dev) const { return data_.get(alpaka::getNativeHandle(dev)); }
template <typename TQueue>
DeviceObject const& get(TQueue const& queue) const {
return get(alpaka::getDev(queue));
}
private:
Impl data_;
};
} // namespace cms::alpakatools
#endif
|