Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_TrackedVector.H
Go to the documentation of this file.
1#ifndef AMREX_TRACKED_VECTOR_H
2#define AMREX_TRACKED_VECTOR_H
3
4#include <AMReX_Config.H>
5
6#include <AMReX.H>
8
9#include <memory>
10#include <stdexcept>
11#include <string>
12#include <type_traits>
13#include <utility>
14#include <vector>
15
16
17namespace amrex::Gpu
18{
44 template <class T>
46 {
47 static_assert(std::is_trivially_copyable<T>(), "TrackedVector can only hold trivially copyable types");
48 using value_type = T;
49 using size_type = std::size_t;
50
51#ifdef AMREX_USE_GPU
53#else
54 using device_vector_type = std::vector<T>;
55#endif
56
57 enum class Status {
61 };
62
63 TrackedVector () = default;
64
65 explicit TrackedVector (size_type a_size)
66 : m_data(std::make_shared<Data>(std::vector<T>(a_size))) {}
67
68 TrackedVector (size_type a_size, value_type const & a_value)
69 : m_data(std::make_shared<Data>(std::vector<T>(a_size, a_value))) {}
70
71 TrackedVector (std::initializer_list<T> a_initializer_list)
72 : m_data(std::make_shared<Data>(std::vector<T>(a_initializer_list))) {}
73
74 TrackedVector (std::vector<T> a_vector)
75 : m_data(std::make_shared<Data>(std::move(a_vector))) {}
76
77 TrackedVector (TrackedVector const & a_vector)
78 {
79 copy_from(a_vector);
80 }
81
83 TrackedVector (TrackedVector && a_vector) noexcept
84 {
85 std::swap(m_data, a_vector.m_data);
86 }
87
90 if (this != &a_vector) {
91 copy_from(a_vector);
92 }
93 return *this;
94 }
95
97 TrackedVector& operator= (TrackedVector && a_vector) noexcept {
98 if (this != &a_vector) {
99 std::swap(m_data, a_vector.m_data);
100 }
101 return *this;
102 }
103
104 ~TrackedVector () = default;
105
106 [[nodiscard]] Status status () const { return m_data->status; }
107
113 [[nodiscard]] std::vector<T> &
114 host () {
115#ifdef AMREX_USE_GPU
116 if (m_data->status == Status::device_dirty) {
117 to_host();
118 }
119 m_data->status = Status::host_dirty;
120#endif
121 return m_data->host;
122 }
123
128 [[nodiscard]] std::vector<T> const &
129 host_const () const {
130#ifdef AMREX_USE_GPU
131 if (m_data->status == Status::device_dirty) {
132 to_host();
133 }
134#endif
135 return m_data->host;
136 }
137
138#ifdef AMREX_USE_GPU
144 [[nodiscard]] device_vector_type &
146 require_amrex("device");
147 register_finalize();
148 if (m_data->status == Status::host_dirty) {
149 to_device();
150 }
151 m_data->status = Status::device_dirty;
152 return m_data->device;
153 }
154
159 [[nodiscard]] device_vector_type const &
160 device_const () const {
161 require_amrex("device_const");
162 register_finalize();
163 if (m_data->status == Status::host_dirty) {
164 to_device();
165 }
166 return m_data->device;
167 }
168#else
170 [[nodiscard]] device_vector_type &
171 device () { return m_data->host; }
172
174 [[nodiscard]] device_vector_type const &
175 device_const () const { return m_data->host; }
176#endif
177
186 {
187#ifdef AMREX_USE_GPU
188 Data::drain_device(*m_data);
189#endif
190 }
191
192 private:
193
194 struct Data {
195 Status status = Status::up_to_date;
196 std::vector<T> host;
197#ifdef AMREX_USE_GPU
198 device_vector_type device;
199 bool finalize_registered = false;
200#endif
201 Data () = default;
202
204 explicit Data (std::vector<T> h)
205 : host(std::move(h))
206 {
207#ifdef AMREX_USE_GPU
208 status = Status::host_dirty;
209#endif
210 }
211
212#ifdef AMREX_USE_GPU
223 static void drain_device (Data& d) {
224 if (d.status == Status::device_dirty) {
226 d.host.resize(d.device.size());
227 if (!d.device.empty()) {
229 d.device.begin(), d.device.end(), d.host.begin());
230 }
231 }
232 d.device.clear();
233 d.device.shrink_to_fit();
234 d.status = Status::host_dirty;
235 }
236#endif
237 };
238
240 static void require_amrex (char const * func) {
241 if (!amrex::Initialized()) {
242 throw std::runtime_error(
243 std::string("TrackedVector::") + func +
244 " called outside of AMReX initialize/finalize");
245 }
246 }
247
248#ifdef AMREX_USE_GPU
253 static void require_copyable_without_amrex (Data const& src, Data const& dst)
254 {
255 if (amrex::Initialized()) {
256 return;
257 }
258 if (src.status == Status::device_dirty) {
259 throw std::runtime_error(
260 "TrackedVector::copy_from: source is device_dirty "
261 "outside an AMReX session (host data is stale)");
262 }
263 if (!src.device.empty()) {
264 throw std::runtime_error(
265 "TrackedVector::copy_from: source retains device storage "
266 "outside an AMReX session");
267 }
268 if (!dst.device.empty()) {
269 throw std::runtime_error(
270 "TrackedVector::copy_from: destination retains device storage "
271 "outside an AMReX session");
272 }
273 if (src.finalize_registered) {
274 throw std::runtime_error(
275 "TrackedVector::copy_from: source finalize callback still registered "
276 "outside an AMReX session");
277 }
278 if (dst.finalize_registered) {
279 throw std::runtime_error(
280 "TrackedVector::copy_from: destination finalize callback still registered "
281 "outside an AMReX session");
282 }
283 }
284#endif
285
287 void copy_from (TrackedVector const & a_vector) {
288#ifdef AMREX_USE_GPU
289 Data const& src = *a_vector.m_data;
290 require_copyable_without_amrex(src, *m_data);
291
292 // Copy status & host data
293 m_data->status = src.status;
294 m_data->host = src.host;
295
296 // Device data & finalize registration:
297 // Device copy only makes sense inside a live AMReX session where
298 // the device arena is available. Outside a session both sides
299 // must already be empty (drained by Finalize / never allocated).
300 if (amrex::Initialized()) {
301 if (src.status == Status::host_dirty) {
302 // host_dirty means the device mirror is stale —
303 // avoid a wasteful D->D copy; it will resync on demand.
304 m_data->device.clear();
305 m_data->device.shrink_to_fit();
306 } else {
307 // Ensure pending kernels that dirtied src.device
308 // have completed before the D->D copy reads it.
309 if (src.status == Status::device_dirty) {
311 }
312 m_data->device = src.device;
313 }
314
315 if (!m_data->finalize_registered) {
316 register_finalize();
317 }
318 }
319#else
320 *m_data = *a_vector.m_data;
321#endif
322 }
323
325 void to_device () const {
326#ifdef AMREX_USE_GPU
327 require_amrex("to_device");
328 auto const size = m_data->host.size();
329 if (size > 0U) {
330 m_data->device.resize(size);
332 m_data->host.begin(), m_data->host.end(), m_data->device.begin());
333 } else {
334 m_data->device.clear();
335 m_data->device.shrink_to_fit();
336 }
337 m_data->status = Status::up_to_date;
338#endif
339 }
340
342 void to_host () const {
343#ifdef AMREX_USE_GPU
344 require_amrex("to_host");
346 m_data->host.resize(m_data->device.size());
347 if (!m_data->device.empty()) {
349 m_data->device.begin(), m_data->device.end(), m_data->host.begin());
350 }
351 m_data->status = Status::up_to_date;
352#endif
353 }
354
360 void register_finalize () const {
361#ifdef AMREX_USE_GPU
362 require_amrex("register_finalize");
363 if (m_data->finalize_registered) { return; }
364 m_data->finalize_registered = true;
365
366 std::weak_ptr<Data> weak_data = m_data;
367
368 amrex::ExecOnFinalize([weak_data]() {
369 auto data = weak_data.lock();
370 if (!data) { return; }
371 Data::drain_device(*data);
372 data->finalize_registered = false;
373 });
374#endif
375 }
376
377 // mutable: lazy-sync accessors (host_const, device_const) need to
378 // update status and trigger copies through the shared_ptr even on
379 // const TrackedVector references.
380 mutable std::shared_ptr<Data> m_data = std::make_shared<Data>();
381 };
382
383}
384
385#endif
Dynamically allocated vector for trivially copyable data.
Definition AMReX_PODVector.H:308
Definition AMReX_BaseFwd.H:55
void copy(HostToDevice, InIter begin, InIter end, OutIter result) noexcept
A host-to-device copy routine. Note this is just a wrapper around memcpy, so it assumes contiguous st...
Definition AMReX_GpuContainers.H:128
static constexpr DeviceToHost deviceToHost
Definition AMReX_GpuContainers.H:106
static constexpr HostToDevice hostToDevice
Definition AMReX_GpuContainers.H:105
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:310
void ExecOnFinalize(std::function< void()>)
We maintain a stack of functions that need to be called in Finalize(). The functions are called in LI...
Definition AMReX.cpp:330
bool Initialized()
Returns true if there are any currently-active and initialized AMReX instances (i....
Definition AMReX.cpp:804
Definition AMReX_TrackedVector.H:46
Status status() const
Definition AMReX_TrackedVector.H:106
TrackedVector(TrackedVector const &a_vector)
Definition AMReX_TrackedVector.H:77
TrackedVector(size_type a_size, value_type const &a_value)
Definition AMReX_TrackedVector.H:68
T value_type
Definition AMReX_TrackedVector.H:48
device_vector_type & device()
Definition AMReX_TrackedVector.H:145
TrackedVector(std::vector< T > a_vector)
Definition AMReX_TrackedVector.H:74
TrackedVector(TrackedVector &&a_vector) noexcept
Definition AMReX_TrackedVector.H:83
std::vector< T > & host()
Definition AMReX_TrackedVector.H:114
TrackedVector & operator=(TrackedVector const &a_vector)
Definition AMReX_TrackedVector.H:89
device_vector_type const & device_const() const
Definition AMReX_TrackedVector.H:160
std::vector< T > const & host_const() const
Definition AMReX_TrackedVector.H:129
TrackedVector(size_type a_size)
Definition AMReX_TrackedVector.H:65
void release_gpu()
Definition AMReX_TrackedVector.H:185
std::size_t size_type
Definition AMReX_TrackedVector.H:49
amrex::Gpu::NonManagedDeviceVector< T > device_vector_type
Definition AMReX_TrackedVector.H:52
Status
Definition AMReX_TrackedVector.H:57
@ host_dirty
device data needs an update
@ up_to_date
host and device data are in sync
@ device_dirty
host data needs an update
TrackedVector(std::initializer_list< T > a_initializer_list)
Definition AMReX_TrackedVector.H:71