xref: /petsc/src/sys/objects/device/interface/memory.cxx (revision c6013d6697bf944c1d88c636ba21f931f774b638)
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 
10*3048253cSJacob Faibussowitsch #include <petsc/private/cpp/object_pool.hpp>
11*3048253cSJacob Faibussowitsch 
12*3048253cSJacob Faibussowitsch namespace Petsc
13*3048253cSJacob Faibussowitsch {
14*3048253cSJacob Faibussowitsch 
15*3048253cSJacob Faibussowitsch namespace memory
16*3048253cSJacob Faibussowitsch {
17*3048253cSJacob Faibussowitsch 
18*3048253cSJacob Faibussowitsch typename PoolAllocated::allocator_type PoolAllocated::pool_{};
19*3048253cSJacob Faibussowitsch 
20*3048253cSJacob Faibussowitsch } // namespace memory
21*3048253cSJacob Faibussowitsch 
22*3048253cSJacob Faibussowitsch } // namespace Petsc
23*3048253cSJacob Faibussowitsch 
240e6b6b59SJacob Faibussowitsch const char *const PetscDeviceCopyModes[] = {"host_to_host", "device_to_host", "host_to_device", "device_to_device", "auto", "PetscDeviceCopyMode", "PETSC_DEVICE_COPY_", nullptr};
25bd2fcf0cSJacob Faibussowitsch static_assert(Petsc::util::to_underlying(PETSC_DEVICE_COPY_HTOH) == 0, "");
26bd2fcf0cSJacob Faibussowitsch static_assert(Petsc::util::to_underlying(PETSC_DEVICE_COPY_DTOH) == 1, "");
27bd2fcf0cSJacob Faibussowitsch static_assert(Petsc::util::to_underlying(PETSC_DEVICE_COPY_HTOD) == 2, "");
28bd2fcf0cSJacob Faibussowitsch static_assert(Petsc::util::to_underlying(PETSC_DEVICE_COPY_DTOD) == 3, "");
29bd2fcf0cSJacob Faibussowitsch static_assert(Petsc::util::to_underlying(PETSC_DEVICE_COPY_AUTO) == 4, "");
300e6b6b59SJacob Faibussowitsch 
313398534bSJacob Faibussowitsch // GCC implementation for std::hash<T*>. LLVM's libc++ is almost 2x slower because they do all
323398534bSJacob Faibussowitsch // kinds of complicated murmur hashing, so we make sure to enforce GCC's version.
333398534bSJacob Faibussowitsch struct PointerHash {
343398534bSJacob Faibussowitsch   template <typename T>
operator ()PointerHash353398534bSJacob Faibussowitsch   PETSC_NODISCARD std::size_t operator()(const T *ptr) const noexcept
363398534bSJacob Faibussowitsch   {
373398534bSJacob Faibussowitsch     return reinterpret_cast<std::size_t>(ptr);
383398534bSJacob Faibussowitsch   }
393398534bSJacob Faibussowitsch };
403398534bSJacob Faibussowitsch 
413398534bSJacob Faibussowitsch // ==========================================================================================
423398534bSJacob Faibussowitsch // PointerAttributes
4381c64944SJacob Faibussowitsch //
4481c64944SJacob Faibussowitsch // A set of attributes for a pointer
453398534bSJacob Faibussowitsch // ==========================================================================================
463398534bSJacob Faibussowitsch 
473398534bSJacob Faibussowitsch struct PointerAttributes {
483398534bSJacob Faibussowitsch   PetscMemType  mtype = PETSC_MEMTYPE_HOST; // memtype of allocation
493398534bSJacob Faibussowitsch   PetscObjectId id    = 0;                  // id of allocation
503398534bSJacob Faibussowitsch   std::size_t   size  = 0;                  // size of allocation (bytes)
513398534bSJacob Faibussowitsch 
523398534bSJacob Faibussowitsch   // even though this is a POD and can be aggregate initialized, the STL uses () constructors
53da81f932SPierre Jolivet   // in unordered_map and so we need to provide a trivial constructor...
540cf3b284SJacob Faibussowitsch   constexpr PointerAttributes() = default;
553398534bSJacob Faibussowitsch   constexpr PointerAttributes(PetscMemType, PetscObjectId, std::size_t) noexcept;
563398534bSJacob Faibussowitsch 
573398534bSJacob Faibussowitsch   bool operator==(const PointerAttributes &) const noexcept;
583398534bSJacob Faibussowitsch 
593398534bSJacob Faibussowitsch   PETSC_NODISCARD bool contains(const void *, const void *) const noexcept;
603398534bSJacob Faibussowitsch };
613398534bSJacob Faibussowitsch 
623398534bSJacob Faibussowitsch // ==========================================================================================
633398534bSJacob Faibussowitsch // PointerAttributes - Public API
643398534bSJacob Faibussowitsch // ==========================================================================================
653398534bSJacob Faibussowitsch 
PointerAttributes(PetscMemType mtype_,PetscObjectId id_,std::size_t size_)663398534bSJacob Faibussowitsch inline constexpr PointerAttributes::PointerAttributes(PetscMemType mtype_, PetscObjectId id_, std::size_t size_) noexcept : mtype(mtype_), id(id_), size(size_) { }
673398534bSJacob Faibussowitsch 
operator ==(const PointerAttributes & other) const683398534bSJacob Faibussowitsch inline bool PointerAttributes::operator==(const PointerAttributes &other) const noexcept
693398534bSJacob Faibussowitsch {
703398534bSJacob Faibussowitsch   return (mtype == other.mtype) && (id == other.id) && (size == other.size);
713398534bSJacob Faibussowitsch }
723398534bSJacob Faibussowitsch 
733398534bSJacob Faibussowitsch /*
743398534bSJacob Faibussowitsch   PointerAttributes::contains - asks and answers the question, does ptr_begin contain ptr
753398534bSJacob Faibussowitsch 
763398534bSJacob Faibussowitsch   Input Parameters:
773398534bSJacob Faibussowitsch + ptr_begin - pointer to the start of the range to check
783398534bSJacob Faibussowitsch - ptr       - the pointer to query
793398534bSJacob Faibussowitsch 
803398534bSJacob Faibussowitsch   Notes:
813398534bSJacob Faibussowitsch   Returns true if ptr falls within ptr_begins range, false otherwise.
823398534bSJacob Faibussowitsch */
contains(const void * ptr_begin,const void * ptr) const833398534bSJacob Faibussowitsch inline bool PointerAttributes::contains(const void *ptr_begin, const void *ptr) const noexcept
843398534bSJacob Faibussowitsch {
853398534bSJacob Faibussowitsch   return (ptr >= ptr_begin) && (ptr < (static_cast<const char *>(ptr_begin) + size));
863398534bSJacob Faibussowitsch }
873398534bSJacob Faibussowitsch 
880e6b6b59SJacob Faibussowitsch // ==========================================================================================
890e6b6b59SJacob Faibussowitsch // MemoryMap
900e6b6b59SJacob Faibussowitsch //
916797ed33SJacob Faibussowitsch // Since the pointers allocated via PetscDeviceAllocate_Private() may be device pointers we
926797ed33SJacob Faibussowitsch // cannot just store meta-data within the pointer itself (as we can't dereference them). So
936797ed33SJacob Faibussowitsch // instead we need to keep an extra map to keep track of them
940e6b6b59SJacob Faibussowitsch //
956797ed33SJacob Faibussowitsch // Each entry maps pointer -> {
966797ed33SJacob Faibussowitsch //   PetscMemType  - The memtype of the pointer
97146a86ebSJacob Faibussowitsch //   PetscObjectId - A unique ID assigned at allocation or registration so auto-dep can
986797ed33SJacob Faibussowitsch //                   identify the pointer
996797ed33SJacob Faibussowitsch //   size          - The size (in bytes) of the allocation
1006797ed33SJacob Faibussowitsch // }
1010e6b6b59SJacob Faibussowitsch // ==========================================================================================
1020e6b6b59SJacob Faibussowitsch 
1030e6b6b59SJacob Faibussowitsch class MemoryMap : public Petsc::RegisterFinalizeable<MemoryMap> {
1040e6b6b59SJacob Faibussowitsch public:
10581c64944SJacob Faibussowitsch   using map_type = Petsc::UnorderedMap<void *, PointerAttributes, PointerHash>;
1060e6b6b59SJacob Faibussowitsch 
1073398534bSJacob Faibussowitsch   map_type map{};
1080e6b6b59SJacob Faibussowitsch 
1096797ed33SJacob Faibussowitsch   PETSC_NODISCARD map_type::const_iterator search_for(const void *, bool = false) const noexcept;
1100e6b6b59SJacob Faibussowitsch 
1110e6b6b59SJacob Faibussowitsch private:
1120e6b6b59SJacob Faibussowitsch   friend class Petsc::RegisterFinalizeable<MemoryMap>;
113089fb57cSJacob Faibussowitsch   PetscErrorCode register_finalize_() noexcept;
114089fb57cSJacob Faibussowitsch   PetscErrorCode finalize_() noexcept;
1150e6b6b59SJacob Faibussowitsch };
1160e6b6b59SJacob Faibussowitsch 
1170e6b6b59SJacob Faibussowitsch // ==========================================================================================
1183398534bSJacob Faibussowitsch // MemoryMap - Private API
1190e6b6b59SJacob Faibussowitsch // ==========================================================================================
1200e6b6b59SJacob Faibussowitsch 
register_finalize_()121d71ae5a4SJacob Faibussowitsch PetscErrorCode MemoryMap::register_finalize_() noexcept
122d71ae5a4SJacob Faibussowitsch {
1230e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1240e6b6b59SJacob Faibussowitsch   // Preallocate, this does give a modest performance bump since unordered_map is so __dog__
1250e6b6b59SJacob Faibussowitsch   // slow if it needs to rehash. Experiments show that users tend not to have more than 5 or
1260e6b6b59SJacob Faibussowitsch   // so concurrently live pointers lying around. 10 at most.
12781c64944SJacob Faibussowitsch   PetscCall(map.reserve(16));
1283ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1290e6b6b59SJacob Faibussowitsch }
1300e6b6b59SJacob Faibussowitsch 
finalize_()131d71ae5a4SJacob Faibussowitsch PetscErrorCode MemoryMap::finalize_() noexcept
132d71ae5a4SJacob Faibussowitsch {
1330e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1340e6b6b59SJacob Faibussowitsch   PetscCall(PetscInfo(nullptr, "Finalizing memory map\n"));
1350e6b6b59SJacob Faibussowitsch   PetscCallCXX(map = map_type{});
1363ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1370e6b6b59SJacob Faibussowitsch }
1380e6b6b59SJacob Faibussowitsch 
1396797ed33SJacob Faibussowitsch // ==========================================================================================
1403398534bSJacob Faibussowitsch // MemoryMap - Public API
1416797ed33SJacob Faibussowitsch // ==========================================================================================
1426797ed33SJacob Faibussowitsch 
1430e6b6b59SJacob Faibussowitsch /*
1440e6b6b59SJacob Faibussowitsch   MemoryMap::search_for - retrieve an iterator to the key-value pair for a pointer in the map
1450e6b6b59SJacob Faibussowitsch 
1466797ed33SJacob Faibussowitsch   Input Parameters:
1476797ed33SJacob Faibussowitsch + ptr       - pointer to search for
1486797ed33SJacob Faibussowitsch - must_find - true if an error is raised if the pointer is not found (default: false)
1490e6b6b59SJacob Faibussowitsch 
1500e6b6b59SJacob Faibussowitsch   Notes:
1510e6b6b59SJacob Faibussowitsch   Accounts for sub-regions, i.e. if ptr is contained within another pointers region, it returns
1520e6b6b59SJacob Faibussowitsch   the iterator to the super-pointers key-value pair.
1530e6b6b59SJacob Faibussowitsch 
1546797ed33SJacob Faibussowitsch   If ptr is not found and must_find is false returns map.end(), otherwise raises an error
1550e6b6b59SJacob Faibussowitsch */
search_for(const void * ptr,bool must_find) const156d71ae5a4SJacob Faibussowitsch MemoryMap::map_type::const_iterator MemoryMap::search_for(const void *ptr, bool must_find) const noexcept
157d71ae5a4SJacob Faibussowitsch {
15881c64944SJacob Faibussowitsch   const auto end_it = map.end();
1596797ed33SJacob Faibussowitsch   auto       it     = map.find(const_cast<map_type::key_type>(ptr));
1600e6b6b59SJacob Faibussowitsch 
1610e6b6b59SJacob Faibussowitsch   // ptr was found, and points to an entire block
1626797ed33SJacob Faibussowitsch   PetscFunctionBegin;
16381c64944SJacob Faibussowitsch   if (it != end_it) PetscFunctionReturn(it);
1640e6b6b59SJacob Faibussowitsch   // wasn't found, but maybe its part of a block. have to search every block for it
1650e6b6b59SJacob Faibussowitsch   // clang-format off
16681c64944SJacob Faibussowitsch   it = std::find_if(map.begin(), end_it, [ptr](map_type::const_iterator::reference map_it) {
1670e6b6b59SJacob Faibussowitsch     return map_it.second.contains(map_it.first, ptr);
1680e6b6b59SJacob Faibussowitsch   });
1690e6b6b59SJacob Faibussowitsch   // clang-format on
17081c64944SJacob 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);
17181c64944SJacob Faibussowitsch   PetscFunctionReturn(it);
1720e6b6b59SJacob Faibussowitsch }
1730e6b6b59SJacob Faibussowitsch 
1740e6b6b59SJacob Faibussowitsch static MemoryMap memory_map;
1750e6b6b59SJacob Faibussowitsch 
1760e6b6b59SJacob Faibussowitsch // ==========================================================================================
1770e6b6b59SJacob Faibussowitsch // Utility functions
1780e6b6b59SJacob Faibussowitsch // ==========================================================================================
1790e6b6b59SJacob Faibussowitsch 
PetscDeviceCheckCapable_Private(PetscDeviceContext dctx,bool cond,const char descr[])180d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDeviceCheckCapable_Private(PetscDeviceContext dctx, bool cond, const char descr[])
181d71ae5a4SJacob Faibussowitsch {
1820e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1830e6b6b59SJacob 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);
1843ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1850e6b6b59SJacob Faibussowitsch }
1860e6b6b59SJacob Faibussowitsch 
1870e6b6b59SJacob Faibussowitsch // A helper utility, since register is called from PetscDeviceRegisterMemory() and
1880e6b6b59SJacob Faibussowitsch // PetscDevicAllocate(). The latter also needs the generated id, so instead of making it search
1890e6b6b59SJacob Faibussowitsch // the map again we just return it here
PetscDeviceRegisterMemory_Private(const void * PETSC_RESTRICT ptr,PetscMemType mtype,std::size_t size,PetscObjectId * PETSC_RESTRICT id=nullptr)190d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDeviceRegisterMemory_Private(const void *PETSC_RESTRICT ptr, PetscMemType mtype, std::size_t size, PetscObjectId *PETSC_RESTRICT id = nullptr)
191d71ae5a4SJacob Faibussowitsch {
1920e6b6b59SJacob Faibussowitsch   auto      &map = memory_map.map;
1930e6b6b59SJacob Faibussowitsch   const auto it  = memory_map.search_for(ptr);
1940e6b6b59SJacob Faibussowitsch 
1950e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1960e6b6b59SJacob Faibussowitsch   if (it == map.cend()) {
1970e6b6b59SJacob Faibussowitsch     // pointer was never registered with the map, insert it and bail
1980e6b6b59SJacob Faibussowitsch     const auto newid = PetscObjectNewId_Internal();
1990e6b6b59SJacob Faibussowitsch 
2000e6b6b59SJacob Faibussowitsch     if (PetscDefined(USE_DEBUG)) {
2013398534bSJacob Faibussowitsch       const auto tmp = PointerAttributes(mtype, newid, size);
2020e6b6b59SJacob Faibussowitsch 
2030e6b6b59SJacob Faibussowitsch       for (const auto &entry : map) {
20481c64944SJacob Faibussowitsch         auto &&attr = entry.second;
20581c64944SJacob Faibussowitsch 
2060e6b6b59SJacob Faibussowitsch         // REVIEW ME: maybe this should just be handled...
2070e6b6b59SJacob 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,
20881c64944SJacob Faibussowitsch                    entry.first, PetscMemTypeToString(attr.mtype), attr.size);
2090e6b6b59SJacob Faibussowitsch       }
2100e6b6b59SJacob Faibussowitsch     }
2110e6b6b59SJacob Faibussowitsch     // clang-format off
2120e6b6b59SJacob Faibussowitsch     if (id) *id = newid;
2130e6b6b59SJacob Faibussowitsch     PetscCallCXX(map.emplace(
2140e6b6b59SJacob Faibussowitsch       std::piecewise_construct,
2150e6b6b59SJacob Faibussowitsch       std::forward_as_tuple(const_cast<MemoryMap::map_type::key_type>(ptr)),
2160e6b6b59SJacob Faibussowitsch       std::forward_as_tuple(mtype, newid, size)
2170e6b6b59SJacob Faibussowitsch     ));
2180e6b6b59SJacob Faibussowitsch     // clang-format on
2193ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
2200e6b6b59SJacob Faibussowitsch   }
2210e6b6b59SJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
2220e6b6b59SJacob Faibussowitsch     const auto &old = it->second;
2230e6b6b59SJacob Faibussowitsch 
2243398534bSJacob 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,
2250e6b6b59SJacob Faibussowitsch                PetscMemTypeToString(old.mtype), old.size, old.id, PetscMemTypeToString(mtype), size, old.id);
2260e6b6b59SJacob Faibussowitsch   }
2270e6b6b59SJacob Faibussowitsch   if (id) *id = it->second.id;
2283ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2290e6b6b59SJacob Faibussowitsch }
2300e6b6b59SJacob Faibussowitsch 
2310e6b6b59SJacob Faibussowitsch /*@C
2320e6b6b59SJacob Faibussowitsch   PetscDeviceRegisterMemory - Register a pointer for use with device-aware memory system
2330e6b6b59SJacob Faibussowitsch 
2340e6b6b59SJacob Faibussowitsch   Not Collective
2350e6b6b59SJacob Faibussowitsch 
2360e6b6b59SJacob Faibussowitsch   Input Parameters:
2370e6b6b59SJacob Faibussowitsch + ptr   - The pointer to register
2380e6b6b59SJacob Faibussowitsch . mtype - The `PetscMemType` of the pointer
2390e6b6b59SJacob Faibussowitsch - size  - The size (in bytes) of the memory region
2400e6b6b59SJacob Faibussowitsch 
2410e6b6b59SJacob Faibussowitsch   Notes:
2420e6b6b59SJacob Faibussowitsch   `ptr` need not point to the beginning of the memory range, however the user should register
2430e6b6b59SJacob Faibussowitsch   the
2440e6b6b59SJacob Faibussowitsch 
2450e6b6b59SJacob Faibussowitsch   It's OK to re-register the same `ptr` repeatedly (subsequent registrations do nothing)
2460e6b6b59SJacob Faibussowitsch   however the given `mtype` and `size` must match the original registration.
2470e6b6b59SJacob Faibussowitsch 
2480e6b6b59SJacob Faibussowitsch   `size` may be 0 (in which case this routine does nothing).
2490e6b6b59SJacob Faibussowitsch 
2500e6b6b59SJacob Faibussowitsch   Level: intermediate
2510e6b6b59SJacob Faibussowitsch 
2520e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceMalloc()`, `PetscDeviceArrayCopy()`, `PetscDeviceFree()`,
2530e6b6b59SJacob Faibussowitsch `PetscDeviceArrayZero()`
2540e6b6b59SJacob Faibussowitsch @*/
PetscDeviceRegisterMemory(const void * PETSC_RESTRICT ptr,PetscMemType mtype,std::size_t size)255d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceRegisterMemory(const void *PETSC_RESTRICT ptr, PetscMemType mtype, std::size_t size)
256d71ae5a4SJacob Faibussowitsch {
2570e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2584f572ea9SToby Isaac   if (PetscMemTypeHost(mtype)) PetscAssertPointer(ptr, 1);
2593ba16761SJacob Faibussowitsch   if (PetscUnlikely(!size)) PetscFunctionReturn(PETSC_SUCCESS); // there is no point registering empty range
2600e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceRegisterMemory_Private(ptr, mtype, size));
2613ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2620e6b6b59SJacob Faibussowitsch }
2630e6b6b59SJacob Faibussowitsch 
2646797ed33SJacob Faibussowitsch /*
2656797ed33SJacob Faibussowitsch   PetscDeviceAllocate_Private - Allocate device-aware memory
2660e6b6b59SJacob Faibussowitsch 
2670e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
2680e6b6b59SJacob Faibussowitsch 
2690e6b6b59SJacob Faibussowitsch   Input Parameters:
2700e6b6b59SJacob Faibussowitsch + dctx      - The `PetscDeviceContext` used to allocate the memory
2710e6b6b59SJacob Faibussowitsch . clear     - Whether or not the memory should be zeroed
2720e6b6b59SJacob Faibussowitsch . mtype     - The type of memory to allocate
2736797ed33SJacob Faibussowitsch . n         - The amount (in bytes) to allocate
2746797ed33SJacob Faibussowitsch - alignment - The alignment requirement (in bytes) of the allocated pointer
2750e6b6b59SJacob Faibussowitsch 
2760e6b6b59SJacob Faibussowitsch   Output Parameter:
2770e6b6b59SJacob Faibussowitsch . ptr - The pointer to store the result in
2780e6b6b59SJacob Faibussowitsch 
2790e6b6b59SJacob Faibussowitsch   Notes:
2800e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceMalloc()` over this routine as it automatically computes
2816797ed33SJacob Faibussowitsch   the size of the allocation and alignment based on the size of the datatype.
2820e6b6b59SJacob Faibussowitsch 
2836797ed33SJacob Faibussowitsch   If the user is unsure about `alignment` -- or unable to compute it -- passing
2846797ed33SJacob Faibussowitsch   `PETSC_MEMALIGN` will always work, though the user should beware that this may be quite
2856797ed33SJacob Faibussowitsch   wasteful for very small allocations.
2866797ed33SJacob Faibussowitsch 
2876797ed33SJacob Faibussowitsch   Memory allocated with this function must be freed with `PetscDeviceFree()` (or
2886797ed33SJacob Faibussowitsch   `PetscDeviceDeallocate_Private()`).
2896797ed33SJacob Faibussowitsch 
2906797ed33SJacob Faibussowitsch   If `n` is zero, then `ptr` is set to `PETSC_NULLPTR`.
2916797ed33SJacob Faibussowitsch 
2926797ed33SJacob Faibussowitsch   This routine falls back to using `PetscMalloc1()` or `PetscCalloc1()` (depending on the value
2936797ed33SJacob Faibussowitsch   of `clear`) if PETSc was not configured with device support. The user should note that
2946797ed33SJacob Faibussowitsch   `mtype` and `alignment` are ignored in this case, as these routines allocate only host memory
2956797ed33SJacob Faibussowitsch   aligned to `PETSC_MEMALIGN`.
2960e6b6b59SJacob Faibussowitsch 
2970e6b6b59SJacob Faibussowitsch   Note result stored `ptr` is immediately valid and the user may freely inspect or manipulate
2980e6b6b59SJacob Faibussowitsch   its value on function return, i.e.\:
2990e6b6b59SJacob Faibussowitsch 
3000e6b6b59SJacob Faibussowitsch .vb
3010e6b6b59SJacob Faibussowitsch   PetscInt *ptr;
3020e6b6b59SJacob Faibussowitsch 
3036797ed33SJacob Faibussowitsch   PetscDeviceAllocate_Private(dctx, PETSC_FALSE, PETSC_MEMTYPE_DEVICE, 20, alignof(PetscInt), (void**)&ptr);
3040e6b6b59SJacob Faibussowitsch 
3050e6b6b59SJacob Faibussowitsch   PetscInt *sub_ptr = ptr + 10; // OK, no need to synchronize
3060e6b6b59SJacob Faibussowitsch 
3070e6b6b59SJacob Faibussowitsch   ptr[0] = 10; // ERROR, directly accessing contents of ptr is undefined until synchronization
3080e6b6b59SJacob Faibussowitsch .ve
3090e6b6b59SJacob Faibussowitsch 
3100e6b6b59SJacob Faibussowitsch   DAG representation:
3110e6b6b59SJacob Faibussowitsch .vb
3120e6b6b59SJacob Faibussowitsch   time ->
3130e6b6b59SJacob Faibussowitsch 
3140e6b6b59SJacob Faibussowitsch   -> dctx - |= CALL =| -\- dctx -->
3150e6b6b59SJacob Faibussowitsch                          \- ptr ->
3160e6b6b59SJacob Faibussowitsch .ve
3170e6b6b59SJacob Faibussowitsch 
3180e6b6b59SJacob Faibussowitsch   Level: intermediate
3190e6b6b59SJacob Faibussowitsch 
3200e6b6b59SJacob Faibussowitsch .N ASYNC_API
3210e6b6b59SJacob Faibussowitsch 
3226797ed33SJacob Faibussowitsch .seealso: `PetscDeviceMalloc()`, `PetscDeviceFree()`, `PetscDeviceDeallocate_Private()`,
3236797ed33SJacob Faibussowitsch `PetscDeviceArrayCopy()`, `PetscDeviceArrayZero()`, `PetscMemType`
3246797ed33SJacob Faibussowitsch */
PetscDeviceAllocate_Private(PetscDeviceContext dctx,PetscBool clear,PetscMemType mtype,std::size_t n,std::size_t alignment,void ** PETSC_RESTRICT ptr)325d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceAllocate_Private(PetscDeviceContext dctx, PetscBool clear, PetscMemType mtype, std::size_t n, std::size_t alignment, void **PETSC_RESTRICT ptr)
326d71ae5a4SJacob Faibussowitsch {
3270e6b6b59SJacob Faibussowitsch   PetscObjectId id = 0;
3280e6b6b59SJacob Faibussowitsch 
3290e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3306797ed33SJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
3316797ed33SJacob Faibussowitsch     const auto is_power_of_2 = [](std::size_t num) { return (num & (num - 1)) == 0; };
3326797ed33SJacob Faibussowitsch 
3336797ed33SJacob Faibussowitsch     PetscCheck(alignment != 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested alignment %zu cannot be 0", alignment);
3346797ed33SJacob Faibussowitsch     PetscCheck(is_power_of_2(alignment), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested alignment %zu must be a power of 2", alignment);
3356797ed33SJacob Faibussowitsch   }
3364f572ea9SToby Isaac   PetscAssertPointer(ptr, 6);
3370e6b6b59SJacob Faibussowitsch   *ptr = nullptr;
3383ba16761SJacob Faibussowitsch   if (PetscUnlikely(!n)) PetscFunctionReturn(PETSC_SUCCESS);
3390e6b6b59SJacob Faibussowitsch   PetscCall(memory_map.register_finalize());
3400e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
3410e6b6b59SJacob Faibussowitsch 
3420e6b6b59SJacob Faibussowitsch   // get our pointer here
3430e6b6b59SJacob Faibussowitsch   if (dctx->ops->memalloc) {
3446797ed33SJacob Faibussowitsch     PetscUseTypeMethod(dctx, memalloc, clear, mtype, n, alignment, ptr);
3450e6b6b59SJacob Faibussowitsch   } else {
3460e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(mtype), "allocating"));
3470e6b6b59SJacob Faibussowitsch     PetscCall(PetscMallocA(1, clear, __LINE__, PETSC_FUNCTION_NAME, __FILE__, n, ptr));
3480e6b6b59SJacob Faibussowitsch   }
3490e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceRegisterMemory_Private(*ptr, mtype, n, &id));
3500e6b6b59SJacob Faibussowitsch   // Note this is a "write" so that the next dctx to try and read from the pointer has to wait
3510e6b6b59SJacob Faibussowitsch   // for the allocation to be ready
3520e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextMarkIntentFromID(dctx, id, PETSC_MEMORY_ACCESS_WRITE, "memory allocation"));
3533ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
3540e6b6b59SJacob Faibussowitsch }
3550e6b6b59SJacob Faibussowitsch 
3566797ed33SJacob Faibussowitsch /*
3576797ed33SJacob Faibussowitsch   PetscDeviceDeallocate_Private - Free device-aware memory
3580e6b6b59SJacob Faibussowitsch 
3590e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
3600e6b6b59SJacob Faibussowitsch 
3610e6b6b59SJacob Faibussowitsch   Input Parameters:
3620e6b6b59SJacob Faibussowitsch + dctx  - The `PetscDeviceContext` used to free the memory
3630e6b6b59SJacob Faibussowitsch - ptr   - The pointer to free
3640e6b6b59SJacob Faibussowitsch 
3652fe279fdSBarry Smith   Level: intermediate
3662fe279fdSBarry Smith 
3670e6b6b59SJacob Faibussowitsch   Notes:
3680e6b6b59SJacob Faibussowitsch   `ptr` must have been allocated using any of `PetscDeviceMalloc()`, `PetscDeviceCalloc()` or
3696797ed33SJacob Faibussowitsch   `PetscDeviceAllocate_Private()`, or registered with the system via `PetscDeviceRegisterMemory()`.
3700e6b6b59SJacob Faibussowitsch 
3710e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceFree()` over this routine as it automatically sets `ptr`
3720e6b6b59SJacob Faibussowitsch   to `PETSC_NULLPTR` on successful deallocation.
3730e6b6b59SJacob Faibussowitsch 
3740e6b6b59SJacob Faibussowitsch   `ptr` may be `NULL`.
3750e6b6b59SJacob Faibussowitsch 
3766797ed33SJacob Faibussowitsch   This routine falls back to using `PetscFree()` if PETSc was not configured with device
3776797ed33SJacob Faibussowitsch   support. The user should note that `PetscFree()` frees only host memory.
3786797ed33SJacob Faibussowitsch 
3790e6b6b59SJacob Faibussowitsch   DAG representation:
3800e6b6b59SJacob Faibussowitsch .vb
3810e6b6b59SJacob Faibussowitsch   time ->
3820e6b6b59SJacob Faibussowitsch 
3830e6b6b59SJacob Faibussowitsch   -> dctx -/- |= CALL =| - dctx ->
3840e6b6b59SJacob Faibussowitsch   -> ptr -/
3850e6b6b59SJacob Faibussowitsch .ve
3860e6b6b59SJacob Faibussowitsch 
3870e6b6b59SJacob Faibussowitsch .N ASYNC_API
3880e6b6b59SJacob Faibussowitsch 
3896797ed33SJacob Faibussowitsch .seealso: `PetscDeviceFree()`, `PetscDeviceAllocate_Private()`
3906797ed33SJacob Faibussowitsch */
PetscDeviceDeallocate_Private(PetscDeviceContext dctx,void * PETSC_RESTRICT ptr)391d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceDeallocate_Private(PetscDeviceContext dctx, void *PETSC_RESTRICT ptr)
392d71ae5a4SJacob Faibussowitsch {
3930e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3940e6b6b59SJacob Faibussowitsch   if (ptr) {
3950e6b6b59SJacob Faibussowitsch     auto      &map      = memory_map.map;
3960e6b6b59SJacob Faibussowitsch     const auto found_it = map.find(const_cast<MemoryMap::map_type::key_type>(ptr));
3970e6b6b59SJacob Faibussowitsch 
3980e6b6b59SJacob Faibussowitsch     if (PetscUnlikelyDebug(found_it == map.end())) {
3990e6b6b59SJacob Faibussowitsch       // OK this is a bad pointer, now determine why
4000e6b6b59SJacob Faibussowitsch       const auto it = memory_map.search_for(ptr);
4010e6b6b59SJacob Faibussowitsch 
4020e6b6b59SJacob Faibussowitsch       // if it is map.cend() then no allocation owns it, meaning it was not allocated by us!
4036797ed33SJacob Faibussowitsch       PetscCheck(it != map.cend(), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Pointer %p was not allocated via PetscDeviceAllocate_Private()", ptr);
4040e6b6b59SJacob Faibussowitsch       // if we are here then we did allocate it but the user has tried to do something along
4050e6b6b59SJacob Faibussowitsch       // the lines of:
4060e6b6b59SJacob Faibussowitsch       //
4070e6b6b59SJacob Faibussowitsch       // allocate(&ptr, size);
4080e6b6b59SJacob Faibussowitsch       // deallocate(ptr+5);
4090e6b6b59SJacob Faibussowitsch       //
41081c64944SJacob Faibussowitsch       auto &&attr = it->second;
41181c64944SJacob 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);
4120e6b6b59SJacob Faibussowitsch     }
41381c64944SJacob Faibussowitsch     auto &&attr = found_it->second;
4146797ed33SJacob Faibussowitsch     PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
4150e6b6b59SJacob Faibussowitsch     // mark intent BEFORE we free, note we mark as write so that we are made to wait on any
4160e6b6b59SJacob Faibussowitsch     // outstanding reads (don't want to kill the pointer before they are done)
41781c64944SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, attr.id, PETSC_MEMORY_ACCESS_WRITE, "memory deallocation"));
4180e6b6b59SJacob Faibussowitsch     // do free
4190e6b6b59SJacob Faibussowitsch     if (dctx->ops->memfree) {
42081c64944SJacob Faibussowitsch       PetscUseTypeMethod(dctx, memfree, attr.mtype, (void **)&ptr);
4210e6b6b59SJacob Faibussowitsch     } else {
42281c64944SJacob Faibussowitsch       PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(attr.mtype), "freeing"));
4230e6b6b59SJacob Faibussowitsch     }
4240e6b6b59SJacob Faibussowitsch     // if ptr still exists, then the device context could not handle it
4250e6b6b59SJacob Faibussowitsch     if (ptr) PetscCall(PetscFree(ptr));
4260e6b6b59SJacob Faibussowitsch     PetscCallCXX(map.erase(found_it));
4270e6b6b59SJacob Faibussowitsch   }
4283ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
4290e6b6b59SJacob Faibussowitsch }
4300e6b6b59SJacob Faibussowitsch 
43110450e9eSJacob Faibussowitsch // PetscClangLinter pragma disable: -fdoc-section-header-unknown
4320e6b6b59SJacob Faibussowitsch /*@C
4330e6b6b59SJacob Faibussowitsch   PetscDeviceMemcpy - Copy memory in a device-aware manner
4340e6b6b59SJacob Faibussowitsch 
4350e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
4360e6b6b59SJacob Faibussowitsch 
4370e6b6b59SJacob Faibussowitsch   Input Parameters:
4380e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to copy the memory
4390e6b6b59SJacob Faibussowitsch . dest - The pointer to copy to
4400e6b6b59SJacob Faibussowitsch . src  - The pointer to copy from
4410e6b6b59SJacob Faibussowitsch - n    - The amount (in bytes) to copy
4420e6b6b59SJacob Faibussowitsch 
4432fe279fdSBarry Smith   Level: intermediate
4442fe279fdSBarry Smith 
4450e6b6b59SJacob Faibussowitsch   Notes:
4466797ed33SJacob Faibussowitsch   Both `dest` and `src` must have been allocated by `PetscDeviceMalloc()` or
4476797ed33SJacob Faibussowitsch   `PetscDeviceCalloc()`.
4480e6b6b59SJacob Faibussowitsch 
4490e6b6b59SJacob Faibussowitsch   `src` and `dest` cannot overlap.
4500e6b6b59SJacob Faibussowitsch 
4510e6b6b59SJacob Faibussowitsch   If both `src` and `dest` are on the host this routine is fully synchronous.
4520e6b6b59SJacob Faibussowitsch 
4530e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceArrayCopy()` over this routine as it automatically
4540e6b6b59SJacob Faibussowitsch   computes the number of bytes to copy from the size of the pointer types.
4550e6b6b59SJacob Faibussowitsch 
4560e6b6b59SJacob Faibussowitsch   DAG representation:
4570e6b6b59SJacob Faibussowitsch .vb
4580e6b6b59SJacob Faibussowitsch   time ->
4590e6b6b59SJacob Faibussowitsch 
4600e6b6b59SJacob Faibussowitsch   -> dctx - |= CALL =| - dctx ->
4610e6b6b59SJacob Faibussowitsch   -> dest --------------------->
4620e6b6b59SJacob Faibussowitsch   -> src ---------------------->
4630e6b6b59SJacob Faibussowitsch .ve
4640e6b6b59SJacob Faibussowitsch 
4650e6b6b59SJacob Faibussowitsch .N ASYNC_API
4660e6b6b59SJacob Faibussowitsch 
4676797ed33SJacob Faibussowitsch .seealso: `PetscDeviceArrayCopy()`, `PetscDeviceMalloc()`, `PetscDeviceCalloc()`,
4686797ed33SJacob Faibussowitsch `PetscDeviceFree()`
4690e6b6b59SJacob Faibussowitsch @*/
PetscDeviceMemcpy(PetscDeviceContext dctx,void * PETSC_RESTRICT dest,const void * PETSC_RESTRICT src,std::size_t n)470d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceMemcpy(PetscDeviceContext dctx, void *PETSC_RESTRICT dest, const void *PETSC_RESTRICT src, std::size_t n)
471d71ae5a4SJacob Faibussowitsch {
4720e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
4733ba16761SJacob Faibussowitsch   if (!n) PetscFunctionReturn(PETSC_SUCCESS);
4740e6b6b59SJacob Faibussowitsch   PetscCheck(dest, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to copy to a NULL pointer");
4750e6b6b59SJacob Faibussowitsch   PetscCheck(src, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to copy from a NULL pointer");
4763ba16761SJacob Faibussowitsch   if (dest == src) PetscFunctionReturn(PETSC_SUCCESS);
4770e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
4780e6b6b59SJacob Faibussowitsch   {
47981c64944SJacob Faibussowitsch     const auto &dest_attr = memory_map.search_for(dest, true)->second;
48081c64944SJacob Faibussowitsch     const auto &src_attr  = memory_map.search_for(src, true)->second;
48181c64944SJacob Faibussowitsch     const auto  mode      = PetscMemTypeToDeviceCopyMode(dest_attr.mtype, src_attr.mtype);
4820e6b6b59SJacob Faibussowitsch 
48381c64944SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, src_attr.id, PETSC_MEMORY_ACCESS_READ, "memory copy (src)"));
48481c64944SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, dest_attr.id, PETSC_MEMORY_ACCESS_WRITE, "memory copy (dest)"));
4850e6b6b59SJacob Faibussowitsch     // perform the copy
4860e6b6b59SJacob Faibussowitsch     if (dctx->ops->memcopy) {
4870e6b6b59SJacob Faibussowitsch       PetscUseTypeMethod(dctx, memcopy, dest, src, n, mode);
4886797ed33SJacob Faibussowitsch       if (mode == PETSC_DEVICE_COPY_HTOD) {
4896797ed33SJacob Faibussowitsch         PetscCall(PetscLogCpuToGpu(n));
4906797ed33SJacob Faibussowitsch       } else if (mode == PETSC_DEVICE_COPY_DTOH) {
4916797ed33SJacob Faibussowitsch         PetscCall(PetscLogGpuToCpu(n));
4926797ed33SJacob Faibussowitsch       }
4930e6b6b59SJacob Faibussowitsch     } else {
4940e6b6b59SJacob Faibussowitsch       // REVIEW ME: we might potentially need to sync here if the memory is device-allocated
4950e6b6b59SJacob Faibussowitsch       // (pinned) but being copied by a host dctx
4960e6b6b59SJacob Faibussowitsch       PetscCall(PetscDeviceCheckCapable_Private(dctx, mode == PETSC_DEVICE_COPY_HTOH, "copying"));
4970e6b6b59SJacob Faibussowitsch       PetscCall(PetscMemcpy(dest, src, n));
4980e6b6b59SJacob Faibussowitsch     }
4990e6b6b59SJacob Faibussowitsch   }
5003ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5010e6b6b59SJacob Faibussowitsch }
5020e6b6b59SJacob Faibussowitsch 
50310450e9eSJacob Faibussowitsch // PetscClangLinter pragma disable: -fdoc-section-header-unknown
5040e6b6b59SJacob Faibussowitsch /*@C
5050e6b6b59SJacob Faibussowitsch   PetscDeviceMemset - Memset device-aware memory
5060e6b6b59SJacob Faibussowitsch 
5070e6b6b59SJacob Faibussowitsch   Not Collective, Asynchronous, Auto-dependency aware
5080e6b6b59SJacob Faibussowitsch 
5090e6b6b59SJacob Faibussowitsch   Input Parameters:
5100e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to memset the memory
5110e6b6b59SJacob Faibussowitsch . ptr  - The pointer to the memory
5120e6b6b59SJacob Faibussowitsch . v    - The value to set
5130e6b6b59SJacob Faibussowitsch - n    - The amount (in bytes) to set
5140e6b6b59SJacob Faibussowitsch 
5152fe279fdSBarry Smith   Level: intermediate
5162fe279fdSBarry Smith 
5170e6b6b59SJacob Faibussowitsch   Notes:
5186797ed33SJacob Faibussowitsch   `ptr` must have been allocated by `PetscDeviceMalloc()` or `PetscDeviceCalloc()`.
5190e6b6b59SJacob Faibussowitsch 
5200e6b6b59SJacob Faibussowitsch   The user should prefer `PetscDeviceArrayZero()` over this routine as it automatically
5210e6b6b59SJacob Faibussowitsch   computes the number of bytes to copy from the size of the pointer types, though they should
5220e6b6b59SJacob Faibussowitsch   note that it only zeros memory.
5230e6b6b59SJacob Faibussowitsch 
5240e6b6b59SJacob Faibussowitsch   This routine is analogous to `memset()`. That is, this routine copies the value
5250e6b6b59SJacob Faibussowitsch   `static_cast<unsigned char>(v)` into each of the first count characters of the object pointed
5260e6b6b59SJacob Faibussowitsch   to by `dest`.
5270e6b6b59SJacob Faibussowitsch 
5280e6b6b59SJacob Faibussowitsch   If `dest` is on device, this routine is asynchronous.
5290e6b6b59SJacob Faibussowitsch 
5300e6b6b59SJacob Faibussowitsch   DAG representation:
5310e6b6b59SJacob Faibussowitsch .vb
5320e6b6b59SJacob Faibussowitsch   time ->
5330e6b6b59SJacob Faibussowitsch 
5340e6b6b59SJacob Faibussowitsch   -> dctx - |= CALL =| - dctx ->
5350e6b6b59SJacob Faibussowitsch   -> dest --------------------->
5360e6b6b59SJacob Faibussowitsch .ve
5370e6b6b59SJacob Faibussowitsch 
5380e6b6b59SJacob Faibussowitsch .N ASYNC_API
5390e6b6b59SJacob Faibussowitsch 
5406797ed33SJacob Faibussowitsch .seealso: `PetscDeviceArrayZero()`, `PetscDeviceMalloc()`, `PetscDeviceCalloc()`,
5416797ed33SJacob Faibussowitsch `PetscDeviceFree()`
5420e6b6b59SJacob Faibussowitsch @*/
PetscDeviceMemset(PetscDeviceContext dctx,void * ptr,PetscInt v,std::size_t n)543d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceMemset(PetscDeviceContext dctx, void *ptr, PetscInt v, std::size_t n)
544d71ae5a4SJacob Faibussowitsch {
5450e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5463ba16761SJacob Faibussowitsch   if (PetscUnlikely(!n)) PetscFunctionReturn(PETSC_SUCCESS);
5470e6b6b59SJacob Faibussowitsch   PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to memset a NULL pointer");
5480e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
5490e6b6b59SJacob Faibussowitsch   {
55081c64944SJacob Faibussowitsch     const auto &attr = memory_map.search_for(ptr, true)->second;
5510e6b6b59SJacob Faibussowitsch 
55281c64944SJacob Faibussowitsch     PetscCall(PetscDeviceContextMarkIntentFromID(dctx, attr.id, PETSC_MEMORY_ACCESS_WRITE, "memory set"));
5530e6b6b59SJacob Faibussowitsch     if (dctx->ops->memset) {
55481c64944SJacob Faibussowitsch       PetscUseTypeMethod(dctx, memset, attr.mtype, ptr, v, n);
5550e6b6b59SJacob Faibussowitsch     } else {
5560e6b6b59SJacob Faibussowitsch       // REVIEW ME: we might potentially need to sync here if the memory is device-allocated
5570e6b6b59SJacob Faibussowitsch       // (pinned) but being memset by a host dctx
55881c64944SJacob Faibussowitsch       PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(attr.mtype), "memsetting"));
5590e6b6b59SJacob Faibussowitsch       std::memset(ptr, static_cast<int>(v), n);
5600e6b6b59SJacob Faibussowitsch     }
5610e6b6b59SJacob Faibussowitsch   }
5623ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5630e6b6b59SJacob Faibussowitsch }
564