xref: /petsc/src/sys/objects/device/interface/memory.cxx (revision 089fb57c6fc1c43c66aab835b8f02b081bcf19ea)
10e6b6b59SJacob Faibussowitsch #include <petsc/private/deviceimpl.h> /*I <petscdevice.h> I*/
20e6b6b59SJacob Faibussowitsch 
30e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/register_finalize.hpp>
46797ed33SJacob Faibussowitsch #include <petsc/private/cpp/type_traits.hpp> // integral_value
581c64944SJacob Faibussowitsch #include <petsc/private/cpp/unordered_map.hpp>
60e6b6b59SJacob Faibussowitsch 
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};
11bd2fcf0cSJacob Faibussowitsch static_assert(Petsc::util::to_underlying(PETSC_DEVICE_COPY_HTOH) == 0, "");
12bd2fcf0cSJacob Faibussowitsch static_assert(Petsc::util::to_underlying(PETSC_DEVICE_COPY_DTOH) == 1, "");
13bd2fcf0cSJacob Faibussowitsch static_assert(Petsc::util::to_underlying(PETSC_DEVICE_COPY_HTOD) == 2, "");
14bd2fcf0cSJacob Faibussowitsch static_assert(Petsc::util::to_underlying(PETSC_DEVICE_COPY_DTOD) == 3, "");
15bd2fcf0cSJacob Faibussowitsch static_assert(Petsc::util::to_underlying(PETSC_DEVICE_COPY_AUTO) == 4, "");
160e6b6b59SJacob Faibussowitsch 
173398534bSJacob Faibussowitsch // GCC implementation for std::hash<T*>. LLVM's libc++ is almost 2x slower because they do all
183398534bSJacob Faibussowitsch // kinds of complicated murmur hashing, so we make sure to enforce GCC's version.
193398534bSJacob Faibussowitsch struct PointerHash {
203398534bSJacob Faibussowitsch   template <typename T>
213398534bSJacob Faibussowitsch   PETSC_NODISCARD std::size_t operator()(const T *ptr) const noexcept
223398534bSJacob Faibussowitsch   {
233398534bSJacob Faibussowitsch     return reinterpret_cast<std::size_t>(ptr);
243398534bSJacob Faibussowitsch   }
253398534bSJacob Faibussowitsch };
263398534bSJacob Faibussowitsch 
273398534bSJacob Faibussowitsch // ==========================================================================================
283398534bSJacob Faibussowitsch // PointerAttributes
2981c64944SJacob Faibussowitsch //
3081c64944SJacob Faibussowitsch // A set of attributes for a pointer
313398534bSJacob Faibussowitsch // ==========================================================================================
323398534bSJacob Faibussowitsch 
333398534bSJacob Faibussowitsch struct PointerAttributes {
343398534bSJacob Faibussowitsch   PetscMemType  mtype = PETSC_MEMTYPE_HOST; // memtype of allocation
353398534bSJacob Faibussowitsch   PetscObjectId id    = 0;                  // id of allocation
363398534bSJacob Faibussowitsch   std::size_t   size  = 0;                  // size of allocation (bytes)
373398534bSJacob Faibussowitsch 
383398534bSJacob Faibussowitsch   // even though this is a POD and can be aggregate initialized, the STL uses () constructors
39da81f932SPierre Jolivet   // in unordered_map and so we need to provide a trivial constructor...
400cf3b284SJacob Faibussowitsch   constexpr PointerAttributes() = default;
413398534bSJacob Faibussowitsch   constexpr PointerAttributes(PetscMemType, PetscObjectId, std::size_t) noexcept;
423398534bSJacob Faibussowitsch 
433398534bSJacob Faibussowitsch   bool operator==(const PointerAttributes &) const noexcept;
443398534bSJacob Faibussowitsch 
453398534bSJacob Faibussowitsch   PETSC_NODISCARD bool contains(const void *, const void *) const noexcept;
463398534bSJacob Faibussowitsch };
473398534bSJacob Faibussowitsch 
483398534bSJacob Faibussowitsch // ==========================================================================================
493398534bSJacob Faibussowitsch // PointerAttributes - Public API
503398534bSJacob Faibussowitsch // ==========================================================================================
513398534bSJacob Faibussowitsch 
523398534bSJacob Faibussowitsch inline constexpr PointerAttributes::PointerAttributes(PetscMemType mtype_, PetscObjectId id_, std::size_t size_) noexcept : mtype(mtype_), id(id_), size(size_) { }
533398534bSJacob Faibussowitsch 
543398534bSJacob Faibussowitsch inline bool PointerAttributes::operator==(const PointerAttributes &other) const noexcept
553398534bSJacob Faibussowitsch {
563398534bSJacob Faibussowitsch   return (mtype == other.mtype) && (id == other.id) && (size == other.size);
573398534bSJacob Faibussowitsch }
583398534bSJacob Faibussowitsch 
593398534bSJacob Faibussowitsch /*
603398534bSJacob Faibussowitsch   PointerAttributes::contains - asks and answers the question, does ptr_begin contain ptr
613398534bSJacob Faibussowitsch 
623398534bSJacob Faibussowitsch   Input Parameters:
633398534bSJacob Faibussowitsch + ptr_begin - pointer to the start of the range to check
643398534bSJacob Faibussowitsch - ptr       - the pointer to query
653398534bSJacob Faibussowitsch 
663398534bSJacob Faibussowitsch   Notes:
673398534bSJacob Faibussowitsch   Returns true if ptr falls within ptr_begins range, false otherwise.
683398534bSJacob Faibussowitsch */
693398534bSJacob Faibussowitsch inline bool PointerAttributes::contains(const void *ptr_begin, const void *ptr) const noexcept
703398534bSJacob Faibussowitsch {
713398534bSJacob Faibussowitsch   return (ptr >= ptr_begin) && (ptr < (static_cast<const char *>(ptr_begin) + size));
723398534bSJacob Faibussowitsch }
733398534bSJacob Faibussowitsch 
740e6b6b59SJacob Faibussowitsch // ==========================================================================================
750e6b6b59SJacob Faibussowitsch // MemoryMap
760e6b6b59SJacob Faibussowitsch //
776797ed33SJacob Faibussowitsch // Since the pointers allocated via PetscDeviceAllocate_Private() may be device pointers we
786797ed33SJacob Faibussowitsch // cannot just store meta-data within the pointer itself (as we can't dereference them). So
796797ed33SJacob Faibussowitsch // instead we need to keep an extra map to keep track of them
800e6b6b59SJacob Faibussowitsch //
816797ed33SJacob Faibussowitsch // Each entry maps pointer -> {
826797ed33SJacob Faibussowitsch //   PetscMemType  - The memtype of the pointer
83146a86ebSJacob Faibussowitsch //   PetscObjectId - A unique ID assigned at allocation or registration so auto-dep can
846797ed33SJacob Faibussowitsch //                   identify the pointer
856797ed33SJacob Faibussowitsch //   size          - The size (in bytes) of the allocation
866797ed33SJacob Faibussowitsch // }
870e6b6b59SJacob Faibussowitsch // ==========================================================================================
880e6b6b59SJacob Faibussowitsch 
890e6b6b59SJacob Faibussowitsch class MemoryMap : public Petsc::RegisterFinalizeable<MemoryMap> {
900e6b6b59SJacob Faibussowitsch public:
9181c64944SJacob Faibussowitsch   using map_type = Petsc::UnorderedMap<void *, PointerAttributes, PointerHash>;
920e6b6b59SJacob Faibussowitsch 
933398534bSJacob Faibussowitsch   map_type map{};
940e6b6b59SJacob Faibussowitsch 
956797ed33SJacob Faibussowitsch   PETSC_NODISCARD map_type::const_iterator search_for(const void *, bool = false) const noexcept;
960e6b6b59SJacob Faibussowitsch 
970e6b6b59SJacob Faibussowitsch private:
980e6b6b59SJacob Faibussowitsch   friend class Petsc::RegisterFinalizeable<MemoryMap>;
99*089fb57cSJacob Faibussowitsch   PetscErrorCode register_finalize_() noexcept;
100*089fb57cSJacob Faibussowitsch   PetscErrorCode finalize_() noexcept;
1010e6b6b59SJacob Faibussowitsch };
1020e6b6b59SJacob Faibussowitsch 
1030e6b6b59SJacob Faibussowitsch // ==========================================================================================
1043398534bSJacob Faibussowitsch // MemoryMap - Private API
1050e6b6b59SJacob Faibussowitsch // ==========================================================================================
1060e6b6b59SJacob Faibussowitsch 
107d71ae5a4SJacob Faibussowitsch PetscErrorCode MemoryMap::register_finalize_() noexcept
108d71ae5a4SJacob Faibussowitsch {
1090e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1100e6b6b59SJacob Faibussowitsch   // Preallocate, this does give a modest performance bump since unordered_map is so __dog__
1110e6b6b59SJacob Faibussowitsch   // slow if it needs to rehash. Experiments show that users tend not to have more than 5 or
1120e6b6b59SJacob Faibussowitsch   // so concurrently live pointers lying around. 10 at most.
11381c64944SJacob Faibussowitsch   PetscCall(map.reserve(16));
1143ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1150e6b6b59SJacob Faibussowitsch }
1160e6b6b59SJacob Faibussowitsch 
117d71ae5a4SJacob Faibussowitsch PetscErrorCode MemoryMap::finalize_() noexcept
118d71ae5a4SJacob Faibussowitsch {
1190e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1200e6b6b59SJacob Faibussowitsch   PetscCall(PetscInfo(nullptr, "Finalizing memory map\n"));
1210e6b6b59SJacob Faibussowitsch   PetscCallCXX(map = map_type{});
1223ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1230e6b6b59SJacob Faibussowitsch }
1240e6b6b59SJacob Faibussowitsch 
1256797ed33SJacob Faibussowitsch // ==========================================================================================
1263398534bSJacob Faibussowitsch // MemoryMap - Public API
1276797ed33SJacob Faibussowitsch // ==========================================================================================
1286797ed33SJacob Faibussowitsch 
1290e6b6b59SJacob Faibussowitsch /*
1300e6b6b59SJacob Faibussowitsch   MemoryMap::search_for - retrieve an iterator to the key-value pair for a pointer in the map
1310e6b6b59SJacob Faibussowitsch 
1326797ed33SJacob Faibussowitsch   Input Parameters:
1336797ed33SJacob Faibussowitsch + ptr       - pointer to search for
1346797ed33SJacob Faibussowitsch - must_find - true if an error is raised if the pointer is not found (default: false)
1350e6b6b59SJacob Faibussowitsch 
1360e6b6b59SJacob Faibussowitsch   Notes:
1370e6b6b59SJacob Faibussowitsch   Accounts for sub-regions, i.e. if ptr is contained within another pointers region, it returns
1380e6b6b59SJacob Faibussowitsch   the iterator to the super-pointers key-value pair.
1390e6b6b59SJacob Faibussowitsch 
1406797ed33SJacob Faibussowitsch   If ptr is not found and must_find is false returns map.end(), otherwise raises an error
1410e6b6b59SJacob Faibussowitsch */
142d71ae5a4SJacob Faibussowitsch MemoryMap::map_type::const_iterator MemoryMap::search_for(const void *ptr, bool must_find) const noexcept
143d71ae5a4SJacob Faibussowitsch {
14481c64944SJacob Faibussowitsch   const auto end_it = map.end();
1456797ed33SJacob Faibussowitsch   auto       it     = map.find(const_cast<map_type::key_type>(ptr));
1460e6b6b59SJacob Faibussowitsch 
1470e6b6b59SJacob Faibussowitsch   // ptr was found, and points to an entire block
1486797ed33SJacob Faibussowitsch   PetscFunctionBegin;
14981c64944SJacob Faibussowitsch   if (it != end_it) PetscFunctionReturn(it);
1500e6b6b59SJacob Faibussowitsch   // wasn't found, but maybe its part of a block. have to search every block for it
1510e6b6b59SJacob Faibussowitsch   // clang-format off
15281c64944SJacob Faibussowitsch   it = std::find_if(map.begin(), end_it, [ptr](map_type::const_iterator::reference map_it) {
1530e6b6b59SJacob Faibussowitsch     return map_it.second.contains(map_it.first, ptr);
1540e6b6b59SJacob Faibussowitsch   });
1550e6b6b59SJacob Faibussowitsch   // clang-format on
15681c64944SJacob Faibussowitsch   PetscCheckAbort(!must_find || it != end_it, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Pointer %p was not registered with the memory tracker, call PetscDeviceRegisterMemory() on it", ptr);
15781c64944SJacob Faibussowitsch   PetscFunctionReturn(it);
1580e6b6b59SJacob Faibussowitsch }
1590e6b6b59SJacob Faibussowitsch 
1600e6b6b59SJacob Faibussowitsch static MemoryMap memory_map;
1610e6b6b59SJacob Faibussowitsch 
1620e6b6b59SJacob Faibussowitsch // ==========================================================================================
1630e6b6b59SJacob Faibussowitsch // Utility functions
1640e6b6b59SJacob Faibussowitsch // ==========================================================================================
1650e6b6b59SJacob Faibussowitsch 
166d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDeviceCheckCapable_Private(PetscDeviceContext dctx, bool cond, const char descr[])
167d71ae5a4SJacob Faibussowitsch {
1680e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1690e6b6b59SJacob 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);
1703ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1710e6b6b59SJacob Faibussowitsch }
1720e6b6b59SJacob Faibussowitsch 
1730e6b6b59SJacob Faibussowitsch // A helper utility, since register is called from PetscDeviceRegisterMemory() and
1740e6b6b59SJacob Faibussowitsch // PetscDevicAllocate(). The latter also needs the generated id, so instead of making it search
1750e6b6b59SJacob Faibussowitsch // the map again we just return it here
176d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDeviceRegisterMemory_Private(const void *PETSC_RESTRICT ptr, PetscMemType mtype, std::size_t size, PetscObjectId *PETSC_RESTRICT id = nullptr)
177d71ae5a4SJacob Faibussowitsch {
1780e6b6b59SJacob Faibussowitsch   auto      &map = memory_map.map;
1790e6b6b59SJacob Faibussowitsch   const auto it  = memory_map.search_for(ptr);
1800e6b6b59SJacob Faibussowitsch 
1810e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1820e6b6b59SJacob Faibussowitsch   if (it == map.cend()) {
1830e6b6b59SJacob Faibussowitsch     // pointer was never registered with the map, insert it and bail
1840e6b6b59SJacob Faibussowitsch     const auto newid = PetscObjectNewId_Internal();
1850e6b6b59SJacob Faibussowitsch 
1860e6b6b59SJacob Faibussowitsch     if (PetscDefined(USE_DEBUG)) {
1873398534bSJacob Faibussowitsch       const auto tmp = PointerAttributes(mtype, newid, size);
1880e6b6b59SJacob Faibussowitsch 
1890e6b6b59SJacob Faibussowitsch       for (const auto &entry : map) {
19081c64944SJacob Faibussowitsch         auto &&attr = entry.second;
19181c64944SJacob Faibussowitsch 
1920e6b6b59SJacob Faibussowitsch         // REVIEW ME: maybe this should just be handled...
1930e6b6b59SJacob 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,
19481c64944SJacob Faibussowitsch                    entry.first, PetscMemTypeToString(attr.mtype), attr.size);
1950e6b6b59SJacob Faibussowitsch       }
1960e6b6b59SJacob Faibussowitsch     }
1970e6b6b59SJacob Faibussowitsch     // clang-format off
1980e6b6b59SJacob Faibussowitsch     if (id) *id = newid;
1990e6b6b59SJacob Faibussowitsch     PetscCallCXX(map.emplace(
2000e6b6b59SJacob Faibussowitsch       std::piecewise_construct,
2010e6b6b59SJacob Faibussowitsch       std::forward_as_tuple(const_cast<MemoryMap::map_type::key_type>(ptr)),
2020e6b6b59SJacob Faibussowitsch       std::forward_as_tuple(mtype, newid, size)
2030e6b6b59SJacob Faibussowitsch     ));
2040e6b6b59SJacob Faibussowitsch     // clang-format on
2053ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
2060e6b6b59SJacob Faibussowitsch   }
2070e6b6b59SJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
2080e6b6b59SJacob Faibussowitsch     const auto &old = it->second;
2090e6b6b59SJacob Faibussowitsch 
2103398534bSJacob Faibussowitsch     PetscCheck(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,
2110e6b6b59SJacob Faibussowitsch                PetscMemTypeToString(old.mtype), old.size, old.id, PetscMemTypeToString(mtype), size, old.id);
2120e6b6b59SJacob Faibussowitsch   }
2130e6b6b59SJacob Faibussowitsch   if (id) *id = it->second.id;
2143ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2150e6b6b59SJacob Faibussowitsch }
2160e6b6b59SJacob Faibussowitsch 
2170e6b6b59SJacob Faibussowitsch /*@C
2180e6b6b59SJacob Faibussowitsch   PetscDeviceRegisterMemory - Register a pointer for use with device-aware memory system
2190e6b6b59SJacob Faibussowitsch 
2200e6b6b59SJacob Faibussowitsch   Not Collective
2210e6b6b59SJacob Faibussowitsch 
2220e6b6b59SJacob Faibussowitsch   Input Parameters:
2230e6b6b59SJacob Faibussowitsch + ptr   - The pointer to register
2240e6b6b59SJacob Faibussowitsch . mtype - The `PetscMemType` of the pointer
2250e6b6b59SJacob Faibussowitsch - size  - The size (in bytes) of the memory region
2260e6b6b59SJacob Faibussowitsch 
2270e6b6b59SJacob Faibussowitsch   Notes:
2280e6b6b59SJacob Faibussowitsch   `ptr` need not point to the beginning of the memory range, however the user should register
2290e6b6b59SJacob Faibussowitsch   the
2300e6b6b59SJacob Faibussowitsch 
2310e6b6b59SJacob Faibussowitsch   It's OK to re-register the same `ptr` repeatedly (subsequent registrations do nothing)
2320e6b6b59SJacob Faibussowitsch   however the given `mtype` and `size` must match the original registration.
2330e6b6b59SJacob Faibussowitsch 
2340e6b6b59SJacob Faibussowitsch   `size` may be 0 (in which case this routine does nothing).
2350e6b6b59SJacob Faibussowitsch 
2360e6b6b59SJacob Faibussowitsch   Level: intermediate
2370e6b6b59SJacob Faibussowitsch 
2380e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceMalloc()`, `PetscDeviceArrayCopy()`, `PetscDeviceFree()`,
2390e6b6b59SJacob Faibussowitsch `PetscDeviceArrayZero()`
2400e6b6b59SJacob Faibussowitsch @*/
241d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceRegisterMemory(const void *PETSC_RESTRICT ptr, PetscMemType mtype, std::size_t size)
242d71ae5a4SJacob Faibussowitsch {
2430e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2440e6b6b59SJacob Faibussowitsch   if (PetscMemTypeHost(mtype)) PetscValidPointer(ptr, 1);
2453ba16761SJacob Faibussowitsch   if (PetscUnlikely(!size)) PetscFunctionReturn(PETSC_SUCCESS); // there is no point registering empty range
2460e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceRegisterMemory_Private(ptr, mtype, size));
2473ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2480e6b6b59SJacob Faibussowitsch }
2490e6b6b59SJacob Faibussowitsch 
2506797ed33SJacob Faibussowitsch /*
2516797ed33SJacob Faibussowitsch   PetscDeviceAllocate_Private - Allocate device-aware memory
2520e6b6b59SJacob Faibussowitsch 
2530e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
2540e6b6b59SJacob Faibussowitsch 
2550e6b6b59SJacob Faibussowitsch   Input Parameters:
2560e6b6b59SJacob Faibussowitsch + dctx      - The `PetscDeviceContext` used to allocate the memory
2570e6b6b59SJacob Faibussowitsch . clear     - Whether or not the memory should be zeroed
2580e6b6b59SJacob Faibussowitsch . mtype     - The type of memory to allocate
2596797ed33SJacob Faibussowitsch . n         - The amount (in bytes) to allocate
2606797ed33SJacob Faibussowitsch - alignment - The alignment requirement (in bytes) of the allocated pointer
2610e6b6b59SJacob Faibussowitsch 
2620e6b6b59SJacob Faibussowitsch   Output Parameter:
2630e6b6b59SJacob Faibussowitsch . ptr - The pointer to store the result in
2640e6b6b59SJacob Faibussowitsch 
2650e6b6b59SJacob Faibussowitsch   Notes:
2660e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceMalloc()` over this routine as it automatically computes
2676797ed33SJacob Faibussowitsch   the size of the allocation and alignment based on the size of the datatype.
2680e6b6b59SJacob Faibussowitsch 
2696797ed33SJacob Faibussowitsch   If the user is unsure about `alignment` -- or unable to compute it -- passing
2706797ed33SJacob Faibussowitsch   `PETSC_MEMALIGN` will always work, though the user should beware that this may be quite
2716797ed33SJacob Faibussowitsch   wasteful for very small allocations.
2726797ed33SJacob Faibussowitsch 
2736797ed33SJacob Faibussowitsch   Memory allocated with this function must be freed with `PetscDeviceFree()` (or
2746797ed33SJacob Faibussowitsch   `PetscDeviceDeallocate_Private()`).
2756797ed33SJacob Faibussowitsch 
2766797ed33SJacob Faibussowitsch   If `n` is zero, then `ptr` is set to `PETSC_NULLPTR`.
2776797ed33SJacob Faibussowitsch 
2786797ed33SJacob Faibussowitsch   This routine falls back to using `PetscMalloc1()` or `PetscCalloc1()` (depending on the value
2796797ed33SJacob Faibussowitsch   of `clear`) if PETSc was not configured with device support. The user should note that
2806797ed33SJacob Faibussowitsch   `mtype` and `alignment` are ignored in this case, as these routines allocate only host memory
2816797ed33SJacob Faibussowitsch   aligned to `PETSC_MEMALIGN`.
2820e6b6b59SJacob Faibussowitsch 
2830e6b6b59SJacob Faibussowitsch   Note result stored `ptr` is immediately valid and the user may freely inspect or manipulate
2840e6b6b59SJacob Faibussowitsch   its value on function return, i.e.\:
2850e6b6b59SJacob Faibussowitsch 
2860e6b6b59SJacob Faibussowitsch .vb
2870e6b6b59SJacob Faibussowitsch   PetscInt *ptr;
2880e6b6b59SJacob Faibussowitsch 
2896797ed33SJacob Faibussowitsch   PetscDeviceAllocate_Private(dctx, PETSC_FALSE, PETSC_MEMTYPE_DEVICE, 20, alignof(PetscInt), (void**)&ptr);
2900e6b6b59SJacob Faibussowitsch 
2910e6b6b59SJacob Faibussowitsch   PetscInt *sub_ptr = ptr + 10; // OK, no need to synchronize
2920e6b6b59SJacob Faibussowitsch 
2930e6b6b59SJacob Faibussowitsch   ptr[0] = 10; // ERROR, directly accessing contents of ptr is undefined until synchronization
2940e6b6b59SJacob Faibussowitsch .ve
2950e6b6b59SJacob Faibussowitsch 
2960e6b6b59SJacob Faibussowitsch   DAG representation:
2970e6b6b59SJacob Faibussowitsch .vb
2980e6b6b59SJacob Faibussowitsch   time ->
2990e6b6b59SJacob Faibussowitsch 
3000e6b6b59SJacob Faibussowitsch   -> dctx - |= CALL =| -\- dctx -->
3010e6b6b59SJacob Faibussowitsch                          \- ptr ->
3020e6b6b59SJacob Faibussowitsch .ve
3030e6b6b59SJacob Faibussowitsch 
3040e6b6b59SJacob Faibussowitsch   Level: intermediate
3050e6b6b59SJacob Faibussowitsch 
3060e6b6b59SJacob Faibussowitsch .N ASYNC_API
3070e6b6b59SJacob Faibussowitsch 
3086797ed33SJacob Faibussowitsch .seealso: `PetscDeviceMalloc()`, `PetscDeviceFree()`, `PetscDeviceDeallocate_Private()`,
3096797ed33SJacob Faibussowitsch `PetscDeviceArrayCopy()`, `PetscDeviceArrayZero()`, `PetscMemType`
3106797ed33SJacob Faibussowitsch */
311d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceAllocate_Private(PetscDeviceContext dctx, PetscBool clear, PetscMemType mtype, std::size_t n, std::size_t alignment, void **PETSC_RESTRICT ptr)
312d71ae5a4SJacob Faibussowitsch {
3130e6b6b59SJacob Faibussowitsch   PetscObjectId id = 0;
3140e6b6b59SJacob Faibussowitsch 
3150e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3166797ed33SJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
3176797ed33SJacob Faibussowitsch     const auto is_power_of_2 = [](std::size_t num) { return (num & (num - 1)) == 0; };
3186797ed33SJacob Faibussowitsch 
3196797ed33SJacob Faibussowitsch     PetscCheck(alignment != 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested alignment %zu cannot be 0", alignment);
3206797ed33SJacob Faibussowitsch     PetscCheck(is_power_of_2(alignment), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested alignment %zu must be a power of 2", alignment);
3216797ed33SJacob Faibussowitsch   }
3226797ed33SJacob Faibussowitsch   PetscValidPointer(ptr, 6);
3230e6b6b59SJacob Faibussowitsch   *ptr = nullptr;
3243ba16761SJacob Faibussowitsch   if (PetscUnlikely(!n)) PetscFunctionReturn(PETSC_SUCCESS);
3250e6b6b59SJacob Faibussowitsch   PetscCall(memory_map.register_finalize());
3260e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
3270e6b6b59SJacob Faibussowitsch 
3280e6b6b59SJacob Faibussowitsch   // get our pointer here
3290e6b6b59SJacob Faibussowitsch   if (dctx->ops->memalloc) {
3306797ed33SJacob Faibussowitsch     PetscUseTypeMethod(dctx, memalloc, clear, mtype, n, alignment, ptr);
3310e6b6b59SJacob Faibussowitsch   } else {
3320e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(mtype), "allocating"));
3330e6b6b59SJacob Faibussowitsch     PetscCall(PetscMallocA(1, clear, __LINE__, PETSC_FUNCTION_NAME, __FILE__, n, ptr));
3340e6b6b59SJacob Faibussowitsch   }
3350e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceRegisterMemory_Private(*ptr, mtype, n, &id));
3360e6b6b59SJacob Faibussowitsch   // Note this is a "write" so that the next dctx to try and read from the pointer has to wait
3370e6b6b59SJacob Faibussowitsch   // for the allocation to be ready
3380e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextMarkIntentFromID(dctx, id, PETSC_MEMORY_ACCESS_WRITE, "memory allocation"));
3393ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
3400e6b6b59SJacob Faibussowitsch }
3410e6b6b59SJacob Faibussowitsch 
3426797ed33SJacob Faibussowitsch /*
3436797ed33SJacob Faibussowitsch   PetscDeviceDeallocate_Private - Free device-aware memory
3440e6b6b59SJacob Faibussowitsch 
3450e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
3460e6b6b59SJacob Faibussowitsch 
3470e6b6b59SJacob Faibussowitsch   Input Parameters:
3480e6b6b59SJacob Faibussowitsch + dctx  - The `PetscDeviceContext` used to free the memory
3490e6b6b59SJacob Faibussowitsch - ptr   - The pointer to free
3500e6b6b59SJacob Faibussowitsch 
3510e6b6b59SJacob Faibussowitsch   Notes:
3520e6b6b59SJacob Faibussowitsch   `ptr` must have been allocated using any of `PetscDeviceMalloc()`, `PetscDeviceCalloc()` or
3536797ed33SJacob Faibussowitsch   `PetscDeviceAllocate_Private()`, or registered with the system via `PetscDeviceRegisterMemory()`.
3540e6b6b59SJacob Faibussowitsch 
3550e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceFree()` over this routine as it automatically sets `ptr`
3560e6b6b59SJacob Faibussowitsch   to `PETSC_NULLPTR` on successful deallocation.
3570e6b6b59SJacob Faibussowitsch 
3580e6b6b59SJacob Faibussowitsch   `ptr` may be `NULL`.
3590e6b6b59SJacob Faibussowitsch 
3606797ed33SJacob Faibussowitsch   This routine falls back to using `PetscFree()` if PETSc was not configured with device
3616797ed33SJacob Faibussowitsch   support. The user should note that `PetscFree()` frees only host memory.
3626797ed33SJacob Faibussowitsch 
3630e6b6b59SJacob Faibussowitsch   DAG representation:
3640e6b6b59SJacob Faibussowitsch .vb
3650e6b6b59SJacob Faibussowitsch   time ->
3660e6b6b59SJacob Faibussowitsch 
3670e6b6b59SJacob Faibussowitsch   -> dctx -/- |= CALL =| - dctx ->
3680e6b6b59SJacob Faibussowitsch   -> ptr -/
3690e6b6b59SJacob Faibussowitsch .ve
3700e6b6b59SJacob Faibussowitsch 
3710e6b6b59SJacob Faibussowitsch   Level: intermediate
3720e6b6b59SJacob Faibussowitsch 
3730e6b6b59SJacob Faibussowitsch .N ASYNC_API
3740e6b6b59SJacob Faibussowitsch 
3756797ed33SJacob Faibussowitsch .seealso: `PetscDeviceFree()`, `PetscDeviceAllocate_Private()`
3766797ed33SJacob Faibussowitsch */
377d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceDeallocate_Private(PetscDeviceContext dctx, void *PETSC_RESTRICT ptr)
378d71ae5a4SJacob Faibussowitsch {
3790e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3800e6b6b59SJacob Faibussowitsch   if (ptr) {
3810e6b6b59SJacob Faibussowitsch     auto      &map      = memory_map.map;
3820e6b6b59SJacob Faibussowitsch     const auto found_it = map.find(const_cast<MemoryMap::map_type::key_type>(ptr));
3830e6b6b59SJacob Faibussowitsch 
3840e6b6b59SJacob Faibussowitsch     if (PetscUnlikelyDebug(found_it == map.end())) {
3850e6b6b59SJacob Faibussowitsch       // OK this is a bad pointer, now determine why
3860e6b6b59SJacob Faibussowitsch       const auto it = memory_map.search_for(ptr);
3870e6b6b59SJacob Faibussowitsch 
3880e6b6b59SJacob Faibussowitsch       // if it is map.cend() then no allocation owns it, meaning it was not allocated by us!
3896797ed33SJacob Faibussowitsch       PetscCheck(it != map.cend(), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Pointer %p was not allocated via PetscDeviceAllocate_Private()", ptr);
3900e6b6b59SJacob Faibussowitsch       // if we are here then we did allocate it but the user has tried to do something along
3910e6b6b59SJacob Faibussowitsch       // the lines of:
3920e6b6b59SJacob Faibussowitsch       //
3930e6b6b59SJacob Faibussowitsch       // allocate(&ptr, size);
3940e6b6b59SJacob Faibussowitsch       // deallocate(ptr+5);
3950e6b6b59SJacob Faibussowitsch       //
39681c64944SJacob Faibussowitsch       auto &&attr = it->second;
39781c64944SJacob 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(attr.mtype), attr.id, attr.size);
3980e6b6b59SJacob Faibussowitsch     }
39981c64944SJacob Faibussowitsch     auto &&attr = found_it->second;
4006797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
4010e6b6b59SJacob Faibussowitsch     // mark intent BEFORE we free, note we mark as write so that we are made to wait on any
4020e6b6b59SJacob Faibussowitsch     // outstanding reads (don't want to kill the pointer before they are done)
40381c64944SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, attr.id, PETSC_MEMORY_ACCESS_WRITE, "memory deallocation"));
4040e6b6b59SJacob Faibussowitsch     // do free
4050e6b6b59SJacob Faibussowitsch     if (dctx->ops->memfree) {
40681c64944SJacob Faibussowitsch       PetscUseTypeMethod(dctx, memfree, attr.mtype, (void **)&ptr);
4070e6b6b59SJacob Faibussowitsch     } else {
40881c64944SJacob Faibussowitsch       PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(attr.mtype), "freeing"));
4090e6b6b59SJacob Faibussowitsch     }
4100e6b6b59SJacob Faibussowitsch     // if ptr still exists, then the device context could not handle it
4110e6b6b59SJacob Faibussowitsch     if (ptr) PetscCall(PetscFree(ptr));
4120e6b6b59SJacob Faibussowitsch     PetscCallCXX(map.erase(found_it));
4130e6b6b59SJacob Faibussowitsch   }
4143ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
4150e6b6b59SJacob Faibussowitsch }
4160e6b6b59SJacob Faibussowitsch 
4170e6b6b59SJacob Faibussowitsch /*@C
4180e6b6b59SJacob Faibussowitsch   PetscDeviceMemcpy - Copy memory in a device-aware manner
4190e6b6b59SJacob Faibussowitsch 
4200e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
4210e6b6b59SJacob Faibussowitsch 
4220e6b6b59SJacob Faibussowitsch   Input Parameters:
4230e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to copy the memory
4240e6b6b59SJacob Faibussowitsch . dest - The pointer to copy to
4250e6b6b59SJacob Faibussowitsch . src  - The pointer to copy from
4260e6b6b59SJacob Faibussowitsch - n    - The amount (in bytes) to copy
4270e6b6b59SJacob Faibussowitsch 
4280e6b6b59SJacob Faibussowitsch   Notes:
4296797ed33SJacob Faibussowitsch   Both `dest` and `src` must have been allocated by `PetscDeviceMalloc()` or
4306797ed33SJacob Faibussowitsch   `PetscDeviceCalloc()`.
4310e6b6b59SJacob Faibussowitsch 
4320e6b6b59SJacob Faibussowitsch   `src` and `dest` cannot overlap.
4330e6b6b59SJacob Faibussowitsch 
4340e6b6b59SJacob Faibussowitsch   If both `src` and `dest` are on the host this routine is fully synchronous.
4350e6b6b59SJacob Faibussowitsch 
4360e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceArrayCopy()` over this routine as it automatically
4370e6b6b59SJacob Faibussowitsch   computes the number of bytes to copy from the size of the pointer types.
4380e6b6b59SJacob Faibussowitsch 
4390e6b6b59SJacob Faibussowitsch   DAG representation:
4400e6b6b59SJacob Faibussowitsch .vb
4410e6b6b59SJacob Faibussowitsch   time ->
4420e6b6b59SJacob Faibussowitsch 
4430e6b6b59SJacob Faibussowitsch   -> dctx - |= CALL =| - dctx ->
4440e6b6b59SJacob Faibussowitsch   -> dest --------------------->
4450e6b6b59SJacob Faibussowitsch   -> src ---------------------->
4460e6b6b59SJacob Faibussowitsch .ve
4470e6b6b59SJacob Faibussowitsch 
4480e6b6b59SJacob Faibussowitsch   Level: intermediate
4490e6b6b59SJacob Faibussowitsch 
4500e6b6b59SJacob Faibussowitsch .N ASYNC_API
4510e6b6b59SJacob Faibussowitsch 
4526797ed33SJacob Faibussowitsch .seealso: `PetscDeviceArrayCopy()`, `PetscDeviceMalloc()`, `PetscDeviceCalloc()`,
4536797ed33SJacob Faibussowitsch `PetscDeviceFree()`
4540e6b6b59SJacob Faibussowitsch @*/
455d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceMemcpy(PetscDeviceContext dctx, void *PETSC_RESTRICT dest, const void *PETSC_RESTRICT src, std::size_t n)
456d71ae5a4SJacob Faibussowitsch {
4570e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
4583ba16761SJacob Faibussowitsch   if (!n) PetscFunctionReturn(PETSC_SUCCESS);
4590e6b6b59SJacob Faibussowitsch   PetscCheck(dest, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to copy to a NULL pointer");
4600e6b6b59SJacob Faibussowitsch   PetscCheck(src, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to copy from a NULL pointer");
4613ba16761SJacob Faibussowitsch   if (dest == src) PetscFunctionReturn(PETSC_SUCCESS);
4620e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
4630e6b6b59SJacob Faibussowitsch   {
46481c64944SJacob Faibussowitsch     const auto &dest_attr = memory_map.search_for(dest, true)->second;
46581c64944SJacob Faibussowitsch     const auto &src_attr  = memory_map.search_for(src, true)->second;
46681c64944SJacob Faibussowitsch     const auto  mode      = PetscMemTypeToDeviceCopyMode(dest_attr.mtype, src_attr.mtype);
4670e6b6b59SJacob Faibussowitsch 
46881c64944SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, src_attr.id, PETSC_MEMORY_ACCESS_READ, "memory copy (src)"));
46981c64944SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, dest_attr.id, PETSC_MEMORY_ACCESS_WRITE, "memory copy (dest)"));
4700e6b6b59SJacob Faibussowitsch     // perform the copy
4710e6b6b59SJacob Faibussowitsch     if (dctx->ops->memcopy) {
4720e6b6b59SJacob Faibussowitsch       PetscUseTypeMethod(dctx, memcopy, dest, src, n, mode);
4736797ed33SJacob Faibussowitsch       if (mode == PETSC_DEVICE_COPY_HTOD) {
4746797ed33SJacob Faibussowitsch         PetscCall(PetscLogCpuToGpu(n));
4756797ed33SJacob Faibussowitsch       } else if (mode == PETSC_DEVICE_COPY_DTOH) {
4766797ed33SJacob Faibussowitsch         PetscCall(PetscLogGpuToCpu(n));
4776797ed33SJacob Faibussowitsch       }
4780e6b6b59SJacob Faibussowitsch     } else {
4790e6b6b59SJacob Faibussowitsch       // REVIEW ME: we might potentially need to sync here if the memory is device-allocated
4800e6b6b59SJacob Faibussowitsch       // (pinned) but being copied by a host dctx
4810e6b6b59SJacob Faibussowitsch       PetscCall(PetscDeviceCheckCapable_Private(dctx, mode == PETSC_DEVICE_COPY_HTOH, "copying"));
4820e6b6b59SJacob Faibussowitsch       PetscCall(PetscMemcpy(dest, src, n));
4830e6b6b59SJacob Faibussowitsch     }
4840e6b6b59SJacob Faibussowitsch   }
4853ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
4860e6b6b59SJacob Faibussowitsch }
4870e6b6b59SJacob Faibussowitsch 
4880e6b6b59SJacob Faibussowitsch /*@C
4890e6b6b59SJacob Faibussowitsch   PetscDeviceMemset - Memset device-aware memory
4900e6b6b59SJacob Faibussowitsch 
4910e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
4920e6b6b59SJacob Faibussowitsch 
4930e6b6b59SJacob Faibussowitsch   Input Parameters:
4940e6b6b59SJacob Faibussowitsch + dctx  - The `PetscDeviceContext` used to memset the memory
4950e6b6b59SJacob Faibussowitsch . ptr   - The pointer to the memory
4960e6b6b59SJacob Faibussowitsch . v     - The value to set
4970e6b6b59SJacob Faibussowitsch - n     - The amount (in bytes) to set
4980e6b6b59SJacob Faibussowitsch 
4990e6b6b59SJacob Faibussowitsch   Notes:
5006797ed33SJacob Faibussowitsch   `ptr` must have been allocated by `PetscDeviceMalloc()` or `PetscDeviceCalloc()`.
5010e6b6b59SJacob Faibussowitsch 
5020e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceArrayZero()` over this routine as it automatically
5030e6b6b59SJacob Faibussowitsch   computes the number of bytes to copy from the size of the pointer types, though they should
5040e6b6b59SJacob Faibussowitsch   note that it only zeros memory.
5050e6b6b59SJacob Faibussowitsch 
5060e6b6b59SJacob Faibussowitsch   This routine is analogous to `memset()`. That is, this routine copies the value
5070e6b6b59SJacob Faibussowitsch   `static_cast<unsigned char>(v)` into each of the first count characters of the object pointed
5080e6b6b59SJacob Faibussowitsch   to by `dest`.
5090e6b6b59SJacob Faibussowitsch 
5100e6b6b59SJacob Faibussowitsch   If `dest` is on device, this routine is asynchronous.
5110e6b6b59SJacob Faibussowitsch 
5120e6b6b59SJacob Faibussowitsch   DAG representation:
5130e6b6b59SJacob Faibussowitsch .vb
5140e6b6b59SJacob Faibussowitsch   time ->
5150e6b6b59SJacob Faibussowitsch 
5160e6b6b59SJacob Faibussowitsch   -> dctx - |= CALL =| - dctx ->
5170e6b6b59SJacob Faibussowitsch   -> dest --------------------->
5180e6b6b59SJacob Faibussowitsch .ve
5190e6b6b59SJacob Faibussowitsch 
5200e6b6b59SJacob Faibussowitsch   Level: intermediate
5210e6b6b59SJacob Faibussowitsch 
5220e6b6b59SJacob Faibussowitsch .N ASYNC_API
5230e6b6b59SJacob Faibussowitsch 
5246797ed33SJacob Faibussowitsch .seealso: `PetscDeviceArrayZero()`, `PetscDeviceMalloc()`, `PetscDeviceCalloc()`,
5256797ed33SJacob Faibussowitsch `PetscDeviceFree()`
5260e6b6b59SJacob Faibussowitsch @*/
527d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceMemset(PetscDeviceContext dctx, void *ptr, PetscInt v, std::size_t n)
528d71ae5a4SJacob Faibussowitsch {
5290e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5303ba16761SJacob Faibussowitsch   if (PetscUnlikely(!n)) PetscFunctionReturn(PETSC_SUCCESS);
5310e6b6b59SJacob Faibussowitsch   PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to memset a NULL pointer");
5320e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
5330e6b6b59SJacob Faibussowitsch   {
53481c64944SJacob Faibussowitsch     const auto &attr = memory_map.search_for(ptr, true)->second;
5350e6b6b59SJacob Faibussowitsch 
53681c64944SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, attr.id, PETSC_MEMORY_ACCESS_WRITE, "memory set"));
5370e6b6b59SJacob Faibussowitsch     if (dctx->ops->memset) {
53881c64944SJacob Faibussowitsch       PetscUseTypeMethod(dctx, memset, attr.mtype, ptr, v, n);
5390e6b6b59SJacob Faibussowitsch     } else {
5400e6b6b59SJacob Faibussowitsch       // REVIEW ME: we might potentially need to sync here if the memory is device-allocated
5410e6b6b59SJacob Faibussowitsch       // (pinned) but being memset by a host dctx
54281c64944SJacob Faibussowitsch       PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(attr.mtype), "memsetting"));
5430e6b6b59SJacob Faibussowitsch       std::memset(ptr, static_cast<int>(v), n);
5440e6b6b59SJacob Faibussowitsch     }
5450e6b6b59SJacob Faibussowitsch   }
5463ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5470e6b6b59SJacob Faibussowitsch }
548