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