MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
mem_manager.hpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#ifndef MFEM_MEM_MANAGER_HPP
13#define MFEM_MEM_MANAGER_HPP
14
15#include "enzyme.hpp"
16#include "globals.hpp"
17#include "error.hpp"
18#include <cstring> // std::memcpy
19#include <type_traits> // std::is_const
20#include <cstddef> // std::max_align_t
21
22#ifdef MFEM_USE_MPI
23// Enable internal hypre timing routines
24#define HYPRE_TIMING
25#include <HYPRE_utilities.h> // for HYPRE_GetMemoryLocation() and others
26#if (21400 <= MFEM_HYPRE_VERSION) && (MFEM_HYPRE_VERSION < 21900)
27#include <_hypre_utilities.h> // for HYPRE_MEMORY_HOST and others
28#endif
29#endif
30
31namespace mfem
32{
33
34// Implementation of MFEM's lightweight device/host memory manager designed to
35// work seamlessly with the OCCA, RAJA, and other kernels supported by MFEM.
36
37/// Memory types supported by MFEM.
38enum class MemoryType
39{
40 HOST, ///< Host memory; using new[] and delete[]
41 HOST_32, ///< Host memory; aligned at 32 bytes
42 HOST_64, ///< Host memory; aligned at 64 bytes
43 HOST_DEBUG, ///< Host memory; allocated from a "host-debug" pool
44 HOST_UMPIRE, /**< Host memory; using an Umpire allocator which can be set
45 with MemoryManager::SetUmpireHostAllocatorName */
46 HOST_PINNED, ///< Host memory: pinned (page-locked)
47 MANAGED, /**< Managed memory; using CUDA or HIP *MallocManaged
48 and *Free */
49 DEVICE, ///< Device memory; using CUDA or HIP *Malloc and *Free
50 DEVICE_DEBUG, /**< Pseudo-device memory; allocated on host from a
51 "device-debug" pool */
52 DEVICE_UMPIRE, /**< Device memory; using an Umpire allocator which can be
53 set with MemoryManager::SetUmpireDeviceAllocatorName */
54 DEVICE_UMPIRE_2, /**< Device memory; using a second Umpire allocator settable
55 with MemoryManager::SetUmpireDevice2AllocatorName */
56 SIZE, ///< Number of host and device memory types
57
58 PRESERVE, /**< Pseudo-MemoryType used as default value for MemoryType
59 parameters to request preservation of existing
60 MemoryType, e.g. in copy constructors. */
61 DEFAULT /**< Pseudo-MemoryType used as default value for MemoryType
62 parameters to request the use of the default host or
63 device MemoryType. */
64};
65
66/// Static casts to 'int' and sizes of some useful memory types.
67constexpr int MemoryTypeSize = static_cast<int>(MemoryType::SIZE);
68constexpr int HostMemoryType = static_cast<int>(MemoryType::HOST);
69constexpr int HostMemoryTypeSize = static_cast<int>(MemoryType::DEVICE);
70constexpr int DeviceMemoryType = static_cast<int>(MemoryType::MANAGED);
72
73/// Memory type names, used during Device:: configuration.
74extern MFEM_EXPORT const char *MemoryTypeName[MemoryTypeSize];
75
76/// Memory classes identify sets of memory types.
77/** This type is used by kernels that can work with multiple MemoryType%s.
78 * For example, kernels that can use DEVICE or MANAGED memory types should
79 * use MemoryClass::DEVICE for their inputs. */
80enum class MemoryClass
81{
82 HOST, /**< Memory types: { HOST, HOST_32, HOST_64, HOST_DEBUG,
83 HOST_UMPIRE, HOST_PINNED, MANAGED } */
84 HOST_32, ///< Memory types: { HOST_32, HOST_64, HOST_DEBUG }
85 HOST_64, ///< Memory types: { HOST_64, HOST_DEBUG }
86 DEVICE, /**< Memory types: { DEVICE, DEVICE_DEBUG, DEVICE_UMPIRE,
87 DEVICE_UMPIRE_2, MANAGED } */
88 MANAGED ///< Memory types: { MANAGED }
89};
90
91/// Return true if the given memory type is in MemoryClass::HOST.
92inline bool IsHostMemory(MemoryType mt) { return mt <= MemoryType::MANAGED; }
93
94/// Return true if the given memory type is in MemoryClass::DEVICE
96{
97 return mt >= MemoryType::MANAGED && mt < MemoryType::SIZE;
98}
99
100/// Return a suitable MemoryType for a given MemoryClass.
102
103/// Return true iff the MemoryType @a mt is contained in the MemoryClass @a mc.
105
106/// Return a suitable MemoryClass from a pair of MemoryClass%es.
107/** Note: this operation is commutative, i.e. a*b = b*a, associative, i.e.
108 (a*b)*c = a*(b*c), and has an identity element: MemoryClass::HOST.
109
110 Currently, the operation is defined as a*b := max(a,b) where the max
111 operation is based on the enumeration ordering:
112
113 HOST < HOST_32 < HOST_64 < DEVICE < MANAGED. */
115
116/// Class used by MFEM to store pointers to host and/or device memory.
117/** The template class parameter, T, must be a plain-old-data (POD) type.
118
119 In many respects this class behaves like a pointer:
120 - When destroyed, a Memory object does NOT automatically delete any
121 allocated memory.
122 - Only the method Delete() will deallocate a Memory object.
123 - Other methods that modify the object (e.g. New(), Wrap(), etc) will
124 simply overwrite the old contents.
125 In other aspects this class differs from a pointer:
126 - Pointer arithmetic is not supported, MakeAlias() should be used instead.
127 - Const Memory object does not allow modification of the content
128 (unlike e.g. a const pointer).
129 - Move constructor and assignment will transfer ownership flags, and
130 Reset() the moved Memory object.
131 - Copy constructor and assignment copy flags. This may result in two Memory
132 objects owning the data which is an invalid state. This invalid state MUST
133 be resolved by users manually using SetHostPtrOwner(),
134 SetDevicePtrOwner(), or ClearOwnerFlags(). It is also possible to call
135 Delete() on only one of the two Memory objects, however this is
136 discouraged because it bypasses the internal ownership flags.
137 - When moving or copying (between host and device) alias Memory objects
138 and/or their base Memory objects, the consistency of memory flags have
139 to be manually taken care of using either Sync() or SyncAlias(). Failure
140 to do so will result in silent misuse of unsynchronized data.
141
142 A Memory object stores up to two different pointers: one host pointer (with
143 MemoryType from MemoryClass::HOST) and one device pointer (currently one of
144 MemoryType: DEVICE, DEVICE_DEBUG, DEVICE_UMPIRE or MANAGED).
145
146 A Memory object can hold (wrap) an externally allocated pointer with any
147 given MemoryType.
148
149 Access to the content of the Memory object can be requested with any given
150 MemoryClass through the methods ReadWrite(), Read(), and Write().
151 Requesting such access may result in additional (internally handled)
152 memory allocation and/or memory copy.
153 - When ReadWrite() is called, the returned pointer becomes the only
154 valid pointer.
155 - When Read() is called, the returned pointer becomes valid, however
156 the other pointer (host or device) may remain valid as well.
157 - When Write() is called, the returned pointer becomes the only valid
158 pointer, however, unlike ReadWrite(), no memory copy will be performed.
159
160 The host memory (pointer from MemoryClass::HOST) can be accessed through the
161 inline methods: `operator[]()`, `operator*()`, the implicit conversion
162 functions `operator T*()`, `operator const T*()`, and the explicit
163 conversion template functions `operator U*()`, `operator const U*()` (with
164 any suitable type U). In certain cases, using these methods may have
165 undefined behavior, e.g. if the host pointer is not currently valid. */
166template <typename T>
168{
169protected:
170 friend class MemoryManager;
171 template <class U> friend class Memory;
172 friend void MemoryPrintFlags(unsigned flags);
173 template <typename VT> friend class MemoryView;
174
175 enum FlagMask: unsigned
176 {
177 // Workaround for use with headers that define REGISTERED as a macro,
178 // e.g. nb30.h (which is included by Windows.h):
179#ifndef REGISTERED
180 REGISTERED = 1 << 0, /**< The host pointer is registered with the
181 MemoryManager */
182#endif
183 // Use the following identifier if REGISTERED is defined as a macro,
184 // e.g. nb30.h (which is included by Windows.h):
185 Registered = 1 << 0, /**< The host pointer is registered with the
186 MemoryManager */
187 OWNS_HOST = 1 << 1, ///< The host pointer will be deleted by Delete()
188 OWNS_DEVICE = 1 << 2, /**< The device pointer will be deleted by
189 Delete() */
190 OWNS_INTERNAL = 1 << 3, ///< Ownership flag for internal Memory data
191 VALID_HOST = 1 << 4, ///< Host pointer is valid
192 VALID_DEVICE = 1 << 5, ///< %Device pointer is valid
193 USE_DEVICE = 1 << 6, /**< Internal device flag, see e.g.
194 Vector::UseDevice() */
195 ALIAS = 1 << 7 ///< Pointer is an alias
196 };
197
198 /// Pointer to host memory. Not owned.
199 /** The type of the pointer is given by the field #h_mt; it can be any type
200 from MemoryClass::HOST. */
202 int capacity; ///< Size of the allocated memory
203 MemoryType h_mt; ///< Host memory type
204 mutable unsigned flags; ///< Bit flags defined from the #FlagMask enum
205 // 'flags' is mutable so that it can be modified in Set{Host,Device}PtrOwner,
206 // Copy{From,To}, {ReadWrite,Read,Write}.
207
208public:
209 /** Default constructor, sets the host pointer to nullptr and the metadata to
210 meaningful default values. */
211 Memory() { Reset(); }
212
213 /// Copy constructor: default.
214 Memory(const Memory &) = default;
215
216 /** Move constructor. Sets the pointers and associated ownership of validity
217 flags of @a *this to those of @a other. Resets @a other. */
218 Memory(Memory &&other)
219 {
220 *this = other;
221 other.Reset();
222 }
223
224 /// Copy-assignment operator: default.
225 Memory &operator=(const Memory &) = default;
226
227 /** Move assignment operator. Sets the pointers and associated ownership of
228 validity flags of @a *this to those of @a other. Resets @a other. */
230 {
231 // Guard self-assignment:
232 if (this == &other) { return *this; }
233 *this = other;
234 other.Reset();
235 return *this;
236 }
237
238 /// Allocate host memory for @a size entries.
239 /** The allocation uses the current host memory type returned by
240 MemoryManager::GetHostMemoryType(). */
241 explicit Memory(int size) { New(size); }
242
243 /// Creates a new empty Memory object with host MemoryType @a mt.
244 explicit Memory(MemoryType mt) { Reset(mt); }
245
246 /** @brief Allocate memory for @a size entries with the given MemoryType
247 @a mt. */
248 /** The newly allocated memory is not initialized, however the given
249 MemoryType is still set as valid. */
250 Memory(int size, MemoryType mt) { New(size, mt); }
251
252 /** @brief Allocate memory for @a size entries with the given host MemoryType
253 @a h_mt and device MemoryType @a d_mt. */
254 /** The newly allocated memory is not initialized. The host pointer is set as
255 valid. */
256 Memory(int size, MemoryType h_mt, MemoryType d_mt) { New(size, h_mt, d_mt); }
257
258 /** @brief Wrap an externally allocated host pointer, @a ptr with the current
259 host memory type returned by MemoryManager::GetHostMemoryType(). */
260 /** The parameter @a own determines whether @a ptr will be deleted when the
261 method Delete() is called. */
262 explicit Memory(T *ptr, int size, bool own) { Wrap(ptr, size, own); }
263
264 /// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
265 /** The new memory object will have the given MemoryType set as valid.
266
267 The given @a ptr must be allocated appropriately for the given
268 MemoryType.
269
270 The parameter @a own determines whether @a ptr will be deleted when the
271 method Delete() is called. */
272 Memory(T *ptr, int size, MemoryType mt, bool own)
273 { Wrap(ptr, size, mt, own); }
274
275 /** @brief Alias constructor. Create a Memory object that points inside the
276 Memory object @a base. */
277 /** The new Memory object uses the same MemoryType(s) as @a base. */
278 Memory(const Memory &base, int offset, int size)
279 { MakeAlias(base, offset, size); }
280
281 /// Destructor: default.
282 /** @note The destructor will NOT delete the current memory. */
283 ~Memory() = default;
284
285 /// Swap without using move assignment, avoiding Reset() calls.
286 void Swap(Memory &other)
287 {
288 Memory tmp(*this);
289 *this = other;
290 other = tmp;
291 }
292
293 /** @brief Return true if the host pointer is owned. Ownership indicates
294 whether the pointer will be deleted by the method Delete(). */
295 bool OwnsHostPtr() const { return flags & OWNS_HOST; }
296
297 /** @brief Set/clear the ownership flag for the host pointer. Ownership
298 indicates whether the pointer will be deleted by the method Delete(). */
299 void SetHostPtrOwner(bool own) const
300 { flags = own ? (flags | OWNS_HOST) : (flags & ~OWNS_HOST); }
301
302 /** @brief Return true if the device pointer is owned. Ownership indicates
303 whether the pointer will be deleted by the method Delete(). */
304 bool OwnsDevicePtr() const { return flags & OWNS_DEVICE; }
305
306 /** @brief Set/clear the ownership flag for the device pointer. Ownership
307 indicates whether the pointer will be deleted by the method Delete(). */
308 void SetDevicePtrOwner(bool own) const
309 { flags = own ? (flags | OWNS_DEVICE) : (flags & ~OWNS_DEVICE); }
310
311 /** @brief Clear the ownership flags for the host and device pointers, as
312 well as any internal data allocated by the Memory object. */
315
316 /// Read the internal device flag.
317 bool UseDevice() const { return flags & USE_DEVICE; }
318
319 /// Set the internal device flag.
320 void UseDevice(bool use_dev) const
321 { flags = use_dev ? (flags | USE_DEVICE) : (flags & ~USE_DEVICE); }
322
323 /// Return the size of the allocated memory.
324 int Capacity() const { return capacity; }
325
326 /// Reset the memory to be empty, ensuring that Delete() will be a no-op.
327 /** This is the Memory class equivalent to setting a pointer to NULL, see
328 Empty().
329
330 @note The current memory is NOT deleted by this method. */
331 void Reset();
332
333 /// Reset the memory and set the host memory type.
334 void Reset(MemoryType host_mt);
335
336 /// Return true if the Memory object is empty, see Reset().
337 /** Default-constructed objects are guaranteed to be empty. */
338 bool Empty() const { return h_ptr == NULL; }
339
340 /** @brief Allocate host memory for @a size entries with the current host
341 memory type returned by MemoryManager::GetHostMemoryType(). */
342 /** @note The current memory is NOT deleted by this method. */
343 inline void New(int size);
344
345 /// Allocate memory for @a size entries with the given MemoryType.
346 /** The newly allocated memory is not initialized, however the given
347 MemoryType is still set as valid.
348
349 When @a mt is a host type, the device MemoryType will be set later, if
350 requested, using the dual type of @a mt, see
351 MemoryManager::GetDualMemoryType().
352
353 When @a mt is a device type, the host MemoryType will be set immediately
354 to be the dual of @a mt, see MemoryManager::GetDualMemoryType().
355
356 @note The current memory is NOT deleted by this method. */
357 inline void New(int size, MemoryType mt);
358
359 /** @brief Allocate memory for @a size entries with the given host MemoryType
360 @a h_mt and device MemoryType @a d_mt. */
361 /** The newly allocated memory is not initialized. The host pointer is set as
362 valid.
363
364 @note The current memory is NOT deleted by this method. */
365 inline void New(int size, MemoryType h_mt, MemoryType d_mt);
366
367 /** @brief Wrap an externally allocated host pointer, @a ptr with the current
368 host memory type returned by MemoryManager::GetHostMemoryType(). */
369 /** The parameter @a own determines whether @a ptr will be deleted when the
370 method Delete() is called.
371
372 @note The current memory is NOT deleted by this method. */
373 inline void Wrap(T *ptr, int size, bool own);
374
375 /// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
376 /** The new memory object will have the given MemoryType set as valid.
377
378 The given @a ptr must be allocated appropriately for the given
379 MemoryType.
380
381 The parameter @a own determines whether @a ptr will be deleted when the
382 method Delete() is called.
383
384 @note The current memory is NOT deleted by this method. */
385 inline void Wrap(T *ptr, int size, MemoryType mt, bool own);
386
387 /** Wrap an externally pair of allocated pointers, @a h_ptr and @a d_ptr,
388 of the given host MemoryType @a h_mt. */
389 /** The new memory object will have the device MemoryType set as valid unless
390 specified otherwise by the parameters @a valid_host and @a valid_device.
391
392 The given @a h_ptr and @a d_ptr must be allocated appropriately for the
393 given host MemoryType and its dual device MemoryType as defined by
394 MemoryManager::GetDualMemoryType().
395
396 The parameter @a own determines whether both @a h_ptr and @a d_ptr will
397 be deleted when the method Delete() is called.
398
399 The parameters @a valid_host and @a valid_device determine which
400 pointers, host and/or device, will be marked as valid; at least one of
401 the two parameters must be set to true.
402
403 @note Ownership can also be controlled by using the following methods:
404 - ClearOwnerFlags,
405 - SetHostPtrOwner,
406 - SetDevicePtrOwner.
407
408 @note The current memory is NOT deleted by this method. */
409 inline void Wrap(T *h_ptr, T *d_ptr, int size, MemoryType h_mt, bool own,
410 bool valid_host = false, bool valid_device = true);
411
412 /// Create a memory object that points inside the memory object @a base.
413 /** The new Memory object uses the same MemoryType(s) as @a base.
414
415 @note The current memory is NOT deleted by this method. */
416 inline void MakeAlias(const Memory &base, int offset, int size);
417
418 /// For internal use only.
419 /// U* must be reinterpret_cast-able to T*
420 template <class U>
421 inline void CopyConvertPtr(const Memory<U> &base);
422
423 /// Set the device MemoryType to be used by the Memory object.
424 /** If the specified @a d_mt is not a device MemoryType, i.e. not one of the
425 types in MemoryClass::DEVICE, then this method will return immediately.
426
427 If the device MemoryType has been previously set to a different type and
428 the actual device memory has been allocated, this method will trigger an
429 error. This method will not perform the actual device memory allocation,
430 however, the allocation may already exist if the MemoryType is the same
431 as the current one.
432
433 If the Memory is an alias Memory, the device MemoryType of its base will
434 be updated as described above. */
436
437 /** @brief Delete the owned pointers and reset the Memory object. */
438 inline void Delete();
439
440 /** @brief Delete the device pointer, if owned. If @a copy_to_host is true
441 and the data is valid only on device, move it to host before deleting.
442 Invalidates the device memory. */
443 inline void DeleteDevice(bool copy_to_host = true);
444
445 /// Array subscript operator for host memory.
446 inline T &operator[](int idx);
447
448 /// Array subscript operator for host memory, const version.
449 inline const T &operator[](int idx) const;
450
451 /// Direct access to the host memory as T* (implicit conversion).
452 /** When the type T is const-qualified, this method can be used only if the
453 host pointer is currently valid (the device pointer may be valid or
454 invalid).
455
456 When the type T is not const-qualified, this method can be used only if
457 the host pointer is the only valid pointer.
458
459 When the Memory is empty, this method can be used and it returns NULL. */
460 inline operator T*();
461
462 /// Direct access to the host memory as const T* (implicit conversion).
463 /** This method can be used only if the host pointer is currently valid (the
464 device pointer may be valid or invalid).
465
466 When the Memory is empty, this method can be used and it returns NULL. */
467 inline operator const T*() const;
468
469 /// Direct access to the host memory via explicit typecast.
470 /** A pointer to type T must be reinterpret_cast-able to a pointer to type U.
471 In particular, this method cannot be used to cast away const-ness from
472 the base type T.
473
474 When the type U is const-qualified, this method can be used only if the
475 host pointer is currently valid (the device pointer may be valid or
476 invalid).
477
478 When the type U is not const-qualified, this method can be used only if
479 the host pointer is the only valid pointer.
480
481 When the Memory is empty, this method can be used and it returns NULL. */
482 template <typename U>
483 inline explicit operator U*();
484
485 /// Direct access to the host memory via explicit typecast, const version.
486 /** A pointer to type T must be reinterpret_cast-able to a pointer to type
487 const U.
488
489 This method can be used only if the host pointer is currently valid (the
490 device pointer may be valid or invalid).
491
492 When the Memory is empty, this method can be used and it returns NULL. */
493 template <typename U>
494 inline explicit operator const U*() const;
495
496 /// Get read-write access to the memory with the given MemoryClass.
497 /** If only read or only write access is needed, then the methods
498 Read() or Write() should be used instead of this method.
499
500 The parameter @a size must not exceed the Capacity(). */
501 inline T *ReadWrite(MemoryClass mc, int size);
502
503 /// Get read-only access to the memory with the given MemoryClass.
504 /** The parameter @a size must not exceed the Capacity(). */
505 inline const T *Read(MemoryClass mc, int size) const;
506
507 /// Get write-only access to the memory with the given MemoryClass.
508 /** The parameter @a size must not exceed the Capacity().
509
510 The contents of the returned pointer is undefined, unless it was
511 validated by a previous call to Read() or ReadWrite() with
512 the same MemoryClass. */
513 inline T *Write(MemoryClass mc, int size);
514
515 /// Copy the host/device pointer validity flags from @a other to @a *this.
516 /** This method synchronizes the pointer validity flags of two Memory objects
517 that use the same host/device pointers, or when @a *this is an alias
518 (sub-Memory) of @a other. Typically, this method should be called after
519 @a other is manipulated in a way that changes its pointer validity flags
520 (e.g. it was moved from device to host memory). */
521 inline void Sync(const Memory &other) const;
522
523 /** @brief Update the alias Memory @a *this to match the memory location (all
524 valid locations) of its base Memory, @a base. */
525 /** This method is useful when alias Memory is moved and manipulated in a
526 different memory space. Such operations render the pointer validity flags
527 of the base incorrect. Calling this method will ensure that @a base is
528 up-to-date. Note that this is achieved by moving/copying @a *this (if
529 necessary), and not @a base. */
530 inline void SyncAlias(const Memory &base, int alias_size) const;
531
532 /** @brief Return a MemoryType that is currently valid. If both the host and
533 the device pointers are currently valid, then the device memory type is
534 returned. */
535 inline MemoryType GetMemoryType() const;
536
537 /// Return the host MemoryType of the Memory object.
538 inline MemoryType GetHostMemoryType() const { return h_mt; }
539
540 /** @brief Return the device MemoryType of the Memory object. If the device
541 MemoryType is not set, return MemoryType::DEFAULT. */
543
544 /** @brief Return true if host pointer is valid */
545 inline bool HostIsValid() const;
546
547 /** @brief Return true if device pointer is valid */
548 inline bool DeviceIsValid() const;
549
550 /// Copy @a size entries from @a src to @a *this.
551 /** The given @a size should not exceed the Capacity() of the source @a src
552 and the destination, @a *this. */
553 inline void CopyFrom(const Memory &src, int size);
554
555 /// Copy @a size entries from the host pointer @a src to @a *this.
556 /** The given @a size should not exceed the Capacity() of @a *this. */
557 inline void CopyFromHost(const T *src, int size);
558
559 /// Copy @a size entries from @a *this to @a dest.
560 /** The given @a size should not exceed the Capacity() of @a *this and the
561 destination, @a dest. */
562 inline void CopyTo(Memory &dest, int size) const;
563
564 /// Copy @a size entries from @a *this to the host pointer @a dest.
565 /** The given @a size should not exceed the Capacity() of @a *this. */
566 inline void CopyToHost(T *dest, int size) const;
567
568 /// Print the internal flags.
569 /** This method can be useful for debugging. It is explicitly instantiated
570 for Memory<T> with T = int and T = real_t. */
571 inline void PrintFlags() const;
572
573 /// If both the host and the device data are valid, compare their contents.
574 /** This method can be useful for debugging. It is explicitly instantiated
575 for Memory<T> with T = int and T = real_t. */
576 inline int CompareHostAndDevice(int size) const;
577
578private:
579 // GCC 4.8 workaround: max_align_t is not in std.
580 static constexpr std::size_t def_align_bytes_()
581 {
582 using namespace std;
583 return alignof(max_align_t);
584 }
585 static constexpr std::size_t def_align_bytes = def_align_bytes_();
586 static constexpr std::size_t new_align_bytes =
587 alignof(T) > def_align_bytes ? alignof(T) : def_align_bytes;
588
589 template <std::size_t align_bytes, bool dummy = true> struct Alloc
590 {
591#if __cplusplus < 201703L
592 static inline T *New(std::size_t)
593 {
594 // Generate an error in debug mode
595 MFEM_ASSERT(false, "overaligned type cannot use MemoryType::HOST");
596 return nullptr;
597 }
598#else
599 static inline T *New(std::size_t size) { return new T[size]; }
600#endif
601 };
602
603#if __cplusplus < 201703L
604 template<bool dummy> struct Alloc<def_align_bytes,dummy>
605 {
606 static inline T *New(std::size_t size) { return new T[size]; }
607 };
608#endif
609
610 // Shortcut for Alloc<new_align_bytes>::New(size)
611 static inline T *NewHOST(std::size_t size)
612 {
613 return Alloc<new_align_bytes>::New(size);
614 }
615};
616
617
618/** @brief Type that enables viewing Vector objects as Array<real_t> objects and
619 vice versa. Currently, viewing methods are provided only for the first
620 direction, see Vector::GetArrayView(). */
621template <typename ViewedType>
623{
624 friend class Vector;
625
626protected:
627 static constexpr bool is_const_view = std::is_const_v<ViewedType>;
628 using T =
629 std::remove_reference_t<decltype((std::remove_cv_t<ViewedType> {})[0])>;
631 std::conditional_t<is_const_view, const Memory<T>, Memory<T>>;
632 using SizeType =
633 std::conditional_t<is_const_view, const int, int>;
634
635 std::remove_cv_t<ViewedType> view;
637 SizeType &base_size; // if is_const_view, this is initialized but not used
638
639 // Keep the constructor private, for now.
640 inline MemoryView(MemoryType &mem, SizeType &size)
641 : base_mem(mem), base_size(size)
642 {
643 // keep for debugging
644 // mfem::out << _MFEM_FUNC_NAME << std::endl;
645 view.data = mem;
646 view.size = size;
647 }
648
649public:
650 MemoryView(const MemoryView &) = delete;
651 MemoryView(MemoryView &&) = delete;
652 MemoryView &operator=(const MemoryView &) = delete;
654
655 inline ~MemoryView()
656 {
657 // keep for debugging
658 // mfem::out << _MFEM_FUNC_NAME << std::endl;
659 if constexpr (!is_const_view)
660 {
661 base_mem = view.data;
662 base_size = view.size;
663 }
664 else
665 {
666 base_mem.flags = view.data.flags;
667 }
668 view.data.Reset();
669 }
670
671 /** @brief Implicit conversion function to `ViewedType &`.
672
673 Implicit conversion may not work automatically when the returned type is
674 used for template parameter deduction. In such cases, use the prefix
675 operator*() to explicitly perform the conversion to `ViewedType &`. */
676 inline operator ViewedType &() { return view; }
677
678 /** @brief Return the view object by reference, `ViewedType &`.
679
680 This is an explicit way to return the view object, alternative to the
681 implicit conversion function to `ViewedType &`. */
682 inline ViewedType &operator*() { return view; }
683};
684
685
686/** @brief Swap of Memory<T> objects for use with standard library algorithms.
687 Also, used by mfem::Swap(). */
688template <typename T>
690{
691 a.Swap(b);
692}
693
694
695/** The MFEM memory manager class. Host-side pointers are inserted into this
696 manager which keeps track of the associated device pointer, and where the
697 data currently resides. */
698class MFEM_EXPORT MemoryManager
699{
700private:
701
702 typedef MemoryType MemType;
703 typedef Memory<int> Mem;
704
705 template <typename T> friend class Memory;
706
707 /// Host memory type set during the Setup.
708 MFEM_ENZYME_INACTIVE static MemoryType host_mem_type;
709
710 /// Device memory type set during the Setup.
711 MFEM_ENZYME_INACTIVE static MemoryType device_mem_type;
712
713 /// Allow to detect if a global memory manager instance exists.
714 MFEM_ENZYME_INACTIVE static bool exists;
715
716 /// Return true if the global memory manager instance exists.
717 static bool Exists() { return exists; }
718
719 /// Array defining the dual MemoryType for each MemoryType
720 /** The dual of a host MemoryType is a device MemoryType and vice versa: the
721 dual of a device MemoryType is a host MemoryType. */
722 MFEM_ENZYME_INACTIVE static MemoryType dual_map[MemoryTypeSize];
723
724 /// Update the dual memory type of @a mt to be @a dual_mt.
725 static void UpdateDualMemoryType(MemoryType mt, MemoryType dual_mt);
726
727 /// True if Configure() was called.
728 MFEM_ENZYME_INACTIVE static bool configured;
729
730 /// Host and device allocator names for Umpire.
731#ifdef MFEM_USE_UMPIRE
732 static const char * h_umpire_name;
733 static const char * d_umpire_name;
734 static const char * d_umpire_2_name;
735#endif
736
737private: // Static methods used by the Memory<T> class
738
739 /// Allocate and register a new pointer. Return the host pointer.
740 /// h_tmp must be already allocated using new T[] if mt is a pure device
741 /// memory type, e.g. CUDA (mt will not be HOST).
742 static void *New_(void *h_tmp, size_t bytes, MemoryType mt, unsigned &flags);
743
744 static void *New_(void *h_tmp, size_t bytes, MemoryType h_mt,
745 MemoryType d_mt, unsigned valid_flags, unsigned &flags);
746
747 /// Register an external pointer of the given MemoryType.
748 /// Return the host pointer.
749 MFEM_ENZYME_INACTIVE static void *Register_(void *ptr, void *h_ptr,
750 size_t bytes, MemoryType mt,
751 bool own, bool alias, unsigned &flags);
752
753 /// Register a pair of external host and device pointers
754 static void Register2_(void *h_ptr, void *d_ptr, size_t bytes,
755 MemoryType h_mt, MemoryType d_mt,
756 bool own, bool alias, unsigned &flags,
757 unsigned valid_flags);
758
759 /// Register an alias. Note: base_h_ptr may be an alias.
760 static void Alias_(void *base_h_ptr, size_t offset, size_t bytes,
761 unsigned base_flags, unsigned &flags);
762
763 static void SetDeviceMemoryType_(void *h_ptr, unsigned flags,
764 MemoryType d_mt);
765
766 /// Un-register and free memory identified by its host pointer.
767 MFEM_ENZYME_FN_LIKE_FREE static void Delete_(void *h_ptr, MemoryType mt,
768 unsigned flags);
769
770 /// Free device memory identified by its host pointer
771 static void DeleteDevice_(void *h_ptr, unsigned & flags);
772
773 /// Check if the memory types given the memory class are valid
774 static bool MemoryClassCheck_(MemoryClass mc, void *h_ptr,
775 MemoryType h_mt, size_t bytes, unsigned flags);
776
777 /// Return a pointer to the memory identified by the host pointer h_ptr for
778 /// access with the given MemoryClass.
779 MFEM_ENZYME_FN_LIKE_DYNCAST static void *ReadWrite_(void *h_ptr,
780 MemoryType h_mt, MemoryClass mc,
781 size_t bytes, unsigned &flags);
782
783 MFEM_ENZYME_FN_LIKE_DYNCAST static const void *Read_(void *h_ptr,
784 MemoryType h_mt, MemoryClass mc,
785 size_t bytes, unsigned &flags);
786
787 MFEM_ENZYME_FN_LIKE_DYNCAST static void *Write_(void *h_ptr, MemoryType h_mt,
788 MemoryClass mc,
789 size_t bytes, unsigned &flags);
790
791 static void SyncAlias_(const void *base_h_ptr, void *alias_h_ptr,
792 size_t alias_bytes, unsigned base_flags,
793 unsigned &alias_flags);
794
795 /// Return the type the of the currently valid memory.
796 /// If more than one types are valid, return a device type.
797 MFEM_ENZYME_INACTIVE static MemoryType GetDeviceMemoryType_(void *h_ptr,
798 bool alias);
799
800 /// Return the type the of the host memory.
801 MFEM_ENZYME_INACTIVE static MemoryType GetHostMemoryType_(void *h_ptr);
802
803 /// Verify that h_mt and h_ptr's h_mt (memory or alias) are equal.
804 static void CheckHostMemoryType_(MemoryType h_mt, void *h_ptr, bool alias);
805
806 /// Copy entries from valid memory type to valid memory type.
807 /// Both dest_h_ptr and src_h_ptr are registered host pointers.
808 static void Copy_(void *dest_h_ptr, const void *src_h_ptr, size_t bytes,
809 unsigned src_flags, unsigned &dest_flags);
810
811 /// Copy entries from valid memory type to host memory, where dest_h_ptr is
812 /// not a registered host pointer and src_h_ptr is a registered host pointer.
813 static void CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
814 size_t bytes, unsigned src_flags);
815
816 /// Copy entries from host memory to valid memory type, where dest_h_ptr is a
817 /// registered host pointer and src_h_ptr is not a registered host pointer.
818 static void CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
819 size_t bytes, unsigned &dest_flags);
820
821 /// Check if the host pointer has been registered in the memory manager.
822 static bool IsKnown_(const void *h_ptr);
823
824 /** @brief Check if the host pointer has been registered as an alias in the
825 memory manager. */
826 static bool IsAlias_(const void *h_ptr);
827
828 /// Compare the contents of the host and the device memory.
829 static int CompareHostAndDevice_(void *h_ptr, size_t size, unsigned flags);
830
831private:
832
833 /// Insert a host address @a h_ptr and size *a bytes in the memory map to be
834 /// managed.
835 void Insert(void *h_ptr, size_t bytes, MemoryType h_mt, MemoryType d_mt);
836
837 /// Insert a device and the host addresses in the memory map
838 void InsertDevice(void *d_ptr, void *h_ptr, size_t bytes,
839 MemoryType h_mt, MemoryType d_mt);
840
841 /// Insert an alias in the alias map
842 void InsertAlias(const void *base_ptr, void *alias_ptr,
843 const size_t bytes, const bool base_is_alias);
844
845 /// Erase an address from the memory map, as well as all its aliases
846 void Erase(void *h_ptr, bool free_dev_ptr = true);
847
848 /// Erase device memory for a given host address
849 void EraseDevice(void *h_ptr);
850
851 /// Erase an alias from the aliases map
852 void EraseAlias(void *alias_ptr);
853
854 /// Return the corresponding device pointer of h_ptr,
855 /// allocating and moving the data if needed
856 void *GetDevicePtr(const void *h_ptr, size_t bytes, bool copy_data);
857
858 /// Return the corresponding device pointer of alias_ptr,
859 /// allocating and moving the data if needed
860 void *GetAliasDevicePtr(const void *alias_ptr, size_t bytes, bool copy_data);
861
862 /// Return the corresponding host pointer of d_ptr,
863 /// allocating and moving the data if needed
864 void *GetHostPtr(const void *d_ptr, size_t bytes, bool copy_data);
865
866 /// Return the corresponding host pointer of alias_ptr,
867 /// allocating and moving the data if needed
868 void *GetAliasHostPtr(const void *alias_ptr, size_t bytes, bool copy_data);
869
870public:
873
874 /// Initialize the memory manager.
875 void Init();
876
877 /// Return the dual MemoryType of the given one, @a mt.
878 /** The default dual memory types are:
879
880 memory type | dual type
881 --------------- | ---------
882 HOST | DEVICE
883 HOST_32 | DEVICE
884 HOST_64 | DEVICE
885 HOST_DEBUG | DEVICE_DEBUG
886 HOST_UMPIRE | DEVICE_UMPIRE
887 HOST_PINNED | DEVICE
888 MANAGED | MANAGED
889 DEVICE | HOST
890 DEVICE_DEBUG | HOST_DEBUG
891 DEVICE_UMPIRE | HOST_UMPIRE
892 DEVICE_UMPIRE_2 | HOST_UMPIRE
893
894 The dual types can be modified before device configuration using the
895 method SetDualMemoryType() or by calling Device::SetMemoryTypes(). */
897 { return dual_map[(int)mt]; }
898
899 /// Set the dual memory type of @a mt to be @a dual_mt.
900 /** This method can only be called before configuration, i.e. before calling
901 Configure(), which is typically done during Device construction.
902
903 One of the types must be a host MemoryType and the other must be a device
904 MemoryType or both types must be the same host memory type. The latter
905 case is only allowed for convenience in setting up pure host execution,
906 so the actual dual is not updated. */
907 static void SetDualMemoryType(MemoryType mt, MemoryType dual_mt);
908
909 /** @brief Configure the Memory manager with given default host and device
910 types. This method will be called when configuring a device.
911
912 The host and device MemoryType%s, @a h_mt and @a d_mt, are set to be dual
913 to each other. */
914 void Configure(const MemoryType h_mt, const MemoryType d_mt);
915
916#ifdef MFEM_USE_UMPIRE
917 /// Set the host Umpire allocator name used with MemoryType::HOST_UMPIRE
918 static void SetUmpireHostAllocatorName(const char * h_name) { h_umpire_name = h_name; }
919 /// Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE
920 static void SetUmpireDeviceAllocatorName(const char * d_name) { d_umpire_name = d_name; }
921 /// Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2
922 static void SetUmpireDevice2AllocatorName(const char * d_name) { d_umpire_2_name = d_name; }
923
924 /// Get the host Umpire allocator name used with MemoryType::HOST_UMPIRE
925 static const char * GetUmpireHostAllocatorName() { return h_umpire_name; }
926 /// Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE
927 static const char * GetUmpireDeviceAllocatorName() { return d_umpire_name; }
928 /// Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2
929 static const char * GetUmpireDevice2AllocatorName() { return d_umpire_2_name; }
930#endif
931
932 /// Free all the device memories
933 void Destroy();
934
935 /// Return true if the pointer is known by the memory manager
936 bool IsKnown(const void *h_ptr) { return IsKnown_(h_ptr); }
937
938 /// Return true if the pointer is known by the memory manager as an alias
939 bool IsAlias(const void *h_ptr) { return IsAlias_(h_ptr); }
940
941 /// Check if the host pointer has been registered in the memory manager
942 void RegisterCheck(void *h_ptr);
943
944 /// Prints all pointers known by the memory manager,
945 /// returning the number of printed pointers
946 int PrintPtrs(std::ostream &out = mfem::out);
947
948 /// Prints all aliases known by the memory manager
949 /// returning the number of printed pointers
950 int PrintAliases(std::ostream &out = mfem::out);
951
952 static MemoryType GetHostMemoryType() { return host_mem_type; }
953 static MemoryType GetDeviceMemoryType() { return device_mem_type; }
954
955#ifdef MFEM_USE_ENZYME
956 static void myfree(void* mem, MemoryType MT, unsigned &flags)
957 {
958 MemoryManager::Delete_(mem, MT, flags);
959 }
961 inline static void* __enzyme_allocation_like1[4] = {(void*)static_cast<void*(*)(void*, size_t, MemoryType, unsigned&)>(MemoryManager::New_),
962 (void*)1, (void*)"-1,2,3", (void*)myfree
963 };
965 inline static void* __enzyme_allocation_like2[4] = {(void*)static_cast<void*(*)(void*, size_t, MemoryType, MemoryType, unsigned, unsigned&)>(MemoryManager::New_),
966 (void*)1, (void*)"-1,2,4", (void*)MemoryManager::Delete_
967 };
968#endif
969};
970
971
972#ifdef MFEM_USE_MPI
973
974#if MFEM_HYPRE_VERSION < 21400
975#define HYPRE_MEMORY_DEVICE (0)
976#define HYPRE_MEMORY_HOST (1)
977#endif
978#if MFEM_HYPRE_VERSION < 21900
980#endif
981
982/// Return the configured HYPRE_MemoryLocation
984{
985#if !defined(HYPRE_USING_GPU)
986 return HYPRE_MEMORY_HOST;
987#elif MFEM_HYPRE_VERSION < 23100
988 return HYPRE_MEMORY_DEVICE;
989#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
990 if (!HYPRE_Initialized()) { return HYPRE_MEMORY_HOST; }
992 HYPRE_GetMemoryLocation(&loc);
993 return loc;
994#endif
995}
996
997/// Return true if HYPRE is configured to use GPU
998inline bool HypreUsingGPU()
999{
1000#if !defined(HYPRE_USING_GPU)
1001 return false;
1002#elif MFEM_HYPRE_VERSION < 23100
1003 return true;
1004#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
1005 return GetHypreMemoryLocation() != HYPRE_MEMORY_HOST;
1006#endif
1007}
1008
1009#endif // MFEM_USE_MPI
1010
1011
1012// Inline methods
1013
1014template <typename T>
1015inline void Memory<T>::Reset()
1016{
1017 h_ptr = NULL;
1019 capacity = 0;
1020 flags = 0;
1021}
1022
1023template <typename T>
1024inline void Memory<T>::Reset(MemoryType host_mt)
1025{
1026 h_ptr = NULL;
1027 h_mt = host_mt;
1028 capacity = 0;
1029 flags = 0;
1030}
1031
1032template <typename T>
1033inline void Memory<T>::New(int size)
1034{
1035 capacity = size;
1036 flags = OWNS_HOST | VALID_HOST;
1038 h_ptr = (h_mt == MemoryType::HOST) ? NewHOST(size) :
1039 (T*)MemoryManager::New_(nullptr, size*sizeof(T), h_mt, flags);
1040}
1041
1042template <typename T>
1043inline void Memory<T>::New(int size, MemoryType mt)
1044{
1045 capacity = size;
1046 const size_t bytes = size*sizeof(T);
1047 const bool mt_host = mt == MemoryType::HOST;
1048 if (mt_host) { flags = OWNS_HOST | VALID_HOST; }
1049 h_mt = IsHostMemory(mt) ? mt : MemoryManager::GetDualMemoryType(mt);
1050 T *h_tmp = (h_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
1051 h_ptr = (mt_host) ? h_tmp : (T*)MemoryManager::New_(h_tmp, bytes, mt, flags);
1052}
1053
1054template <typename T>
1055inline void Memory<T>::New(int size, MemoryType host_mt, MemoryType device_mt)
1056{
1057 capacity = size;
1058 const size_t bytes = size*sizeof(T);
1059 this->h_mt = host_mt;
1060 T *h_tmp = (host_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
1061 h_ptr = (T*)MemoryManager::New_(h_tmp, bytes, host_mt, device_mt,
1062 VALID_HOST, flags);
1063}
1064
1065template <typename T>
1066inline void Memory<T>::Wrap(T *ptr, int size, bool own)
1067{
1068 h_ptr = ptr;
1069 capacity = size;
1070 flags = (own ? OWNS_HOST : 0) | VALID_HOST;
1072#ifdef MFEM_DEBUG
1073 if (own && MemoryManager::Exists())
1074 {
1075 MemoryType h_ptr_mt = MemoryManager::GetHostMemoryType_(h_ptr);
1076 MFEM_VERIFY(h_mt == h_ptr_mt,
1077 "h_mt = " << (int)h_mt << ", h_ptr_mt = " << (int)h_ptr_mt);
1078 }
1079#endif
1080 if (own && h_mt != MemoryType::HOST)
1081 {
1082 const size_t bytes = size*sizeof(T);
1083 MemoryManager::Register_(ptr, ptr, bytes, h_mt, own, false, flags);
1084 }
1085}
1086
1087template <typename T>
1088inline void Memory<T>::Wrap(T *ptr, int size, MemoryType mt, bool own)
1089{
1090 capacity = size;
1091 if (IsHostMemory(mt))
1092 {
1093 h_mt = mt;
1094 h_ptr = ptr;
1095 if (mt == MemoryType::HOST || !own)
1096 {
1097 // Skip registration
1098 flags = (own ? OWNS_HOST : 0) | VALID_HOST;
1099 return;
1100 }
1101 }
1102 else
1103 {
1105 h_ptr = (h_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
1106 }
1107 flags = 0;
1108 h_ptr = (T*)MemoryManager::Register_(ptr, h_ptr, size*sizeof(T), mt,
1109 own, false, flags);
1110}
1111
1112template <typename T>
1113inline void Memory<T>::Wrap(T *h_ptr_, T *d_ptr, int size, MemoryType h_mt_,
1114 bool own, bool valid_host, bool valid_device)
1115{
1116 h_mt = h_mt_;
1117 flags = 0;
1118 h_ptr = h_ptr_;
1119 capacity = size;
1120 MFEM_ASSERT(IsHostMemory(h_mt),"");
1121 MFEM_ASSERT(valid_host || valid_device,"");
1122 const size_t bytes = size*sizeof(T);
1124 MemoryManager::Register2_(h_ptr, d_ptr, bytes, h_mt, d_mt,
1125 own, false, flags,
1126 valid_host*VALID_HOST|valid_device*VALID_DEVICE);
1127}
1128
1129template <typename T>
1130inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
1131{
1132 MFEM_ASSERT(0 <= offset, "invalid offset = " << offset);
1133 MFEM_ASSERT(0 <= size, "invalid size = " << size);
1134 MFEM_ASSERT(offset + size <= base.capacity,
1135 "invalid offset + size = " << offset + size
1136 << " > base capacity = " << base.capacity);
1137 capacity = size;
1138 h_mt = base.h_mt;
1139 h_ptr = base.h_ptr + offset;
1140 if (!(base.flags & Registered))
1141 {
1142 if (
1143#if !defined(HYPRE_USING_GPU)
1144 // If the following condition is true then MemoryManager::Exists()
1145 // should also be true:
1147#elif MFEM_HYPRE_VERSION < 23100
1148 // When HYPRE_USING_GPU is defined and HYPRE < 2.31.0, we always
1149 // register the 'base' if the MemoryManager::Exists():
1150 MemoryManager::Exists()
1151#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
1153 (MemoryManager::Exists() && HypreUsingGPU())
1154#endif
1155 )
1156 {
1157 // Register 'base':
1158 MemoryManager::Register_(base.h_ptr, nullptr, base.capacity*sizeof(T),
1159 base.h_mt, base.flags & OWNS_HOST,
1160 base.flags & ALIAS, base.flags);
1161 }
1162 else
1163 {
1164 // Copy the flags from 'base', setting the ALIAS flag to true, and
1165 // setting both OWNS_HOST and OWNS_DEVICE to false:
1166 flags = (base.flags | ALIAS) & ~(OWNS_HOST | OWNS_DEVICE);
1167 return;
1168 }
1169 }
1170 const size_t s_bytes = size*sizeof(T);
1171 const size_t o_bytes = offset*sizeof(T);
1172 MemoryManager::Alias_(base.h_ptr, o_bytes, s_bytes, base.flags, flags);
1173}
1174
1175template <typename T>
1176template <class U>
1177inline void Memory<T>::CopyConvertPtr(const Memory<U> &base)
1178{
1179 h_ptr = reinterpret_cast<T*>(base.h_ptr); // can also use (T*)base
1180 capacity = base.capacity;
1181 h_mt = base.h_mt;
1182 flags = base.flags;
1183}
1184
1185template <typename T>
1187{
1188 if (!IsDeviceMemory(d_mt)) { return; }
1189 if (!(flags & Registered))
1190 {
1191 MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1192 flags & OWNS_HOST, flags & ALIAS, flags);
1193 }
1194 MemoryManager::SetDeviceMemoryType_(h_ptr, flags, d_mt);
1195}
1196
1197template <typename T>
1199{
1200 const bool registered = flags & Registered;
1201 const bool mt_host = h_mt == MemoryType::HOST;
1202 const bool std_delete = !registered && mt_host;
1203
1204 if (!std_delete)
1205 {
1206 MemoryManager::Delete_((void*)h_ptr, h_mt, flags);
1207 }
1208
1209 if (mt_host)
1210 {
1211 if (flags & OWNS_HOST) { delete [] h_ptr; }
1212 }
1213 Reset(h_mt);
1214}
1215
1216template <typename T>
1217inline void Memory<T>::DeleteDevice(bool copy_to_host)
1218{
1219 if (flags & Registered)
1220 {
1221 if (copy_to_host) { Read(MemoryClass::HOST, capacity); }
1222 MemoryManager::DeleteDevice_((void*)h_ptr, flags);
1223 }
1224}
1225
1226template <typename T>
1227inline T &Memory<T>::operator[](int idx)
1228{
1229 MFEM_ASSERT((flags & VALID_HOST) && !(flags & VALID_DEVICE),
1230 "invalid host pointer access");
1231 return h_ptr[idx];
1232}
1233
1234template <typename T>
1235inline const T &Memory<T>::operator[](int idx) const
1236{
1237 MFEM_ASSERT((flags & VALID_HOST), "invalid host pointer access");
1238 return h_ptr[idx];
1239}
1240
1241template <typename T>
1243{
1244 MFEM_ASSERT(Empty() ||
1245 ((flags & VALID_HOST) &&
1246 (std::is_const<T>::value || !(flags & VALID_DEVICE))),
1247 "invalid host pointer access");
1248 return h_ptr;
1249}
1250
1251template <typename T>
1252inline Memory<T>::operator const T*() const
1253{
1254 MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
1255 return h_ptr;
1256}
1257
1258template <typename T> template <typename U>
1260{
1261 MFEM_ASSERT(Empty() ||
1262 ((flags & VALID_HOST) &&
1263 (std::is_const<U>::value || !(flags & VALID_DEVICE))),
1264 "invalid host pointer access");
1265 return reinterpret_cast<U*>(h_ptr);
1266}
1267
1268template <typename T> template <typename U>
1269inline Memory<T>::operator const U*() const
1270{
1271 MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
1272 return reinterpret_cast<U*>(h_ptr);
1273}
1274
1275template <typename T>
1276inline T *Memory<T>::ReadWrite(MemoryClass mc, int size)
1277{
1278 const size_t bytes = size * sizeof(T);
1279 if (!(flags & Registered))
1280 {
1281 if (mc == MemoryClass::HOST) { return h_ptr; }
1282 MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1283 flags & OWNS_HOST, flags & ALIAS, flags);
1284 }
1285 return (T*)MemoryManager::ReadWrite_(h_ptr, h_mt, mc, bytes, flags);
1286}
1287
1288template <typename T>
1289inline const T *Memory<T>::Read(MemoryClass mc, int size) const
1290{
1291 const size_t bytes = size * sizeof(T);
1292 if (!(flags & Registered))
1293 {
1294 if (mc == MemoryClass::HOST) { return h_ptr; }
1295 MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1296 flags & OWNS_HOST, flags & ALIAS, flags);
1297 }
1298 return (const T*)MemoryManager::Read_(h_ptr, h_mt, mc, bytes, flags);
1299}
1300
1301template <typename T>
1302inline T *Memory<T>::Write(MemoryClass mc, int size)
1303{
1304 const size_t bytes = size * sizeof(T);
1305 if (!(flags & Registered))
1306 {
1307 if (mc == MemoryClass::HOST) { return h_ptr; }
1308 MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1309 flags & OWNS_HOST, flags & ALIAS, flags);
1310 }
1311 return (T*)MemoryManager::Write_(h_ptr, h_mt, mc, bytes, flags);
1312}
1313
1314template <typename T>
1315inline void Memory<T>::Sync(const Memory &other) const
1316{
1317 if (!(flags & Registered) && (other.flags & Registered))
1318 {
1319 MFEM_ASSERT(h_ptr == other.h_ptr &&
1320 (flags & ALIAS) == (other.flags & ALIAS),
1321 "invalid input");
1322 flags = (flags | Registered) & ~(OWNS_DEVICE | OWNS_INTERNAL);
1323 }
1324 flags = (flags & ~(VALID_HOST | VALID_DEVICE)) |
1325 (other.flags & (VALID_HOST | VALID_DEVICE));
1326}
1327
1328template <typename T>
1329inline void Memory<T>::SyncAlias(const Memory &base, int alias_size) const
1330{
1331 // Assuming that if *this is registered then base is also registered.
1332 MFEM_ASSERT(!(flags & Registered) || (base.flags & Registered),
1333 "invalid base state");
1334 if (!(base.flags & Registered)) { return; }
1335 MemoryManager::SyncAlias_(base.h_ptr, h_ptr, alias_size*sizeof(T),
1336 base.flags, flags);
1337}
1338
1339template <typename T>
1341{
1342 if (h_ptr == nullptr || !(flags & VALID_DEVICE)) { return h_mt; }
1343 return MemoryManager::GetDeviceMemoryType_(h_ptr, flags & ALIAS);
1344}
1345
1346template <typename T>
1348{
1349 if (!(flags & Registered)) { return MemoryType::DEFAULT; }
1350 return MemoryManager::GetDeviceMemoryType_(h_ptr, flags & ALIAS);
1351}
1352
1353template <typename T>
1354inline bool Memory<T>::HostIsValid() const
1355{
1356 return flags & VALID_HOST ? true : false;
1357}
1358
1359template <typename T>
1360inline bool Memory<T>::DeviceIsValid() const
1361{
1362 return flags & VALID_DEVICE ? true : false;
1363}
1364
1365template <typename T>
1366inline void Memory<T>::CopyFrom(const Memory &src, int size)
1367{
1368 MFEM_VERIFY(src.capacity>=size && capacity>=size, "Incorrect size");
1369 if (size <= 0) { return; }
1370 if (!(flags & Registered) && !(src.flags & Registered))
1371 {
1372 if (h_ptr != src.h_ptr)
1373 {
1374 MFEM_ASSERT(h_ptr + size <= src.h_ptr || src.h_ptr + size <= h_ptr,
1375 "data overlaps!");
1376 std::memcpy(h_ptr, src, size*sizeof(T));
1377 }
1378 // *this is not registered, so (flags & VALID_HOST) must be true
1379 }
1380 else
1381 {
1382 MemoryManager::Copy_(h_ptr, src.h_ptr, size*sizeof(T), src.flags, flags);
1383 }
1384}
1385
1386template <typename T>
1387inline void Memory<T>::CopyFromHost(const T *src, int size)
1388{
1389 MFEM_VERIFY(capacity>=size, "Incorrect size");
1390 if (size <= 0) { return; }
1391 if (!(flags & Registered))
1392 {
1393 if (h_ptr != src)
1394 {
1395 MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
1396 "data overlaps!");
1397 std::memcpy(h_ptr, src, size*sizeof(T));
1398 }
1399 // *this is not registered, so (flags & VALID_HOST) must be true
1400 }
1401 else
1402 {
1403 MemoryManager::CopyFromHost_(h_ptr, src, size*sizeof(T), flags);
1404 }
1405}
1406
1407template <typename T>
1408inline void Memory<T>::CopyTo(Memory &dest, int size) const
1409{
1410 dest.CopyFrom(*this, size);
1411}
1412
1413template <typename T>
1414inline void Memory<T>::CopyToHost(T *dest, int size) const
1415{
1416 MFEM_VERIFY(capacity>=size, "Incorrect size");
1417 if (size <= 0) { return; }
1418 if (!(flags & Registered))
1419 {
1420 if (h_ptr != dest)
1421 {
1422 MFEM_ASSERT(h_ptr + size <= dest || dest + size <= h_ptr,
1423 "data overlaps!");
1424 std::memcpy(dest, h_ptr, size*sizeof(T));
1425 }
1426 }
1427 else
1428 {
1429 MemoryManager::CopyToHost_(dest, h_ptr, size*sizeof(T), flags);
1430 }
1431}
1432
1433
1434/** @brief Print the state of a Memory object based on its internal flags.
1435 Useful in a debugger. See also Memory<T>::PrintFlags(). */
1436extern void MemoryPrintFlags(unsigned flags);
1437
1438
1439template <typename T>
1440inline void Memory<T>::PrintFlags() const
1441{
1442 MemoryPrintFlags(flags);
1443}
1444
1445template <typename T>
1446inline int Memory<T>::CompareHostAndDevice(int size) const
1447{
1448 if (!(flags & VALID_HOST) || !(flags & VALID_DEVICE)) { return 0; }
1449 return MemoryManager::CompareHostAndDevice_(h_ptr, size*sizeof(T), flags);
1450}
1451
1452
1453/// The (single) global memory manager object
1454extern MFEM_EXPORT MemoryManager mm;
1455
1456} // namespace mfem
1457
1458#endif // MFEM_MEM_MANAGER_HPP
A class to initialize the size of a Tensor.
Definition dtensor.hpp:57
bool IsKnown(const void *h_ptr)
Return true if the pointer is known by the memory manager.
static void SetUmpireDeviceAllocatorName(const char *d_name)
Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE.
static void myfree(void *mem, MemoryType MT, unsigned &flags)
static const char * GetUmpireHostAllocatorName()
Get the host Umpire allocator name used with MemoryType::HOST_UMPIRE.
static void SetUmpireHostAllocatorName(const char *h_name)
Set the host Umpire allocator name used with MemoryType::HOST_UMPIRE.
static MemoryType GetHostMemoryType()
bool IsAlias(const void *h_ptr)
Return true if the pointer is known by the memory manager as an alias.
static const char * GetUmpireDevice2AllocatorName()
Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2.
static void SetUmpireDevice2AllocatorName(const char *d_name)
Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2.
static MemoryType GetDeviceMemoryType()
static MemoryType GetDualMemoryType(MemoryType mt)
Return the dual MemoryType of the given one, mt.
__attribute__((used)) inline static void *__enzyme_allocation_like2[4]
static const char * GetUmpireDeviceAllocatorName()
Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE.
__attribute__((used)) inline static void *__enzyme_allocation_like1[4]
Type that enables viewing Vector objects as Array<real_t> objects and vice versa. Currently,...
MemoryView(const MemoryView &)=delete
SizeType & base_size
MemoryView(MemoryView &&)=delete
std::conditional_t< is_const_view, const int, int > SizeType
std::remove_reference_t< decltype((std::remove_cv_t< ViewedType > {})[0])> T
std::conditional_t< is_const_view, const Memory< T >, Memory< T > > MemoryType
MemoryView & operator=(const MemoryView &)=delete
static constexpr bool is_const_view
std::remove_cv_t< ViewedType > view
MemoryType & base_mem
ViewedType & operator*()
Return the view object by reference, ViewedType &.
MemoryView & operator=(MemoryView &&)=delete
MemoryView(MemoryType &mem, SizeType &size)
Class used by MFEM to store pointers to host and/or device memory.
void SetHostPtrOwner(bool own) const
Set/clear the ownership flag for the host pointer. Ownership indicates whether the pointer will be de...
void New(int size, MemoryType mt)
Allocate memory for size entries with the given MemoryType.
bool OwnsDevicePtr() const
Return true if the device pointer is owned. Ownership indicates whether the pointer will be deleted b...
T * Write(MemoryClass mc, int size)
Get write-only access to the memory with the given MemoryClass.
Memory(int size, MemoryType mt)
Allocate memory for size entries with the given MemoryType mt.
void Wrap(T *ptr, int size, MemoryType mt, bool own)
Wrap an externally allocated pointer, ptr, of the given MemoryType.
Memory(const Memory &base, int offset, int size)
Alias constructor. Create a Memory object that points inside the Memory object base.
MemoryType GetHostMemoryType() const
Return the host MemoryType of the Memory object.
bool DeviceIsValid() const
Return true if device pointer is valid.
Memory(int size, MemoryType h_mt, MemoryType d_mt)
Allocate memory for size entries with the given host MemoryType h_mt and device MemoryType d_mt.
void SetDeviceMemoryType(MemoryType d_mt)
Set the device MemoryType to be used by the Memory object.
void SetDevicePtrOwner(bool own) const
Set/clear the ownership flag for the device pointer. Ownership indicates whether the pointer will be ...
Memory(const Memory &)=default
Copy constructor: default.
int Capacity() const
Return the size of the allocated memory.
void CopyFromHost(const T *src, int size)
Copy size entries from the host pointer src to *this.
T & operator[](int idx)
Array subscript operator for host memory.
void Swap(Memory &other)
Swap without using move assignment, avoiding Reset() calls.
void New(int size, MemoryType h_mt, MemoryType d_mt)
Allocate memory for size entries with the given host MemoryType h_mt and device MemoryType d_mt.
@ OWNS_INTERNAL
Ownership flag for internal Memory data.
@ VALID_HOST
Host pointer is valid.
@ OWNS_HOST
The host pointer will be deleted by Delete()
@ VALID_DEVICE
Device pointer is valid
@ ALIAS
Pointer is an alias.
MemoryType GetDeviceMemoryType() const
Return the device MemoryType of the Memory object. If the device MemoryType is not set,...
T * ReadWrite(MemoryClass mc, int size)
Get read-write access to the memory with the given MemoryClass.
MemoryType h_mt
Host memory type.
void Reset(MemoryType host_mt)
Reset the memory and set the host memory type.
void MakeAlias(const Memory &base, int offset, int size)
Create a memory object that points inside the memory object base.
bool HostIsValid() const
Return true if host pointer is valid.
~Memory()=default
Destructor: default.
Memory & operator=(Memory &&other)
bool UseDevice() const
Read the internal device flag.
void CopyToHost(T *dest, int size) const
Copy size entries from *this to the host pointer dest.
Memory(Memory &&other)
void SyncAlias(const Memory &base, int alias_size) const
Update the alias Memory *this to match the memory location (all valid locations) of its base Memory,...
bool OwnsHostPtr() const
Return true if the host pointer is owned. Ownership indicates whether the pointer will be deleted by ...
friend void MemoryPrintFlags(unsigned flags)
Print the state of a Memory object based on its internal flags. Useful in a debugger....
MemoryType GetMemoryType() const
Return a MemoryType that is currently valid. If both the host and the device pointers are currently v...
bool Empty() const
Return true if the Memory object is empty, see Reset().
T * h_ptr
Pointer to host memory. Not owned.
void Sync(const Memory &other) const
Copy the host/device pointer validity flags from other to *this.
void CopyConvertPtr(const Memory< U > &base)
void PrintFlags() const
Print the internal flags.
void DeleteDevice(bool copy_to_host=true)
Delete the device pointer, if owned. If copy_to_host is true and the data is valid only on device,...
unsigned flags
Bit flags defined from the FlagMask enum.
const T * Read(MemoryClass mc, int size) const
Get read-only access to the memory with the given MemoryClass.
void UseDevice(bool use_dev) const
Set the internal device flag.
int CompareHostAndDevice(int size) const
If both the host and the device data are valid, compare their contents.
void CopyFrom(const Memory &src, int size)
Copy size entries from src to *this.
void Reset()
Reset the memory to be empty, ensuring that Delete() will be a no-op.
Memory(int size)
Allocate host memory for size entries.
void Wrap(T *ptr, int size, bool own)
Wrap an externally allocated host pointer, ptr with the current host memory type returned by MemoryMa...
void Delete()
Delete the owned pointers and reset the Memory object.
Memory(T *ptr, int size, MemoryType mt, bool own)
Wrap an externally allocated pointer, ptr, of the given MemoryType.
void CopyTo(Memory &dest, int size) const
Copy size entries from *this to dest.
void ClearOwnerFlags() const
Clear the ownership flags for the host and device pointers, as well as any internal data allocated by...
const T & operator[](int idx) const
Array subscript operator for host memory, const version.
Memory(MemoryType mt)
Creates a new empty Memory object with host MemoryType mt.
void Wrap(T *h_ptr, T *d_ptr, int size, MemoryType h_mt, bool own, bool valid_host=false, bool valid_device=true)
int capacity
Size of the allocated memory.
void New(int size)
Allocate host memory for size entries with the current host memory type returned by MemoryManager::Ge...
Memory & operator=(const Memory &)=default
Copy-assignment operator: default.
Memory(T *ptr, int size, bool own)
Wrap an externally allocated host pointer, ptr with the current host memory type returned by MemoryMa...
Vector data type.
Definition vector.hpp:82
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
bool IsDeviceMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::DEVICE.
const T * Read(const Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for read access to mem with the mfem::Device's DeviceMemoryClass, if on_dev = true,...
Definition device.hpp:369
constexpr int DeviceMemoryType
void swap(Array< T > &a, Array< T > &b)
Swap of Array<T> objects for use with standard library algorithms. Also, used by mfem::Swap().
Definition array.hpp:776
MemoryClass operator*(MemoryClass mc1, MemoryClass mc2)
Return a suitable MemoryClass from a pair of MemoryClasses.
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
MemoryClass
Memory classes identify sets of memory types.
constexpr int MemoryTypeSize
Static casts to 'int' and sizes of some useful memory types.
MemoryManager mm
The (single) global memory manager object.
int HYPRE_MemoryLocation
MemoryType GetMemoryType(MemoryClass mc)
Return a suitable MemoryType for a given MemoryClass.
constexpr int HostMemoryTypeSize
bool IsHostMemory(MemoryType mt)
Return true if the given memory type is in MemoryClass::HOST.
const char * MemoryTypeName[MemoryTypeSize]
Memory type names, used during Device:: configuration.
constexpr int HostMemoryType
bool HypreUsingGPU()
Return true if HYPRE is configured to use GPU.
constexpr int DeviceMemoryTypeSize
MemoryType
Memory types supported by MFEM.
@ HOST_32
Host memory; aligned at 32 bytes.
@ SIZE
Number of host and device memory types.
@ HOST_64
Host memory; aligned at 64 bytes.
@ HOST
Host memory; using new[] and delete[].
@ HOST_PINNED
Host memory: pinned (page-locked)
@ HOST_DEBUG
Host memory; allocated from a "host-debug" pool.
@ DEVICE
Device memory; using CUDA or HIP *Malloc and *Free.
HYPRE_MemoryLocation GetHypreMemoryLocation()
Return the configured HYPRE_MemoryLocation.
void MemoryPrintFlags(unsigned flags)
Print the state of a Memory object based on its internal flags. Useful in a debugger....
bool MemoryClassContainsType(MemoryClass mc, MemoryType mt)
Return true iff the MemoryType mt is contained in the MemoryClass mc.
STL namespace.