xref: /petsc/src/sys/objects/device/interface/memory.cxx (revision 6797ed33bfc5684cca29e5d9b9bbfc6c8aac93e0)
10e6b6b59SJacob Faibussowitsch #include <petsc/private/deviceimpl.h> /*I <petscdevice.h> I*/
20e6b6b59SJacob Faibussowitsch 
30e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/register_finalize.hpp>
4*6797ed33SJacob Faibussowitsch #include <petsc/private/cpp/type_traits.hpp> // integral_value
50e6b6b59SJacob Faibussowitsch 
60e6b6b59SJacob Faibussowitsch #include <unordered_map>
70e6b6b59SJacob Faibussowitsch #include <algorithm> // std::find_if
80e6b6b59SJacob Faibussowitsch #include <cstring>   // std::memset
90e6b6b59SJacob Faibussowitsch 
100e6b6b59SJacob Faibussowitsch const char *const PetscDeviceCopyModes[] = {"host_to_host", "device_to_host", "host_to_device", "device_to_device", "auto", "PetscDeviceCopyMode", "PETSC_DEVICE_COPY_", nullptr};
110e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_HTOH) == 0, "");
120e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_DTOH) == 1, "");
130e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_HTOD) == 2, "");
140e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_DTOD) == 3, "");
150e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_AUTO) == 4, "");
160e6b6b59SJacob Faibussowitsch 
170e6b6b59SJacob Faibussowitsch // ==========================================================================================
180e6b6b59SJacob Faibussowitsch // MemoryMap
190e6b6b59SJacob Faibussowitsch //
20*6797ed33SJacob Faibussowitsch // Since the pointers allocated via PetscDeviceAllocate_Private() may be device pointers we
21*6797ed33SJacob Faibussowitsch // cannot just store meta-data within the pointer itself (as we can't dereference them). So
22*6797ed33SJacob Faibussowitsch // instead we need to keep an extra map to keep track of them
230e6b6b59SJacob Faibussowitsch //
24*6797ed33SJacob Faibussowitsch // Each entry maps pointer -> {
25*6797ed33SJacob Faibussowitsch //   PetscMemType  - The memtype of the pointer
26*6797ed33SJacob Faibussowitsch //   PetscObjectId - A unique ID assigned at allocation or registratrion so auto-dep can
27*6797ed33SJacob Faibussowitsch //                   identify the pointer
28*6797ed33SJacob Faibussowitsch //   size          - The size (in bytes) of the allocation
29*6797ed33SJacob Faibussowitsch // }
300e6b6b59SJacob Faibussowitsch // ==========================================================================================
310e6b6b59SJacob Faibussowitsch 
320e6b6b59SJacob Faibussowitsch // GCC implementation for std::hash<T*>. LLVM's libc++ is almost 2x slower because they do all
330e6b6b59SJacob Faibussowitsch // kinds of complicated murmur hashing, so we make sure to enforce GCC's version.
340e6b6b59SJacob Faibussowitsch struct PointerHash {
35*6797ed33SJacob Faibussowitsch   template <typename T>
36*6797ed33SJacob Faibussowitsch   PETSC_NODISCARD std::size_t operator()(const T *ptr) const noexcept {
37*6797ed33SJacob Faibussowitsch     return reinterpret_cast<std::size_t>(ptr);
38*6797ed33SJacob Faibussowitsch   }
390e6b6b59SJacob Faibussowitsch };
400e6b6b59SJacob Faibussowitsch 
410e6b6b59SJacob Faibussowitsch class MemoryMap : public Petsc::RegisterFinalizeable<MemoryMap> {
420e6b6b59SJacob Faibussowitsch public:
430e6b6b59SJacob Faibussowitsch   struct PointerAttributes {
440e6b6b59SJacob Faibussowitsch     PetscMemType  mtype{}; // memtype of allocation
450e6b6b59SJacob Faibussowitsch     PetscObjectId id{};    // id of allocation
460e6b6b59SJacob Faibussowitsch     std::size_t   size{};  // size of allocation (bytes)
470e6b6b59SJacob Faibussowitsch 
480e6b6b59SJacob Faibussowitsch     // even though this is a POD and can be aggregate initialized, the STL uses () constructors
490e6b6b59SJacob Faibussowitsch     // in unordered_map and so we need to provide a trivial contructor...
500e6b6b59SJacob Faibussowitsch     constexpr PointerAttributes(PetscMemType, PetscObjectId, std::size_t) noexcept;
510e6b6b59SJacob Faibussowitsch     constexpr PointerAttributes() noexcept                                              = default;
520e6b6b59SJacob Faibussowitsch     constexpr PointerAttributes(const PointerAttributes &) noexcept                     = default;
530e6b6b59SJacob Faibussowitsch     PETSC_CONSTEXPR_14 PointerAttributes &operator=(const PointerAttributes &) noexcept = default;
540e6b6b59SJacob Faibussowitsch     constexpr PointerAttributes(PointerAttributes &&) noexcept                          = default;
550e6b6b59SJacob Faibussowitsch     PETSC_CONSTEXPR_14 PointerAttributes &operator=(PointerAttributes &&) noexcept      = default;
560e6b6b59SJacob Faibussowitsch 
570e6b6b59SJacob Faibussowitsch     bool operator==(const PointerAttributes &) const noexcept;
580e6b6b59SJacob Faibussowitsch 
590e6b6b59SJacob Faibussowitsch     PETSC_NODISCARD bool contains(const void *, const void *) const noexcept;
600e6b6b59SJacob Faibussowitsch   };
610e6b6b59SJacob Faibussowitsch 
620e6b6b59SJacob Faibussowitsch   using map_type = std::unordered_map<void *, PointerAttributes, PointerHash>;
630e6b6b59SJacob Faibussowitsch 
640e6b6b59SJacob Faibussowitsch   map_type map;
650e6b6b59SJacob Faibussowitsch 
660e6b6b59SJacob Faibussowitsch   // return the iterator of the allocation containing ptr, or map.cend() if not found
67*6797ed33SJacob Faibussowitsch   PETSC_NODISCARD map_type::const_iterator search_for(const void *, bool = false) const noexcept;
680e6b6b59SJacob Faibussowitsch 
690e6b6b59SJacob Faibussowitsch private:
700e6b6b59SJacob Faibussowitsch   friend class Petsc::RegisterFinalizeable<MemoryMap>;
710e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode register_finalize_() noexcept;
720e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode finalize_() noexcept;
730e6b6b59SJacob Faibussowitsch };
740e6b6b59SJacob Faibussowitsch 
750e6b6b59SJacob Faibussowitsch // ==========================================================================================
760e6b6b59SJacob Faibussowitsch // PointerAttributes
770e6b6b59SJacob Faibussowitsch // ==========================================================================================
780e6b6b59SJacob Faibussowitsch 
790e6b6b59SJacob Faibussowitsch constexpr MemoryMap::PointerAttributes::PointerAttributes(PetscMemType mtype_, PetscObjectId id_, std::size_t size_) noexcept : mtype(mtype_), id(id_), size(size_) { }
800e6b6b59SJacob Faibussowitsch 
810e6b6b59SJacob Faibussowitsch bool MemoryMap::PointerAttributes::operator==(const PointerAttributes &other) const noexcept {
820e6b6b59SJacob Faibussowitsch   return mtype == other.mtype && id == other.id && size == other.size;
830e6b6b59SJacob Faibussowitsch }
840e6b6b59SJacob Faibussowitsch 
850e6b6b59SJacob Faibussowitsch bool MemoryMap::PointerAttributes::contains(const void *ptr_begin, const void *ptr) const noexcept {
860e6b6b59SJacob Faibussowitsch   return (ptr >= ptr_begin) && (ptr < (static_cast<const char *>(ptr_begin) + size));
870e6b6b59SJacob Faibussowitsch }
880e6b6b59SJacob Faibussowitsch 
890e6b6b59SJacob Faibussowitsch // ==========================================================================================
90*6797ed33SJacob Faibussowitsch // Memory map - Private API
910e6b6b59SJacob Faibussowitsch // ==========================================================================================
920e6b6b59SJacob Faibussowitsch 
930e6b6b59SJacob Faibussowitsch PetscErrorCode MemoryMap::register_finalize_() noexcept {
940e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
950e6b6b59SJacob Faibussowitsch   // Preallocate, this does give a modest performance bump since unordered_map is so __dog__
960e6b6b59SJacob Faibussowitsch   // slow if it needs to rehash. Experiments show that users tend not to have more than 5 or
970e6b6b59SJacob Faibussowitsch   // so concurrently live pointers lying around. 10 at most.
980e6b6b59SJacob Faibussowitsch   PetscCallCXX(map.reserve(16));
990e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
1000e6b6b59SJacob Faibussowitsch }
1010e6b6b59SJacob Faibussowitsch 
1020e6b6b59SJacob Faibussowitsch PetscErrorCode MemoryMap::finalize_() noexcept {
1030e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1040e6b6b59SJacob Faibussowitsch   PetscCall(PetscInfo(nullptr, "Finalizing memory map\n"));
1050e6b6b59SJacob Faibussowitsch   PetscCallCXX(map = map_type{});
1060e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
1070e6b6b59SJacob Faibussowitsch }
1080e6b6b59SJacob Faibussowitsch 
109*6797ed33SJacob Faibussowitsch // ==========================================================================================
110*6797ed33SJacob Faibussowitsch // Memory map - Public API
111*6797ed33SJacob Faibussowitsch // ==========================================================================================
112*6797ed33SJacob Faibussowitsch 
1130e6b6b59SJacob Faibussowitsch /*
1140e6b6b59SJacob Faibussowitsch   MemoryMap::search_for - retrieve an iterator to the key-value pair for a pointer in the map
1150e6b6b59SJacob Faibussowitsch 
116*6797ed33SJacob Faibussowitsch   Input Parameters:
117*6797ed33SJacob Faibussowitsch + ptr       - pointer to search for
118*6797ed33SJacob Faibussowitsch - must_find - true if an error is raised if the pointer is not found (default: false)
1190e6b6b59SJacob Faibussowitsch 
1200e6b6b59SJacob Faibussowitsch   Notes:
1210e6b6b59SJacob Faibussowitsch   Accounts for sub-regions, i.e. if ptr is contained within another pointers region, it returns
1220e6b6b59SJacob Faibussowitsch   the iterator to the super-pointers key-value pair.
1230e6b6b59SJacob Faibussowitsch 
124*6797ed33SJacob Faibussowitsch   If ptr is not found and must_find is false returns map.end(), otherwise raises an error
1250e6b6b59SJacob Faibussowitsch */
126*6797ed33SJacob Faibussowitsch MemoryMap::map_type::const_iterator MemoryMap::search_for(const void *ptr, bool must_find) const noexcept {
1270e6b6b59SJacob Faibussowitsch   const auto end = map.end();
128*6797ed33SJacob Faibussowitsch   auto       it  = map.find(const_cast<map_type::key_type>(ptr));
1290e6b6b59SJacob Faibussowitsch 
1300e6b6b59SJacob Faibussowitsch   // ptr was found, and points to an entire block
131*6797ed33SJacob Faibussowitsch   PetscFunctionBegin;
132*6797ed33SJacob Faibussowitsch   if (it != end) PetscFunctionReturn(it);
1330e6b6b59SJacob Faibussowitsch   // wasn't found, but maybe its part of a block. have to search every block for it
1340e6b6b59SJacob Faibussowitsch   // clang-format off
135*6797ed33SJacob Faibussowitsch   it = std::find_if(map.begin(), end, [ptr](const map_type::const_iterator::value_type &map_it) {
1360e6b6b59SJacob Faibussowitsch     return map_it.second.contains(map_it.first, ptr);
1370e6b6b59SJacob Faibussowitsch   });
138*6797ed33SJacob Faibussowitsch   PetscCheckAbort(!must_find || it != end, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Pointer %p was not registered with the memory tracker, call PetscDeviceRegisterMemory() on it", ptr);
139*6797ed33SJacob Faibussowitsch   PetscFunctionReturn(it);
1400e6b6b59SJacob Faibussowitsch   // clang-format on
1410e6b6b59SJacob Faibussowitsch }
1420e6b6b59SJacob Faibussowitsch 
1430e6b6b59SJacob Faibussowitsch static MemoryMap memory_map;
1440e6b6b59SJacob Faibussowitsch 
1450e6b6b59SJacob Faibussowitsch // ==========================================================================================
1460e6b6b59SJacob Faibussowitsch // Utility functions
1470e6b6b59SJacob Faibussowitsch // ==========================================================================================
1480e6b6b59SJacob Faibussowitsch 
1490e6b6b59SJacob Faibussowitsch static PetscErrorCode PetscDeviceCheckCapable_Private(PetscDeviceContext dctx, bool cond, const char descr[]) {
1500e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1510e6b6b59SJacob Faibussowitsch   PetscCheck(cond, PETSC_COMM_SELF, PETSC_ERR_SUP, "Device context (id: %" PetscInt64_FMT ", name: %s, type: %s) can only handle %s host memory", PetscObjectCast(dctx)->id, PetscObjectCast(dctx)->name, dctx->device ? PetscDeviceTypes[dctx->device->type] : "unknown", descr);
1520e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
1530e6b6b59SJacob Faibussowitsch }
1540e6b6b59SJacob Faibussowitsch 
1550e6b6b59SJacob Faibussowitsch // A helper utility, since register is called from PetscDeviceRegisterMemory() and
1560e6b6b59SJacob Faibussowitsch // PetscDevicAllocate(). The latter also needs the generated id, so instead of making it search
1570e6b6b59SJacob Faibussowitsch // the map again we just return it here
1580e6b6b59SJacob Faibussowitsch static PetscErrorCode PetscDeviceRegisterMemory_Private(const void *PETSC_RESTRICT ptr, PetscMemType mtype, std::size_t size, PetscObjectId *PETSC_RESTRICT id = nullptr) {
1590e6b6b59SJacob Faibussowitsch   auto      &map = memory_map.map;
1600e6b6b59SJacob Faibussowitsch   const auto it  = memory_map.search_for(ptr);
1610e6b6b59SJacob Faibussowitsch 
1620e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1630e6b6b59SJacob Faibussowitsch   if (it == map.cend()) {
1640e6b6b59SJacob Faibussowitsch     // pointer was never registered with the map, insert it and bail
1650e6b6b59SJacob Faibussowitsch     const auto newid = PetscObjectNewId_Internal();
1660e6b6b59SJacob Faibussowitsch 
1670e6b6b59SJacob Faibussowitsch     if (PetscDefined(USE_DEBUG)) {
1680e6b6b59SJacob Faibussowitsch       const auto tmp = MemoryMap::PointerAttributes(mtype, newid, size);
1690e6b6b59SJacob Faibussowitsch 
1700e6b6b59SJacob Faibussowitsch       for (const auto &entry : map) {
1710e6b6b59SJacob Faibussowitsch         // REVIEW ME: maybe this should just be handled...
1720e6b6b59SJacob Faibussowitsch         PetscCheck(!tmp.contains(ptr, entry.first), PETSC_COMM_SELF, PETSC_ERR_ORDER, "Trying to register pointer %p (memtype %s, size %zu) but it appears you have already registered a sub-region of it (pointer %p, memtype %s, size %zu). Must register the larger region first", ptr, PetscMemTypeToString(mtype), size,
1730e6b6b59SJacob Faibussowitsch                    entry.first, PetscMemTypeToString(entry.second.mtype), entry.second.size);
1740e6b6b59SJacob Faibussowitsch       }
1750e6b6b59SJacob Faibussowitsch     }
1760e6b6b59SJacob Faibussowitsch     // clang-format off
1770e6b6b59SJacob Faibussowitsch     if (id) *id = newid;
1780e6b6b59SJacob Faibussowitsch     PetscCallCXX(map.emplace(
1790e6b6b59SJacob Faibussowitsch       std::piecewise_construct,
1800e6b6b59SJacob Faibussowitsch       std::forward_as_tuple(const_cast<MemoryMap::map_type::key_type>(ptr)),
1810e6b6b59SJacob Faibussowitsch       std::forward_as_tuple(mtype, newid, size)
1820e6b6b59SJacob Faibussowitsch     ));
1830e6b6b59SJacob Faibussowitsch     // clang-format on
1840e6b6b59SJacob Faibussowitsch     PetscFunctionReturn(0);
1850e6b6b59SJacob Faibussowitsch   }
1860e6b6b59SJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
1870e6b6b59SJacob Faibussowitsch     const auto &old = it->second;
1880e6b6b59SJacob Faibussowitsch 
1890e6b6b59SJacob Faibussowitsch     PetscCheck(MemoryMap::PointerAttributes(mtype, old.id, size) == old, PETSC_COMM_SELF, PETSC_ERR_LIB, "Pointer %p appears to have been previously allocated with memtype %s, size %zu and assigned id %" PetscInt64_FMT ", which does not match new values: (mtype %s, size %zu, id %" PetscInt64_FMT ")", it->first,
1900e6b6b59SJacob Faibussowitsch                PetscMemTypeToString(old.mtype), old.size, old.id, PetscMemTypeToString(mtype), size, old.id);
1910e6b6b59SJacob Faibussowitsch   }
1920e6b6b59SJacob Faibussowitsch   if (id) *id = it->second.id;
1930e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
1940e6b6b59SJacob Faibussowitsch }
1950e6b6b59SJacob Faibussowitsch 
1960e6b6b59SJacob Faibussowitsch /*@C
1970e6b6b59SJacob Faibussowitsch   PetscDeviceRegisterMemory - Register a pointer for use with device-aware memory system
1980e6b6b59SJacob Faibussowitsch 
1990e6b6b59SJacob Faibussowitsch   Not Collective
2000e6b6b59SJacob Faibussowitsch 
2010e6b6b59SJacob Faibussowitsch   Input Parameters:
2020e6b6b59SJacob Faibussowitsch + ptr   - The pointer to register
2030e6b6b59SJacob Faibussowitsch . mtype - The `PetscMemType` of the pointer
2040e6b6b59SJacob Faibussowitsch - size  - The size (in bytes) of the memory region
2050e6b6b59SJacob Faibussowitsch 
2060e6b6b59SJacob Faibussowitsch   Notes:
2070e6b6b59SJacob Faibussowitsch   `ptr` need not point to the beginning of the memory range, however the user should register
2080e6b6b59SJacob Faibussowitsch   the
2090e6b6b59SJacob Faibussowitsch 
2100e6b6b59SJacob Faibussowitsch   It's OK to re-register the same `ptr` repeatedly (subsequent registrations do nothing)
2110e6b6b59SJacob Faibussowitsch   however the given `mtype` and `size` must match the original registration.
2120e6b6b59SJacob Faibussowitsch 
2130e6b6b59SJacob Faibussowitsch   `size` may be 0 (in which case this routine does nothing).
2140e6b6b59SJacob Faibussowitsch 
2150e6b6b59SJacob Faibussowitsch   Level: intermediate
2160e6b6b59SJacob Faibussowitsch 
2170e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceMalloc()`, `PetscDeviceArrayCopy()`, `PetscDeviceFree()`,
2180e6b6b59SJacob Faibussowitsch `PetscDeviceArrayZero()`
2190e6b6b59SJacob Faibussowitsch @*/
2200e6b6b59SJacob Faibussowitsch PetscErrorCode PetscDeviceRegisterMemory(const void *PETSC_RESTRICT ptr, PetscMemType mtype, std::size_t size) {
2210e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2220e6b6b59SJacob Faibussowitsch   if (PetscMemTypeHost(mtype)) PetscValidPointer(ptr, 1);
2230e6b6b59SJacob Faibussowitsch   if (PetscUnlikely(!size)) PetscFunctionReturn(0); // there is no point registering empty range
2240e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceRegisterMemory_Private(ptr, mtype, size));
2250e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
2260e6b6b59SJacob Faibussowitsch }
2270e6b6b59SJacob Faibussowitsch 
228*6797ed33SJacob Faibussowitsch /*
229*6797ed33SJacob Faibussowitsch   PetscDeviceAllocate_Private - Allocate device-aware memory
2300e6b6b59SJacob Faibussowitsch 
2310e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
2320e6b6b59SJacob Faibussowitsch 
2330e6b6b59SJacob Faibussowitsch   Input Parameters:
2340e6b6b59SJacob Faibussowitsch + dctx      - The `PetscDeviceContext` used to allocate the memory
2350e6b6b59SJacob Faibussowitsch . clear     - Whether or not the memory should be zeroed
2360e6b6b59SJacob Faibussowitsch . mtype     - The type of memory to allocate
237*6797ed33SJacob Faibussowitsch . n         - The amount (in bytes) to allocate
238*6797ed33SJacob Faibussowitsch - alignment - The alignment requirement (in bytes) of the allocated pointer
2390e6b6b59SJacob Faibussowitsch 
2400e6b6b59SJacob Faibussowitsch   Output Parameter:
2410e6b6b59SJacob Faibussowitsch . ptr - The pointer to store the result in
2420e6b6b59SJacob Faibussowitsch 
2430e6b6b59SJacob Faibussowitsch   Notes:
2440e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceMalloc()` over this routine as it automatically computes
245*6797ed33SJacob Faibussowitsch   the size of the allocation and alignment based on the size of the datatype.
2460e6b6b59SJacob Faibussowitsch 
247*6797ed33SJacob Faibussowitsch   If the user is unsure about `alignment` -- or unable to compute it -- passing
248*6797ed33SJacob Faibussowitsch   `PETSC_MEMALIGN` will always work, though the user should beware that this may be quite
249*6797ed33SJacob Faibussowitsch   wasteful for very small allocations.
250*6797ed33SJacob Faibussowitsch 
251*6797ed33SJacob Faibussowitsch   Memory allocated with this function must be freed with `PetscDeviceFree()` (or
252*6797ed33SJacob Faibussowitsch   `PetscDeviceDeallocate_Private()`).
253*6797ed33SJacob Faibussowitsch 
254*6797ed33SJacob Faibussowitsch   If `n` is zero, then `ptr` is set to `PETSC_NULLPTR`.
255*6797ed33SJacob Faibussowitsch 
256*6797ed33SJacob Faibussowitsch   This routine falls back to using `PetscMalloc1()` or `PetscCalloc1()` (depending on the value
257*6797ed33SJacob Faibussowitsch   of `clear`) if PETSc was not configured with device support. The user should note that
258*6797ed33SJacob Faibussowitsch   `mtype` and `alignment` are ignored in this case, as these routines allocate only host memory
259*6797ed33SJacob Faibussowitsch   aligned to `PETSC_MEMALIGN`.
2600e6b6b59SJacob Faibussowitsch 
2610e6b6b59SJacob Faibussowitsch   Note result stored `ptr` is immediately valid and the user may freely inspect or manipulate
2620e6b6b59SJacob Faibussowitsch   its value on function return, i.e.\:
2630e6b6b59SJacob Faibussowitsch 
2640e6b6b59SJacob Faibussowitsch .vb
2650e6b6b59SJacob Faibussowitsch   PetscInt *ptr;
2660e6b6b59SJacob Faibussowitsch 
267*6797ed33SJacob Faibussowitsch   PetscDeviceAllocate_Private(dctx, PETSC_FALSE, PETSC_MEMTYPE_DEVICE, 20, alignof(PetscInt), (void**)&ptr);
2680e6b6b59SJacob Faibussowitsch 
2690e6b6b59SJacob Faibussowitsch   PetscInt *sub_ptr = ptr + 10; // OK, no need to synchronize
2700e6b6b59SJacob Faibussowitsch 
2710e6b6b59SJacob Faibussowitsch   ptr[0] = 10; // ERROR, directly accessing contents of ptr is undefined until synchronization
2720e6b6b59SJacob Faibussowitsch .ve
2730e6b6b59SJacob Faibussowitsch 
2740e6b6b59SJacob Faibussowitsch   DAG representation:
2750e6b6b59SJacob Faibussowitsch .vb
2760e6b6b59SJacob Faibussowitsch   time ->
2770e6b6b59SJacob Faibussowitsch 
2780e6b6b59SJacob Faibussowitsch   -> dctx - |= CALL =| -\- dctx -->
2790e6b6b59SJacob Faibussowitsch                          \- ptr ->
2800e6b6b59SJacob Faibussowitsch .ve
2810e6b6b59SJacob Faibussowitsch 
2820e6b6b59SJacob Faibussowitsch   Level: intermediate
2830e6b6b59SJacob Faibussowitsch 
2840e6b6b59SJacob Faibussowitsch .N ASYNC_API
2850e6b6b59SJacob Faibussowitsch 
286*6797ed33SJacob Faibussowitsch .seealso: `PetscDeviceMalloc()`, `PetscDeviceFree()`, `PetscDeviceDeallocate_Private()`,
287*6797ed33SJacob Faibussowitsch `PetscDeviceArrayCopy()`, `PetscDeviceArrayZero()`, `PetscMemType`
288*6797ed33SJacob Faibussowitsch */
289*6797ed33SJacob Faibussowitsch PetscErrorCode PetscDeviceAllocate_Private(PetscDeviceContext dctx, PetscBool clear, PetscMemType mtype, std::size_t n, std::size_t alignment, void **PETSC_RESTRICT ptr) {
2900e6b6b59SJacob Faibussowitsch   PetscObjectId id = 0;
2910e6b6b59SJacob Faibussowitsch 
2920e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
293*6797ed33SJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
294*6797ed33SJacob Faibussowitsch     const auto is_power_of_2 = [](std::size_t num) { return (num & (num - 1)) == 0; };
295*6797ed33SJacob Faibussowitsch 
296*6797ed33SJacob Faibussowitsch     PetscCheck(alignment != 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested alignment %zu cannot be 0", alignment);
297*6797ed33SJacob Faibussowitsch     PetscCheck(is_power_of_2(alignment), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested alignment %zu must be a power of 2", alignment);
298*6797ed33SJacob Faibussowitsch   }
299*6797ed33SJacob Faibussowitsch   PetscValidPointer(ptr, 6);
3000e6b6b59SJacob Faibussowitsch   *ptr = nullptr;
3010e6b6b59SJacob Faibussowitsch   if (PetscUnlikely(!n)) PetscFunctionReturn(0);
3020e6b6b59SJacob Faibussowitsch   PetscCall(memory_map.register_finalize());
3030e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
3040e6b6b59SJacob Faibussowitsch 
3050e6b6b59SJacob Faibussowitsch   // get our pointer here
3060e6b6b59SJacob Faibussowitsch   if (dctx->ops->memalloc) {
307*6797ed33SJacob Faibussowitsch     PetscUseTypeMethod(dctx, memalloc, clear, mtype, n, alignment, ptr);
3080e6b6b59SJacob Faibussowitsch   } else {
3090e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(mtype), "allocating"));
3100e6b6b59SJacob Faibussowitsch     PetscCall(PetscMallocA(1, clear, __LINE__, PETSC_FUNCTION_NAME, __FILE__, n, ptr));
3110e6b6b59SJacob Faibussowitsch   }
3120e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceRegisterMemory_Private(*ptr, mtype, n, &id));
3130e6b6b59SJacob Faibussowitsch   // Note this is a "write" so that the next dctx to try and read from the pointer has to wait
3140e6b6b59SJacob Faibussowitsch   // for the allocation to be ready
3150e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextMarkIntentFromID(dctx, id, PETSC_MEMORY_ACCESS_WRITE, "memory allocation"));
3160e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
3170e6b6b59SJacob Faibussowitsch }
3180e6b6b59SJacob Faibussowitsch 
319*6797ed33SJacob Faibussowitsch /*
320*6797ed33SJacob Faibussowitsch   PetscDeviceDeallocate_Private - Free device-aware memory
3210e6b6b59SJacob Faibussowitsch 
3220e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
3230e6b6b59SJacob Faibussowitsch 
3240e6b6b59SJacob Faibussowitsch   Input Parameters:
3250e6b6b59SJacob Faibussowitsch + dctx  - The `PetscDeviceContext` used to free the memory
3260e6b6b59SJacob Faibussowitsch - ptr   - The pointer to free
3270e6b6b59SJacob Faibussowitsch 
3280e6b6b59SJacob Faibussowitsch   Notes:
3290e6b6b59SJacob Faibussowitsch   `ptr` must have been allocated using any of `PetscDeviceMalloc()`, `PetscDeviceCalloc()` or
330*6797ed33SJacob Faibussowitsch   `PetscDeviceAllocate_Private()`, or registered with the system via `PetscDeviceRegisterMemory()`.
3310e6b6b59SJacob Faibussowitsch 
3320e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceFree()` over this routine as it automatically sets `ptr`
3330e6b6b59SJacob Faibussowitsch   to `PETSC_NULLPTR` on successful deallocation.
3340e6b6b59SJacob Faibussowitsch 
3350e6b6b59SJacob Faibussowitsch   `ptr` may be `NULL`.
3360e6b6b59SJacob Faibussowitsch 
337*6797ed33SJacob Faibussowitsch   This routine falls back to using `PetscFree()` if PETSc was not configured with device
338*6797ed33SJacob Faibussowitsch   support. The user should note that `PetscFree()` frees only host memory.
339*6797ed33SJacob Faibussowitsch 
3400e6b6b59SJacob Faibussowitsch   DAG representation:
3410e6b6b59SJacob Faibussowitsch .vb
3420e6b6b59SJacob Faibussowitsch   time ->
3430e6b6b59SJacob Faibussowitsch 
3440e6b6b59SJacob Faibussowitsch   -> dctx -/- |= CALL =| - dctx ->
3450e6b6b59SJacob Faibussowitsch   -> ptr -/
3460e6b6b59SJacob Faibussowitsch .ve
3470e6b6b59SJacob Faibussowitsch 
3480e6b6b59SJacob Faibussowitsch   Level: intermediate
3490e6b6b59SJacob Faibussowitsch 
3500e6b6b59SJacob Faibussowitsch .N ASYNC_API
3510e6b6b59SJacob Faibussowitsch 
352*6797ed33SJacob Faibussowitsch .seealso: `PetscDeviceFree()`, `PetscDeviceAllocate_Private()`
353*6797ed33SJacob Faibussowitsch */
354*6797ed33SJacob Faibussowitsch PetscErrorCode PetscDeviceDeallocate_Private(PetscDeviceContext dctx, void *PETSC_RESTRICT ptr) {
3550e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3560e6b6b59SJacob Faibussowitsch   if (ptr) {
3570e6b6b59SJacob Faibussowitsch     auto      &map      = memory_map.map;
3580e6b6b59SJacob Faibussowitsch     const auto found_it = map.find(const_cast<MemoryMap::map_type::key_type>(ptr));
3590e6b6b59SJacob Faibussowitsch 
3600e6b6b59SJacob Faibussowitsch     if (PetscUnlikelyDebug(found_it == map.end())) {
3610e6b6b59SJacob Faibussowitsch       // OK this is a bad pointer, now determine why
3620e6b6b59SJacob Faibussowitsch       const auto it = memory_map.search_for(ptr);
3630e6b6b59SJacob Faibussowitsch 
3640e6b6b59SJacob Faibussowitsch       // if it is map.cend() then no allocation owns it, meaning it was not allocated by us!
365*6797ed33SJacob Faibussowitsch       PetscCheck(it != map.cend(), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Pointer %p was not allocated via PetscDeviceAllocate_Private()", ptr);
3660e6b6b59SJacob Faibussowitsch       // if we are here then we did allocate it but the user has tried to do something along
3670e6b6b59SJacob Faibussowitsch       // the lines of:
3680e6b6b59SJacob Faibussowitsch       //
3690e6b6b59SJacob Faibussowitsch       // allocate(&ptr, size);
3700e6b6b59SJacob Faibussowitsch       // deallocate(ptr+5);
3710e6b6b59SJacob Faibussowitsch       //
3720e6b6b59SJacob Faibussowitsch       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Attempting to deallocate pointer %p which is a suballocation of %p (memtype %s, id %" PetscInt64_FMT ", size %zu bytes)", ptr, it->first, PetscMemTypeToString(it->second.mtype), it->second.id,
3730e6b6b59SJacob Faibussowitsch               it->second.size);
3740e6b6b59SJacob Faibussowitsch     }
3750e6b6b59SJacob Faibussowitsch 
376*6797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
3770e6b6b59SJacob Faibussowitsch     // mark intent BEFORE we free, note we mark as write so that we are made to wait on any
3780e6b6b59SJacob Faibussowitsch     // outstanding reads (don't want to kill the pointer before they are done)
3790e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, found_it->second.id, PETSC_MEMORY_ACCESS_WRITE, "memory deallocation"));
3800e6b6b59SJacob Faibussowitsch     // do free
3810e6b6b59SJacob Faibussowitsch     if (dctx->ops->memfree) {
3820e6b6b59SJacob Faibussowitsch       PetscUseTypeMethod(dctx, memfree, found_it->second.mtype, (void **)&ptr);
3830e6b6b59SJacob Faibussowitsch     } else {
3840e6b6b59SJacob Faibussowitsch       PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(found_it->second.mtype), "freeing"));
3850e6b6b59SJacob Faibussowitsch     }
3860e6b6b59SJacob Faibussowitsch     // if ptr still exists, then the device context could not handle it
3870e6b6b59SJacob Faibussowitsch     if (ptr) PetscCall(PetscFree(ptr));
3880e6b6b59SJacob Faibussowitsch     PetscCallCXX(map.erase(found_it));
3890e6b6b59SJacob Faibussowitsch   }
3900e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
3910e6b6b59SJacob Faibussowitsch }
3920e6b6b59SJacob Faibussowitsch 
3930e6b6b59SJacob Faibussowitsch /*@C
3940e6b6b59SJacob Faibussowitsch   PetscDeviceMemcpy - Copy memory in a device-aware manner
3950e6b6b59SJacob Faibussowitsch 
3960e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
3970e6b6b59SJacob Faibussowitsch 
3980e6b6b59SJacob Faibussowitsch   Input Parameters:
3990e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to copy the memory
4000e6b6b59SJacob Faibussowitsch . dest - The pointer to copy to
4010e6b6b59SJacob Faibussowitsch . src  - The pointer to copy from
4020e6b6b59SJacob Faibussowitsch - n    - The amount (in bytes) to copy
4030e6b6b59SJacob Faibussowitsch 
4040e6b6b59SJacob Faibussowitsch   Notes:
405*6797ed33SJacob Faibussowitsch   Both `dest` and `src` must have been allocated by `PetscDeviceMalloc()` or
406*6797ed33SJacob Faibussowitsch   `PetscDeviceCalloc()`.
4070e6b6b59SJacob Faibussowitsch 
4080e6b6b59SJacob Faibussowitsch   `src` and `dest` cannot overlap.
4090e6b6b59SJacob Faibussowitsch 
4100e6b6b59SJacob Faibussowitsch   If both `src` and `dest` are on the host this routine is fully synchronous.
4110e6b6b59SJacob Faibussowitsch 
4120e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceArrayCopy()` over this routine as it automatically
4130e6b6b59SJacob Faibussowitsch   computes the number of bytes to copy from the size of the pointer types.
4140e6b6b59SJacob Faibussowitsch 
4150e6b6b59SJacob Faibussowitsch   DAG representation:
4160e6b6b59SJacob Faibussowitsch .vb
4170e6b6b59SJacob Faibussowitsch   time ->
4180e6b6b59SJacob Faibussowitsch 
4190e6b6b59SJacob Faibussowitsch   -> dctx - |= CALL =| - dctx ->
4200e6b6b59SJacob Faibussowitsch   -> dest --------------------->
4210e6b6b59SJacob Faibussowitsch   -> src ---------------------->
4220e6b6b59SJacob Faibussowitsch .ve
4230e6b6b59SJacob Faibussowitsch 
4240e6b6b59SJacob Faibussowitsch   Level: intermediate
4250e6b6b59SJacob Faibussowitsch 
4260e6b6b59SJacob Faibussowitsch .N ASYNC_API
4270e6b6b59SJacob Faibussowitsch 
428*6797ed33SJacob Faibussowitsch .seealso: `PetscDeviceArrayCopy()`, `PetscDeviceMalloc()`, `PetscDeviceCalloc()`,
429*6797ed33SJacob Faibussowitsch `PetscDeviceFree()`
4300e6b6b59SJacob Faibussowitsch @*/
4310e6b6b59SJacob Faibussowitsch PetscErrorCode PetscDeviceMemcpy(PetscDeviceContext dctx, void *PETSC_RESTRICT dest, const void *PETSC_RESTRICT src, std::size_t n) {
4320e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
4330e6b6b59SJacob Faibussowitsch   if (!n) PetscFunctionReturn(0);
4340e6b6b59SJacob Faibussowitsch   PetscCheck(dest, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to copy to a NULL pointer");
4350e6b6b59SJacob Faibussowitsch   PetscCheck(src, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to copy from a NULL pointer");
4360e6b6b59SJacob Faibussowitsch   if (dest == src) PetscFunctionReturn(0);
4370e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
4380e6b6b59SJacob Faibussowitsch   {
439*6797ed33SJacob Faibussowitsch     const auto dest_it = memory_map.search_for(dest, true);
440*6797ed33SJacob Faibussowitsch     const auto src_it  = memory_map.search_for(src, true);
441*6797ed33SJacob Faibussowitsch     const auto mode    = PetscMemTypeToDeviceCopyMode(dest_it->second.mtype, src_it->second.mtype);
4420e6b6b59SJacob Faibussowitsch 
4430e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, src_it->second.id, PETSC_MEMORY_ACCESS_READ, "memory copy (src)"));
4440e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, dest_it->second.id, PETSC_MEMORY_ACCESS_WRITE, "memory copy (dest)"));
4450e6b6b59SJacob Faibussowitsch     // perform the copy
4460e6b6b59SJacob Faibussowitsch     if (dctx->ops->memcopy) {
4470e6b6b59SJacob Faibussowitsch       PetscUseTypeMethod(dctx, memcopy, dest, src, n, mode);
448*6797ed33SJacob Faibussowitsch       if (mode == PETSC_DEVICE_COPY_HTOD) {
449*6797ed33SJacob Faibussowitsch         PetscCall(PetscLogCpuToGpu(n));
450*6797ed33SJacob Faibussowitsch       } else if (mode == PETSC_DEVICE_COPY_DTOH) {
451*6797ed33SJacob Faibussowitsch         PetscCall(PetscLogGpuToCpu(n));
452*6797ed33SJacob Faibussowitsch       }
4530e6b6b59SJacob Faibussowitsch     } else {
4540e6b6b59SJacob Faibussowitsch       // REVIEW ME: we might potentially need to sync here if the memory is device-allocated
4550e6b6b59SJacob Faibussowitsch       // (pinned) but being copied by a host dctx
4560e6b6b59SJacob Faibussowitsch       PetscCall(PetscDeviceCheckCapable_Private(dctx, mode == PETSC_DEVICE_COPY_HTOH, "copying"));
4570e6b6b59SJacob Faibussowitsch       PetscCall(PetscMemcpy(dest, src, n));
4580e6b6b59SJacob Faibussowitsch     }
4590e6b6b59SJacob Faibussowitsch   }
4600e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
4610e6b6b59SJacob Faibussowitsch }
4620e6b6b59SJacob Faibussowitsch 
4630e6b6b59SJacob Faibussowitsch /*@C
4640e6b6b59SJacob Faibussowitsch   PetscDeviceMemset - Memset device-aware memory
4650e6b6b59SJacob Faibussowitsch 
4660e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
4670e6b6b59SJacob Faibussowitsch 
4680e6b6b59SJacob Faibussowitsch   Input Parameters:
4690e6b6b59SJacob Faibussowitsch + dctx  - The `PetscDeviceContext` used to memset the memory
4700e6b6b59SJacob Faibussowitsch . ptr   - The pointer to the memory
4710e6b6b59SJacob Faibussowitsch . v     - The value to set
4720e6b6b59SJacob Faibussowitsch - n     - The amount (in bytes) to set
4730e6b6b59SJacob Faibussowitsch 
4740e6b6b59SJacob Faibussowitsch   Notes:
475*6797ed33SJacob Faibussowitsch   `ptr` must have been allocated by `PetscDeviceMalloc()` or `PetscDeviceCalloc()`.
4760e6b6b59SJacob Faibussowitsch 
4770e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceArrayZero()` over this routine as it automatically
4780e6b6b59SJacob Faibussowitsch   computes the number of bytes to copy from the size of the pointer types, though they should
4790e6b6b59SJacob Faibussowitsch   note that it only zeros memory.
4800e6b6b59SJacob Faibussowitsch 
4810e6b6b59SJacob Faibussowitsch   This routine is analogous to `memset()`. That is, this routine copies the value
4820e6b6b59SJacob Faibussowitsch   `static_cast<unsigned char>(v)` into each of the first count characters of the object pointed
4830e6b6b59SJacob Faibussowitsch   to by `dest`.
4840e6b6b59SJacob Faibussowitsch 
4850e6b6b59SJacob Faibussowitsch   If `dest` is on device, this routine is asynchronous.
4860e6b6b59SJacob Faibussowitsch 
4870e6b6b59SJacob Faibussowitsch   DAG representation:
4880e6b6b59SJacob Faibussowitsch .vb
4890e6b6b59SJacob Faibussowitsch   time ->
4900e6b6b59SJacob Faibussowitsch 
4910e6b6b59SJacob Faibussowitsch   -> dctx - |= CALL =| - dctx ->
4920e6b6b59SJacob Faibussowitsch   -> dest --------------------->
4930e6b6b59SJacob Faibussowitsch .ve
4940e6b6b59SJacob Faibussowitsch 
4950e6b6b59SJacob Faibussowitsch   Level: intermediate
4960e6b6b59SJacob Faibussowitsch 
4970e6b6b59SJacob Faibussowitsch .N ASYNC_API
4980e6b6b59SJacob Faibussowitsch 
499*6797ed33SJacob Faibussowitsch .seealso: `PetscDeviceArrayZero()`, `PetscDeviceMalloc()`, `PetscDeviceCalloc()`,
500*6797ed33SJacob Faibussowitsch `PetscDeviceFree()`
5010e6b6b59SJacob Faibussowitsch @*/
5020e6b6b59SJacob Faibussowitsch PetscErrorCode PetscDeviceMemset(PetscDeviceContext dctx, void *ptr, PetscInt v, std::size_t n) {
5030e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5040e6b6b59SJacob Faibussowitsch   if (PetscUnlikely(!n)) PetscFunctionReturn(0);
5050e6b6b59SJacob Faibussowitsch   PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to memset a NULL pointer");
5060e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
5070e6b6b59SJacob Faibussowitsch   {
508*6797ed33SJacob Faibussowitsch     const auto ptr_it = memory_map.search_for(ptr, true);
509*6797ed33SJacob Faibussowitsch     const auto mtype  = ptr_it->second.mtype;
5100e6b6b59SJacob Faibussowitsch 
5110e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, ptr_it->second.id, PETSC_MEMORY_ACCESS_WRITE, "memory set"));
5120e6b6b59SJacob Faibussowitsch     if (dctx->ops->memset) {
513*6797ed33SJacob Faibussowitsch       PetscUseTypeMethod(dctx, memset, mtype, ptr, v, n);
5140e6b6b59SJacob Faibussowitsch     } else {
5150e6b6b59SJacob Faibussowitsch       // REVIEW ME: we might potentially need to sync here if the memory is device-allocated
5160e6b6b59SJacob Faibussowitsch       // (pinned) but being memset by a host dctx
517*6797ed33SJacob Faibussowitsch       PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(mtype), "memsetting"));
5180e6b6b59SJacob Faibussowitsch       std::memset(ptr, static_cast<int>(v), n);
5190e6b6b59SJacob Faibussowitsch     }
5200e6b6b59SJacob Faibussowitsch   }
5210e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
5220e6b6b59SJacob Faibussowitsch }
523