1*0e6b6b59SJacob Faibussowitsch #include <petsc/private/deviceimpl.h> /*I <petscdevice.h> I*/ 2*0e6b6b59SJacob Faibussowitsch 3*0e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/register_finalize.hpp> 4*0e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/array.hpp> 5*0e6b6b59SJacob Faibussowitsch 6*0e6b6b59SJacob Faibussowitsch #include <unordered_map> 7*0e6b6b59SJacob Faibussowitsch #include <algorithm> // std::find_if 8*0e6b6b59SJacob Faibussowitsch #include <cstring> // std::memset 9*0e6b6b59SJacob Faibussowitsch 10*0e6b6b59SJacob Faibussowitsch const char *const PetscDeviceCopyModes[] = {"host_to_host", "device_to_host", "host_to_device", "device_to_device", "auto", "PetscDeviceCopyMode", "PETSC_DEVICE_COPY_", nullptr}; 11*0e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_HTOH) == 0, ""); 12*0e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_DTOH) == 1, ""); 13*0e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_HTOD) == 2, ""); 14*0e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_DTOD) == 3, ""); 15*0e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_AUTO) == 4, ""); 16*0e6b6b59SJacob Faibussowitsch 17*0e6b6b59SJacob Faibussowitsch // ========================================================================================== 18*0e6b6b59SJacob Faibussowitsch // MemoryMap 19*0e6b6b59SJacob Faibussowitsch // 20*0e6b6b59SJacob Faibussowitsch // Since the pointers allocated via PetscDeviceAllocate() may be device pointers we cannot just 21*0e6b6b59SJacob Faibussowitsch // store meta-data within the pointer itself (as we can't dereference them). So instead we need 22*0e6b6b59SJacob Faibussowitsch // to keep an extra map to keep track of them 23*0e6b6b59SJacob Faibussowitsch // 24*0e6b6b59SJacob Faibussowitsch // Each entry maps pointer -> [PetscMemType, PetscObjectId, size] 25*0e6b6b59SJacob Faibussowitsch // ========================================================================================== 26*0e6b6b59SJacob Faibussowitsch 27*0e6b6b59SJacob Faibussowitsch // GCC implementation for std::hash<T*>. LLVM's libc++ is almost 2x slower because they do all 28*0e6b6b59SJacob Faibussowitsch // kinds of complicated murmur hashing, so we make sure to enforce GCC's version. 29*0e6b6b59SJacob Faibussowitsch struct PointerHash { 30*0e6b6b59SJacob Faibussowitsch std::size_t operator()(const void *ptr) const noexcept { return reinterpret_cast<std::size_t>(ptr); } 31*0e6b6b59SJacob Faibussowitsch }; 32*0e6b6b59SJacob Faibussowitsch 33*0e6b6b59SJacob Faibussowitsch class MemoryMap : public Petsc::RegisterFinalizeable<MemoryMap> { 34*0e6b6b59SJacob Faibussowitsch public: 35*0e6b6b59SJacob Faibussowitsch struct PointerAttributes { 36*0e6b6b59SJacob Faibussowitsch PetscMemType mtype{}; // memtype of allocation 37*0e6b6b59SJacob Faibussowitsch PetscObjectId id{}; // id of allocation 38*0e6b6b59SJacob Faibussowitsch std::size_t size{}; // size of allocation (bytes) 39*0e6b6b59SJacob Faibussowitsch 40*0e6b6b59SJacob Faibussowitsch // even though this is a POD and can be aggregate initialized, the STL uses () constructors 41*0e6b6b59SJacob Faibussowitsch // in unordered_map and so we need to provide a trivial contructor... 42*0e6b6b59SJacob Faibussowitsch constexpr PointerAttributes(PetscMemType, PetscObjectId, std::size_t) noexcept; 43*0e6b6b59SJacob Faibussowitsch constexpr PointerAttributes() noexcept = default; 44*0e6b6b59SJacob Faibussowitsch constexpr PointerAttributes(const PointerAttributes &) noexcept = default; 45*0e6b6b59SJacob Faibussowitsch PETSC_CONSTEXPR_14 PointerAttributes &operator=(const PointerAttributes &) noexcept = default; 46*0e6b6b59SJacob Faibussowitsch constexpr PointerAttributes(PointerAttributes &&) noexcept = default; 47*0e6b6b59SJacob Faibussowitsch PETSC_CONSTEXPR_14 PointerAttributes &operator=(PointerAttributes &&) noexcept = default; 48*0e6b6b59SJacob Faibussowitsch 49*0e6b6b59SJacob Faibussowitsch bool operator==(const PointerAttributes &) const noexcept; 50*0e6b6b59SJacob Faibussowitsch 51*0e6b6b59SJacob Faibussowitsch PETSC_NODISCARD bool contains(const void *, const void *) const noexcept; 52*0e6b6b59SJacob Faibussowitsch }; 53*0e6b6b59SJacob Faibussowitsch 54*0e6b6b59SJacob Faibussowitsch using map_type = std::unordered_map<void *, PointerAttributes, PointerHash>; 55*0e6b6b59SJacob Faibussowitsch 56*0e6b6b59SJacob Faibussowitsch map_type map; 57*0e6b6b59SJacob Faibussowitsch 58*0e6b6b59SJacob Faibussowitsch // return the iterator of the allocation containing ptr, or map.cend() if not found 59*0e6b6b59SJacob Faibussowitsch PETSC_NODISCARD map_type::const_iterator search_for(const void *) const noexcept; 60*0e6b6b59SJacob Faibussowitsch 61*0e6b6b59SJacob Faibussowitsch private: 62*0e6b6b59SJacob Faibussowitsch friend class Petsc::RegisterFinalizeable<MemoryMap>; 63*0e6b6b59SJacob Faibussowitsch PETSC_NODISCARD PetscErrorCode register_finalize_() noexcept; 64*0e6b6b59SJacob Faibussowitsch PETSC_NODISCARD PetscErrorCode finalize_() noexcept; 65*0e6b6b59SJacob Faibussowitsch }; 66*0e6b6b59SJacob Faibussowitsch 67*0e6b6b59SJacob Faibussowitsch // ========================================================================================== 68*0e6b6b59SJacob Faibussowitsch // PointerAttributes 69*0e6b6b59SJacob Faibussowitsch // ========================================================================================== 70*0e6b6b59SJacob Faibussowitsch 71*0e6b6b59SJacob Faibussowitsch constexpr MemoryMap::PointerAttributes::PointerAttributes(PetscMemType mtype_, PetscObjectId id_, std::size_t size_) noexcept : mtype(mtype_), id(id_), size(size_) { } 72*0e6b6b59SJacob Faibussowitsch 73*0e6b6b59SJacob Faibussowitsch bool MemoryMap::PointerAttributes::operator==(const PointerAttributes &other) const noexcept { 74*0e6b6b59SJacob Faibussowitsch return mtype == other.mtype && id == other.id && size == other.size; 75*0e6b6b59SJacob Faibussowitsch } 76*0e6b6b59SJacob Faibussowitsch 77*0e6b6b59SJacob Faibussowitsch bool MemoryMap::PointerAttributes::contains(const void *ptr_begin, const void *ptr) const noexcept { 78*0e6b6b59SJacob Faibussowitsch return (ptr >= ptr_begin) && (ptr < (static_cast<const char *>(ptr_begin) + size)); 79*0e6b6b59SJacob Faibussowitsch } 80*0e6b6b59SJacob Faibussowitsch 81*0e6b6b59SJacob Faibussowitsch // ========================================================================================== 82*0e6b6b59SJacob Faibussowitsch // Memory map functions 83*0e6b6b59SJacob Faibussowitsch // ========================================================================================== 84*0e6b6b59SJacob Faibussowitsch 85*0e6b6b59SJacob Faibussowitsch PetscErrorCode MemoryMap::register_finalize_() noexcept { 86*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 87*0e6b6b59SJacob Faibussowitsch // Preallocate, this does give a modest performance bump since unordered_map is so __dog__ 88*0e6b6b59SJacob Faibussowitsch // slow if it needs to rehash. Experiments show that users tend not to have more than 5 or 89*0e6b6b59SJacob Faibussowitsch // so concurrently live pointers lying around. 10 at most. 90*0e6b6b59SJacob Faibussowitsch PetscCallCXX(map.reserve(16)); 91*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 92*0e6b6b59SJacob Faibussowitsch } 93*0e6b6b59SJacob Faibussowitsch 94*0e6b6b59SJacob Faibussowitsch PetscErrorCode MemoryMap::finalize_() noexcept { 95*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 96*0e6b6b59SJacob Faibussowitsch PetscCall(PetscInfo(nullptr, "Finalizing memory map\n")); 97*0e6b6b59SJacob Faibussowitsch PetscCallCXX(map = map_type{}); 98*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 99*0e6b6b59SJacob Faibussowitsch } 100*0e6b6b59SJacob Faibussowitsch 101*0e6b6b59SJacob Faibussowitsch /* 102*0e6b6b59SJacob Faibussowitsch MemoryMap::search_for - retrieve an iterator to the key-value pair for a pointer in the map 103*0e6b6b59SJacob Faibussowitsch 104*0e6b6b59SJacob Faibussowitsch Input Parameter: 105*0e6b6b59SJacob Faibussowitsch . ptr - pointer to search for 106*0e6b6b59SJacob Faibussowitsch 107*0e6b6b59SJacob Faibussowitsch Notes: 108*0e6b6b59SJacob Faibussowitsch Accounts for sub-regions, i.e. if ptr is contained within another pointers region, it returns 109*0e6b6b59SJacob Faibussowitsch the iterator to the super-pointers key-value pair. 110*0e6b6b59SJacob Faibussowitsch 111*0e6b6b59SJacob Faibussowitsch If ptr is not found, returns map.end() 112*0e6b6b59SJacob Faibussowitsch */ 113*0e6b6b59SJacob Faibussowitsch MemoryMap::map_type::const_iterator MemoryMap::search_for(const void *ptr) const noexcept { 114*0e6b6b59SJacob Faibussowitsch const auto end = map.end(); 115*0e6b6b59SJacob Faibussowitsch const auto it = map.find(const_cast<map_type::key_type>(ptr)); 116*0e6b6b59SJacob Faibussowitsch 117*0e6b6b59SJacob Faibussowitsch // ptr was found, and points to an entire block 118*0e6b6b59SJacob Faibussowitsch if (it != end) return it; 119*0e6b6b59SJacob Faibussowitsch // wasn't found, but maybe its part of a block. have to search every block for it 120*0e6b6b59SJacob Faibussowitsch // clang-format off 121*0e6b6b59SJacob Faibussowitsch return std::find_if(map.begin(), end, [ptr](const map_type::const_iterator::value_type &map_it) { 122*0e6b6b59SJacob Faibussowitsch return map_it.second.contains(map_it.first, ptr); 123*0e6b6b59SJacob Faibussowitsch }); 124*0e6b6b59SJacob Faibussowitsch // clang-format on 125*0e6b6b59SJacob Faibussowitsch } 126*0e6b6b59SJacob Faibussowitsch 127*0e6b6b59SJacob Faibussowitsch static MemoryMap memory_map; 128*0e6b6b59SJacob Faibussowitsch 129*0e6b6b59SJacob Faibussowitsch // ========================================================================================== 130*0e6b6b59SJacob Faibussowitsch // Utility functions 131*0e6b6b59SJacob Faibussowitsch // ========================================================================================== 132*0e6b6b59SJacob Faibussowitsch 133*0e6b6b59SJacob Faibussowitsch static PetscErrorCode PetscDeviceCheckCapable_Private(PetscDeviceContext dctx, bool cond, const char descr[]) { 134*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 135*0e6b6b59SJacob 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); 136*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 137*0e6b6b59SJacob Faibussowitsch } 138*0e6b6b59SJacob Faibussowitsch 139*0e6b6b59SJacob Faibussowitsch // A helper utility, since register is called from PetscDeviceRegisterMemory() and 140*0e6b6b59SJacob Faibussowitsch // PetscDevicAllocate(). The latter also needs the generated id, so instead of making it search 141*0e6b6b59SJacob Faibussowitsch // the map again we just return it here 142*0e6b6b59SJacob Faibussowitsch static PetscErrorCode PetscDeviceRegisterMemory_Private(const void *PETSC_RESTRICT ptr, PetscMemType mtype, std::size_t size, PetscObjectId *PETSC_RESTRICT id = nullptr) { 143*0e6b6b59SJacob Faibussowitsch auto &map = memory_map.map; 144*0e6b6b59SJacob Faibussowitsch const auto it = memory_map.search_for(ptr); 145*0e6b6b59SJacob Faibussowitsch 146*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 147*0e6b6b59SJacob Faibussowitsch if (it == map.cend()) { 148*0e6b6b59SJacob Faibussowitsch // pointer was never registered with the map, insert it and bail 149*0e6b6b59SJacob Faibussowitsch const auto newid = PetscObjectNewId_Internal(); 150*0e6b6b59SJacob Faibussowitsch 151*0e6b6b59SJacob Faibussowitsch if (PetscDefined(USE_DEBUG)) { 152*0e6b6b59SJacob Faibussowitsch const auto tmp = MemoryMap::PointerAttributes(mtype, newid, size); 153*0e6b6b59SJacob Faibussowitsch 154*0e6b6b59SJacob Faibussowitsch for (const auto &entry : map) { 155*0e6b6b59SJacob Faibussowitsch // REVIEW ME: maybe this should just be handled... 156*0e6b6b59SJacob 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, 157*0e6b6b59SJacob Faibussowitsch entry.first, PetscMemTypeToString(entry.second.mtype), entry.second.size); 158*0e6b6b59SJacob Faibussowitsch } 159*0e6b6b59SJacob Faibussowitsch } 160*0e6b6b59SJacob Faibussowitsch // clang-format off 161*0e6b6b59SJacob Faibussowitsch if (id) *id = newid; 162*0e6b6b59SJacob Faibussowitsch PetscCallCXX(map.emplace( 163*0e6b6b59SJacob Faibussowitsch std::piecewise_construct, 164*0e6b6b59SJacob Faibussowitsch std::forward_as_tuple(const_cast<MemoryMap::map_type::key_type>(ptr)), 165*0e6b6b59SJacob Faibussowitsch std::forward_as_tuple(mtype, newid, size) 166*0e6b6b59SJacob Faibussowitsch )); 167*0e6b6b59SJacob Faibussowitsch // clang-format on 168*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 169*0e6b6b59SJacob Faibussowitsch } 170*0e6b6b59SJacob Faibussowitsch if (PetscDefined(USE_DEBUG)) { 171*0e6b6b59SJacob Faibussowitsch const auto &old = it->second; 172*0e6b6b59SJacob Faibussowitsch 173*0e6b6b59SJacob 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, 174*0e6b6b59SJacob Faibussowitsch PetscMemTypeToString(old.mtype), old.size, old.id, PetscMemTypeToString(mtype), size, old.id); 175*0e6b6b59SJacob Faibussowitsch } 176*0e6b6b59SJacob Faibussowitsch if (id) *id = it->second.id; 177*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 178*0e6b6b59SJacob Faibussowitsch } 179*0e6b6b59SJacob Faibussowitsch 180*0e6b6b59SJacob Faibussowitsch /*@C 181*0e6b6b59SJacob Faibussowitsch PetscDeviceRegisterMemory - Register a pointer for use with device-aware memory system 182*0e6b6b59SJacob Faibussowitsch 183*0e6b6b59SJacob Faibussowitsch Not Collective 184*0e6b6b59SJacob Faibussowitsch 185*0e6b6b59SJacob Faibussowitsch Input Parameters: 186*0e6b6b59SJacob Faibussowitsch + ptr - The pointer to register 187*0e6b6b59SJacob Faibussowitsch . mtype - The `PetscMemType` of the pointer 188*0e6b6b59SJacob Faibussowitsch - size - The size (in bytes) of the memory region 189*0e6b6b59SJacob Faibussowitsch 190*0e6b6b59SJacob Faibussowitsch Notes: 191*0e6b6b59SJacob Faibussowitsch `ptr` need not point to the beginning of the memory range, however the user should register 192*0e6b6b59SJacob Faibussowitsch the 193*0e6b6b59SJacob Faibussowitsch 194*0e6b6b59SJacob Faibussowitsch It's OK to re-register the same `ptr` repeatedly (subsequent registrations do nothing) 195*0e6b6b59SJacob Faibussowitsch however the given `mtype` and `size` must match the original registration. 196*0e6b6b59SJacob Faibussowitsch 197*0e6b6b59SJacob Faibussowitsch `size` may be 0 (in which case this routine does nothing). 198*0e6b6b59SJacob Faibussowitsch 199*0e6b6b59SJacob Faibussowitsch Level: intermediate 200*0e6b6b59SJacob Faibussowitsch 201*0e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceMalloc()`, `PetscDeviceArrayCopy()`, `PetscDeviceFree()`, 202*0e6b6b59SJacob Faibussowitsch `PetscDeviceArrayZero()` 203*0e6b6b59SJacob Faibussowitsch @*/ 204*0e6b6b59SJacob Faibussowitsch PetscErrorCode PetscDeviceRegisterMemory(const void *PETSC_RESTRICT ptr, PetscMemType mtype, std::size_t size) { 205*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 206*0e6b6b59SJacob Faibussowitsch if (PetscMemTypeHost(mtype)) PetscValidPointer(ptr, 1); 207*0e6b6b59SJacob Faibussowitsch if (PetscUnlikely(!size)) PetscFunctionReturn(0); // there is no point registering empty range 208*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceRegisterMemory_Private(ptr, mtype, size)); 209*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 210*0e6b6b59SJacob Faibussowitsch } 211*0e6b6b59SJacob Faibussowitsch 212*0e6b6b59SJacob Faibussowitsch /*@C 213*0e6b6b59SJacob Faibussowitsch PetscDeviceAllocate - Allocate device-aware memory 214*0e6b6b59SJacob Faibussowitsch 215*0e6b6b59SJacob Faibussowitsch Not Collective, Asynchronous, Auto-dependency aware 216*0e6b6b59SJacob Faibussowitsch 217*0e6b6b59SJacob Faibussowitsch Input Parameters: 218*0e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to allocate the memory 219*0e6b6b59SJacob Faibussowitsch . clear - Whether or not the memory should be zeroed 220*0e6b6b59SJacob Faibussowitsch . mtype - The type of memory to allocate 221*0e6b6b59SJacob Faibussowitsch - n - The amount (in bytes) to allocate 222*0e6b6b59SJacob Faibussowitsch 223*0e6b6b59SJacob Faibussowitsch Output Parameter: 224*0e6b6b59SJacob Faibussowitsch . ptr - The pointer to store the result in 225*0e6b6b59SJacob Faibussowitsch 226*0e6b6b59SJacob Faibussowitsch Notes: 227*0e6b6b59SJacob Faibussowitsch The user should prefer `PetscDeviceMalloc()` over this routine as it automatically computes 228*0e6b6b59SJacob Faibussowitsch the size of the allocation based on the size of the datatype. 229*0e6b6b59SJacob Faibussowitsch 230*0e6b6b59SJacob Faibussowitsch Memory allocated with this function must be freed with `PetscDeviceFree()` or 231*0e6b6b59SJacob Faibussowitsch `PetscDeviceDeallocate()`. 232*0e6b6b59SJacob Faibussowitsch 233*0e6b6b59SJacob Faibussowitsch Note result stored `ptr` is immediately valid and the user may freely inspect or manipulate 234*0e6b6b59SJacob Faibussowitsch its value on function return, i.e.\: 235*0e6b6b59SJacob Faibussowitsch 236*0e6b6b59SJacob Faibussowitsch .vb 237*0e6b6b59SJacob Faibussowitsch PetscInt *ptr; 238*0e6b6b59SJacob Faibussowitsch 239*0e6b6b59SJacob Faibussowitsch PetscDeviceAllocate(dctx, PETSC_FALSE, PETSC_MEMTYPE_DEVICE, 20, (void**)&ptr); 240*0e6b6b59SJacob Faibussowitsch 241*0e6b6b59SJacob Faibussowitsch PetscInt *sub_ptr = ptr + 10; // OK, no need to synchronize 242*0e6b6b59SJacob Faibussowitsch 243*0e6b6b59SJacob Faibussowitsch ptr[0] = 10; // ERROR, directly accessing contents of ptr is undefined until synchronization 244*0e6b6b59SJacob Faibussowitsch .ve 245*0e6b6b59SJacob Faibussowitsch 246*0e6b6b59SJacob Faibussowitsch If `n` is zero, then `ptr` is set to `PETSC_NULLPTR`. 247*0e6b6b59SJacob Faibussowitsch 248*0e6b6b59SJacob Faibussowitsch DAG representation: 249*0e6b6b59SJacob Faibussowitsch .vb 250*0e6b6b59SJacob Faibussowitsch time -> 251*0e6b6b59SJacob Faibussowitsch 252*0e6b6b59SJacob Faibussowitsch -> dctx - |= CALL =| -\- dctx --> 253*0e6b6b59SJacob Faibussowitsch \- ptr -> 254*0e6b6b59SJacob Faibussowitsch .ve 255*0e6b6b59SJacob Faibussowitsch 256*0e6b6b59SJacob Faibussowitsch Level: intermediate 257*0e6b6b59SJacob Faibussowitsch 258*0e6b6b59SJacob Faibussowitsch .N ASYNC_API 259*0e6b6b59SJacob Faibussowitsch 260*0e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceMalloc()`, `PetscGetMemType()`, `PetscDeviceFree()`, 261*0e6b6b59SJacob Faibussowitsch `PetscDeviceDeallocate()`, `PetscDeviceArrayCopy()`, `PetscDeviceArrayZero()` 262*0e6b6b59SJacob Faibussowitsch @*/ 263*0e6b6b59SJacob Faibussowitsch PetscErrorCode PetscDeviceAllocate(PetscDeviceContext dctx, PetscBool clear, PetscMemType mtype, size_t n, void **PETSC_RESTRICT ptr) { 264*0e6b6b59SJacob Faibussowitsch PetscObjectId id = 0; 265*0e6b6b59SJacob Faibussowitsch 266*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 267*0e6b6b59SJacob Faibussowitsch PetscValidPointer(ptr, 5); 268*0e6b6b59SJacob Faibussowitsch *ptr = nullptr; 269*0e6b6b59SJacob Faibussowitsch if (PetscUnlikely(!n)) PetscFunctionReturn(0); 270*0e6b6b59SJacob Faibussowitsch PetscCall(memory_map.register_finalize()); 271*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 272*0e6b6b59SJacob Faibussowitsch 273*0e6b6b59SJacob Faibussowitsch // get our pointer here 274*0e6b6b59SJacob Faibussowitsch if (dctx->ops->memalloc) { 275*0e6b6b59SJacob Faibussowitsch PetscUseTypeMethod(dctx, memalloc, clear, mtype, n, ptr); 276*0e6b6b59SJacob Faibussowitsch } else { 277*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(mtype), "allocating")); 278*0e6b6b59SJacob Faibussowitsch PetscCall(PetscMallocA(1, clear, __LINE__, PETSC_FUNCTION_NAME, __FILE__, n, ptr)); 279*0e6b6b59SJacob Faibussowitsch } 280*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceRegisterMemory_Private(*ptr, mtype, n, &id)); 281*0e6b6b59SJacob Faibussowitsch // Note this is a "write" so that the next dctx to try and read from the pointer has to wait 282*0e6b6b59SJacob Faibussowitsch // for the allocation to be ready 283*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, id, PETSC_MEMORY_ACCESS_WRITE, "memory allocation")); 284*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 285*0e6b6b59SJacob Faibussowitsch } 286*0e6b6b59SJacob Faibussowitsch 287*0e6b6b59SJacob Faibussowitsch /*@C 288*0e6b6b59SJacob Faibussowitsch PetscDeviceDeallocate - Free device-aware memory 289*0e6b6b59SJacob Faibussowitsch 290*0e6b6b59SJacob Faibussowitsch Not Collective, Asynchronous, Auto-dependency aware 291*0e6b6b59SJacob Faibussowitsch 292*0e6b6b59SJacob Faibussowitsch Input Parameters: 293*0e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to free the memory 294*0e6b6b59SJacob Faibussowitsch - ptr - The pointer to free 295*0e6b6b59SJacob Faibussowitsch 296*0e6b6b59SJacob Faibussowitsch Notes: 297*0e6b6b59SJacob Faibussowitsch `ptr` must have been allocated using any of `PetscDeviceMalloc()`, `PetscDeviceCalloc()` or 298*0e6b6b59SJacob Faibussowitsch `PetscDeviceAllocate()`, or registered with the system via `PetscDeviceRegisterMemory()`. 299*0e6b6b59SJacob Faibussowitsch 300*0e6b6b59SJacob Faibussowitsch The user should prefer `PetscDeviceFree()` over this routine as it automatically sets `ptr` 301*0e6b6b59SJacob Faibussowitsch to `PETSC_NULLPTR` on successful deallocation. 302*0e6b6b59SJacob Faibussowitsch 303*0e6b6b59SJacob Faibussowitsch `ptr` may be `NULL`. 304*0e6b6b59SJacob Faibussowitsch 305*0e6b6b59SJacob Faibussowitsch DAG representation: 306*0e6b6b59SJacob Faibussowitsch .vb 307*0e6b6b59SJacob Faibussowitsch time -> 308*0e6b6b59SJacob Faibussowitsch 309*0e6b6b59SJacob Faibussowitsch -> dctx -/- |= CALL =| - dctx -> 310*0e6b6b59SJacob Faibussowitsch -> ptr -/ 311*0e6b6b59SJacob Faibussowitsch .ve 312*0e6b6b59SJacob Faibussowitsch 313*0e6b6b59SJacob Faibussowitsch Level: intermediate 314*0e6b6b59SJacob Faibussowitsch 315*0e6b6b59SJacob Faibussowitsch .N ASYNC_API 316*0e6b6b59SJacob Faibussowitsch 317*0e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceFree()`, `PetscDeviceAllocate()` 318*0e6b6b59SJacob Faibussowitsch @*/ 319*0e6b6b59SJacob Faibussowitsch PetscErrorCode PetscDeviceDeallocate(PetscDeviceContext dctx, void *PETSC_RESTRICT ptr) { 320*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 321*0e6b6b59SJacob Faibussowitsch if (ptr) { 322*0e6b6b59SJacob Faibussowitsch auto &map = memory_map.map; 323*0e6b6b59SJacob Faibussowitsch const auto found_it = map.find(const_cast<MemoryMap::map_type::key_type>(ptr)); 324*0e6b6b59SJacob Faibussowitsch 325*0e6b6b59SJacob Faibussowitsch if (PetscUnlikelyDebug(found_it == map.end())) { 326*0e6b6b59SJacob Faibussowitsch // OK this is a bad pointer, now determine why 327*0e6b6b59SJacob Faibussowitsch const auto it = memory_map.search_for(ptr); 328*0e6b6b59SJacob Faibussowitsch 329*0e6b6b59SJacob Faibussowitsch // if it is map.cend() then no allocation owns it, meaning it was not allocated by us! 330*0e6b6b59SJacob Faibussowitsch PetscCheck(it != map.cend(), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Pointer %p was not allocated via PetscDeviceAllocate()", ptr); 331*0e6b6b59SJacob Faibussowitsch // if we are here then we did allocate it but the user has tried to do something along 332*0e6b6b59SJacob Faibussowitsch // the lines of: 333*0e6b6b59SJacob Faibussowitsch // 334*0e6b6b59SJacob Faibussowitsch // allocate(&ptr, size); 335*0e6b6b59SJacob Faibussowitsch // deallocate(ptr+5); 336*0e6b6b59SJacob Faibussowitsch // 337*0e6b6b59SJacob 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, 338*0e6b6b59SJacob Faibussowitsch it->second.size); 339*0e6b6b59SJacob Faibussowitsch } 340*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 341*0e6b6b59SJacob Faibussowitsch 342*0e6b6b59SJacob Faibussowitsch // mark intent BEFORE we free, note we mark as write so that we are made to wait on any 343*0e6b6b59SJacob Faibussowitsch // outstanding reads (don't want to kill the pointer before they are done) 344*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, found_it->second.id, PETSC_MEMORY_ACCESS_WRITE, "memory deallocation")); 345*0e6b6b59SJacob Faibussowitsch 346*0e6b6b59SJacob Faibussowitsch // do free 347*0e6b6b59SJacob Faibussowitsch if (dctx->ops->memfree) { 348*0e6b6b59SJacob Faibussowitsch PetscUseTypeMethod(dctx, memfree, found_it->second.mtype, (void **)&ptr); 349*0e6b6b59SJacob Faibussowitsch } else { 350*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(found_it->second.mtype), "freeing")); 351*0e6b6b59SJacob Faibussowitsch } 352*0e6b6b59SJacob Faibussowitsch // if ptr still exists, then the device context could not handle it 353*0e6b6b59SJacob Faibussowitsch if (ptr) PetscCall(PetscFree(ptr)); 354*0e6b6b59SJacob Faibussowitsch PetscCallCXX(map.erase(found_it)); 355*0e6b6b59SJacob Faibussowitsch } 356*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 357*0e6b6b59SJacob Faibussowitsch } 358*0e6b6b59SJacob Faibussowitsch 359*0e6b6b59SJacob Faibussowitsch /*@C 360*0e6b6b59SJacob Faibussowitsch PetscDeviceMemcpy - Copy memory in a device-aware manner 361*0e6b6b59SJacob Faibussowitsch 362*0e6b6b59SJacob Faibussowitsch Not Collective, Asynchronous, Auto-dependency aware 363*0e6b6b59SJacob Faibussowitsch 364*0e6b6b59SJacob Faibussowitsch Input Parameters: 365*0e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to copy the memory 366*0e6b6b59SJacob Faibussowitsch . dest - The pointer to copy to 367*0e6b6b59SJacob Faibussowitsch . src - The pointer to copy from 368*0e6b6b59SJacob Faibussowitsch - n - The amount (in bytes) to copy 369*0e6b6b59SJacob Faibussowitsch 370*0e6b6b59SJacob Faibussowitsch Notes: 371*0e6b6b59SJacob Faibussowitsch Both `dest` and `src` must have been allocated by `PetscDeviceAllocate()` or 372*0e6b6b59SJacob Faibussowitsch `PetscDeviceMalloc()` or registered with `PetscDeviceRegisterMemory()`. 373*0e6b6b59SJacob Faibussowitsch 374*0e6b6b59SJacob Faibussowitsch `src` and `dest` cannot overlap. 375*0e6b6b59SJacob Faibussowitsch 376*0e6b6b59SJacob Faibussowitsch If both `src` and `dest` are on the host this routine is fully synchronous. 377*0e6b6b59SJacob Faibussowitsch 378*0e6b6b59SJacob Faibussowitsch The user should prefer `PetscDeviceArrayCopy()` over this routine as it automatically 379*0e6b6b59SJacob Faibussowitsch computes the number of bytes to copy from the size of the pointer types. 380*0e6b6b59SJacob Faibussowitsch 381*0e6b6b59SJacob Faibussowitsch DAG representation: 382*0e6b6b59SJacob Faibussowitsch .vb 383*0e6b6b59SJacob Faibussowitsch time -> 384*0e6b6b59SJacob Faibussowitsch 385*0e6b6b59SJacob Faibussowitsch -> dctx - |= CALL =| - dctx -> 386*0e6b6b59SJacob Faibussowitsch -> dest ---------------------> 387*0e6b6b59SJacob Faibussowitsch -> src ----------------------> 388*0e6b6b59SJacob Faibussowitsch .ve 389*0e6b6b59SJacob Faibussowitsch 390*0e6b6b59SJacob Faibussowitsch Level: intermediate 391*0e6b6b59SJacob Faibussowitsch 392*0e6b6b59SJacob Faibussowitsch .N ASYNC_API 393*0e6b6b59SJacob Faibussowitsch 394*0e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceArrayCopy()`, `PetscDeviceMalloc()`, `PetscDeviceFree()` 395*0e6b6b59SJacob Faibussowitsch @*/ 396*0e6b6b59SJacob Faibussowitsch PetscErrorCode PetscDeviceMemcpy(PetscDeviceContext dctx, void *PETSC_RESTRICT dest, const void *PETSC_RESTRICT src, std::size_t n) { 397*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 398*0e6b6b59SJacob Faibussowitsch if (!n) PetscFunctionReturn(0); 399*0e6b6b59SJacob Faibussowitsch PetscCheck(dest, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to copy to a NULL pointer"); 400*0e6b6b59SJacob Faibussowitsch PetscCheck(src, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to copy from a NULL pointer"); 401*0e6b6b59SJacob Faibussowitsch if (dest == src) PetscFunctionReturn(0); 402*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 403*0e6b6b59SJacob Faibussowitsch 404*0e6b6b59SJacob Faibussowitsch { 405*0e6b6b59SJacob Faibussowitsch const auto dest_it = memory_map.search_for(dest); 406*0e6b6b59SJacob Faibussowitsch const auto src_it = memory_map.search_for(src); 407*0e6b6b59SJacob Faibussowitsch 408*0e6b6b59SJacob Faibussowitsch PetscAssert(dest_it != memory_map.map.cend(), PETSC_COMM_SELF, PETSC_ERR_POINTER, "Destination pointer %p was not registered with the memory tracker, call PetscDeviceRegisterMemory() on it", dest); 409*0e6b6b59SJacob Faibussowitsch PetscAssert(src_it != memory_map.map.cend(), PETSC_COMM_SELF, PETSC_ERR_POINTER, "Source pointer %p was not registered with the memory tracker, call PetscDeviceRegisterMemory() on it", src); 410*0e6b6b59SJacob Faibussowitsch 411*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, src_it->second.id, PETSC_MEMORY_ACCESS_READ, "memory copy (src)")); 412*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, dest_it->second.id, PETSC_MEMORY_ACCESS_WRITE, "memory copy (dest)")); 413*0e6b6b59SJacob Faibussowitsch 414*0e6b6b59SJacob Faibussowitsch const auto mode = PetscMemTypeToDeviceCopyMode(dest_it->second.mtype, src_it->second.mtype); 415*0e6b6b59SJacob Faibussowitsch // perform the copy 416*0e6b6b59SJacob Faibussowitsch if (dctx->ops->memcopy) { 417*0e6b6b59SJacob Faibussowitsch PetscUseTypeMethod(dctx, memcopy, dest, src, n, mode); 418*0e6b6b59SJacob Faibussowitsch } else { 419*0e6b6b59SJacob Faibussowitsch // REVIEW ME: we might potentially need to sync here if the memory is device-allocated 420*0e6b6b59SJacob Faibussowitsch // (pinned) but being copied by a host dctx 421*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceCheckCapable_Private(dctx, mode == PETSC_DEVICE_COPY_HTOH, "copying")); 422*0e6b6b59SJacob Faibussowitsch PetscCall(PetscMemcpy(dest, src, n)); 423*0e6b6b59SJacob Faibussowitsch } 424*0e6b6b59SJacob Faibussowitsch 425*0e6b6b59SJacob Faibussowitsch if (mode == PETSC_DEVICE_COPY_HTOD) { 426*0e6b6b59SJacob Faibussowitsch PetscCall(PetscLogCpuToGpu(n)); 427*0e6b6b59SJacob Faibussowitsch } else if (mode == PETSC_DEVICE_COPY_DTOH) { 428*0e6b6b59SJacob Faibussowitsch PetscCall(PetscLogGpuToCpu(n)); 429*0e6b6b59SJacob Faibussowitsch } 430*0e6b6b59SJacob Faibussowitsch } 431*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 432*0e6b6b59SJacob Faibussowitsch } 433*0e6b6b59SJacob Faibussowitsch 434*0e6b6b59SJacob Faibussowitsch /*@C 435*0e6b6b59SJacob Faibussowitsch PetscDeviceMemset - Memset device-aware memory 436*0e6b6b59SJacob Faibussowitsch 437*0e6b6b59SJacob Faibussowitsch Not Collective, Asynchronous, Auto-dependency aware 438*0e6b6b59SJacob Faibussowitsch 439*0e6b6b59SJacob Faibussowitsch Input Parameters: 440*0e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to memset the memory 441*0e6b6b59SJacob Faibussowitsch . ptr - The pointer to the memory 442*0e6b6b59SJacob Faibussowitsch . v - The value to set 443*0e6b6b59SJacob Faibussowitsch - n - The amount (in bytes) to set 444*0e6b6b59SJacob Faibussowitsch 445*0e6b6b59SJacob Faibussowitsch Notes: 446*0e6b6b59SJacob Faibussowitsch `ptr` must have been allocated by `PetscDeviceAllocate()` or `PetscDeviceMalloc()` or 447*0e6b6b59SJacob Faibussowitsch registered with `PetscDeviceRegisterMemory()`. 448*0e6b6b59SJacob Faibussowitsch 449*0e6b6b59SJacob Faibussowitsch The user should prefer `PetscDeviceArrayZero()` over this routine as it automatically 450*0e6b6b59SJacob Faibussowitsch computes the number of bytes to copy from the size of the pointer types, though they should 451*0e6b6b59SJacob Faibussowitsch note that it only zeros memory. 452*0e6b6b59SJacob Faibussowitsch 453*0e6b6b59SJacob Faibussowitsch This routine is analogous to `memset()`. That is, this routine copies the value 454*0e6b6b59SJacob Faibussowitsch `static_cast<unsigned char>(v)` into each of the first count characters of the object pointed 455*0e6b6b59SJacob Faibussowitsch to by `dest`. 456*0e6b6b59SJacob Faibussowitsch 457*0e6b6b59SJacob Faibussowitsch If `dest` is on device, this routine is asynchronous. 458*0e6b6b59SJacob Faibussowitsch 459*0e6b6b59SJacob Faibussowitsch DAG representation: 460*0e6b6b59SJacob Faibussowitsch .vb 461*0e6b6b59SJacob Faibussowitsch time -> 462*0e6b6b59SJacob Faibussowitsch 463*0e6b6b59SJacob Faibussowitsch -> dctx - |= CALL =| - dctx -> 464*0e6b6b59SJacob Faibussowitsch -> dest ---------------------> 465*0e6b6b59SJacob Faibussowitsch .ve 466*0e6b6b59SJacob Faibussowitsch 467*0e6b6b59SJacob Faibussowitsch Level: intermediate 468*0e6b6b59SJacob Faibussowitsch 469*0e6b6b59SJacob Faibussowitsch .N ASYNC_API 470*0e6b6b59SJacob Faibussowitsch 471*0e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceArrayZero()`, `PetscDeviceMalloc()`, `PetscDeviceFree()` 472*0e6b6b59SJacob Faibussowitsch @*/ 473*0e6b6b59SJacob Faibussowitsch PetscErrorCode PetscDeviceMemset(PetscDeviceContext dctx, void *ptr, PetscInt v, std::size_t n) { 474*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 475*0e6b6b59SJacob Faibussowitsch if (PetscUnlikely(!n)) PetscFunctionReturn(0); 476*0e6b6b59SJacob Faibussowitsch PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to memset a NULL pointer"); 477*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 478*0e6b6b59SJacob Faibussowitsch { 479*0e6b6b59SJacob Faibussowitsch const auto ptr_it = memory_map.search_for(ptr); 480*0e6b6b59SJacob Faibussowitsch 481*0e6b6b59SJacob Faibussowitsch // mark 482*0e6b6b59SJacob Faibussowitsch PetscAssert(ptr_it != memory_map.map.cend(), PETSC_COMM_SELF, PETSC_ERR_POINTER, "Pointer %p was not registered with the memory tracker, call PetscDeviceRegisterMemory() on it", ptr); 483*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, ptr_it->second.id, PETSC_MEMORY_ACCESS_WRITE, "memory set")); 484*0e6b6b59SJacob Faibussowitsch 485*0e6b6b59SJacob Faibussowitsch // set 486*0e6b6b59SJacob Faibussowitsch if (dctx->ops->memset) { 487*0e6b6b59SJacob Faibussowitsch PetscUseTypeMethod(dctx, memset, ptr_it->second.mtype, ptr, v, n); 488*0e6b6b59SJacob Faibussowitsch } else { 489*0e6b6b59SJacob Faibussowitsch // REVIEW ME: we might potentially need to sync here if the memory is device-allocated 490*0e6b6b59SJacob Faibussowitsch // (pinned) but being memset by a host dctx 491*0e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(ptr_it->second.mtype), "memsetting")); 492*0e6b6b59SJacob Faibussowitsch std::memset(ptr, static_cast<int>(v), n); 493*0e6b6b59SJacob Faibussowitsch } 494*0e6b6b59SJacob Faibussowitsch } 495*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 496*0e6b6b59SJacob Faibussowitsch } 497