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