MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
mem_manager.hpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, 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 friend void MemoryPrintFlags(unsigned flags);
172
173 enum FlagMask: unsigned
174 {
175 // Workaround for use with headers that define REGISTERED as a macro,
176 // e.g. nb30.h (which is included by Windows.h):
177#ifndef REGISTERED
178 REGISTERED = 1 << 0, /**< The host pointer is registered with the
179 MemoryManager */
180#endif
181 // Use the following identifier if REGISTERED is defined as a macro,
182 // e.g. nb30.h (which is included by Windows.h):
183 Registered = 1 << 0, /**< The host pointer is registered with the
184 MemoryManager */
185 OWNS_HOST = 1 << 1, ///< The host pointer will be deleted by Delete()
186 OWNS_DEVICE = 1 << 2, /**< The device pointer will be deleted by
187 Delete() */
188 OWNS_INTERNAL = 1 << 3, ///< Ownership flag for internal Memory data
189 VALID_HOST = 1 << 4, ///< Host pointer is valid
190 VALID_DEVICE = 1 << 5, ///< %Device pointer is valid
191 USE_DEVICE = 1 << 6, /**< Internal device flag, see e.g.
192 Vector::UseDevice() */
193 ALIAS = 1 << 7 ///< Pointer is an alias
194 };
195
196 /// Pointer to host memory. Not owned.
197 /** The type of the pointer is given by the field #h_mt; it can be any type
198 from MemoryClass::HOST. */
200 int capacity; ///< Size of the allocated memory
201 MemoryType h_mt; ///< Host memory type
202 mutable unsigned flags; ///< Bit flags defined from the #FlagMask enum
203 // 'flags' is mutable so that it can be modified in Set{Host,Device}PtrOwner,
204 // Copy{From,To}, {ReadWrite,Read,Write}.
205
206public:
207 /** Default constructor, sets the host pointer to nullptr and the metadata to
208 meaningful default values. */
209 Memory() { Reset(); }
210
211 /// Copy constructor: default.
212 Memory(const Memory &) = default;
213
214 /** Move constructor. Sets the pointers and associated ownership of validity
215 flags of @a *this to those of @a other. Resets @a other. */
216 Memory(Memory &&other)
217 {
218 *this = other;
219 other.Reset();
220 }
221
222 /// Copy-assignment operator: default.
223 Memory &operator=(const Memory &) = default;
224
225 /** Move assignment operator. Sets the pointers and associated ownership of
226 validity flags of @a *this to those of @a other. Resets @a other. */
228 {
229 // Guard self-assignment:
230 if (this == &other) { return *this; }
231 *this = other;
232 other.Reset();
233 return *this;
234 }
235
236 /// Allocate host memory for @a size entries.
237 /** The allocation uses the current host memory type returned by
238 MemoryManager::GetHostMemoryType(). */
239 explicit Memory(int size) { New(size); }
240
241 /// Creates a new empty Memory object with host MemoryType @a mt.
242 explicit Memory(MemoryType mt) { Reset(mt); }
243
244 /** @brief Allocate memory for @a size entries with the given MemoryType
245 @a mt. */
246 /** The newly allocated memory is not initialized, however the given
247 MemoryType is still set as valid. */
248 Memory(int size, MemoryType mt) { New(size, mt); }
249
250 /** @brief Allocate memory for @a size entries with the given host MemoryType
251 @a h_mt and device MemoryType @a d_mt. */
252 /** The newly allocated memory is not initialized. The host pointer is set as
253 valid. */
254 Memory(int size, MemoryType h_mt, MemoryType d_mt) { New(size, h_mt, d_mt); }
255
256 /** @brief Wrap an externally allocated host pointer, @a ptr with the current
257 host memory type returned by MemoryManager::GetHostMemoryType(). */
258 /** The parameter @a own determines whether @a ptr will be deleted when the
259 method Delete() is called. */
260 explicit Memory(T *ptr, int size, bool own) { Wrap(ptr, size, own); }
261
262 /// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
263 /** The new memory object will have the given MemoryType set as valid.
264
265 The given @a ptr must be allocated appropriately for the given
266 MemoryType.
267
268 The parameter @a own determines whether @a ptr will be deleted when the
269 method Delete() is called. */
270 Memory(T *ptr, int size, MemoryType mt, bool own)
271 { Wrap(ptr, size, mt, own); }
272
273 /** @brief Alias constructor. Create a Memory object that points inside the
274 Memory object @a base. */
275 /** The new Memory object uses the same MemoryType(s) as @a base. */
276 Memory(const Memory &base, int offset, int size)
277 { MakeAlias(base, offset, size); }
278
279 /// Destructor: default.
280 /** @note The destructor will NOT delete the current memory. */
281 ~Memory() = default;
282
283 /// Swap without using move assignment, avoiding Reset() calls.
284 void Swap(Memory &other)
285 {
286 Memory tmp(*this);
287 *this = other;
288 other = tmp;
289 }
290
291 /** @brief Return true if the host pointer is owned. Ownership indicates
292 whether the pointer will be deleted by the method Delete(). */
293 bool OwnsHostPtr() const { return flags & OWNS_HOST; }
294
295 /** @brief Set/clear the ownership flag for the host pointer. Ownership
296 indicates whether the pointer will be deleted by the method Delete(). */
297 void SetHostPtrOwner(bool own) const
298 { flags = own ? (flags | OWNS_HOST) : (flags & ~OWNS_HOST); }
299
300 /** @brief Return true if the device pointer is owned. Ownership indicates
301 whether the pointer will be deleted by the method Delete(). */
302 bool OwnsDevicePtr() const { return flags & OWNS_DEVICE; }
303
304 /** @brief Set/clear the ownership flag for the device pointer. Ownership
305 indicates whether the pointer will be deleted by the method Delete(). */
306 void SetDevicePtrOwner(bool own) const
307 { flags = own ? (flags | OWNS_DEVICE) : (flags & ~OWNS_DEVICE); }
308
309 /** @brief Clear the ownership flags for the host and device pointers, as
310 well as any internal data allocated by the Memory object. */
313
314 /// Read the internal device flag.
315 bool UseDevice() const { return flags & USE_DEVICE; }
316
317 /// Set the internal device flag.
318 void UseDevice(bool use_dev) const
319 { flags = use_dev ? (flags | USE_DEVICE) : (flags & ~USE_DEVICE); }
320
321 /// Return the size of the allocated memory.
322 int Capacity() const { return capacity; }
323
324 /// Reset the memory to be empty, ensuring that Delete() will be a no-op.
325 /** This is the Memory class equivalent to setting a pointer to NULL, see
326 Empty().
327
328 @note The current memory is NOT deleted by this method. */
329 void Reset();
330
331 /// Reset the memory and set the host memory type.
332 void Reset(MemoryType host_mt);
333
334 /// Return true if the Memory object is empty, see Reset().
335 /** Default-constructed objects are uninitialized, so they are not guaranteed
336 to be empty. */
337 bool Empty() const { return h_ptr == NULL; }
338
339 /** @brief Allocate host memory for @a size entries with the current host
340 memory type returned by MemoryManager::GetHostMemoryType(). */
341 /** @note The current memory is NOT deleted by this method. */
342 inline void New(int size);
343
344 /// Allocate memory for @a size entries with the given MemoryType.
345 /** The newly allocated memory is not initialized, however the given
346 MemoryType is still set as valid.
347
348 When @a mt is a host type, the device MemoryType will be set later, if
349 requested, using the dual type of @a mt, see
350 MemoryManager::GetDualMemoryType().
351
352 When @a mt is a device type, the host MemoryType will be set immediately
353 to be the dual of @a mt, see MemoryManager::GetDualMemoryType().
354
355 @note The current memory is NOT deleted by this method. */
356 inline void New(int size, MemoryType mt);
357
358 /** @brief Allocate memory for @a size entries with the given host MemoryType
359 @a h_mt and device MemoryType @a d_mt. */
360 /** The newly allocated memory is not initialized. The host pointer is set as
361 valid.
362
363 @note The current memory is NOT deleted by this method. */
364 inline void New(int size, MemoryType h_mt, MemoryType d_mt);
365
366 /** @brief Wrap an externally allocated host pointer, @a ptr with the current
367 host memory type returned by MemoryManager::GetHostMemoryType(). */
368 /** The parameter @a own determines whether @a ptr will be deleted when the
369 method Delete() is called.
370
371 @note The current memory is NOT deleted by this method. */
372 inline void Wrap(T *ptr, int size, bool own);
373
374 /// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
375 /** The new memory object will have the given MemoryType set as valid.
376
377 The given @a ptr must be allocated appropriately for the given
378 MemoryType.
379
380 The parameter @a own determines whether @a ptr will be deleted when the
381 method Delete() is called.
382
383 @note The current memory is NOT deleted by this method. */
384 inline void Wrap(T *ptr, int size, MemoryType mt, bool own);
385
386 /** Wrap an externally pair of allocated pointers, @a h_ptr and @a d_ptr,
387 of the given host MemoryType @a h_mt. */
388 /** The new memory object will have the device MemoryType set as valid unless
389 specified otherwise by the parameters @a valid_host and @a valid_device.
390
391 The given @a h_ptr and @a d_ptr must be allocated appropriately for the
392 given host MemoryType and its dual device MemoryType as defined by
393 MemoryManager::GetDualMemoryType().
394
395 The parameter @a own determines whether both @a h_ptr and @a d_ptr will
396 be deleted when the method Delete() is called.
397
398 The parameters @a valid_host and @a valid_device determine which
399 pointers, host and/or device, will be marked as valid; at least one of
400 the two parameters must be set to true.
401
402 @note Ownership can also be controlled by using the following methods:
403 - ClearOwnerFlags,
404 - SetHostPtrOwner,
405 - SetDevicePtrOwner.
406
407 @note The current memory is NOT deleted by this method. */
408 inline void Wrap(T *h_ptr, T *d_ptr, int size, MemoryType h_mt, bool own,
409 bool valid_host = false, bool valid_device = true);
410
411 /// Create a memory object that points inside the memory object @a base.
412 /** The new Memory object uses the same MemoryType(s) as @a base.
413
414 @note The current memory is NOT deleted by this method. */
415 inline void MakeAlias(const Memory &base, int offset, int size);
416
417 /// Set the device MemoryType to be used by the Memory object.
418 /** If the specified @a d_mt is not a device MemoryType, i.e. not one of the
419 types in MemoryClass::DEVICE, then this method will return immediately.
420
421 If the device MemoryType has been previously set to a different type and
422 the actual device memory has been allocated, this method will trigger an
423 error. This method will not perform the actual device memory allocation,
424 however, the allocation may already exist if the MemoryType is the same
425 as the current one.
426
427 If the Memory is an alias Memory, the device MemoryType of its base will
428 be updated as described above. */
430
431 /** @brief Delete the owned pointers and reset the Memory object. */
432 inline void Delete();
433
434 /** @brief Delete the device pointer, if owned. If @a copy_to_host is true
435 and the data is valid only on device, move it to host before deleting.
436 Invalidates the device memory. */
437 inline void DeleteDevice(bool copy_to_host = true);
438
439 /// Array subscript operator for host memory.
440 inline T &operator[](int idx);
441
442 /// Array subscript operator for host memory, const version.
443 inline const T &operator[](int idx) const;
444
445 /// Direct access to the host memory as T* (implicit conversion).
446 /** When the type T is const-qualified, this method can be used only if the
447 host pointer is currently valid (the device pointer may be valid or
448 invalid).
449
450 When the type T is not const-qualified, this method can be used only if
451 the host pointer is the only valid pointer.
452
453 When the Memory is empty, this method can be used and it returns NULL. */
454 inline operator T*();
455
456 /// Direct access to the host memory as const T* (implicit conversion).
457 /** This method can be used only if the host pointer is currently valid (the
458 device pointer may be valid or invalid).
459
460 When the Memory is empty, this method can be used and it returns NULL. */
461 inline operator const T*() const;
462
463 /// Direct access to the host memory via explicit typecast.
464 /** A pointer to type T must be reinterpret_cast-able to a pointer to type U.
465 In particular, this method cannot be used to cast away const-ness from
466 the base type T.
467
468 When the type U is const-qualified, this method can be used only if the
469 host pointer is currently valid (the device pointer may be valid or
470 invalid).
471
472 When the type U is not const-qualified, this method can be used only if
473 the host pointer is the only valid pointer.
474
475 When the Memory is empty, this method can be used and it returns NULL. */
476 template <typename U>
477 inline explicit operator U*();
478
479 /// Direct access to the host memory via explicit typecast, const version.
480 /** A pointer to type T must be reinterpret_cast-able to a pointer to type
481 const U.
482
483 This method can be used only if the host pointer is currently valid (the
484 device pointer may be valid or invalid).
485
486 When the Memory is empty, this method can be used and it returns NULL. */
487 template <typename U>
488 inline explicit operator const U*() const;
489
490 /// Get read-write access to the memory with the given MemoryClass.
491 /** If only read or only write access is needed, then the methods
492 Read() or Write() should be used instead of this method.
493
494 The parameter @a size must not exceed the Capacity(). */
495 inline T *ReadWrite(MemoryClass mc, int size);
496
497 /// Get read-only access to the memory with the given MemoryClass.
498 /** The parameter @a size must not exceed the Capacity(). */
499 inline const T *Read(MemoryClass mc, int size) const;
500
501 /// Get write-only access to the memory with the given MemoryClass.
502 /** The parameter @a size must not exceed the Capacity().
503
504 The contents of the returned pointer is undefined, unless it was
505 validated by a previous call to Read() or ReadWrite() with
506 the same MemoryClass. */
507 inline T *Write(MemoryClass mc, int size);
508
509 /// Copy the host/device pointer validity flags from @a other to @a *this.
510 /** This method synchronizes the pointer validity flags of two Memory objects
511 that use the same host/device pointers, or when @a *this is an alias
512 (sub-Memory) of @a other. Typically, this method should be called after
513 @a other is manipulated in a way that changes its pointer validity flags
514 (e.g. it was moved from device to host memory). */
515 inline void Sync(const Memory &other) const;
516
517 /** @brief Update the alias Memory @a *this to match the memory location (all
518 valid locations) of its base Memory, @a base. */
519 /** This method is useful when alias Memory is moved and manipulated in a
520 different memory space. Such operations render the pointer validity flags
521 of the base incorrect. Calling this method will ensure that @a base is
522 up-to-date. Note that this is achieved by moving/copying @a *this (if
523 necessary), and not @a base. */
524 inline void SyncAlias(const Memory &base, int alias_size) const;
525
526 /** @brief Return a MemoryType that is currently valid. If both the host and
527 the device pointers are currently valid, then the device memory type is
528 returned. */
529 inline MemoryType GetMemoryType() const;
530
531 /// Return the host MemoryType of the Memory object.
532 inline MemoryType GetHostMemoryType() const { return h_mt; }
533
534 /** @brief Return the device MemoryType of the Memory object. If the device
535 MemoryType is not set, return MemoryType::DEFAULT. */
537
538 /** @brief Return true if host pointer is valid */
539 inline bool HostIsValid() const;
540
541 /** @brief Return true if device pointer is valid */
542 inline bool DeviceIsValid() const;
543
544 /// Copy @a size entries from @a src to @a *this.
545 /** The given @a size should not exceed the Capacity() of the source @a src
546 and the destination, @a *this. */
547 inline void CopyFrom(const Memory &src, int size);
548
549 /// Copy @a size entries from the host pointer @a src to @a *this.
550 /** The given @a size should not exceed the Capacity() of @a *this. */
551 inline void CopyFromHost(const T *src, int size);
552
553 /// Copy @a size entries from @a *this to @a dest.
554 /** The given @a size should not exceed the Capacity() of @a *this and the
555 destination, @a dest. */
556 inline void CopyTo(Memory &dest, int size) const;
557
558 /// Copy @a size entries from @a *this to the host pointer @a dest.
559 /** The given @a size should not exceed the Capacity() of @a *this. */
560 inline void CopyToHost(T *dest, int size) const;
561
562 /// Print the internal flags.
563 /** This method can be useful for debugging. It is explicitly instantiated
564 for Memory<T> with T = int and T = real_t. */
565 inline void PrintFlags() const;
566
567 /// If both the host and the device data are valid, compare their contents.
568 /** This method can be useful for debugging. It is explicitly instantiated
569 for Memory<T> with T = int and T = real_t. */
570 inline int CompareHostAndDevice(int size) const;
571
572private:
573 // GCC 4.8 workaround: max_align_t is not in std.
574 static constexpr std::size_t def_align_bytes_()
575 {
576 using namespace std;
577 return alignof(max_align_t);
578 }
579 static constexpr std::size_t def_align_bytes = def_align_bytes_();
580 static constexpr std::size_t new_align_bytes =
581 alignof(T) > def_align_bytes ? alignof(T) : def_align_bytes;
582
583 template <std::size_t align_bytes, bool dummy = true> struct Alloc
584 {
585#if __cplusplus < 201703L
586 static inline T *New(std::size_t)
587 {
588 // Generate an error in debug mode
589 MFEM_ASSERT(false, "overaligned type cannot use MemoryType::HOST");
590 return nullptr;
591 }
592#else
593 static inline T *New(std::size_t size) { return new T[size]; }
594#endif
595 };
596
597#if __cplusplus < 201703L
598 template<bool dummy> struct Alloc<def_align_bytes,dummy>
599 {
600 static inline T *New(std::size_t size) { return new T[size]; }
601 };
602#endif
603
604 // Shortcut for Alloc<new_align_bytes>::New(size)
605 static inline T *NewHOST(std::size_t size)
606 {
607 return Alloc<new_align_bytes>::New(size);
608 }
609};
610
611
612/** @brief Swap of Memory<T> objects for use with standard library algorithms.
613 Also, used by mfem::Swap(). */
614template <typename T>
616{
617 a.Swap(b);
618}
619
620
621/** The MFEM memory manager class. Host-side pointers are inserted into this
622 manager which keeps track of the associated device pointer, and where the
623 data currently resides. */
624class MFEM_EXPORT MemoryManager
625{
626private:
627
628 typedef MemoryType MemType;
629 typedef Memory<int> Mem;
630
631 template <typename T> friend class Memory;
632
633 /// Host memory type set during the Setup.
634 MFEM_ENZYME_INACTIVE static MemoryType host_mem_type;
635
636 /// Device memory type set during the Setup.
637 MFEM_ENZYME_INACTIVE static MemoryType device_mem_type;
638
639 /// Allow to detect if a global memory manager instance exists.
640 MFEM_ENZYME_INACTIVE static bool exists;
641
642 /// Return true if the global memory manager instance exists.
643 static bool Exists() { return exists; }
644
645 /// Array defining the dual MemoryType for each MemoryType
646 /** The dual of a host MemoryType is a device MemoryType and vice versa: the
647 dual of a device MemoryType is a host MemoryType. */
648 MFEM_ENZYME_INACTIVE static MemoryType dual_map[MemoryTypeSize];
649
650 /// Update the dual memory type of @a mt to be @a dual_mt.
651 static void UpdateDualMemoryType(MemoryType mt, MemoryType dual_mt);
652
653 /// True if Configure() was called.
654 MFEM_ENZYME_INACTIVE static bool configured;
655
656 /// Host and device allocator names for Umpire.
657#ifdef MFEM_USE_UMPIRE
658 static const char * h_umpire_name;
659 static const char * d_umpire_name;
660 static const char * d_umpire_2_name;
661#endif
662
663private: // Static methods used by the Memory<T> class
664
665 /// Allocate and register a new pointer. Return the host pointer.
666 /// h_tmp must be already allocated using new T[] if mt is a pure device
667 /// memory type, e.g. CUDA (mt will not be HOST).
668 static void *New_(void *h_tmp, size_t bytes, MemoryType mt, unsigned &flags);
669
670 static void *New_(void *h_tmp, size_t bytes, MemoryType h_mt,
671 MemoryType d_mt, unsigned valid_flags, unsigned &flags);
672
673 /// Register an external pointer of the given MemoryType.
674 /// Return the host pointer.
675 MFEM_ENZYME_INACTIVE static void *Register_(void *ptr, void *h_ptr,
676 size_t bytes, MemoryType mt,
677 bool own, bool alias, unsigned &flags);
678
679 /// Register a pair of external host and device pointers
680 static void Register2_(void *h_ptr, void *d_ptr, size_t bytes,
681 MemoryType h_mt, MemoryType d_mt,
682 bool own, bool alias, unsigned &flags,
683 unsigned valid_flags);
684
685 /// Register an alias. Note: base_h_ptr may be an alias.
686 static void Alias_(void *base_h_ptr, size_t offset, size_t bytes,
687 unsigned base_flags, unsigned &flags);
688
689 static void SetDeviceMemoryType_(void *h_ptr, unsigned flags,
690 MemoryType d_mt);
691
692 /// Un-register and free memory identified by its host pointer.
693 MFEM_ENZYME_FN_LIKE_FREE static void Delete_(void *h_ptr, MemoryType mt,
694 unsigned flags);
695
696 /// Free device memory identified by its host pointer
697 static void DeleteDevice_(void *h_ptr, unsigned & flags);
698
699 /// Check if the memory types given the memory class are valid
700 static bool MemoryClassCheck_(MemoryClass mc, void *h_ptr,
701 MemoryType h_mt, size_t bytes, unsigned flags);
702
703 /// Return a pointer to the memory identified by the host pointer h_ptr for
704 /// access with the given MemoryClass.
705 MFEM_ENZYME_FN_LIKE_DYNCAST static void *ReadWrite_(void *h_ptr,
706 MemoryType h_mt, MemoryClass mc,
707 size_t bytes, unsigned &flags);
708
709 MFEM_ENZYME_FN_LIKE_DYNCAST static const void *Read_(void *h_ptr,
710 MemoryType h_mt, MemoryClass mc,
711 size_t bytes, unsigned &flags);
712
713 MFEM_ENZYME_FN_LIKE_DYNCAST static void *Write_(void *h_ptr, MemoryType h_mt,
714 MemoryClass mc,
715 size_t bytes, unsigned &flags);
716
717 static void SyncAlias_(const void *base_h_ptr, void *alias_h_ptr,
718 size_t alias_bytes, unsigned base_flags,
719 unsigned &alias_flags);
720
721 /// Return the type the of the currently valid memory.
722 /// If more than one types are valid, return a device type.
723 MFEM_ENZYME_INACTIVE static MemoryType GetDeviceMemoryType_(void *h_ptr,
724 bool alias);
725
726 /// Return the type the of the host memory.
727 MFEM_ENZYME_INACTIVE static MemoryType GetHostMemoryType_(void *h_ptr);
728
729 /// Verify that h_mt and h_ptr's h_mt (memory or alias) are equal.
730 static void CheckHostMemoryType_(MemoryType h_mt, void *h_ptr, bool alias);
731
732 /// Copy entries from valid memory type to valid memory type.
733 /// Both dest_h_ptr and src_h_ptr are registered host pointers.
734 static void Copy_(void *dest_h_ptr, const void *src_h_ptr, size_t bytes,
735 unsigned src_flags, unsigned &dest_flags);
736
737 /// Copy entries from valid memory type to host memory, where dest_h_ptr is
738 /// not a registered host pointer and src_h_ptr is a registered host pointer.
739 static void CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
740 size_t bytes, unsigned src_flags);
741
742 /// Copy entries from host memory to valid memory type, where dest_h_ptr is a
743 /// registered host pointer and src_h_ptr is not a registered host pointer.
744 static void CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
745 size_t bytes, unsigned &dest_flags);
746
747 /// Check if the host pointer has been registered in the memory manager.
748 static bool IsKnown_(const void *h_ptr);
749
750 /** @brief Check if the host pointer has been registered as an alias in the
751 memory manager. */
752 static bool IsAlias_(const void *h_ptr);
753
754 /// Compare the contents of the host and the device memory.
755 static int CompareHostAndDevice_(void *h_ptr, size_t size, unsigned flags);
756
757private:
758
759 /// Insert a host address @a h_ptr and size *a bytes in the memory map to be
760 /// managed.
761 void Insert(void *h_ptr, size_t bytes, MemoryType h_mt, MemoryType d_mt);
762
763 /// Insert a device and the host addresses in the memory map
764 void InsertDevice(void *d_ptr, void *h_ptr, size_t bytes,
765 MemoryType h_mt, MemoryType d_mt);
766
767 /// Insert an alias in the alias map
768 void InsertAlias(const void *base_ptr, void *alias_ptr,
769 const size_t bytes, const bool base_is_alias);
770
771 /// Erase an address from the memory map, as well as all its aliases
772 void Erase(void *h_ptr, bool free_dev_ptr = true);
773
774 /// Erase device memory for a given host address
775 void EraseDevice(void *h_ptr);
776
777 /// Erase an alias from the aliases map
778 void EraseAlias(void *alias_ptr);
779
780 /// Return the corresponding device pointer of h_ptr,
781 /// allocating and moving the data if needed
782 void *GetDevicePtr(const void *h_ptr, size_t bytes, bool copy_data);
783
784 /// Return the corresponding device pointer of alias_ptr,
785 /// allocating and moving the data if needed
786 void *GetAliasDevicePtr(const void *alias_ptr, size_t bytes, bool copy_data);
787
788 /// Return the corresponding host pointer of d_ptr,
789 /// allocating and moving the data if needed
790 void *GetHostPtr(const void *d_ptr, size_t bytes, bool copy_data);
791
792 /// Return the corresponding host pointer of alias_ptr,
793 /// allocating and moving the data if needed
794 void *GetAliasHostPtr(const void *alias_ptr, size_t bytes, bool copy_data);
795
796public:
799
800 /// Initialize the memory manager.
801 void Init();
802
803 /// Return the dual MemoryType of the given one, @a mt.
804 /** The default dual memory types are:
805
806 memory type | dual type
807 --------------- | ---------
808 HOST | DEVICE
809 HOST_32 | DEVICE
810 HOST_64 | DEVICE
811 HOST_DEBUG | DEVICE_DEBUG
812 HOST_UMPIRE | DEVICE_UMPIRE
813 HOST_PINNED | DEVICE
814 MANAGED | MANAGED
815 DEVICE | HOST
816 DEVICE_DEBUG | HOST_DEBUG
817 DEVICE_UMPIRE | HOST_UMPIRE
818 DEVICE_UMPIRE_2 | HOST_UMPIRE
819
820 The dual types can be modified before device configuration using the
821 method SetDualMemoryType() or by calling Device::SetMemoryTypes(). */
823 { return dual_map[(int)mt]; }
824
825 /// Set the dual memory type of @a mt to be @a dual_mt.
826 /** This method can only be called before configuration, i.e. before calling
827 Configure(), which is typically done during Device construction.
828
829 One of the types must be a host MemoryType and the other must be a device
830 MemoryType or both types must be the same host memory type. The latter
831 case is only allowed for convenience in setting up pure host execution,
832 so the actual dual is not updated. */
833 static void SetDualMemoryType(MemoryType mt, MemoryType dual_mt);
834
835 /** @brief Configure the Memory manager with given default host and device
836 types. This method will be called when configuring a device.
837
838 The host and device MemoryType%s, @a h_mt and @a d_mt, are set to be dual
839 to each other. */
840 void Configure(const MemoryType h_mt, const MemoryType d_mt);
841
842#ifdef MFEM_USE_UMPIRE
843 /// Set the host Umpire allocator name used with MemoryType::HOST_UMPIRE
844 static void SetUmpireHostAllocatorName(const char * h_name) { h_umpire_name = h_name; }
845 /// Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE
846 static void SetUmpireDeviceAllocatorName(const char * d_name) { d_umpire_name = d_name; }
847 /// Set the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2
848 static void SetUmpireDevice2AllocatorName(const char * d_name) { d_umpire_2_name = d_name; }
849
850 /// Get the host Umpire allocator name used with MemoryType::HOST_UMPIRE
851 static const char * GetUmpireHostAllocatorName() { return h_umpire_name; }
852 /// Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE
853 static const char * GetUmpireDeviceAllocatorName() { return d_umpire_name; }
854 /// Get the device Umpire allocator name used with MemoryType::DEVICE_UMPIRE_2
855 static const char * GetUmpireDevice2AllocatorName() { return d_umpire_2_name; }
856#endif
857
858 /// Free all the device memories
859 void Destroy();
860
861 /// Return true if the pointer is known by the memory manager
862 bool IsKnown(const void *h_ptr) { return IsKnown_(h_ptr); }
863
864 /// Return true if the pointer is known by the memory manager as an alias
865 bool IsAlias(const void *h_ptr) { return IsAlias_(h_ptr); }
866
867 /// Check if the host pointer has been registered in the memory manager
868 void RegisterCheck(void *h_ptr);
869
870 /// Prints all pointers known by the memory manager,
871 /// returning the number of printed pointers
872 int PrintPtrs(std::ostream &out = mfem::out);
873
874 /// Prints all aliases known by the memory manager
875 /// returning the number of printed pointers
876 int PrintAliases(std::ostream &out = mfem::out);
877
878 static MemoryType GetHostMemoryType() { return host_mem_type; }
879 static MemoryType GetDeviceMemoryType() { return device_mem_type; }
880
881#ifdef MFEM_USE_ENZYME
882 static void myfree(void* mem, MemoryType MT, unsigned &flags)
883 {
884 MemoryManager::Delete_(mem, MT, flags);
885 }
887 inline static void* __enzyme_allocation_like1[4] = {(void*)static_cast<void*(*)(void*, size_t, MemoryType, unsigned&)>(MemoryManager::New_),
888 (void*)1, (void*)"-1,2,3", (void*)myfree
889 };
891 inline static void* __enzyme_allocation_like2[4] = {(void*)static_cast<void*(*)(void*, size_t, MemoryType, MemoryType, unsigned, unsigned&)>(MemoryManager::New_),
892 (void*)1, (void*)"-1,2,4", (void*)MemoryManager::Delete_
893 };
894#endif
895};
896
897
898#ifdef MFEM_USE_MPI
899
900#if MFEM_HYPRE_VERSION < 21400
901#define HYPRE_MEMORY_DEVICE (0)
902#define HYPRE_MEMORY_HOST (1)
903#endif
904#if MFEM_HYPRE_VERSION < 21900
906#endif
907
908/// Return the configured HYPRE_MemoryLocation
910{
911#if !defined(HYPRE_USING_GPU)
912 return HYPRE_MEMORY_HOST;
913#elif MFEM_HYPRE_VERSION < 23100
914 return HYPRE_MEMORY_DEVICE;
915#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
916 if (!HYPRE_Initialized()) { return HYPRE_MEMORY_HOST; }
918 HYPRE_GetMemoryLocation(&loc);
919 return loc;
920#endif
921}
922
923/// Return true if HYPRE is configured to use GPU
924inline bool HypreUsingGPU()
925{
926#if !defined(HYPRE_USING_GPU)
927 return false;
928#elif MFEM_HYPRE_VERSION < 23100
929 return true;
930#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
931 return GetHypreMemoryLocation() != HYPRE_MEMORY_HOST;
932#endif
933}
934
935#endif // MFEM_USE_MPI
936
937
938// Inline methods
939
940template <typename T>
941inline void Memory<T>::Reset()
942{
943 h_ptr = NULL;
945 capacity = 0;
946 flags = 0;
947}
948
949template <typename T>
950inline void Memory<T>::Reset(MemoryType host_mt)
951{
952 h_ptr = NULL;
953 h_mt = host_mt;
954 capacity = 0;
955 flags = 0;
956}
957
958template <typename T>
959inline void Memory<T>::New(int size)
960{
961 capacity = size;
962 flags = OWNS_HOST | VALID_HOST;
964 h_ptr = (h_mt == MemoryType::HOST) ? NewHOST(size) :
965 (T*)MemoryManager::New_(nullptr, size*sizeof(T), h_mt, flags);
966}
967
968template <typename T>
969inline void Memory<T>::New(int size, MemoryType mt)
970{
971 capacity = size;
972 const size_t bytes = size*sizeof(T);
973 const bool mt_host = mt == MemoryType::HOST;
974 if (mt_host) { flags = OWNS_HOST | VALID_HOST; }
976 T *h_tmp = (h_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
977 h_ptr = (mt_host) ? h_tmp : (T*)MemoryManager::New_(h_tmp, bytes, mt, flags);
978}
979
980template <typename T>
981inline void Memory<T>::New(int size, MemoryType host_mt, MemoryType device_mt)
982{
983 capacity = size;
984 const size_t bytes = size*sizeof(T);
985 this->h_mt = host_mt;
986 T *h_tmp = (host_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
987 h_ptr = (T*)MemoryManager::New_(h_tmp, bytes, host_mt, device_mt,
988 VALID_HOST, flags);
989}
990
991template <typename T>
992inline void Memory<T>::Wrap(T *ptr, int size, bool own)
993{
994 h_ptr = ptr;
995 capacity = size;
996 flags = (own ? OWNS_HOST : 0) | VALID_HOST;
998#ifdef MFEM_DEBUG
999 if (own && MemoryManager::Exists())
1000 {
1001 MemoryType h_ptr_mt = MemoryManager::GetHostMemoryType_(h_ptr);
1002 MFEM_VERIFY(h_mt == h_ptr_mt,
1003 "h_mt = " << (int)h_mt << ", h_ptr_mt = " << (int)h_ptr_mt);
1004 }
1005#endif
1006 if (own && h_mt != MemoryType::HOST)
1007 {
1008 const size_t bytes = size*sizeof(T);
1009 MemoryManager::Register_(ptr, ptr, bytes, h_mt, own, false, flags);
1010 }
1011}
1012
1013template <typename T>
1014inline void Memory<T>::Wrap(T *ptr, int size, MemoryType mt, bool own)
1015{
1016 capacity = size;
1017 if (IsHostMemory(mt))
1018 {
1019 h_mt = mt;
1020 h_ptr = ptr;
1021 if (mt == MemoryType::HOST || !own)
1022 {
1023 // Skip registration
1024 flags = (own ? OWNS_HOST : 0) | VALID_HOST;
1025 return;
1026 }
1027 }
1028 else
1029 {
1031 h_ptr = (h_mt == MemoryType::HOST) ? NewHOST(size) : nullptr;
1032 }
1033 flags = 0;
1034 h_ptr = (T*)MemoryManager::Register_(ptr, h_ptr, size*sizeof(T), mt,
1035 own, false, flags);
1036}
1037
1038template <typename T>
1039inline void Memory<T>::Wrap(T *h_ptr_, T *d_ptr, int size, MemoryType h_mt_,
1040 bool own, bool valid_host, bool valid_device)
1041{
1042 h_mt = h_mt_;
1043 flags = 0;
1044 h_ptr = h_ptr_;
1045 capacity = size;
1046 MFEM_ASSERT(IsHostMemory(h_mt),"");
1047 MFEM_ASSERT(valid_host || valid_device,"");
1048 const size_t bytes = size*sizeof(T);
1050 MemoryManager::Register2_(h_ptr, d_ptr, bytes, h_mt, d_mt,
1051 own, false, flags,
1052 valid_host*VALID_HOST|valid_device*VALID_DEVICE);
1053}
1054
1055template <typename T>
1056inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
1057{
1058 MFEM_ASSERT(0 <= offset, "invalid offset = " << offset);
1059 MFEM_ASSERT(0 <= size, "invalid size = " << size);
1060 MFEM_ASSERT(offset + size <= base.capacity,
1061 "invalid offset + size = " << offset + size
1062 << " > base capacity = " << base.capacity);
1063 capacity = size;
1064 h_mt = base.h_mt;
1065 h_ptr = base.h_ptr + offset;
1066 if (!(base.flags & Registered))
1067 {
1068 if (
1069#if !defined(HYPRE_USING_GPU)
1070 // If the following condition is true then MemoryManager::Exists()
1071 // should also be true:
1073#elif MFEM_HYPRE_VERSION < 23100
1074 // When HYPRE_USING_GPU is defined and HYPRE < 2.31.0, we always
1075 // register the 'base' if the MemoryManager::Exists():
1076 MemoryManager::Exists()
1077#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
1079 (MemoryManager::Exists() && HypreUsingGPU())
1080#endif
1081 )
1082 {
1083 // Register 'base':
1084 MemoryManager::Register_(base.h_ptr, nullptr, base.capacity*sizeof(T),
1085 base.h_mt, base.flags & OWNS_HOST,
1086 base.flags & ALIAS, base.flags);
1087 }
1088 else
1089 {
1090 // Copy the flags from 'base', setting the ALIAS flag to true, and
1091 // setting both OWNS_HOST and OWNS_DEVICE to false:
1092 flags = (base.flags | ALIAS) & ~(OWNS_HOST | OWNS_DEVICE);
1093 return;
1094 }
1095 }
1096 const size_t s_bytes = size*sizeof(T);
1097 const size_t o_bytes = offset*sizeof(T);
1098 MemoryManager::Alias_(base.h_ptr, o_bytes, s_bytes, base.flags, flags);
1099}
1100
1101template <typename T>
1103{
1104 if (!IsDeviceMemory(d_mt)) { return; }
1105 if (!(flags & Registered))
1106 {
1107 MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1108 flags & OWNS_HOST, flags & ALIAS, flags);
1109 }
1110 MemoryManager::SetDeviceMemoryType_(h_ptr, flags, d_mt);
1111}
1112
1113template <typename T>
1115{
1116 const bool registered = flags & Registered;
1117 const bool mt_host = h_mt == MemoryType::HOST;
1118 const bool std_delete = !registered && mt_host;
1119
1120 if (!std_delete)
1121 {
1122 MemoryManager::Delete_((void*)h_ptr, h_mt, flags);
1123 }
1124
1125 if (mt_host)
1126 {
1127 if (flags & OWNS_HOST) { delete [] h_ptr; }
1128 }
1129 Reset(h_mt);
1130}
1131
1132template <typename T>
1133inline void Memory<T>::DeleteDevice(bool copy_to_host)
1134{
1135 if (flags & Registered)
1136 {
1137 if (copy_to_host) { Read(MemoryClass::HOST, capacity); }
1138 MemoryManager::DeleteDevice_((void*)h_ptr, flags);
1139 }
1140}
1141
1142template <typename T>
1143inline T &Memory<T>::operator[](int idx)
1144{
1145 MFEM_ASSERT((flags & VALID_HOST) && !(flags & VALID_DEVICE),
1146 "invalid host pointer access");
1147 return h_ptr[idx];
1148}
1149
1150template <typename T>
1151inline const T &Memory<T>::operator[](int idx) const
1152{
1153 MFEM_ASSERT((flags & VALID_HOST), "invalid host pointer access");
1154 return h_ptr[idx];
1155}
1156
1157template <typename T>
1159{
1160 MFEM_ASSERT(Empty() ||
1161 ((flags & VALID_HOST) &&
1162 (std::is_const<T>::value || !(flags & VALID_DEVICE))),
1163 "invalid host pointer access");
1164 return h_ptr;
1165}
1166
1167template <typename T>
1168inline Memory<T>::operator const T*() const
1169{
1170 MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
1171 return h_ptr;
1172}
1173
1174template <typename T> template <typename U>
1176{
1177 MFEM_ASSERT(Empty() ||
1178 ((flags & VALID_HOST) &&
1179 (std::is_const<U>::value || !(flags & VALID_DEVICE))),
1180 "invalid host pointer access");
1181 return reinterpret_cast<U*>(h_ptr);
1182}
1183
1184template <typename T> template <typename U>
1185inline Memory<T>::operator const U*() const
1186{
1187 MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
1188 return reinterpret_cast<U*>(h_ptr);
1189}
1190
1191template <typename T>
1192inline T *Memory<T>::ReadWrite(MemoryClass mc, int size)
1193{
1194 const size_t bytes = size * sizeof(T);
1195 if (!(flags & Registered))
1196 {
1197 if (mc == MemoryClass::HOST) { return h_ptr; }
1198 MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1199 flags & OWNS_HOST, flags & ALIAS, flags);
1200 }
1201 return (T*)MemoryManager::ReadWrite_(h_ptr, h_mt, mc, bytes, flags);
1202}
1203
1204template <typename T>
1205inline const T *Memory<T>::Read(MemoryClass mc, int size) const
1206{
1207 const size_t bytes = size * sizeof(T);
1208 if (!(flags & Registered))
1209 {
1210 if (mc == MemoryClass::HOST) { return h_ptr; }
1211 MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1212 flags & OWNS_HOST, flags & ALIAS, flags);
1213 }
1214 return (const T*)MemoryManager::Read_(h_ptr, h_mt, mc, bytes, flags);
1215}
1216
1217template <typename T>
1218inline T *Memory<T>::Write(MemoryClass mc, int size)
1219{
1220 const size_t bytes = size * sizeof(T);
1221 if (!(flags & Registered))
1222 {
1223 if (mc == MemoryClass::HOST) { return h_ptr; }
1224 MemoryManager::Register_(h_ptr, nullptr, capacity*sizeof(T), h_mt,
1225 flags & OWNS_HOST, flags & ALIAS, flags);
1226 }
1227 return (T*)MemoryManager::Write_(h_ptr, h_mt, mc, bytes, flags);
1228}
1229
1230template <typename T>
1231inline void Memory<T>::Sync(const Memory &other) const
1232{
1233 if (!(flags & Registered) && (other.flags & Registered))
1234 {
1235 MFEM_ASSERT(h_ptr == other.h_ptr &&
1236 (flags & ALIAS) == (other.flags & ALIAS),
1237 "invalid input");
1238 flags = (flags | Registered) & ~(OWNS_DEVICE | OWNS_INTERNAL);
1239 }
1240 flags = (flags & ~(VALID_HOST | VALID_DEVICE)) |
1241 (other.flags & (VALID_HOST | VALID_DEVICE));
1242}
1243
1244template <typename T>
1245inline void Memory<T>::SyncAlias(const Memory &base, int alias_size) const
1246{
1247 // Assuming that if *this is registered then base is also registered.
1248 MFEM_ASSERT(!(flags & Registered) || (base.flags & Registered),
1249 "invalid base state");
1250 if (!(base.flags & Registered)) { return; }
1251 MemoryManager::SyncAlias_(base.h_ptr, h_ptr, alias_size*sizeof(T),
1252 base.flags, flags);
1253}
1254
1255template <typename T>
1257{
1258 if (h_ptr == nullptr || !(flags & VALID_DEVICE)) { return h_mt; }
1259 return MemoryManager::GetDeviceMemoryType_(h_ptr, flags & ALIAS);
1260}
1261
1262template <typename T>
1264{
1265 if (!(flags & Registered)) { return MemoryType::DEFAULT; }
1266 return MemoryManager::GetDeviceMemoryType_(h_ptr, flags & ALIAS);
1267}
1268
1269template <typename T>
1270inline bool Memory<T>::HostIsValid() const
1271{
1272 return flags & VALID_HOST ? true : false;
1273}
1274
1275template <typename T>
1276inline bool Memory<T>::DeviceIsValid() const
1277{
1278 return flags & VALID_DEVICE ? true : false;
1279}
1280
1281template <typename T>
1282inline void Memory<T>::CopyFrom(const Memory &src, int size)
1283{
1284 MFEM_VERIFY(src.capacity>=size && capacity>=size, "Incorrect size");
1285 if (size <= 0) { return; }
1286 if (!(flags & Registered) && !(src.flags & Registered))
1287 {
1288 if (h_ptr != src.h_ptr)
1289 {
1290 MFEM_ASSERT(h_ptr + size <= src.h_ptr || src.h_ptr + size <= h_ptr,
1291 "data overlaps!");
1292 std::memcpy(h_ptr, src, size*sizeof(T));
1293 }
1294 // *this is not registered, so (flags & VALID_HOST) must be true
1295 }
1296 else
1297 {
1298 MemoryManager::Copy_(h_ptr, src.h_ptr, size*sizeof(T), src.flags, flags);
1299 }
1300}
1301
1302template <typename T>
1303inline void Memory<T>::CopyFromHost(const T *src, int size)
1304{
1305 MFEM_VERIFY(capacity>=size, "Incorrect size");
1306 if (size <= 0) { return; }
1307 if (!(flags & Registered))
1308 {
1309 if (h_ptr != src)
1310 {
1311 MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
1312 "data overlaps!");
1313 std::memcpy(h_ptr, src, size*sizeof(T));
1314 }
1315 // *this is not registered, so (flags & VALID_HOST) must be true
1316 }
1317 else
1318 {
1319 MemoryManager::CopyFromHost_(h_ptr, src, size*sizeof(T), flags);
1320 }
1321}
1322
1323template <typename T>
1324inline void Memory<T>::CopyTo(Memory &dest, int size) const
1325{
1326 dest.CopyFrom(*this, size);
1327}
1328
1329template <typename T>
1330inline void Memory<T>::CopyToHost(T *dest, int size) const
1331{
1332 MFEM_VERIFY(capacity>=size, "Incorrect size");
1333 if (size <= 0) { return; }
1334 if (!(flags & Registered))
1335 {
1336 if (h_ptr != dest)
1337 {
1338 MFEM_ASSERT(h_ptr + size <= dest || dest + size <= h_ptr,
1339 "data overlaps!");
1340 std::memcpy(dest, h_ptr, size*sizeof(T));
1341 }
1342 }
1343 else
1344 {
1345 MemoryManager::CopyToHost_(dest, h_ptr, size*sizeof(T), flags);
1346 }
1347}
1348
1349
1350/** @brief Print the state of a Memory object based on its internal flags.
1351 Useful in a debugger. See also Memory<T>::PrintFlags(). */
1352extern void MemoryPrintFlags(unsigned flags);
1353
1354
1355template <typename T>
1356inline void Memory<T>::PrintFlags() const
1357{
1358 MemoryPrintFlags(flags);
1359}
1360
1361template <typename T>
1362inline int Memory<T>::CompareHostAndDevice(int size) const
1363{
1364 if (!(flags & VALID_HOST) || !(flags & VALID_DEVICE)) { return 0; }
1365 return MemoryManager::CompareHostAndDevice_(h_ptr, size*sizeof(T), flags);
1366}
1367
1368
1369/// The (single) global memory manager object
1370extern MFEM_EXPORT MemoryManager mm;
1371
1372} // namespace mfem
1373
1374#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]
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 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...
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:348
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:747
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.