-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathManagedArray.hpp
533 lines (459 loc) · 15.9 KB
/
ManagedArray.hpp
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
//////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-24, Lawrence Livermore National Security, LLC and CHAI
// project contributors. See the CHAI LICENSE file for details.
//
// SPDX-License-Identifier: BSD-3-Clause
//////////////////////////////////////////////////////////////////////////////
#ifndef CHAI_ManagedArray_HPP
#define CHAI_ManagedArray_HPP
#include "chai/config.hpp"
#include "chai/ArrayManager.hpp"
#include "chai/ChaiMacros.hpp"
#include "chai/Types.hpp"
#include "umpire/Allocator.hpp"
#include <cstddef>
namespace chai
{
namespace {
inline ExecutionSpace get_default_space() {
return ArrayManager::getInstance()->getDefaultAllocationSpace();
}
}
struct InvalidConstCast;
/*!
* \class CHAICopyable
*
* \brief Provides an interface for types that are copyable.
*
* If a class inherits from CHAICopyable, then the stored items of type T
* are moved when the copy constructor is called.
*/
class CHAICopyable
{
};
/*!
* \class ManagedArray
*
* \brief Provides an array-like class that automatically transfers data
* between memory spaces.
*
* The ManagedArray class interacts with the ArrayManager to provide a
* smart-array object that will automatically move its data between different
* memory spaces on the system. Data motion happens when the copy constructor
* is called, so the ManagedArray works best when used in co-operation with a
* programming model like RAJA.
*
* \include ./examples/ex1.cpp
*
* \tparam T The type of elements stored in the ManagedArray.
*/
template <typename T>
class ManagedArray : public CHAICopyable
{
public:
using T_non_const = typename std::remove_const<T>::type;
CHAI_HOST_DEVICE ManagedArray();
/*!
* \brief Default constructor creates a ManagedArray with no allocations.
*/
ManagedArray(
std::initializer_list<chai::ExecutionSpace> spaces,
std::initializer_list<umpire::Allocator> allocators);
/*!
* \brief Constructor to create a ManagedArray with specified size, allocated
* in the provided space.
*
* If space is NONE, the storage will be allocated in the default space. The
* default space for these allocations can be set with the
* setDefaultAllocationSpace method of the ArrayManager.
*
* \param elems Number of elements in the array.
* \param space Execution space in which to allocate the array.
*/
CHAI_HOST_DEVICE ManagedArray(size_t elems, ExecutionSpace space = get_default_space());
ManagedArray(
size_t elems,
std::initializer_list<chai::ExecutionSpace> spaces,
std::initializer_list<umpire::Allocator> allocators,
ExecutionSpace space = NONE);
/*!
* \brief Copy constructor handles data movement.
*
* The copy constructor interacts with the ArrayManager to move the
* ManagedArray's data between execution spaces.
*
* \param other ManagedArray being copied.
*/
CHAI_HOST_DEVICE ManagedArray(ManagedArray const& other);
CHAI_HOST ManagedArray(PointerRecord* record, ExecutionSpace space);
/*!
* \brief Allocate data for the ManagedArray in the specified space.
*
* The default space for allocations is the CPU.
*
* \param elems Number of elements to allocate.
* \param space Execution space in which to allocate data.
* \param cback User defined callback for memory events (alloc, free, move)
*/
CHAI_HOST void allocate(size_t elems,
ExecutionSpace space = CPU,
const UserCallback& cback =
[] (const PointerRecord*, Action, ExecutionSpace) {});
/*!
* \brief Reallocate data for the ManagedArray.
*
* Reallocation will happen in all spaces the data exists
*
* \param elems Number of elements to allocate.
*/
CHAI_HOST void reallocate(size_t elems);
/*!
* \brief Free all data allocated by this ManagedArray.
*/
CHAI_HOST void free(ExecutionSpace space = NONE);
/*!
* \brief Reset array state.
*
* The next space that accesses this array will be considered a first touch,
* and no data will be migrated.
*/
CHAI_HOST void reset();
/*!
* \brief Get the number of elements in the array.
*
* \return The number of elements in the array
*/
CHAI_HOST_DEVICE size_t size() const;
/*!
* \brief Register this ManagedArray object as 'touched' in the given space.
*
* \param space The space to register a touch.
*/
CHAI_HOST void registerTouch(ExecutionSpace space);
CHAI_HOST void move(ExecutionSpace space=NONE,
bool registerTouch=!std::is_const<T>::value) const;
CHAI_HOST_DEVICE ManagedArray<T> slice(size_t begin, size_t elems=(size_t)-1) const;
/*!
* \brief Return reference to i-th element of the ManagedArray.
*
* \param i Element to return reference to.
*
* \return Reference to i-th element.
*/
template <typename Idx>
CHAI_HOST_DEVICE T& operator[](const Idx i) const;
/*!
* \brief get access to m_active_pointer
* @return a copy of m_active_base_pointer
*/
CHAI_HOST_DEVICE T* getActiveBasePointer() const;
/*!
* \brief get access to m_active_pointer
* @return a copy of m_active_pointer
*/
CHAI_HOST_DEVICE T* getActivePointer() const;
/*!
* \brief Move data to the current execution space (actually determined
* by where the code is executing) and return a raw pointer.
*
* \return Raw pointer to data in the current execution space
*/
CHAI_HOST_DEVICE T* data() const;
/*!
* \brief Move data to the current execution space (actually determined
* by where the code is executing) and return a raw pointer. Do
* not mark data as touched since a pointer to const is returned.
*
* \return Raw pointer to data in the current execution space
*/
CHAI_HOST_DEVICE const T* cdata() const;
/*!
* \brief Return the raw pointer to the data in the given execution
* space. Optionally move the data to that execution space.
*
* \param space The execution space from which to retrieve the raw pointer.
* \param do_move Ensure data at that pointer is live and valid.
*
* @return A copy of the pointer in the given execution space
*/
CHAI_HOST T* data(ExecutionSpace space, bool do_move = true) const;
/*!
* \brief Move data to the current execution space (actually determined
* by where the code is executing) and return an iterator to the
* beginning of the array.
*
* \return Iterator (as raw pointer) to the start of the array in the
* current execution space
*/
CHAI_HOST_DEVICE T* begin() const;
/*!
* \brief Move data to the current execution space (actually determined
* by where the code is executing) and return an iterator to
* one past the end of the array.
*
* \return Iterator (as raw pointer) to the element after the last element
* of the array in the current execution space
*/
CHAI_HOST_DEVICE T* end() const;
/*!
* \brief
*
*/
// operator ManagedArray<typename std::conditional<!std::is_const<T>::value,
// const T, InvalidConstCast>::type> () const;
template <typename U = T>
operator typename std::enable_if<!std::is_const<U>::value,
ManagedArray<const U> >::type() const;
CHAI_HOST_DEVICE ManagedArray(T* data,
ArrayManager* array_manager,
size_t elems,
PointerRecord* pointer_record);
ManagedArray<T>& operator=(ManagedArray const & other) = default;
CHAI_HOST_DEVICE bool operator==(const ManagedArray<T>& rhs) const;
CHAI_HOST_DEVICE bool operator!=(const ManagedArray<T>& from) const;
CHAI_HOST_DEVICE explicit operator bool() const;
#if defined(CHAI_ENABLE_PICK)
/*!
* \brief Return the value of element i in the ManagedArray.
* ExecutionSpace space to the current one
*
* \param index The index of the element to be fetched
* \param space The index of the element to be fetched
* \return The value of the i-th element in the ManagedArray.
* \tparam T_non_const The (non-const) type of data value in ManagedArray.
*/
CHAI_HOST_DEVICE T_non_const pick(size_t i) const;
/*!
* \brief Set the value of element i in the ManagedArray to be val.
*
* \param index The index of the element to be set
* \param val Source location of the value
* \tparam T The type of data value in ManagedArray.
*/
CHAI_HOST_DEVICE void set(size_t i, T val) const;
/*!
* \brief Increment the value of element i in the ManagedArray.
*
* \param index The index of the element to be incremented
* \tparam T The type of data value in ManagedArray.
*/
CHAI_HOST_DEVICE void incr(size_t i) const;
/*!
* \brief Decrement the value of element i in the ManagedArray.
*
* \param index The index of the element to be decremented
* \tparam T The type of data value in ManagedArray.
*/
CHAI_HOST_DEVICE void decr(size_t i) const;
#endif
#ifndef CHAI_DISABLE_RM
/*!
* \brief Assign a user-defined callback triggerd upon memory migration.
*
* The callback is a function of the form
*
* void callback(chai::ExecutionSpace moved_to, size_t num_bytes)
*
* Where moved_to is the execution space that the data was moved to, and
* num_bytes is the number of bytes moved.
*
*/
CHAI_HOST void setUserCallback(UserCallback const& cback)
{
if (m_pointer_record && m_pointer_record != &ArrayManager::s_null_record) {
m_pointer_record->m_user_callback = cback;
}
}
private:
/*!
* \brief Moves the inner data of a ManagedArray.
*
* Called in the copy constructor of ManagedArray. In this implementation, the
* inner type inherits from CHAICopyable, so the inner data will be moved. For
* example, this version of the method is called when there are nested
* ManagedArrays.
*/
template <bool B = std::is_base_of<CHAICopyable, T>::value,
typename std::enable_if<B, int>::type = 0>
CHAI_HOST void moveInnerImpl() const;
/*!
* \brief Does nothing since the inner data type does not inherit from
* CHAICopyable.
*
* Called in the copy constructor of ManagedArray. In this implementation, the
* inner type does not inherit from CHAICopyable, so nothing will be done. For
* example, this version of the method is called when there are not nested
* ManagedArrays.
*/
template <bool B = std::is_base_of<CHAICopyable, T>::value,
typename std::enable_if<!B, int>::type = 0>
CHAI_HOST void moveInnerImpl() const;
#endif
public:
CHAI_HOST_DEVICE void shallowCopy(ManagedArray<T> const& other) const
{
m_active_pointer = other.m_active_pointer;
m_active_base_pointer = other.m_active_base_pointer;
m_resource_manager = other.m_resource_manager;
m_size = other.m_size;
m_offset = other.m_offset;
m_pointer_record = other.m_pointer_record;
m_is_slice = other.m_is_slice;
#ifndef CHAI_DISABLE_RM
#if !defined(CHAI_DEVICE_COMPILE)
// if we can, ensure elems is based off the pointer_record size out of paranoia
if (m_pointer_record != nullptr && !m_is_slice) {
m_size = m_pointer_record->m_size;
}
#endif
#endif
}
/*!
* Accessor for m_is_slice -whether this array was created with a slice() command.
*/
CHAI_HOST_DEVICE bool isSlice() { return m_is_slice;}
private:
CHAI_HOST void modify(size_t i, const T& val) const;
// The following are only used by ManagedArray.inl, but for template
// shenanigan reasons need to be defined here.
#if !defined(CHAI_DISABLE_RM)
// if T is a CHAICopyable, then it is important to initialize all the
// ManagedArrays at allocation, since it is extremely easy to trigger
// a moveInnerImpl, which expects inner values to be initialized.
template <bool B = std::is_base_of<CHAICopyable, T>::value,
typename std::enable_if<B, int>::type = 0>
CHAI_HOST bool initInner(size_t start = 0)
{
for (size_t i = start; i < m_size/sizeof(T); ++i) {
m_active_base_pointer[i] = T();
}
return true;
}
// Do not deep initialize if T is not a CHAICopyable.
template <bool B = std::is_base_of<CHAICopyable, T>::value,
typename std::enable_if<!B, int>::type = 0>
CHAI_HOST bool initInner(size_t = 0)
{
return false;
}
#endif
protected:
/*!
* Currently active data pointer.
*/
mutable T* m_active_pointer = nullptr;
mutable T* m_active_base_pointer = nullptr;
/*!
* Pointer to ArrayManager instance.
*/
mutable ArrayManager* m_resource_manager = nullptr;
/*!
* Number of elements in the ManagedArray.
*/
mutable size_t m_size = 0;
mutable size_t m_offset = 0;
/*!
* Pointer to PointerRecord data.
*/
mutable PointerRecord* m_pointer_record = nullptr;
mutable bool m_is_slice = false;
};
/*!
* \brief Construct a ManagedArray from an externally allocated pointer.
*
* The pointer can exist in any valid ExecutionSpace, and can either be "owned"
* or "unowned". An owned pointer will be deallocated by the ArrayManager when
* free is called on the returned ManagedArray object.
*
* \param data Pointer to the raw data.
* \param elems Number of elements in the data pointer.
* \param space ExecutionSpace where the data pointer exists.
* \param owned If true, data will be deallocated by the ArrayManager.
*
* \tparam T Type of the raw data.
*
* \return A new ManagedArray containing the raw data pointer.
*/
template <typename T>
ManagedArray<T> makeManagedArray(T* data,
size_t elems,
ExecutionSpace space,
bool owned)
{
#if !defined(CHAI_DISABLE_RM)
ArrayManager* manager = ArrayManager::getInstance();
// First, try and find an existing PointerRecord for the pointer
PointerRecord* record = manager->getPointerRecord(data);
bool existingRecord = true;
if (record == &ArrayManager::s_null_record) {
// create a new pointer record for external pointer
record = manager->makeManaged(data, sizeof(T) * elems, space, owned);
existingRecord = false;
}
ManagedArray<T> array(record, space);
if (existingRecord && !owned) {
// pointer has an owning PointerRecord, create a non-owning ManagedArray
// slice
array = array.slice(0, elems);
}
if (!std::is_const<T>::value) {
array.registerTouch(space);
}
#else
PointerRecord recordTmp;
recordTmp.m_pointers[space] = data;
recordTmp.m_size = sizeof(T) * elems;
recordTmp.m_owned[space] = owned;
ManagedArray<T> array(&recordTmp, space);
#endif
return array;
}
/*!
* \brief Create a copy of the given ManagedArray with a single allocation in
* the active space of the given array.
*
* \param array The ManagedArray to copy.
*
* \tparam T Type of the raw data.
*
* \return A copy of the given ManagedArray.
*/
template <typename T>
ManagedArray<T> deepCopy(ManagedArray<T> const& array)
{
T* data_ptr = array.getActiveBasePointer();
ArrayManager* manager = ArrayManager::getInstance();
PointerRecord const* record = manager->getPointerRecord(data_ptr);
PointerRecord* copy_record = manager->deepCopyRecord(record);
return ManagedArray<T>(copy_record, copy_record->m_last_space);
}
template <typename T>
CHAI_INLINE CHAI_HOST_DEVICE ManagedArray<T> ManagedArray<T>::slice( size_t offset, size_t elems) const
{
ManagedArray<T> slice;
slice.m_resource_manager = m_resource_manager;
if (elems == (size_t) -1) {
elems = size() - offset;
}
if (offset + elems > size()) {
#if !defined(CHAI_DEVICE_COMPILE)
CHAI_LOG(Debug,
"Invalid slice. No active pointer or index out of bounds");
#endif
} else {
slice.m_pointer_record = m_pointer_record;
slice.m_active_base_pointer = m_active_base_pointer;
slice.m_offset = offset + m_offset;
slice.m_active_pointer = m_active_base_pointer + slice.m_offset;
slice.m_size = elems*sizeof(T);
slice.m_is_slice = true;
}
return slice;
}
} // end of namespace chai
#if defined(CHAI_DISABLE_RM)
#include "chai/ManagedArray_thin.inl"
#else
#include "chai/ManagedArray.inl"
#endif
#endif // CHAI_ManagedArray_HPP