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 50e6b6b59SJacob Faibussowitsch 60e6b6b59SJacob Faibussowitsch #include <unordered_map> 70e6b6b59SJacob Faibussowitsch #include <algorithm> // std::find_if 80e6b6b59SJacob Faibussowitsch #include <cstring> // std::memset 90e6b6b59SJacob Faibussowitsch 100e6b6b59SJacob Faibussowitsch const char *const PetscDeviceCopyModes[] = {"host_to_host", "device_to_host", "host_to_device", "device_to_device", "auto", "PetscDeviceCopyMode", "PETSC_DEVICE_COPY_", nullptr}; 110e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_HTOH) == 0, ""); 120e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_DTOH) == 1, ""); 130e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_HTOD) == 2, ""); 140e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_DTOD) == 3, ""); 150e6b6b59SJacob Faibussowitsch static_assert(Petsc::util::integral_value(PETSC_DEVICE_COPY_AUTO) == 4, ""); 160e6b6b59SJacob Faibussowitsch 170e6b6b59SJacob Faibussowitsch // ========================================================================================== 180e6b6b59SJacob Faibussowitsch // MemoryMap 190e6b6b59SJacob Faibussowitsch // 206797ed33SJacob Faibussowitsch // Since the pointers allocated via PetscDeviceAllocate_Private() may be device pointers we 216797ed33SJacob Faibussowitsch // cannot just store meta-data within the pointer itself (as we can't dereference them). So 226797ed33SJacob Faibussowitsch // instead we need to keep an extra map to keep track of them 230e6b6b59SJacob Faibussowitsch // 246797ed33SJacob Faibussowitsch // Each entry maps pointer -> { 256797ed33SJacob Faibussowitsch // PetscMemType - The memtype of the pointer 266797ed33SJacob Faibussowitsch // PetscObjectId - A unique ID assigned at allocation or registratrion so auto-dep can 276797ed33SJacob Faibussowitsch // identify the pointer 286797ed33SJacob Faibussowitsch // size - The size (in bytes) of the allocation 296797ed33SJacob Faibussowitsch // } 300e6b6b59SJacob Faibussowitsch // ========================================================================================== 310e6b6b59SJacob Faibussowitsch 320e6b6b59SJacob Faibussowitsch // GCC implementation for std::hash<T*>. LLVM's libc++ is almost 2x slower because they do all 330e6b6b59SJacob Faibussowitsch // kinds of complicated murmur hashing, so we make sure to enforce GCC's version. 340e6b6b59SJacob Faibussowitsch struct PointerHash { 356797ed33SJacob Faibussowitsch template <typename T> 36*d71ae5a4SJacob Faibussowitsch PETSC_NODISCARD std::size_t operator()(const T *ptr) const noexcept 37*d71ae5a4SJacob Faibussowitsch { 386797ed33SJacob Faibussowitsch return reinterpret_cast<std::size_t>(ptr); 396797ed33SJacob Faibussowitsch } 400e6b6b59SJacob Faibussowitsch }; 410e6b6b59SJacob Faibussowitsch 420e6b6b59SJacob Faibussowitsch class MemoryMap : public Petsc::RegisterFinalizeable<MemoryMap> { 430e6b6b59SJacob Faibussowitsch public: 440e6b6b59SJacob Faibussowitsch struct PointerAttributes { 450e6b6b59SJacob Faibussowitsch PetscMemType mtype{}; // memtype of allocation 460e6b6b59SJacob Faibussowitsch PetscObjectId id{}; // id of allocation 470e6b6b59SJacob Faibussowitsch std::size_t size{}; // size of allocation (bytes) 480e6b6b59SJacob Faibussowitsch 490e6b6b59SJacob Faibussowitsch // even though this is a POD and can be aggregate initialized, the STL uses () constructors 500e6b6b59SJacob Faibussowitsch // in unordered_map and so we need to provide a trivial contructor... 510e6b6b59SJacob Faibussowitsch constexpr PointerAttributes(PetscMemType, PetscObjectId, std::size_t) noexcept; 520e6b6b59SJacob Faibussowitsch constexpr PointerAttributes() noexcept = default; 530e6b6b59SJacob Faibussowitsch constexpr PointerAttributes(const PointerAttributes &) noexcept = default; 540e6b6b59SJacob Faibussowitsch PETSC_CONSTEXPR_14 PointerAttributes &operator=(const PointerAttributes &) noexcept = default; 550e6b6b59SJacob Faibussowitsch constexpr PointerAttributes(PointerAttributes &&) noexcept = default; 560e6b6b59SJacob Faibussowitsch PETSC_CONSTEXPR_14 PointerAttributes &operator=(PointerAttributes &&) noexcept = default; 570e6b6b59SJacob Faibussowitsch 580e6b6b59SJacob Faibussowitsch bool operator==(const PointerAttributes &) const noexcept; 590e6b6b59SJacob Faibussowitsch 600e6b6b59SJacob Faibussowitsch PETSC_NODISCARD bool contains(const void *, const void *) const noexcept; 610e6b6b59SJacob Faibussowitsch }; 620e6b6b59SJacob Faibussowitsch 630e6b6b59SJacob Faibussowitsch using map_type = std::unordered_map<void *, PointerAttributes, PointerHash>; 640e6b6b59SJacob Faibussowitsch 650e6b6b59SJacob Faibussowitsch map_type map; 660e6b6b59SJacob Faibussowitsch 670e6b6b59SJacob Faibussowitsch // return the iterator of the allocation containing ptr, or map.cend() if not found 686797ed33SJacob Faibussowitsch PETSC_NODISCARD map_type::const_iterator search_for(const void *, bool = false) const noexcept; 690e6b6b59SJacob Faibussowitsch 700e6b6b59SJacob Faibussowitsch private: 710e6b6b59SJacob Faibussowitsch friend class Petsc::RegisterFinalizeable<MemoryMap>; 720e6b6b59SJacob Faibussowitsch PETSC_NODISCARD PetscErrorCode register_finalize_() noexcept; 730e6b6b59SJacob Faibussowitsch PETSC_NODISCARD PetscErrorCode finalize_() noexcept; 740e6b6b59SJacob Faibussowitsch }; 750e6b6b59SJacob Faibussowitsch 760e6b6b59SJacob Faibussowitsch // ========================================================================================== 770e6b6b59SJacob Faibussowitsch // PointerAttributes 780e6b6b59SJacob Faibussowitsch // ========================================================================================== 790e6b6b59SJacob Faibussowitsch 800e6b6b59SJacob Faibussowitsch constexpr MemoryMap::PointerAttributes::PointerAttributes(PetscMemType mtype_, PetscObjectId id_, std::size_t size_) noexcept : mtype(mtype_), id(id_), size(size_) { } 810e6b6b59SJacob Faibussowitsch 82*d71ae5a4SJacob Faibussowitsch bool MemoryMap::PointerAttributes::operator==(const PointerAttributes &other) const noexcept 83*d71ae5a4SJacob Faibussowitsch { 840e6b6b59SJacob Faibussowitsch return mtype == other.mtype && id == other.id && size == other.size; 850e6b6b59SJacob Faibussowitsch } 860e6b6b59SJacob Faibussowitsch 87*d71ae5a4SJacob Faibussowitsch bool MemoryMap::PointerAttributes::contains(const void *ptr_begin, const void *ptr) const noexcept 88*d71ae5a4SJacob Faibussowitsch { 890e6b6b59SJacob Faibussowitsch return (ptr >= ptr_begin) && (ptr < (static_cast<const char *>(ptr_begin) + size)); 900e6b6b59SJacob Faibussowitsch } 910e6b6b59SJacob Faibussowitsch 920e6b6b59SJacob Faibussowitsch // ========================================================================================== 936797ed33SJacob Faibussowitsch // Memory map - Private API 940e6b6b59SJacob Faibussowitsch // ========================================================================================== 950e6b6b59SJacob Faibussowitsch 96*d71ae5a4SJacob Faibussowitsch PetscErrorCode MemoryMap::register_finalize_() noexcept 97*d71ae5a4SJacob Faibussowitsch { 980e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 990e6b6b59SJacob Faibussowitsch // Preallocate, this does give a modest performance bump since unordered_map is so __dog__ 1000e6b6b59SJacob Faibussowitsch // slow if it needs to rehash. Experiments show that users tend not to have more than 5 or 1010e6b6b59SJacob Faibussowitsch // so concurrently live pointers lying around. 10 at most. 1020e6b6b59SJacob Faibussowitsch PetscCallCXX(map.reserve(16)); 1030e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 1040e6b6b59SJacob Faibussowitsch } 1050e6b6b59SJacob Faibussowitsch 106*d71ae5a4SJacob Faibussowitsch PetscErrorCode MemoryMap::finalize_() noexcept 107*d71ae5a4SJacob Faibussowitsch { 1080e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1090e6b6b59SJacob Faibussowitsch PetscCall(PetscInfo(nullptr, "Finalizing memory map\n")); 1100e6b6b59SJacob Faibussowitsch PetscCallCXX(map = map_type{}); 1110e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 1120e6b6b59SJacob Faibussowitsch } 1130e6b6b59SJacob Faibussowitsch 1146797ed33SJacob Faibussowitsch // ========================================================================================== 1156797ed33SJacob Faibussowitsch // Memory map - Public API 1166797ed33SJacob Faibussowitsch // ========================================================================================== 1176797ed33SJacob Faibussowitsch 1180e6b6b59SJacob Faibussowitsch /* 1190e6b6b59SJacob Faibussowitsch MemoryMap::search_for - retrieve an iterator to the key-value pair for a pointer in the map 1200e6b6b59SJacob Faibussowitsch 1216797ed33SJacob Faibussowitsch Input Parameters: 1226797ed33SJacob Faibussowitsch + ptr - pointer to search for 1236797ed33SJacob Faibussowitsch - must_find - true if an error is raised if the pointer is not found (default: false) 1240e6b6b59SJacob Faibussowitsch 1250e6b6b59SJacob Faibussowitsch Notes: 1260e6b6b59SJacob Faibussowitsch Accounts for sub-regions, i.e. if ptr is contained within another pointers region, it returns 1270e6b6b59SJacob Faibussowitsch the iterator to the super-pointers key-value pair. 1280e6b6b59SJacob Faibussowitsch 1296797ed33SJacob Faibussowitsch If ptr is not found and must_find is false returns map.end(), otherwise raises an error 1300e6b6b59SJacob Faibussowitsch */ 131*d71ae5a4SJacob Faibussowitsch MemoryMap::map_type::const_iterator MemoryMap::search_for(const void *ptr, bool must_find) const noexcept 132*d71ae5a4SJacob Faibussowitsch { 1330e6b6b59SJacob Faibussowitsch const auto end = map.end(); 1346797ed33SJacob Faibussowitsch auto it = map.find(const_cast<map_type::key_type>(ptr)); 1350e6b6b59SJacob Faibussowitsch 1360e6b6b59SJacob Faibussowitsch // ptr was found, and points to an entire block 1376797ed33SJacob Faibussowitsch PetscFunctionBegin; 1386797ed33SJacob Faibussowitsch if (it != end) PetscFunctionReturn(it); 1390e6b6b59SJacob Faibussowitsch // wasn't found, but maybe its part of a block. have to search every block for it 1400e6b6b59SJacob Faibussowitsch // clang-format off 1416797ed33SJacob Faibussowitsch it = std::find_if(map.begin(), end, [ptr](const map_type::const_iterator::value_type &map_it) { 1420e6b6b59SJacob Faibussowitsch return map_it.second.contains(map_it.first, ptr); 1430e6b6b59SJacob Faibussowitsch }); 1446797ed33SJacob Faibussowitsch PetscCheckAbort(!must_find || it != end, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Pointer %p was not registered with the memory tracker, call PetscDeviceRegisterMemory() on it", ptr); 1456797ed33SJacob Faibussowitsch PetscFunctionReturn(it); 1460e6b6b59SJacob Faibussowitsch // clang-format on 1470e6b6b59SJacob Faibussowitsch } 1480e6b6b59SJacob Faibussowitsch 1490e6b6b59SJacob Faibussowitsch static MemoryMap memory_map; 1500e6b6b59SJacob Faibussowitsch 1510e6b6b59SJacob Faibussowitsch // ========================================================================================== 1520e6b6b59SJacob Faibussowitsch // Utility functions 1530e6b6b59SJacob Faibussowitsch // ========================================================================================== 1540e6b6b59SJacob Faibussowitsch 155*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDeviceCheckCapable_Private(PetscDeviceContext dctx, bool cond, const char descr[]) 156*d71ae5a4SJacob Faibussowitsch { 1570e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1580e6b6b59SJacob 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); 1590e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 1600e6b6b59SJacob Faibussowitsch } 1610e6b6b59SJacob Faibussowitsch 1620e6b6b59SJacob Faibussowitsch // A helper utility, since register is called from PetscDeviceRegisterMemory() and 1630e6b6b59SJacob Faibussowitsch // PetscDevicAllocate(). The latter also needs the generated id, so instead of making it search 1640e6b6b59SJacob Faibussowitsch // the map again we just return it here 165*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDeviceRegisterMemory_Private(const void *PETSC_RESTRICT ptr, PetscMemType mtype, std::size_t size, PetscObjectId *PETSC_RESTRICT id = nullptr) 166*d71ae5a4SJacob Faibussowitsch { 1670e6b6b59SJacob Faibussowitsch auto &map = memory_map.map; 1680e6b6b59SJacob Faibussowitsch const auto it = memory_map.search_for(ptr); 1690e6b6b59SJacob Faibussowitsch 1700e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1710e6b6b59SJacob Faibussowitsch if (it == map.cend()) { 1720e6b6b59SJacob Faibussowitsch // pointer was never registered with the map, insert it and bail 1730e6b6b59SJacob Faibussowitsch const auto newid = PetscObjectNewId_Internal(); 1740e6b6b59SJacob Faibussowitsch 1750e6b6b59SJacob Faibussowitsch if (PetscDefined(USE_DEBUG)) { 1760e6b6b59SJacob Faibussowitsch const auto tmp = MemoryMap::PointerAttributes(mtype, newid, size); 1770e6b6b59SJacob Faibussowitsch 1780e6b6b59SJacob Faibussowitsch for (const auto &entry : map) { 1790e6b6b59SJacob Faibussowitsch // REVIEW ME: maybe this should just be handled... 1800e6b6b59SJacob 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, 1810e6b6b59SJacob Faibussowitsch entry.first, PetscMemTypeToString(entry.second.mtype), entry.second.size); 1820e6b6b59SJacob Faibussowitsch } 1830e6b6b59SJacob Faibussowitsch } 1840e6b6b59SJacob Faibussowitsch // clang-format off 1850e6b6b59SJacob Faibussowitsch if (id) *id = newid; 1860e6b6b59SJacob Faibussowitsch PetscCallCXX(map.emplace( 1870e6b6b59SJacob Faibussowitsch std::piecewise_construct, 1880e6b6b59SJacob Faibussowitsch std::forward_as_tuple(const_cast<MemoryMap::map_type::key_type>(ptr)), 1890e6b6b59SJacob Faibussowitsch std::forward_as_tuple(mtype, newid, size) 1900e6b6b59SJacob Faibussowitsch )); 1910e6b6b59SJacob Faibussowitsch // clang-format on 1920e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 1930e6b6b59SJacob Faibussowitsch } 1940e6b6b59SJacob Faibussowitsch if (PetscDefined(USE_DEBUG)) { 1950e6b6b59SJacob Faibussowitsch const auto &old = it->second; 1960e6b6b59SJacob Faibussowitsch 1970e6b6b59SJacob 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, 1980e6b6b59SJacob Faibussowitsch PetscMemTypeToString(old.mtype), old.size, old.id, PetscMemTypeToString(mtype), size, old.id); 1990e6b6b59SJacob Faibussowitsch } 2000e6b6b59SJacob Faibussowitsch if (id) *id = it->second.id; 2010e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 2020e6b6b59SJacob Faibussowitsch } 2030e6b6b59SJacob Faibussowitsch 2040e6b6b59SJacob Faibussowitsch /*@C 2050e6b6b59SJacob Faibussowitsch PetscDeviceRegisterMemory - Register a pointer for use with device-aware memory system 2060e6b6b59SJacob Faibussowitsch 2070e6b6b59SJacob Faibussowitsch Not Collective 2080e6b6b59SJacob Faibussowitsch 2090e6b6b59SJacob Faibussowitsch Input Parameters: 2100e6b6b59SJacob Faibussowitsch + ptr - The pointer to register 2110e6b6b59SJacob Faibussowitsch . mtype - The `PetscMemType` of the pointer 2120e6b6b59SJacob Faibussowitsch - size - The size (in bytes) of the memory region 2130e6b6b59SJacob Faibussowitsch 2140e6b6b59SJacob Faibussowitsch Notes: 2150e6b6b59SJacob Faibussowitsch `ptr` need not point to the beginning of the memory range, however the user should register 2160e6b6b59SJacob Faibussowitsch the 2170e6b6b59SJacob Faibussowitsch 2180e6b6b59SJacob Faibussowitsch It's OK to re-register the same `ptr` repeatedly (subsequent registrations do nothing) 2190e6b6b59SJacob Faibussowitsch however the given `mtype` and `size` must match the original registration. 2200e6b6b59SJacob Faibussowitsch 2210e6b6b59SJacob Faibussowitsch `size` may be 0 (in which case this routine does nothing). 2220e6b6b59SJacob Faibussowitsch 2230e6b6b59SJacob Faibussowitsch Level: intermediate 2240e6b6b59SJacob Faibussowitsch 2250e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceMalloc()`, `PetscDeviceArrayCopy()`, `PetscDeviceFree()`, 2260e6b6b59SJacob Faibussowitsch `PetscDeviceArrayZero()` 2270e6b6b59SJacob Faibussowitsch @*/ 228*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceRegisterMemory(const void *PETSC_RESTRICT ptr, PetscMemType mtype, std::size_t size) 229*d71ae5a4SJacob Faibussowitsch { 2300e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 2310e6b6b59SJacob Faibussowitsch if (PetscMemTypeHost(mtype)) PetscValidPointer(ptr, 1); 2320e6b6b59SJacob Faibussowitsch if (PetscUnlikely(!size)) PetscFunctionReturn(0); // there is no point registering empty range 2330e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceRegisterMemory_Private(ptr, mtype, size)); 2340e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 2350e6b6b59SJacob Faibussowitsch } 2360e6b6b59SJacob Faibussowitsch 2376797ed33SJacob Faibussowitsch /* 2386797ed33SJacob Faibussowitsch PetscDeviceAllocate_Private - Allocate device-aware memory 2390e6b6b59SJacob Faibussowitsch 2400e6b6b59SJacob Faibussowitsch Not Collective, Asynchronous, Auto-dependency aware 2410e6b6b59SJacob Faibussowitsch 2420e6b6b59SJacob Faibussowitsch Input Parameters: 2430e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to allocate the memory 2440e6b6b59SJacob Faibussowitsch . clear - Whether or not the memory should be zeroed 2450e6b6b59SJacob Faibussowitsch . mtype - The type of memory to allocate 2466797ed33SJacob Faibussowitsch . n - The amount (in bytes) to allocate 2476797ed33SJacob Faibussowitsch - alignment - The alignment requirement (in bytes) of the allocated pointer 2480e6b6b59SJacob Faibussowitsch 2490e6b6b59SJacob Faibussowitsch Output Parameter: 2500e6b6b59SJacob Faibussowitsch . ptr - The pointer to store the result in 2510e6b6b59SJacob Faibussowitsch 2520e6b6b59SJacob Faibussowitsch Notes: 2530e6b6b59SJacob Faibussowitsch The user should prefer `PetscDeviceMalloc()` over this routine as it automatically computes 2546797ed33SJacob Faibussowitsch the size of the allocation and alignment based on the size of the datatype. 2550e6b6b59SJacob Faibussowitsch 2566797ed33SJacob Faibussowitsch If the user is unsure about `alignment` -- or unable to compute it -- passing 2576797ed33SJacob Faibussowitsch `PETSC_MEMALIGN` will always work, though the user should beware that this may be quite 2586797ed33SJacob Faibussowitsch wasteful for very small allocations. 2596797ed33SJacob Faibussowitsch 2606797ed33SJacob Faibussowitsch Memory allocated with this function must be freed with `PetscDeviceFree()` (or 2616797ed33SJacob Faibussowitsch `PetscDeviceDeallocate_Private()`). 2626797ed33SJacob Faibussowitsch 2636797ed33SJacob Faibussowitsch If `n` is zero, then `ptr` is set to `PETSC_NULLPTR`. 2646797ed33SJacob Faibussowitsch 2656797ed33SJacob Faibussowitsch This routine falls back to using `PetscMalloc1()` or `PetscCalloc1()` (depending on the value 2666797ed33SJacob Faibussowitsch of `clear`) if PETSc was not configured with device support. The user should note that 2676797ed33SJacob Faibussowitsch `mtype` and `alignment` are ignored in this case, as these routines allocate only host memory 2686797ed33SJacob Faibussowitsch aligned to `PETSC_MEMALIGN`. 2690e6b6b59SJacob Faibussowitsch 2700e6b6b59SJacob Faibussowitsch Note result stored `ptr` is immediately valid and the user may freely inspect or manipulate 2710e6b6b59SJacob Faibussowitsch its value on function return, i.e.\: 2720e6b6b59SJacob Faibussowitsch 2730e6b6b59SJacob Faibussowitsch .vb 2740e6b6b59SJacob Faibussowitsch PetscInt *ptr; 2750e6b6b59SJacob Faibussowitsch 2766797ed33SJacob Faibussowitsch PetscDeviceAllocate_Private(dctx, PETSC_FALSE, PETSC_MEMTYPE_DEVICE, 20, alignof(PetscInt), (void**)&ptr); 2770e6b6b59SJacob Faibussowitsch 2780e6b6b59SJacob Faibussowitsch PetscInt *sub_ptr = ptr + 10; // OK, no need to synchronize 2790e6b6b59SJacob Faibussowitsch 2800e6b6b59SJacob Faibussowitsch ptr[0] = 10; // ERROR, directly accessing contents of ptr is undefined until synchronization 2810e6b6b59SJacob Faibussowitsch .ve 2820e6b6b59SJacob Faibussowitsch 2830e6b6b59SJacob Faibussowitsch DAG representation: 2840e6b6b59SJacob Faibussowitsch .vb 2850e6b6b59SJacob Faibussowitsch time -> 2860e6b6b59SJacob Faibussowitsch 2870e6b6b59SJacob Faibussowitsch -> dctx - |= CALL =| -\- dctx --> 2880e6b6b59SJacob Faibussowitsch \- ptr -> 2890e6b6b59SJacob Faibussowitsch .ve 2900e6b6b59SJacob Faibussowitsch 2910e6b6b59SJacob Faibussowitsch Level: intermediate 2920e6b6b59SJacob Faibussowitsch 2930e6b6b59SJacob Faibussowitsch .N ASYNC_API 2940e6b6b59SJacob Faibussowitsch 2956797ed33SJacob Faibussowitsch .seealso: `PetscDeviceMalloc()`, `PetscDeviceFree()`, `PetscDeviceDeallocate_Private()`, 2966797ed33SJacob Faibussowitsch `PetscDeviceArrayCopy()`, `PetscDeviceArrayZero()`, `PetscMemType` 2976797ed33SJacob Faibussowitsch */ 298*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceAllocate_Private(PetscDeviceContext dctx, PetscBool clear, PetscMemType mtype, std::size_t n, std::size_t alignment, void **PETSC_RESTRICT ptr) 299*d71ae5a4SJacob Faibussowitsch { 3000e6b6b59SJacob Faibussowitsch PetscObjectId id = 0; 3010e6b6b59SJacob Faibussowitsch 3020e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 3036797ed33SJacob Faibussowitsch if (PetscDefined(USE_DEBUG)) { 3046797ed33SJacob Faibussowitsch const auto is_power_of_2 = [](std::size_t num) { return (num & (num - 1)) == 0; }; 3056797ed33SJacob Faibussowitsch 3066797ed33SJacob Faibussowitsch PetscCheck(alignment != 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested alignment %zu cannot be 0", alignment); 3076797ed33SJacob Faibussowitsch PetscCheck(is_power_of_2(alignment), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested alignment %zu must be a power of 2", alignment); 3086797ed33SJacob Faibussowitsch } 3096797ed33SJacob Faibussowitsch PetscValidPointer(ptr, 6); 3100e6b6b59SJacob Faibussowitsch *ptr = nullptr; 3110e6b6b59SJacob Faibussowitsch if (PetscUnlikely(!n)) PetscFunctionReturn(0); 3120e6b6b59SJacob Faibussowitsch PetscCall(memory_map.register_finalize()); 3130e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 3140e6b6b59SJacob Faibussowitsch 3150e6b6b59SJacob Faibussowitsch // get our pointer here 3160e6b6b59SJacob Faibussowitsch if (dctx->ops->memalloc) { 3176797ed33SJacob Faibussowitsch PetscUseTypeMethod(dctx, memalloc, clear, mtype, n, alignment, ptr); 3180e6b6b59SJacob Faibussowitsch } else { 3190e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(mtype), "allocating")); 3200e6b6b59SJacob Faibussowitsch PetscCall(PetscMallocA(1, clear, __LINE__, PETSC_FUNCTION_NAME, __FILE__, n, ptr)); 3210e6b6b59SJacob Faibussowitsch } 3220e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceRegisterMemory_Private(*ptr, mtype, n, &id)); 3230e6b6b59SJacob Faibussowitsch // Note this is a "write" so that the next dctx to try and read from the pointer has to wait 3240e6b6b59SJacob Faibussowitsch // for the allocation to be ready 3250e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, id, PETSC_MEMORY_ACCESS_WRITE, "memory allocation")); 3260e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 3270e6b6b59SJacob Faibussowitsch } 3280e6b6b59SJacob Faibussowitsch 3296797ed33SJacob Faibussowitsch /* 3306797ed33SJacob Faibussowitsch PetscDeviceDeallocate_Private - Free device-aware memory 3310e6b6b59SJacob Faibussowitsch 3320e6b6b59SJacob Faibussowitsch Not Collective, Asynchronous, Auto-dependency aware 3330e6b6b59SJacob Faibussowitsch 3340e6b6b59SJacob Faibussowitsch Input Parameters: 3350e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to free the memory 3360e6b6b59SJacob Faibussowitsch - ptr - The pointer to free 3370e6b6b59SJacob Faibussowitsch 3380e6b6b59SJacob Faibussowitsch Notes: 3390e6b6b59SJacob Faibussowitsch `ptr` must have been allocated using any of `PetscDeviceMalloc()`, `PetscDeviceCalloc()` or 3406797ed33SJacob Faibussowitsch `PetscDeviceAllocate_Private()`, or registered with the system via `PetscDeviceRegisterMemory()`. 3410e6b6b59SJacob Faibussowitsch 3420e6b6b59SJacob Faibussowitsch The user should prefer `PetscDeviceFree()` over this routine as it automatically sets `ptr` 3430e6b6b59SJacob Faibussowitsch to `PETSC_NULLPTR` on successful deallocation. 3440e6b6b59SJacob Faibussowitsch 3450e6b6b59SJacob Faibussowitsch `ptr` may be `NULL`. 3460e6b6b59SJacob Faibussowitsch 3476797ed33SJacob Faibussowitsch This routine falls back to using `PetscFree()` if PETSc was not configured with device 3486797ed33SJacob Faibussowitsch support. The user should note that `PetscFree()` frees only host memory. 3496797ed33SJacob Faibussowitsch 3500e6b6b59SJacob Faibussowitsch DAG representation: 3510e6b6b59SJacob Faibussowitsch .vb 3520e6b6b59SJacob Faibussowitsch time -> 3530e6b6b59SJacob Faibussowitsch 3540e6b6b59SJacob Faibussowitsch -> dctx -/- |= CALL =| - dctx -> 3550e6b6b59SJacob Faibussowitsch -> ptr -/ 3560e6b6b59SJacob Faibussowitsch .ve 3570e6b6b59SJacob Faibussowitsch 3580e6b6b59SJacob Faibussowitsch Level: intermediate 3590e6b6b59SJacob Faibussowitsch 3600e6b6b59SJacob Faibussowitsch .N ASYNC_API 3610e6b6b59SJacob Faibussowitsch 3626797ed33SJacob Faibussowitsch .seealso: `PetscDeviceFree()`, `PetscDeviceAllocate_Private()` 3636797ed33SJacob Faibussowitsch */ 364*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceDeallocate_Private(PetscDeviceContext dctx, void *PETSC_RESTRICT ptr) 365*d71ae5a4SJacob Faibussowitsch { 3660e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 3670e6b6b59SJacob Faibussowitsch if (ptr) { 3680e6b6b59SJacob Faibussowitsch auto &map = memory_map.map; 3690e6b6b59SJacob Faibussowitsch const auto found_it = map.find(const_cast<MemoryMap::map_type::key_type>(ptr)); 3700e6b6b59SJacob Faibussowitsch 3710e6b6b59SJacob Faibussowitsch if (PetscUnlikelyDebug(found_it == map.end())) { 3720e6b6b59SJacob Faibussowitsch // OK this is a bad pointer, now determine why 3730e6b6b59SJacob Faibussowitsch const auto it = memory_map.search_for(ptr); 3740e6b6b59SJacob Faibussowitsch 3750e6b6b59SJacob Faibussowitsch // if it is map.cend() then no allocation owns it, meaning it was not allocated by us! 3766797ed33SJacob Faibussowitsch PetscCheck(it != map.cend(), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Pointer %p was not allocated via PetscDeviceAllocate_Private()", ptr); 3770e6b6b59SJacob Faibussowitsch // if we are here then we did allocate it but the user has tried to do something along 3780e6b6b59SJacob Faibussowitsch // the lines of: 3790e6b6b59SJacob Faibussowitsch // 3800e6b6b59SJacob Faibussowitsch // allocate(&ptr, size); 3810e6b6b59SJacob Faibussowitsch // deallocate(ptr+5); 3820e6b6b59SJacob Faibussowitsch // 3830e6b6b59SJacob 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, 3840e6b6b59SJacob Faibussowitsch it->second.size); 3850e6b6b59SJacob Faibussowitsch } 3860e6b6b59SJacob Faibussowitsch 3876797ed33SJacob Faibussowitsch PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 3880e6b6b59SJacob Faibussowitsch // mark intent BEFORE we free, note we mark as write so that we are made to wait on any 3890e6b6b59SJacob Faibussowitsch // outstanding reads (don't want to kill the pointer before they are done) 3900e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, found_it->second.id, PETSC_MEMORY_ACCESS_WRITE, "memory deallocation")); 3910e6b6b59SJacob Faibussowitsch // do free 3920e6b6b59SJacob Faibussowitsch if (dctx->ops->memfree) { 3930e6b6b59SJacob Faibussowitsch PetscUseTypeMethod(dctx, memfree, found_it->second.mtype, (void **)&ptr); 3940e6b6b59SJacob Faibussowitsch } else { 3950e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(found_it->second.mtype), "freeing")); 3960e6b6b59SJacob Faibussowitsch } 3970e6b6b59SJacob Faibussowitsch // if ptr still exists, then the device context could not handle it 3980e6b6b59SJacob Faibussowitsch if (ptr) PetscCall(PetscFree(ptr)); 3990e6b6b59SJacob Faibussowitsch PetscCallCXX(map.erase(found_it)); 4000e6b6b59SJacob Faibussowitsch } 4010e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 4020e6b6b59SJacob Faibussowitsch } 4030e6b6b59SJacob Faibussowitsch 4040e6b6b59SJacob Faibussowitsch /*@C 4050e6b6b59SJacob Faibussowitsch PetscDeviceMemcpy - Copy memory in a device-aware manner 4060e6b6b59SJacob Faibussowitsch 4070e6b6b59SJacob Faibussowitsch Not Collective, Asynchronous, Auto-dependency aware 4080e6b6b59SJacob Faibussowitsch 4090e6b6b59SJacob Faibussowitsch Input Parameters: 4100e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to copy the memory 4110e6b6b59SJacob Faibussowitsch . dest - The pointer to copy to 4120e6b6b59SJacob Faibussowitsch . src - The pointer to copy from 4130e6b6b59SJacob Faibussowitsch - n - The amount (in bytes) to copy 4140e6b6b59SJacob Faibussowitsch 4150e6b6b59SJacob Faibussowitsch Notes: 4166797ed33SJacob Faibussowitsch Both `dest` and `src` must have been allocated by `PetscDeviceMalloc()` or 4176797ed33SJacob Faibussowitsch `PetscDeviceCalloc()`. 4180e6b6b59SJacob Faibussowitsch 4190e6b6b59SJacob Faibussowitsch `src` and `dest` cannot overlap. 4200e6b6b59SJacob Faibussowitsch 4210e6b6b59SJacob Faibussowitsch If both `src` and `dest` are on the host this routine is fully synchronous. 4220e6b6b59SJacob Faibussowitsch 4230e6b6b59SJacob Faibussowitsch The user should prefer `PetscDeviceArrayCopy()` over this routine as it automatically 4240e6b6b59SJacob Faibussowitsch computes the number of bytes to copy from the size of the pointer types. 4250e6b6b59SJacob Faibussowitsch 4260e6b6b59SJacob Faibussowitsch DAG representation: 4270e6b6b59SJacob Faibussowitsch .vb 4280e6b6b59SJacob Faibussowitsch time -> 4290e6b6b59SJacob Faibussowitsch 4300e6b6b59SJacob Faibussowitsch -> dctx - |= CALL =| - dctx -> 4310e6b6b59SJacob Faibussowitsch -> dest ---------------------> 4320e6b6b59SJacob Faibussowitsch -> src ----------------------> 4330e6b6b59SJacob Faibussowitsch .ve 4340e6b6b59SJacob Faibussowitsch 4350e6b6b59SJacob Faibussowitsch Level: intermediate 4360e6b6b59SJacob Faibussowitsch 4370e6b6b59SJacob Faibussowitsch .N ASYNC_API 4380e6b6b59SJacob Faibussowitsch 4396797ed33SJacob Faibussowitsch .seealso: `PetscDeviceArrayCopy()`, `PetscDeviceMalloc()`, `PetscDeviceCalloc()`, 4406797ed33SJacob Faibussowitsch `PetscDeviceFree()` 4410e6b6b59SJacob Faibussowitsch @*/ 442*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceMemcpy(PetscDeviceContext dctx, void *PETSC_RESTRICT dest, const void *PETSC_RESTRICT src, std::size_t n) 443*d71ae5a4SJacob Faibussowitsch { 4440e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 4450e6b6b59SJacob Faibussowitsch if (!n) PetscFunctionReturn(0); 4460e6b6b59SJacob Faibussowitsch PetscCheck(dest, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to copy to a NULL pointer"); 4470e6b6b59SJacob Faibussowitsch PetscCheck(src, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to copy from a NULL pointer"); 4480e6b6b59SJacob Faibussowitsch if (dest == src) PetscFunctionReturn(0); 4490e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 4500e6b6b59SJacob Faibussowitsch { 4516797ed33SJacob Faibussowitsch const auto dest_it = memory_map.search_for(dest, true); 4526797ed33SJacob Faibussowitsch const auto src_it = memory_map.search_for(src, true); 4536797ed33SJacob Faibussowitsch const auto mode = PetscMemTypeToDeviceCopyMode(dest_it->second.mtype, src_it->second.mtype); 4540e6b6b59SJacob Faibussowitsch 4550e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, src_it->second.id, PETSC_MEMORY_ACCESS_READ, "memory copy (src)")); 4560e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, dest_it->second.id, PETSC_MEMORY_ACCESS_WRITE, "memory copy (dest)")); 4570e6b6b59SJacob Faibussowitsch // perform the copy 4580e6b6b59SJacob Faibussowitsch if (dctx->ops->memcopy) { 4590e6b6b59SJacob Faibussowitsch PetscUseTypeMethod(dctx, memcopy, dest, src, n, mode); 4606797ed33SJacob Faibussowitsch if (mode == PETSC_DEVICE_COPY_HTOD) { 4616797ed33SJacob Faibussowitsch PetscCall(PetscLogCpuToGpu(n)); 4626797ed33SJacob Faibussowitsch } else if (mode == PETSC_DEVICE_COPY_DTOH) { 4636797ed33SJacob Faibussowitsch PetscCall(PetscLogGpuToCpu(n)); 4646797ed33SJacob Faibussowitsch } 4650e6b6b59SJacob Faibussowitsch } else { 4660e6b6b59SJacob Faibussowitsch // REVIEW ME: we might potentially need to sync here if the memory is device-allocated 4670e6b6b59SJacob Faibussowitsch // (pinned) but being copied by a host dctx 4680e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceCheckCapable_Private(dctx, mode == PETSC_DEVICE_COPY_HTOH, "copying")); 4690e6b6b59SJacob Faibussowitsch PetscCall(PetscMemcpy(dest, src, n)); 4700e6b6b59SJacob Faibussowitsch } 4710e6b6b59SJacob Faibussowitsch } 4720e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 4730e6b6b59SJacob Faibussowitsch } 4740e6b6b59SJacob Faibussowitsch 4750e6b6b59SJacob Faibussowitsch /*@C 4760e6b6b59SJacob Faibussowitsch PetscDeviceMemset - Memset device-aware memory 4770e6b6b59SJacob Faibussowitsch 4780e6b6b59SJacob Faibussowitsch Not Collective, Asynchronous, Auto-dependency aware 4790e6b6b59SJacob Faibussowitsch 4800e6b6b59SJacob Faibussowitsch Input Parameters: 4810e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext` used to memset the memory 4820e6b6b59SJacob Faibussowitsch . ptr - The pointer to the memory 4830e6b6b59SJacob Faibussowitsch . v - The value to set 4840e6b6b59SJacob Faibussowitsch - n - The amount (in bytes) to set 4850e6b6b59SJacob Faibussowitsch 4860e6b6b59SJacob Faibussowitsch Notes: 4876797ed33SJacob Faibussowitsch `ptr` must have been allocated by `PetscDeviceMalloc()` or `PetscDeviceCalloc()`. 4880e6b6b59SJacob Faibussowitsch 4890e6b6b59SJacob Faibussowitsch The user should prefer `PetscDeviceArrayZero()` over this routine as it automatically 4900e6b6b59SJacob Faibussowitsch computes the number of bytes to copy from the size of the pointer types, though they should 4910e6b6b59SJacob Faibussowitsch note that it only zeros memory. 4920e6b6b59SJacob Faibussowitsch 4930e6b6b59SJacob Faibussowitsch This routine is analogous to `memset()`. That is, this routine copies the value 4940e6b6b59SJacob Faibussowitsch `static_cast<unsigned char>(v)` into each of the first count characters of the object pointed 4950e6b6b59SJacob Faibussowitsch to by `dest`. 4960e6b6b59SJacob Faibussowitsch 4970e6b6b59SJacob Faibussowitsch If `dest` is on device, this routine is asynchronous. 4980e6b6b59SJacob Faibussowitsch 4990e6b6b59SJacob Faibussowitsch DAG representation: 5000e6b6b59SJacob Faibussowitsch .vb 5010e6b6b59SJacob Faibussowitsch time -> 5020e6b6b59SJacob Faibussowitsch 5030e6b6b59SJacob Faibussowitsch -> dctx - |= CALL =| - dctx -> 5040e6b6b59SJacob Faibussowitsch -> dest ---------------------> 5050e6b6b59SJacob Faibussowitsch .ve 5060e6b6b59SJacob Faibussowitsch 5070e6b6b59SJacob Faibussowitsch Level: intermediate 5080e6b6b59SJacob Faibussowitsch 5090e6b6b59SJacob Faibussowitsch .N ASYNC_API 5100e6b6b59SJacob Faibussowitsch 5116797ed33SJacob Faibussowitsch .seealso: `PetscDeviceArrayZero()`, `PetscDeviceMalloc()`, `PetscDeviceCalloc()`, 5126797ed33SJacob Faibussowitsch `PetscDeviceFree()` 5130e6b6b59SJacob Faibussowitsch @*/ 514*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceMemset(PetscDeviceContext dctx, void *ptr, PetscInt v, std::size_t n) 515*d71ae5a4SJacob Faibussowitsch { 5160e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 5170e6b6b59SJacob Faibussowitsch if (PetscUnlikely(!n)) PetscFunctionReturn(0); 5180e6b6b59SJacob Faibussowitsch PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_POINTER, "Trying to memset a NULL pointer"); 5190e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 5200e6b6b59SJacob Faibussowitsch { 5216797ed33SJacob Faibussowitsch const auto ptr_it = memory_map.search_for(ptr, true); 5226797ed33SJacob Faibussowitsch const auto mtype = ptr_it->second.mtype; 5230e6b6b59SJacob Faibussowitsch 5240e6b6b59SJacob Faibussowitsch PetscCall(PetscDeviceContextMarkIntentFromID(dctx, ptr_it->second.id, PETSC_MEMORY_ACCESS_WRITE, "memory set")); 5250e6b6b59SJacob Faibussowitsch if (dctx->ops->memset) { 5266797ed33SJacob Faibussowitsch PetscUseTypeMethod(dctx, memset, mtype, ptr, v, n); 5270e6b6b59SJacob Faibussowitsch } else { 5280e6b6b59SJacob Faibussowitsch // REVIEW ME: we might potentially need to sync here if the memory is device-allocated 5290e6b6b59SJacob Faibussowitsch // (pinned) but being memset by a host dctx 5306797ed33SJacob Faibussowitsch PetscCall(PetscDeviceCheckCapable_Private(dctx, PetscMemTypeHost(mtype), "memsetting")); 5310e6b6b59SJacob Faibussowitsch std::memset(ptr, static_cast<int>(v), n); 5320e6b6b59SJacob Faibussowitsch } 5330e6b6b59SJacob Faibussowitsch } 5340e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 5350e6b6b59SJacob Faibussowitsch } 536