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