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