Arcane  4.2.1.0
Developer documentation
Loading...
Searching...
No Matches
CudaAcceleratorRuntime.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* CudaAcceleratorRuntime.cc (C) 2000-2026 */
9/* */
10/* Runtime for 'Cuda'. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arccore/accelerator_native/CudaAccelerator.h"
15
16#include "arccore/base/CheckedConvert.h"
17#include "arccore/base/FatalErrorException.h"
18
19#include "arccore/common/internal/MemoryUtilsInternal.h"
20#include "arccore/common/internal/IMemoryResourceMngInternal.h"
21
22#include "arccore/common/accelerator/RunQueueBuildInfo.h"
23#include "arccore/common/accelerator/Memory.h"
24#include "arccore/common/accelerator/DeviceInfoList.h"
25#include "arccore/common/accelerator/KernelLaunchArgs.h"
26#include "arccore/common/accelerator/RunQueue.h"
27#include "arccore/common/accelerator/DeviceMemoryInfo.h"
28#include "arccore/common/accelerator/NativeStream.h"
29#include "arccore/common/accelerator/internal/IRunnerRuntime.h"
30#include "arccore/common/accelerator/internal/RegisterRuntimeInfo.h"
31#include "arccore/common/accelerator/internal/RunCommandImpl.h"
32#include "arccore/common/accelerator/internal/IRunQueueStream.h"
33#include "arccore/common/accelerator/internal/IRunQueueEventImpl.h"
34#include "arccore/common/accelerator/internal/AcceleratorMemoryAllocatorBase.h"
35
36#include "arccore/accelerator_native/runtime/Cupti.h"
37
38#include <sstream>
39#include <unordered_map>
40#include <mutex>
41#include <algorithm>
42#include <iostream>
43
44#include <cuda.h>
45
46// For std::memset
47#include <cstring>
48
49#ifdef ARCCORE_HAS_CUDA_NVTOOLSEXT
50#include <nvtx3/nvToolsExt.h>
51#endif
52
53namespace Arcane::Accelerator::Cuda
54{
55using Impl::KernelLaunchArgs;
56
57namespace
58{
59 Int32 global_cupti_flush = 0;
60 CuptiInfo global_cupti_info;
61} // namespace
62
63/*---------------------------------------------------------------------------*/
64/*---------------------------------------------------------------------------*/
65
66// Starting from CUDA 13, there is a new cudaMemLocation type
67// for methods such as cudeMemAdvise or cudaMemPrefetch
68#if defined(ARCCORE_USING_CUDA13_OR_GREATER)
69inline cudaMemLocation
70_getMemoryLocation(int device_id)
71{
72 cudaMemLocation mem_location;
73 mem_location.type = cudaMemLocationTypeDevice;
74 mem_location.id = device_id;
75 if (device_id == cudaCpuDeviceId)
76 mem_location.type = cudaMemLocationTypeHost;
77 else {
78 mem_location.type = cudaMemLocationTypeDevice;
79 mem_location.id = device_id;
80 }
81 return mem_location;
82}
83#else
84inline int
85_getMemoryLocation(int device_id)
86{
87 return device_id;
88}
89#endif
90
91/*---------------------------------------------------------------------------*/
92/*---------------------------------------------------------------------------*/
93
95{
96 public:
97
98 virtual ~ConcreteAllocator() = default;
99
100 public:
101
102 virtual cudaError_t _allocate(void** ptr, size_t new_size) = 0;
103 virtual cudaError_t _deallocate(void* ptr) = 0;
104};
105
106/*---------------------------------------------------------------------------*/
107/*---------------------------------------------------------------------------*/
108
109template <typename ConcreteAllocatorType>
110class UnderlyingAllocator
112{
113 public:
114
115 UnderlyingAllocator() = default;
116
117 public:
118
119 void* allocateMemory(Int64 size) final
120 {
121 void* out = nullptr;
122 ARCCORE_CHECK_CUDA(m_concrete_allocator._allocate(&out, size));
123 return out;
124 }
125 void freeMemory(void* ptr, [[maybe_unused]] Int64 size) final
126 {
127 ARCCORE_CHECK_CUDA_NOTHROW(m_concrete_allocator._deallocate(ptr));
128 }
129
130 void doMemoryCopy(void* destination, const void* source, Int64 size) final
131 {
132 ARCCORE_CHECK_CUDA(cudaMemcpy(destination, source, size, cudaMemcpyDefault));
133 }
134
135 eMemoryResource memoryResource() const final
136 {
137 return m_concrete_allocator.memoryResource();
138 }
139
140 public:
141
142 ConcreteAllocatorType m_concrete_allocator;
143};
144
145/*---------------------------------------------------------------------------*/
146/*---------------------------------------------------------------------------*/
147
148class UnifiedMemoryConcreteAllocator
149: public ConcreteAllocator
150{
151 public:
152
153 UnifiedMemoryConcreteAllocator()
154 {
155 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_CUDA_USE_ALLOC_ATS", true))
156 m_use_ats = v.value();
157 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_CUDA_MEMORY_HINT_ON_DEVICE", true))
158 m_use_hint_as_mainly_device = (v.value() != 0);
159 }
160
161 cudaError_t _deallocate(void* ptr) final
162 {
163 if (m_use_ats) {
164 ::free(ptr);
165 return cudaSuccess;
166 }
167 //std::cout << "CUDA_MANAGED_FREE ptr=" << ptr << "\n";
168 return ::cudaFree(ptr);
169 }
170
171 cudaError_t _allocate(void** ptr, size_t new_size) final
172 {
173 if (m_use_ats) {
174 *ptr = ::aligned_alloc(128, new_size);
175 }
176 else {
177 auto r = ::cudaMallocManaged(ptr, new_size, cudaMemAttachGlobal);
178 //std::cout << "CUDA_MANAGED_MALLOC ptr=" << (*ptr) << " size=" << new_size << "\n";
179 //if (new_size < 4000)
180 //std::cout << "STACK=" << platform::getStackTrace() << "\n";
181
182 if (r != cudaSuccess)
183 return r;
184
185 // If requested, indicates that we prefer to allocate on the GPU.
186 // NOTE: In this case, we retrieve the current device to position the
187 // preferred location. If we use MemoryPool, this allocation will only
188 // be performed once. If the default device for a thread changes during
189 // computation, there will be an inconsistency. To avoid this, we could
190 // call cudaMemAdvise() for each allocation (via _applyHint()) but these
191 // operations are quite costly and if there are many allocations, a
192 // performance loss may result.
194 int device_id = 0;
195 void* p = *ptr;
196 cudaGetDevice(&device_id);
197 ARCCORE_CHECK_CUDA(cudaMemAdvise(p, new_size, cudaMemAdviseSetPreferredLocation, _getMemoryLocation(device_id)));
198 ARCCORE_CHECK_CUDA(cudaMemAdvise(p, new_size, cudaMemAdviseSetAccessedBy, _getMemoryLocation(cudaCpuDeviceId)));
199 }
200 }
201
202 return cudaSuccess;
203 }
204
205 constexpr eMemoryResource memoryResource() const { return eMemoryResource::UnifiedMemory; }
206
207 public:
208
209 bool m_use_ats = false;
213};
214
215/*---------------------------------------------------------------------------*/
216/*---------------------------------------------------------------------------*/
217
225class UnifiedMemoryCudaMemoryAllocator
226: public AcceleratorMemoryAllocatorBase
227{
228 public:
229 public:
230
231 UnifiedMemoryCudaMemoryAllocator()
232 : AcceleratorMemoryAllocatorBase("UnifiedMemoryCudaMemory", new UnderlyingAllocator<UnifiedMemoryConcreteAllocator>())
233 {
234 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_CUDA_MALLOC_TRACE", true))
235 _setTraceLevel(v.value());
236 }
237
238 void initialize()
239 {
240 _doInitializeUVM(true);
241 }
242
243 public:
244
245 void notifyMemoryArgsChanged([[maybe_unused]] MemoryAllocationArgs old_args,
246 MemoryAllocationArgs new_args, AllocatedMemoryInfo ptr) final
247 {
248 void* p = ptr.baseAddress();
249 Int64 s = ptr.capacity();
250 if (p && s > 0)
251 _applyHint(ptr.baseAddress(), ptr.size(), new_args);
252 }
253
254 protected:
255
256 void _applyHint(void* p, size_t new_size, MemoryAllocationArgs args)
257 {
258 eMemoryLocationHint hint = args.memoryLocationHint();
259 // Uses the active device to position the GPU by default
260 // We only do this if the hint requires it to avoid calling
261 // cudaGetDevice() every time.
262 int device_id = 0;
264 cudaGetDevice(&device_id);
265 }
266 auto device_memory_location = _getMemoryLocation(device_id);
267 auto cpu_memory_location = _getMemoryLocation(cudaCpuDeviceId);
268
269 //std::cout << "SET_MEMORY_HINT name=" << args.arrayName() << " size=" << new_size << " hint=" << (int)hint << "\n";
271 ARCCORE_CHECK_CUDA(cudaMemAdvise(p, new_size, cudaMemAdviseSetPreferredLocation, device_memory_location));
272 ARCCORE_CHECK_CUDA(cudaMemAdvise(p, new_size, cudaMemAdviseSetAccessedBy, cpu_memory_location));
273 }
275 ARCCORE_CHECK_CUDA(cudaMemAdvise(p, new_size, cudaMemAdviseSetPreferredLocation, cpu_memory_location));
276 //ARCCORE_CHECK_CUDA(cudaMemAdvise(p, new_size, cudaMemAdviseSetAccessedBy, 0));
277 }
279 ARCCORE_CHECK_CUDA(cudaMemAdvise(p, new_size, cudaMemAdviseSetReadMostly, device_memory_location));
280 }
281 }
282 void _removeHint(void* p, size_t size, MemoryAllocationArgs args)
283 {
284 eMemoryLocationHint hint = args.memoryLocationHint();
285 if (hint == eMemoryLocationHint::None)
286 return;
287 int device_id = 0;
288 ARCCORE_CHECK_CUDA(cudaMemAdvise(p, size, cudaMemAdviseUnsetReadMostly, _getMemoryLocation(device_id)));
289 }
290
291 private:
292
293 bool m_use_ats = false;
294};
295
296/*---------------------------------------------------------------------------*/
297/*---------------------------------------------------------------------------*/
298
300: public ConcreteAllocator
301{
302 public:
303
304 cudaError_t _allocate(void** ptr, size_t new_size) final
305 {
306 return ::cudaMallocHost(ptr, new_size);
307 }
308 cudaError_t _deallocate(void* ptr) final
309 {
310 return ::cudaFreeHost(ptr);
311 }
312 constexpr eMemoryResource memoryResource() const { return eMemoryResource::HostPinned; }
313};
314
315/*---------------------------------------------------------------------------*/
316/*---------------------------------------------------------------------------*/
317
318class HostPinnedCudaMemoryAllocator
319: public AcceleratorMemoryAllocatorBase
320{
321 public:
322 public:
323
324 HostPinnedCudaMemoryAllocator()
325 : AcceleratorMemoryAllocatorBase("HostPinnedCudaMemory", new UnderlyingAllocator<HostPinnedConcreteAllocator>())
326 {
327 }
328
329 public:
330
331 void initialize()
332 {
334 }
335};
336
337/*---------------------------------------------------------------------------*/
338/*---------------------------------------------------------------------------*/
339
340class DeviceConcreteAllocator
341: public ConcreteAllocator
342{
343 public:
344
345 DeviceConcreteAllocator()
346 {
347 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_CUDA_USE_ALLOC_ATS", true))
348 m_use_ats = v.value();
349 }
350
351 cudaError_t _allocate(void** ptr, size_t new_size) final
352 {
353 if (m_use_ats) {
354 // FIXME: it does not work on WIN32
355 *ptr = std::aligned_alloc(128, new_size);
356 if (*ptr)
357 return cudaSuccess;
358 return cudaErrorMemoryAllocation;
359 }
360 cudaError_t r = ::cudaMalloc(ptr, new_size);
361 //std::cout << "ALLOCATE_DEVICE ptr=" << (*ptr) << " size=" << new_size << " r=" << (int)r << "\n";
362 return r;
363 }
364 cudaError_t _deallocate(void* ptr) final
365 {
366 if (m_use_ats) {
367 std::free(ptr);
368 return cudaSuccess;
369 }
370 //std::cout << "FREE_DEVICE ptr=" << ptr << "\n";
371 return ::cudaFree(ptr);
372 }
373
374 constexpr eMemoryResource memoryResource() const { return eMemoryResource::Device; }
375
376 private:
377
378 bool m_use_ats = false;
379};
380
381/*---------------------------------------------------------------------------*/
382/*---------------------------------------------------------------------------*/
383
384class DeviceCudaMemoryAllocator
385: public AcceleratorMemoryAllocatorBase
386{
387
388 public:
389
390 DeviceCudaMemoryAllocator()
391 : AcceleratorMemoryAllocatorBase("DeviceCudaMemoryAllocator", new UnderlyingAllocator<DeviceConcreteAllocator>())
392 {
393 }
394
395 public:
396
397 void initialize()
398 {
400 }
401};
402
403/*---------------------------------------------------------------------------*/
404/*---------------------------------------------------------------------------*/
405
406namespace
407{
408 UnifiedMemoryCudaMemoryAllocator unified_memory_cuda_memory_allocator;
409 HostPinnedCudaMemoryAllocator host_pinned_cuda_memory_allocator;
410 DeviceCudaMemoryAllocator device_cuda_memory_allocator;
411} // namespace
412
413/*---------------------------------------------------------------------------*/
414/*---------------------------------------------------------------------------*/
415
416void initializeCudaMemoryAllocators()
417{
418 unified_memory_cuda_memory_allocator.initialize();
419 device_cuda_memory_allocator.initialize();
420 host_pinned_cuda_memory_allocator.initialize();
421}
422
423void finalizeCudaMemoryAllocators(ITraceMng* tm)
424{
425 unified_memory_cuda_memory_allocator.finalize(tm);
426 device_cuda_memory_allocator.finalize(tm);
427 host_pinned_cuda_memory_allocator.finalize(tm);
428}
429
430/*---------------------------------------------------------------------------*/
431/*---------------------------------------------------------------------------*/
432
433void arcaneCheckCudaErrors(const TraceInfo& ti, CUresult e)
434{
435 if (e == CUDA_SUCCESS)
436 return;
437 const char* error_name = nullptr;
438 CUresult e2 = cuGetErrorName(e, &error_name);
439 if (e2 != CUDA_SUCCESS)
440 error_name = "Unknown";
441
442 const char* error_message = nullptr;
443 CUresult e3 = cuGetErrorString(e, &error_message);
444 if (e3 != CUDA_SUCCESS)
445 error_message = "Unknown";
446
447 ARCCORE_FATAL("CUDA Error trace={0} e={1} name={2} message={3}",
448 ti, e, error_name, error_message);
449}
450
451/*---------------------------------------------------------------------------*/
452/*---------------------------------------------------------------------------*/
453
463{
464 public:
465
466 Int32 getNbThreadPerBlock(const void* kernel_ptr)
467 {
468 std::scoped_lock lock(m_mutex);
469 auto x = m_nb_thread_per_block_map.find(kernel_ptr);
470 if (x != m_nb_thread_per_block_map.end())
471 return x->second;
472 int min_grid_size = 0;
473 int computed_block_size = 0;
474 int wanted_shared_memory = 0;
475 cudaError_t r = cudaOccupancyMaxPotentialBlockSize(&min_grid_size, &computed_block_size, kernel_ptr, wanted_shared_memory);
476 if (r != cudaSuccess)
477 computed_block_size = 0;
478 int num_block_0 = 0;
479 cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_block_0, kernel_ptr, 256, wanted_shared_memory);
480 int num_block_1 = 0;
481 cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_block_1, kernel_ptr, 1024, wanted_shared_memory);
482
483 cudaFuncAttributes func_attr;
484 cudaFuncGetAttributes(&func_attr, kernel_ptr);
485 m_nb_thread_per_block_map[kernel_ptr] = computed_block_size;
486 std::cout << "ComputedBlockSize=" << computed_block_size << " n0=" << num_block_0 << " n1=" << num_block_1
487 << " min_grid_size=" << min_grid_size << " nb_reg=" << func_attr.numRegs;
488
489#if CUDART_VERSION >= 12040
490 // cudaFuncGetName is only available in 12.4
491 const char* func_name = nullptr;
492 cudaFuncGetName(&func_name, kernel_ptr);
493 std::cout << " name=" << func_name << "\n";
494#endif
495
496 return computed_block_size;
497 }
498
499 private:
500
501 std::unordered_map<const void*, Int32> m_nb_thread_per_block_map;
502 std::mutex m_mutex;
503};
504
505/*---------------------------------------------------------------------------*/
506/*---------------------------------------------------------------------------*/
507
508class CudaRunQueueStream
510{
511 public:
512
513 CudaRunQueueStream(Impl::IRunnerRuntime* runtime, const RunQueueBuildInfo& bi)
514 : m_runtime(runtime)
515 {
516 if (bi.isDefault())
517 ARCCORE_CHECK_CUDA(cudaStreamCreate(&m_cuda_stream));
518 else {
519 int priority = bi.priority();
520 ARCCORE_CHECK_CUDA(cudaStreamCreateWithPriority(&m_cuda_stream, cudaStreamDefault, priority));
521 }
522 }
523 ~CudaRunQueueStream() override
524 {
525 ARCCORE_CHECK_CUDA_NOTHROW(cudaStreamDestroy(m_cuda_stream));
526 }
527
528 public:
529
530 void notifyBeginLaunchKernel([[maybe_unused]] Impl::RunCommandImpl& c) override
531 {
532#ifdef ARCCORE_HAS_CUDA_NVTOOLSEXT
533 auto kname = c.kernelName();
534 if (kname.empty())
535 nvtxRangePush(c.traceInfo().name());
536 else
537 nvtxRangePush(kname.localstr());
538#endif
539 return m_runtime->notifyBeginLaunchKernel();
540 }
542 {
543#ifdef ARCCORE_HAS_CUDA_NVTOOLSEXT
544 nvtxRangePop();
545#endif
546 return m_runtime->notifyEndLaunchKernel();
547 }
548 void barrier() override
549 {
550 ARCCORE_CHECK_CUDA(cudaStreamSynchronize(m_cuda_stream));
551 if (global_cupti_flush > 0)
552 global_cupti_info.flush();
553 }
554 bool _barrierNoException() override
555 {
556 return (cudaStreamSynchronize(m_cuda_stream) != cudaSuccess);
557 }
558 void copyMemory(const MemoryCopyArgs& args) override
559 {
560 auto source_bytes = args.source().bytes();
561 auto r = cudaMemcpyAsync(args.destination().data(), source_bytes.data(),
562 source_bytes.size(), cudaMemcpyDefault, m_cuda_stream);
563 ARCCORE_CHECK_CUDA(r);
564 if (!args.isAsync())
565 barrier();
566 }
567 void prefetchMemory(const MemoryPrefetchArgs& args) override
568 {
569 auto src = args.source().bytes();
570 if (src.size() == 0)
571 return;
572 DeviceId d = args.deviceId();
573 int device = cudaCpuDeviceId;
574 if (!d.isHost())
575 device = d.asInt32();
576 //std::cout << "PREFETCH device=" << device << " host(id)=" << cudaCpuDeviceId
577 // << " size=" << args.source().size() << " data=" << src.data() << "\n";
578 auto mem_location = _getMemoryLocation(device);
579#if defined(ARCCORE_USING_CUDA13_OR_GREATER)
580 auto r = cudaMemPrefetchAsync(src.data(), src.size(), mem_location, 0, m_cuda_stream);
581#else
582 auto r = cudaMemPrefetchAsync(src.data(), src.size(), mem_location, m_cuda_stream);
583#endif
584 ARCCORE_CHECK_CUDA(r);
585 if (!args.isAsync())
586 barrier();
587 }
589 {
590 return Impl::NativeStream(&m_cuda_stream);
591 }
592
593 public:
594
595 cudaStream_t trueStream() const
596 {
597 return m_cuda_stream;
598 }
599
600 private:
601
602 Impl::IRunnerRuntime* m_runtime = nullptr;
603 cudaStream_t m_cuda_stream = nullptr;
604};
605
606/*---------------------------------------------------------------------------*/
607/*---------------------------------------------------------------------------*/
608
609class CudaRunQueueEvent
611{
612 public:
613
614 explicit CudaRunQueueEvent(bool has_timer)
615 {
616 if (has_timer)
617 ARCCORE_CHECK_CUDA(cudaEventCreate(&m_cuda_event));
618 else
619 ARCCORE_CHECK_CUDA(cudaEventCreateWithFlags(&m_cuda_event, cudaEventDisableTiming));
620 }
621 ~CudaRunQueueEvent() override
622 {
623 ARCCORE_CHECK_CUDA_NOTHROW(cudaEventDestroy(m_cuda_event));
624 }
625
626 public:
627
628 // Register the event within a RunQueue
629 void recordQueue(Impl::IRunQueueStream* stream) final
630 {
631 auto* rq = static_cast<CudaRunQueueStream*>(stream);
632 ARCCORE_CHECK_CUDA(cudaEventRecord(m_cuda_event, rq->trueStream()));
633 }
634
635 void wait() final
636 {
637 ARCCORE_CHECK_CUDA(cudaEventSynchronize(m_cuda_event));
638 }
639
640 void waitForEvent(Impl::IRunQueueStream* stream) final
641 {
642 auto* rq = static_cast<CudaRunQueueStream*>(stream);
643 ARCCORE_CHECK_CUDA(cudaStreamWaitEvent(rq->trueStream(), m_cuda_event, cudaEventWaitDefault));
644 }
645
646 Int64 elapsedTime(IRunQueueEventImpl* start_event) final
647 {
648 // NOTE: Events must have been created with the timer active
649 ARCCORE_CHECK_POINTER(start_event);
650 auto* true_start_event = static_cast<CudaRunQueueEvent*>(start_event);
651 float time_in_ms = 0.0;
652
653 // TODO: check if necessary
654 // ARCCORE_CHECK_CUDA(cudaEventSynchronize(m_cuda_event));
655
656 ARCCORE_CHECK_CUDA(cudaEventElapsedTime(&time_in_ms, true_start_event->m_cuda_event, m_cuda_event));
657 double x = time_in_ms * 1.0e6;
658 Int64 nano_time = static_cast<Int64>(x);
659 return nano_time;
660 }
661
662 bool hasPendingWork() final
663 {
664 cudaError_t v = cudaEventQuery(m_cuda_event);
665 if (v == cudaErrorNotReady)
666 return true;
667 ARCCORE_CHECK_CUDA(v);
668 return false;
669 }
670
671 private:
672
673 cudaEvent_t m_cuda_event;
674};
675
676/*---------------------------------------------------------------------------*/
677/*---------------------------------------------------------------------------*/
678
681{
682 public:
683
684 ~CudaRunnerRuntime() override = default;
685
686 public:
687
688 void notifyBeginLaunchKernel() override
689 {
690 ++m_nb_kernel_launched;
691 if (m_is_verbose)
692 std::cout << "BEGIN CUDA KERNEL!\n";
693 }
694 void notifyEndLaunchKernel() override
695 {
696 ARCCORE_CHECK_CUDA(cudaGetLastError());
697 if (m_is_verbose)
698 std::cout << "END CUDA KERNEL!\n";
699 }
700 void barrier() override
701 {
702 ARCCORE_CHECK_CUDA(cudaDeviceSynchronize());
703 }
704 eExecutionPolicy executionPolicy() const override
705 {
707 }
708 Impl::IRunQueueStream* createStream(const RunQueueBuildInfo& bi) override
709 {
710 return new CudaRunQueueStream(this, bi);
711 }
712 Impl::IRunQueueEventImpl* createEventImpl() override
713 {
714 return new CudaRunQueueEvent(false);
715 }
716 Impl::IRunQueueEventImpl* createEventImplWithTimer() override
717 {
718 return new CudaRunQueueEvent(true);
719 }
720 void setMemoryAdvice(ConstMemoryView buffer, eMemoryAdvice advice, DeviceId device_id) override
721 {
722 auto v = buffer.bytes();
723 const void* ptr = v.data();
724 size_t count = v.size();
725 int device = device_id.asInt32();
726 cudaMemoryAdvise cuda_advise;
727
728 if (advice == eMemoryAdvice::MostlyRead)
729 cuda_advise = cudaMemAdviseSetReadMostly;
731 cuda_advise = cudaMemAdviseSetPreferredLocation;
732 else if (advice == eMemoryAdvice::AccessedByDevice)
733 cuda_advise = cudaMemAdviseSetAccessedBy;
734 else if (advice == eMemoryAdvice::PreferredLocationHost) {
735 cuda_advise = cudaMemAdviseSetPreferredLocation;
736 device = cudaCpuDeviceId;
737 }
738 else if (advice == eMemoryAdvice::AccessedByHost) {
739 cuda_advise = cudaMemAdviseSetAccessedBy;
740 device = cudaCpuDeviceId;
741 }
742 else
743 return;
744 //std::cout << "MEMADVISE p=" << ptr << " size=" << count << " advise = " << cuda_advise << " id = " << device << "\n";
745 ARCCORE_CHECK_CUDA(cudaMemAdvise(ptr, count, cuda_advise, _getMemoryLocation(device)));
746 }
747 void unsetMemoryAdvice(ConstMemoryView buffer, eMemoryAdvice advice, DeviceId device_id) override
748 {
749 auto v = buffer.bytes();
750 const void* ptr = v.data();
751 size_t count = v.size();
752 int device = device_id.asInt32();
753 cudaMemoryAdvise cuda_advise;
754
755 if (advice == eMemoryAdvice::MostlyRead)
756 cuda_advise = cudaMemAdviseUnsetReadMostly;
758 cuda_advise = cudaMemAdviseUnsetPreferredLocation;
759 else if (advice == eMemoryAdvice::AccessedByDevice)
760 cuda_advise = cudaMemAdviseUnsetAccessedBy;
761 else if (advice == eMemoryAdvice::PreferredLocationHost) {
762 cuda_advise = cudaMemAdviseUnsetPreferredLocation;
763 device = cudaCpuDeviceId;
764 }
765 else if (advice == eMemoryAdvice::AccessedByHost) {
766 cuda_advise = cudaMemAdviseUnsetAccessedBy;
767 device = cudaCpuDeviceId;
768 }
769 else
770 return;
771 ARCCORE_CHECK_CUDA(cudaMemAdvise(ptr, count, cuda_advise, _getMemoryLocation(device)));
772 }
773
774 void setCurrentDevice(DeviceId device_id) final
775 {
776 Int32 id = device_id.asInt32();
777 if (!device_id.isAccelerator())
778 ARCCORE_FATAL("Device {0} is not an accelerator device", id);
779 ARCCORE_CHECK_CUDA(cudaSetDevice(id));
780 }
781
782 const IDeviceInfoList* deviceInfoList() final { return &m_device_info_list; }
783
784 void startProfiling() override
785 {
786 global_cupti_info.start();
787 }
788
789 void stopProfiling() override
790 {
791 global_cupti_info.stop();
792 }
793
794 bool isProfilingActive() override
795 {
796 return global_cupti_info.isActive();
797 }
798
799 void getPointerAttribute(PointerAttribute& attribute, const void* ptr) override
800 {
801 cudaPointerAttributes ca;
802 ARCCORE_CHECK_CUDA(cudaPointerGetAttributes(&ca, ptr));
803 // NOTE: the Arcane type 'ePointerMemoryType' normally has the same values
804 // as the corresponding CUDA type, so a simple cast can be done.
805 auto mem_type = static_cast<ePointerMemoryType>(ca.type);
806 _fillPointerAttribute(attribute, mem_type, ca.device,
807 ptr, ca.devicePointer, ca.hostPointer);
808 }
809
810 DeviceMemoryInfo getDeviceMemoryInfo(DeviceId device_id) override
811 {
812 int d = 0;
813 int wanted_d = device_id.asInt32();
814 ARCCORE_CHECK_CUDA(cudaGetDevice(&d));
815 if (d != wanted_d)
816 ARCCORE_CHECK_CUDA(cudaSetDevice(wanted_d));
817 size_t free_mem = 0;
818 size_t total_mem = 0;
819 ARCCORE_CHECK_CUDA(cudaMemGetInfo(&free_mem, &total_mem));
820 if (d != wanted_d)
821 ARCCORE_CHECK_CUDA(cudaSetDevice(d));
823 dmi.setFreeMemory(free_mem);
824 dmi.setTotalMemory(total_mem);
825 return dmi;
826 }
827
828 void pushProfilerRange(const String& name, Int32 color_rgb) override
829 {
830#ifdef ARCCORE_HAS_CUDA_NVTOOLSEXT
831 if (color_rgb >= 0) {
832 // NOTE: It would be necessary to do: nvtxEventAttributes_t eventAttrib = { 0 };
833 // but this causes many 'missing initializer for member' warnings
834 nvtxEventAttributes_t eventAttrib;
835 std::memset(&eventAttrib, 0, sizeof(nvtxEventAttributes_t));
836 eventAttrib.version = NVTX_VERSION;
837 eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
838 eventAttrib.colorType = NVTX_COLOR_ARGB;
839 eventAttrib.color = color_rgb;
840 eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
841 eventAttrib.message.ascii = name.localstr();
842 nvtxRangePushEx(&eventAttrib);
843 }
844 else
845 nvtxRangePush(name.localstr());
846#endif
847 }
848 void popProfilerRange() override
849 {
850#ifdef ARCCORE_HAS_CUDA_NVTOOLSEXT
851 nvtxRangePop();
852#endif
853 }
854
855 void finalize(ITraceMng* tm) override
856 {
857 finalizeCudaMemoryAllocators(tm);
858 }
859
860 KernelLaunchArgs computeKernalLaunchArgs(const KernelLaunchArgs& orig_args,
861 const void* kernel_ptr,
862 Int64 total_loop_size) override
863 {
864 Int32 shared_memory = orig_args.sharedMemorySize();
865 if (orig_args.isCooperative()) {
866 // In cooperative mode, ensure that we do not launch more blocks
867 // than the maximum that can reside on the GPU.
868 Int32 nb_thread = orig_args.nbThreadPerBlock();
869 Int32 nb_block = orig_args.nbBlockPerGrid();
870 int nb_block_per_sm = 0;
871 ARCCORE_CHECK_CUDA(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&nb_block_per_sm, kernel_ptr, nb_thread, shared_memory));
872
873 int max_block = static_cast<int>((nb_block_per_sm * m_multi_processor_count) * m_cooperative_ratio);
874 max_block = std::max(max_block, 1);
875 if (nb_block > max_block) {
876 KernelLaunchArgs modified_args(orig_args);
877 modified_args.setNbBlockPerGrid(max_block);
878 return modified_args;
879 }
880 return orig_args;
881 }
882
883 if (!m_use_computed_occupancy)
884 return orig_args;
885 if (shared_memory < 0)
886 shared_memory = 0;
887 // For now, we do not perform calculation if shared memory is non-zero.
888 if (shared_memory != 0)
889 return orig_args;
890 Int32 computed_block_size = m_occupancy_map.getNbThreadPerBlock(kernel_ptr);
891 if (computed_block_size == 0)
892 return orig_args;
893
894 // Here, we use the number of threads per block to achieve a
895 // maximum occupancy.
896 KernelLaunchArgs modified_args(orig_args);
897 Int64 big_b = (total_loop_size + computed_block_size - 1) / computed_block_size;
898 int blocks_per_grid = CheckedConvert::toInt32(big_b);
899 modified_args.setNbBlockPerGrid(blocks_per_grid);
900 modified_args.setNbThreadPerBlock(computed_block_size);
901 return modified_args;
902 }
903
904 public:
905
906 void fillDevices(bool is_verbose);
907 void build()
908 {
909 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_USE_COMPUTED_OCCUPANCY", true))
910 m_use_computed_occupancy = v.value();
911 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_ACCELERATOR_COOPERATIVE_RATIO", true)) {
912 Int32 x = v.value();
913 x = std::clamp(x, 10, 100);
914 m_cooperative_ratio = x / 100.0;
915 }
916 }
917
918 private:
919
920 Int64 m_nb_kernel_launched = 0;
921 bool m_is_verbose = false;
922 bool m_use_computed_occupancy = false;
923 Int32 m_multi_processor_count = 0;
924 double m_cooperative_ratio = 1.0;
925 Impl::DeviceInfoList m_device_info_list;
926 OccupancyMap m_occupancy_map;
927};
928
929/*---------------------------------------------------------------------------*/
930/*---------------------------------------------------------------------------*/
931
932void CudaRunnerRuntime::
933fillDevices(bool is_verbose)
934{
935 int nb_device = 0;
936 ARCCORE_CHECK_CUDA(cudaGetDeviceCount(&nb_device));
937 std::ostream& omain = std::cout;
938 if (is_verbose)
939 omain << "ArcaneCUDA: Initialize Arcane CUDA runtime nb_available_device=" << nb_device << "\n";
940 for (int i = 0; i < nb_device; ++i) {
941 cudaDeviceProp dp;
942 cudaGetDeviceProperties(&dp, i);
943 int runtime_version = 0;
944 cudaRuntimeGetVersion(&runtime_version);
945 int driver_version = 0;
946 cudaDriverGetVersion(&driver_version);
947 std::ostringstream ostr;
948 std::ostream& o = ostr;
949 o << "Device " << i << " name=" << dp.name << "\n";
950 o << " Driver version = " << (driver_version / 1000) << "." << (driver_version % 1000) << "\n";
951 o << " Runtime version = " << (runtime_version / 1000) << "." << (runtime_version % 1000) << "\n";
952 o << " computeCapability = " << dp.major << "." << dp.minor << "\n";
953 o << " totalGlobalMem = " << dp.totalGlobalMem << "\n";
954 o << " sharedMemPerBlock = " << dp.sharedMemPerBlock << "\n";
955 o << " sharedMemPerMultiprocessor = " << dp.sharedMemPerMultiprocessor << "\n";
956 o << " sharedMemPerBlockOptin = " << dp.sharedMemPerBlockOptin << "\n";
957 o << " regsPerBlock = " << dp.regsPerBlock << "\n";
958 o << " warpSize = " << dp.warpSize << "\n";
959 o << " memPitch = " << dp.memPitch << "\n";
960 o << " maxThreadsPerBlock = " << dp.maxThreadsPerBlock << "\n";
961 o << " maxBlocksPerMultiProcessor = " << dp.maxBlocksPerMultiProcessor << "\n";
962 o << " maxThreadsPerMultiProcessor = " << dp.maxThreadsPerMultiProcessor << "\n";
963 o << " totalConstMem = " << dp.totalConstMem << "\n";
964 o << " cooperativeLaunch = " << dp.cooperativeLaunch << "\n";
965 o << " multiProcessorCount = " << dp.multiProcessorCount << "\n";
966 o << " integrated = " << dp.integrated << "\n";
967 o << " canMapHostMemory = " << dp.canMapHostMemory << "\n";
968 o << " directManagedMemAccessFromHost = " << dp.directManagedMemAccessFromHost << "\n";
969 o << " hostNativeAtomicSupported = " << dp.hostNativeAtomicSupported << "\n";
970 o << " pageableMemoryAccess = " << dp.pageableMemoryAccess << "\n";
971 o << " concurrentManagedAccess = " << dp.concurrentManagedAccess << "\n";
972 o << " pageableMemoryAccessUsesHostPageTables = " << dp.pageableMemoryAccessUsesHostPageTables << "\n";
973 o << " hostNativeAtomicSupported = " << dp.hostNativeAtomicSupported << "\n";
974 o << " maxThreadsDim = " << dp.maxThreadsDim[0] << " " << dp.maxThreadsDim[1]
975 << " " << dp.maxThreadsDim[2] << "\n";
976 o << " maxGridSize = " << dp.maxGridSize[0] << " " << dp.maxGridSize[1]
977 << " " << dp.maxGridSize[2] << "\n";
978 o << " pciInfo = " << dp.pciDomainID << " " << dp.pciBusID << " " << dp.pciDeviceID << "\n";
979 o << " memoryBusWitdh = " << dp.memoryBusWidth << " bits\n";
980
981 int clock_rate = 0;
982 ARCCORE_CHECK_CUDA(cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, i));
983 o << " clockRate = " << (clock_rate / 1000) << " MHz\n";
984
985 int memory_clock_rate = 0;
986 ARCCORE_CHECK_CUDA(cudaDeviceGetAttribute(&memory_clock_rate, cudaDevAttrMemoryClockRate, i));
987 o << " memoryClockRate = " << (memory_clock_rate / 1000) << " MHz\n";
988
989 Real memory_bandwith = ((dp.memoryBusWidth * memory_clock_rate * 2.0) / 8.0) / 1.0e6;
990 o << " MemoryBandwith = " << memory_bandwith << " GB/s\n";
991
992#if !defined(ARCCORE_USING_CUDA13_OR_GREATER)
993 o << " deviceOverlap = " << dp.deviceOverlap << "\n";
994 o << " computeMode = " << dp.computeMode << "\n";
995 o << " kernelExecTimeoutEnabled = " << dp.kernelExecTimeoutEnabled << "\n";
996#endif
997
998 // TODO: We assume that all GPUs are the same and therefore
999 // that the number of SM per GPU is the same. This is used to
1000 // calculate the number of blocks in cooperative mode.
1001 m_multi_processor_count = dp.multiProcessorCount;
1002
1003 {
1004 int least_val = 0;
1005 int greatest_val = 0;
1006 ARCCORE_CHECK_CUDA(cudaDeviceGetStreamPriorityRange(&least_val, &greatest_val));
1007 o << " leastPriority = " << least_val << " greatestPriority = " << greatest_val << "\n";
1008 }
1009 std::ostringstream device_uuid_ostr;
1010 {
1011 CUdevice device;
1012 ARCCORE_CHECK_CUDA(cuDeviceGet(&device, i));
1013 CUuuid device_uuid;
1014 ARCCORE_CHECK_CUDA(cuDeviceGetUuid(&device_uuid, device));
1015 o << " deviceUuid=";
1016 Impl::printUUID(device_uuid_ostr, device_uuid.bytes);
1017 o << device_uuid_ostr.str();
1018 o << "\n";
1019 }
1020 String description(ostr.str());
1021 if (is_verbose)
1022 omain << description;
1023
1024 DeviceInfo device_info;
1025 device_info.setDescription(description);
1026 device_info.setDeviceId(DeviceId(i));
1027 device_info.setName(dp.name);
1028 device_info.setWarpSize(dp.warpSize);
1029 device_info.setUUIDAsString(device_uuid_ostr.str());
1030 device_info.setSharedMemoryPerBlock(static_cast<Int32>(dp.sharedMemPerBlock));
1031 device_info.setSharedMemoryPerMultiprocessor(static_cast<Int32>(dp.sharedMemPerMultiprocessor));
1032 device_info.setSharedMemoryPerBlockOptin(static_cast<Int32>(dp.sharedMemPerBlockOptin));
1033 device_info.setTotalConstMemory(static_cast<Int32>(dp.totalConstMem));
1034 device_info.setPCIDomainID(dp.pciDomainID);
1035 device_info.setPCIBusID(dp.pciBusID);
1036 device_info.setPCIDeviceID(dp.pciDeviceID);
1037 m_device_info_list.addDevice(device_info);
1038 }
1039
1040 Int32 global_cupti_level = 0;
1041
1042 // Check if Cupti is active
1043 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_CUPTI_LEVEL", true))
1044 global_cupti_level = v.value();
1045 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_CUPTI_FLUSH", true))
1046 global_cupti_flush = v.value();
1047 bool do_print_cupti = true;
1048 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_CUPTI_PRINT", true))
1049 do_print_cupti = (v.value() != 0);
1050
1051 if (global_cupti_level > 0) {
1052#ifndef ARCCORE_HAS_CUDA_CUPTI
1053 ARCCORE_FATAL("Trying to enable CUPTI but Arcane is not compiled with cupti support");
1054#endif
1055 global_cupti_info.init(global_cupti_level, do_print_cupti);
1056 global_cupti_info.start();
1057 }
1058}
1059
1060/*---------------------------------------------------------------------------*/
1061/*---------------------------------------------------------------------------*/
1062
1064: public IMemoryCopier
1065{
1066 void copy(ConstMemoryView from, [[maybe_unused]] eMemoryResource from_mem,
1067 MutableMemoryView to, [[maybe_unused]] eMemoryResource to_mem,
1068 const RunQueue* queue) override
1069 {
1070 if (queue) {
1071 queue->copyMemory(MemoryCopyArgs(to.bytes(), from.bytes()).addAsync(queue->isAsync()));
1072 return;
1073 }
1074 // 'cudaMemcpyDefault' automatically knows what to do by only considering
1075 // the pointer values. We should see if using \a from_mem and \a to_mem
1076 // can improve performance.
1077 ARCCORE_CHECK_CUDA(cudaMemcpy(to.data(), from.data(), from.bytes().size(), cudaMemcpyDefault));
1078 }
1079};
1080
1081/*---------------------------------------------------------------------------*/
1082/*---------------------------------------------------------------------------*/
1083
1084} // End namespace Arcane::Accelerator::Cuda
1085
1086using namespace Arcane;
1087
1088namespace
1089{
1090Accelerator::Cuda::CudaRunnerRuntime global_cuda_runtime;
1091Accelerator::Cuda::CudaMemoryCopier global_cuda_memory_copier;
1092
1093void _setAllocator(Accelerator::AcceleratorMemoryAllocatorBase* allocator)
1094{
1096 eMemoryResource mem = allocator->memoryResource();
1097 mrm->setAllocator(mem, allocator);
1098 mrm->setMemoryPool(mem, allocator->memoryPool());
1099}
1100
1101} // namespace
1102
1103/*---------------------------------------------------------------------------*/
1104/*---------------------------------------------------------------------------*/
1105
1106// This function is the entry point used when dynamically loading
1107// this library
1108extern "C" ARCCORE_EXPORT void
1109arcaneRegisterAcceleratorRuntimecuda(Arcane::Accelerator::RegisterRuntimeInfo& init_info)
1110{
1111 using namespace Arcane::Accelerator::Cuda;
1112 global_cuda_runtime.build();
1113 Accelerator::Impl::setUsingCUDARuntime(true);
1114 Accelerator::Impl::setCUDARunQueueRuntime(&global_cuda_runtime);
1115 initializeCudaMemoryAllocators();
1117 MemoryUtils::setAcceleratorHostMemoryAllocator(&unified_memory_cuda_memory_allocator);
1118 IMemoryResourceMngInternal* mrm = MemoryUtils::getDataMemoryResourceMng()->_internal();
1119 mrm->setIsAccelerator(true);
1120 _setAllocator(&unified_memory_cuda_memory_allocator);
1121 _setAllocator(&host_pinned_cuda_memory_allocator);
1122 _setAllocator(&device_cuda_memory_allocator);
1123 mrm->setCopier(&global_cuda_memory_copier);
1124 global_cuda_runtime.fillDevices(init_info.isVerbose());
1125}
1126
1127/*---------------------------------------------------------------------------*/
1128/*---------------------------------------------------------------------------*/
#define ARCCORE_FATAL(...)
Macro throwing a FatalErrorException.
#define ARCCORE_CHECK_POINTER(ptr)
Macro that returns the pointer ptr if it is not null or throws an exception if it is null.
Base class of a specific allocator for accelerator.
eMemoryResource memoryResource() const final
Memory resource provided by the allocator.
void _doInitializeDevice(bool default_use_memory_pool=false)
Initialization for Device memory.
void _doInitializeHostPinned(bool default_use_memory_pool=false)
Initialization for HostPinned memory.
void _doInitializeUVM(bool default_use_memory_pool=false)
Initialization for UVM memory.
void copy(ConstMemoryView from, eMemoryResource from_mem, MutableMemoryView to, eMemoryResource to_mem, const RunQueue *queue) override
Copies the data from from to to with the queue queue.
void barrier() override
Blocks until all actions associated with this queue are finished.
void notifyBeginLaunchKernel(Impl::RunCommandImpl &c) override
Notification before command launch.
bool _barrierNoException() override
Barrier without exception. Returns true in case of error.
Impl::NativeStream nativeStream() override
Pointer to the internal structure dependent on the implementation.
void prefetchMemory(const MemoryPrefetchArgs &args) override
Performs a prefetch of a memory region.
void notifyEndLaunchKernel(Impl::RunCommandImpl &) override
Notification of command launch completion.
void copyMemory(const MemoryCopyArgs &args) override
Performs a copy between two memory regions.
Singleton class to manage CUPTI.
Definition Cupti.h:39
Map containing the ideal occupancy for a given kernel.
void * allocateMemory(Int64 size) final
Allocates a block for size bytes.
void freeMemory(void *ptr, Int64 size) final
Frees the block located at address address containing size bytes.
void notifyMemoryArgsChanged(MemoryAllocationArgs old_args, MemoryAllocationArgs new_args, AllocatedMemoryInfo ptr) final
Notifies of a change in instance-specific arguments.
bool isHost() const
Indicates if the instance is associated with the host.
bool isAccelerator() const
Indicates if the instance is associated with an accelerator.
Interface for event implementation.
Interface of an execution stream for a RunQueue.
Interface of the runtime associated with an accelerator.
bool isCooperative() const
Indicates if running in cooperative mode (i.e. cudaLaunchCooperativeKernel).
bool isDefault() const
Indicates if the instance only has default values.
bool isAsync() const
Indicates if the execution queue is asynchronous.
Definition RunQueue.cc:320
void copyMemory(const MemoryCopyArgs &args) const
Copies information between two memory regions.
Definition RunQueue.cc:237
Information about an allocated memory region.
Constant view on a contiguous memory region containing fixed-size elements.
constexpr SpanType bytes() const
View in byte form.
constexpr const std::byte * data() const
Pointer to the memory region.
Template class for converting a type.
Interface for memory copies with accelerator support.
Internal part of Arcane's 'IMemoryResourceMng'.
virtual void setAllocator(eMemoryResource r, IMemoryAllocator *allocator)=0
Sets the allocator for resource r.
virtual void setMemoryPool(eMemoryResource r, IMemoryPool *pool)=0
Sets the memory pool for resource r.
virtual void setIsAccelerator(bool v)=0
Indicates if an accelerator is available.
virtual void setCopier(IMemoryCopier *copier)=0
Sets the copying instance.
virtual IMemoryResourceMngInternal * _internal()=0
Internal interface.
Class containing information to specialize allocations.
Mutable view on a contiguous memory region containing fixed-size elements.
constexpr std::byte * data() const
Pointer to the memory region.
constexpr SpanType bytes() const
View in byte form.
constexpr __host__ __device__ pointer data() const noexcept
Pointer to the start of the view.
Definition Span.h:537
constexpr __host__ __device__ SizeType size() const noexcept
Returns the size of the array.
Definition Span.h:325
const char * localstr() const
Returns the conversion of the instance into UTF-8 encoding.
Definition String.cc:229
@ AccessedByHost
Indicates that the memory region is accessed by the host.
@ PreferredLocationDevice
Prefers memory placement on the accelerator.
@ MostlyRead
Indicates that the memory region is primarily read-only.
@ AccessedByDevice
Indicates that the memory region is accessed by the device.
ePointerMemoryType
Memory type for a pointer.
eExecutionPolicy
Execution policy for a Runner.
@ CUDA
Execution policy using the CUDA environment.
IMemoryRessourceMng * getDataMemoryResourceMng()
Memory resource manager for data.
IMemoryAllocator * setAcceleratorHostMemoryAllocator(IMemoryAllocator *a)
Sets the specific allocator for accelerators.
void setDefaultDataMemoryResource(eMemoryResource mem_resource)
Sets the memory resource used for the data memory allocator.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::int64_t Int64
Signed integer type of 64 bits.
eMemoryLocationHint
Indices for expected memory location.
@ MainlyHost
Indicates that the data will primarily be used on the CPU.
@ HostAndDeviceMostlyRead
Indicates that the data will be used both on the accelerator and on the CPU and will not be frequentl...
@ MainlyDevice
Indicates that the data will primarily be used on the accelerator.
double Real
Type representing a real number.
eMemoryResource
List of available memory resources.
@ HostPinned
Allocates on the host.
@ UnifiedMemory
Allocates using unified memory.
@ Device
Allocates on the device.
std::int32_t Int32
Signed integer type of 32 bits.