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