Arcane  4.2.1.0
Developer documentation
Loading...
Searching...
No Matches
HipAcceleratorRuntime.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/* HipAcceleratorRuntime.cc (C) 2000-2026 */
9/* */
10/* Runtime for 'HIP'. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arccore/accelerator_native/HipAccelerator.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 <sstream>
37#include <algorithm>
38#include <iostream>
39
40#ifdef ARCCORE_HAS_ROCTX
41#include <roctx.h>
42#endif
43
44using namespace Arccore;
45
46namespace Arcane::Accelerator::Hip
47{
48using Impl::KernelLaunchArgs;
49
50/*---------------------------------------------------------------------------*/
51/*---------------------------------------------------------------------------*/
52
54{
55 public:
56
57 virtual ~ConcreteAllocator() = default;
58
59 public:
60
61 virtual hipError_t _allocate(void** ptr, size_t new_size) = 0;
62 virtual hipError_t _deallocate(void* ptr) = 0;
63};
64
65/*---------------------------------------------------------------------------*/
66/*---------------------------------------------------------------------------*/
67
68template <typename ConcreteAllocatorType>
69class UnderlyingAllocator
71{
72 public:
73
74 UnderlyingAllocator() = default;
75
76 public:
77
78 void* allocateMemory(Int64 size) final
79 {
80 void* out = nullptr;
81 ARCCORE_CHECK_HIP(m_concrete_allocator._allocate(&out, size));
82 return out;
83 }
84 void freeMemory(void* ptr, [[maybe_unused]] Int64 size) final
85 {
86 ARCCORE_CHECK_HIP_NOTHROW(m_concrete_allocator._deallocate(ptr));
87 }
88
89 void doMemoryCopy(void* destination, const void* source, Int64 size) final
90 {
91 ARCCORE_CHECK_HIP(hipMemcpy(destination, source, size, hipMemcpyDefault));
92 }
93
94 eMemoryResource memoryResource() const final
95 {
96 return m_concrete_allocator.memoryResource();
97 }
98
99 public:
100
101 ConcreteAllocatorType m_concrete_allocator;
102};
103
104/*---------------------------------------------------------------------------*/
105/*---------------------------------------------------------------------------*/
106
108: public ConcreteAllocator
109{
110 public:
111
112 hipError_t _deallocate(void* ptr) final
113 {
114 return ::hipFree(ptr);
115 }
116
117 hipError_t _allocate(void** ptr, size_t new_size) final
118 {
119 auto r = ::hipMallocManaged(ptr, new_size, hipMemAttachGlobal);
120 return r;
121 }
122
123 constexpr eMemoryResource memoryResource() const { return eMemoryResource::UnifiedMemory; }
124};
125
126/*---------------------------------------------------------------------------*/
127/*---------------------------------------------------------------------------*/
128
129class UnifiedMemoryHipMemoryAllocator
130: public AcceleratorMemoryAllocatorBase
131{
132 public:
133
134 UnifiedMemoryHipMemoryAllocator()
135 : AcceleratorMemoryAllocatorBase("UnifiedMemoryHipMemory", new UnderlyingAllocator<UnifiedMemoryConcreteAllocator>())
136 {
137 }
138
139 public:
140
141 void initialize()
142 {
143 _doInitializeUVM(true);
144 }
145};
146
147/*---------------------------------------------------------------------------*/
148/*---------------------------------------------------------------------------*/
149
151: public ConcreteAllocator
152{
153 public:
154
155 hipError_t _allocate(void** ptr, size_t new_size) final
156 {
157 return ::hipHostMalloc(ptr, new_size);
158 }
159 hipError_t _deallocate(void* ptr) final
160 {
161 return ::hipHostFree(ptr);
162 }
163 constexpr eMemoryResource memoryResource() const { return eMemoryResource::HostPinned; }
164};
165
166/*---------------------------------------------------------------------------*/
167/*---------------------------------------------------------------------------*/
168
169class HostPinnedHipMemoryAllocator
170: public AcceleratorMemoryAllocatorBase
171{
172 public:
173 public:
174
175 HostPinnedHipMemoryAllocator()
176 : AcceleratorMemoryAllocatorBase("HostPinnedHipMemory", new UnderlyingAllocator<HostPinnedConcreteAllocator>())
177 {
178 }
179
180 public:
181
182 void initialize()
183 {
185 }
186};
187
188/*---------------------------------------------------------------------------*/
189/*---------------------------------------------------------------------------*/
190
191class DeviceConcreteAllocator
192: public ConcreteAllocator
193{
194 public:
195
196 DeviceConcreteAllocator()
197 {
198 }
199
200 hipError_t _allocate(void** ptr, size_t new_size) final
201 {
202 hipError_t r = ::hipMalloc(ptr, new_size);
203 return r;
204 }
205 hipError_t _deallocate(void* ptr) final
206 {
207 return ::hipFree(ptr);
208 }
209
210 constexpr eMemoryResource memoryResource() const { return eMemoryResource::Device; }
211};
212
213/*---------------------------------------------------------------------------*/
214/*---------------------------------------------------------------------------*/
215
216class DeviceHipMemoryAllocator
217: public AcceleratorMemoryAllocatorBase
218{
219
220 public:
221
222 DeviceHipMemoryAllocator()
223 : AcceleratorMemoryAllocatorBase("DeviceHipMemoryAllocator", new UnderlyingAllocator<DeviceConcreteAllocator>())
224 {
225 }
226
227 public:
228
229 void initialize()
230 {
232 }
233};
234
235/*---------------------------------------------------------------------------*/
236/*---------------------------------------------------------------------------*/
237
238namespace
239{
240 UnifiedMemoryHipMemoryAllocator unified_memory_hip_memory_allocator;
241 HostPinnedHipMemoryAllocator host_pinned_hip_memory_allocator;
242 DeviceHipMemoryAllocator device_hip_memory_allocator;
243} // namespace
244
245/*---------------------------------------------------------------------------*/
246/*---------------------------------------------------------------------------*/
247
248void initializeHipMemoryAllocators()
249{
250 unified_memory_hip_memory_allocator.initialize();
251 device_hip_memory_allocator.initialize();
252 host_pinned_hip_memory_allocator.initialize();
253}
254
255void finalizeHipMemoryAllocators(ITraceMng* tm)
256{
257 unified_memory_hip_memory_allocator.finalize(tm);
258 device_hip_memory_allocator.finalize(tm);
259 host_pinned_hip_memory_allocator.finalize(tm);
260}
261
262/*---------------------------------------------------------------------------*/
263/*---------------------------------------------------------------------------*/
264
265class HipRunQueueStream
267{
268 public:
269
270 HipRunQueueStream(Impl::IRunnerRuntime* runtime, const RunQueueBuildInfo& bi)
271 : m_runtime(runtime)
272 {
273 if (bi.isDefault())
274 ARCCORE_CHECK_HIP(hipStreamCreate(&m_hip_stream));
275 else {
276 int priority = bi.priority();
277 ARCCORE_CHECK_HIP(hipStreamCreateWithPriority(&m_hip_stream, hipStreamDefault, priority));
278 }
279 }
280 ~HipRunQueueStream() override
281 {
282 ARCCORE_CHECK_HIP_NOTHROW(hipStreamDestroy(m_hip_stream));
283 }
284
285 public:
286
287 void notifyBeginLaunchKernel([[maybe_unused]] Impl::RunCommandImpl& c) override
288 {
289#ifdef ARCCORE_HAS_ROCTX
290 auto kname = c.kernelName();
291 if (kname.empty())
292 roctxRangePush(c.traceInfo().name());
293 else
294 roctxRangePush(kname.localstr());
295#endif
296 return m_runtime->notifyBeginLaunchKernel();
297 }
299 {
300#ifdef ARCCORE_HAS_ROCTX
301 roctxRangePop();
302#endif
303 return m_runtime->notifyEndLaunchKernel();
304 }
305 void barrier() override
306 {
307 ARCCORE_CHECK_HIP(hipStreamSynchronize(m_hip_stream));
308 }
309 bool _barrierNoException() override
310 {
311 return hipStreamSynchronize(m_hip_stream) != hipSuccess;
312 }
313 void copyMemory(const MemoryCopyArgs& args) override
314 {
315 auto r = hipMemcpyAsync(args.destination().data(), args.source().data(),
316 args.source().bytes().size(), hipMemcpyDefault, m_hip_stream);
317 ARCCORE_CHECK_HIP(r);
318 if (!args.isAsync())
319 barrier();
320 }
321 void prefetchMemory(const MemoryPrefetchArgs& args) override
322 {
323 auto src = args.source().bytes();
324 if (src.size() == 0)
325 return;
326 DeviceId d = args.deviceId();
327 int device = hipCpuDeviceId;
328 if (!d.isHost())
329 device = d.asInt32();
330 auto r = hipMemPrefetchAsync(src.data(), src.size(), device, m_hip_stream);
331 ARCCORE_CHECK_HIP(r);
332 if (!args.isAsync())
333 barrier();
334 }
336 {
337 return Impl::NativeStream(&m_hip_stream);
338 }
339
340 public:
341
342 hipStream_t trueStream() const
343 {
344 return m_hip_stream;
345 }
346
347 private:
348
349 Impl::IRunnerRuntime* m_runtime;
350 hipStream_t m_hip_stream;
351};
352
353/*---------------------------------------------------------------------------*/
354/*---------------------------------------------------------------------------*/
355
356class HipRunQueueEvent
358{
359 public:
360
361 explicit HipRunQueueEvent(bool has_timer)
362 {
363 if (has_timer)
364 ARCCORE_CHECK_HIP(hipEventCreate(&m_hip_event));
365 else
366 ARCCORE_CHECK_HIP(hipEventCreateWithFlags(&m_hip_event, hipEventDisableTiming));
367 }
368 ~HipRunQueueEvent() override
369 {
370 ARCCORE_CHECK_HIP_NOTHROW(hipEventDestroy(m_hip_event));
371 }
372
373 public:
374
375 // Register the event within a RunQueue
376 void recordQueue(Impl::IRunQueueStream* stream) final
377 {
378 auto* rq = static_cast<HipRunQueueStream*>(stream);
379 ARCCORE_CHECK_HIP(hipEventRecord(m_hip_event, rq->trueStream()));
380 }
381
382 void wait() final
383 {
384 ARCCORE_CHECK_HIP(hipEventSynchronize(m_hip_event));
385 }
386
387 void waitForEvent(Impl::IRunQueueStream* stream) final
388 {
389 auto* rq = static_cast<HipRunQueueStream*>(stream);
390 ARCCORE_CHECK_HIP(hipStreamWaitEvent(rq->trueStream(), m_hip_event, 0));
391 }
392
393 Int64 elapsedTime(IRunQueueEventImpl* from_event) final
394 {
395 auto* true_from_event = static_cast<HipRunQueueEvent*>(from_event);
396 ARCCORE_CHECK_POINTER(true_from_event);
397 float time_in_ms = 0.0;
398 ARCCORE_CHECK_HIP(hipEventElapsedTime(&time_in_ms, true_from_event->m_hip_event, m_hip_event));
399 double x = time_in_ms * 1.0e6;
400 Int64 nano_time = static_cast<Int64>(x);
401 return nano_time;
402 }
403
404 bool hasPendingWork() final
405 {
406 hipError_t v = hipEventQuery(m_hip_event);
407 if (v == hipErrorNotReady)
408 return true;
409 ARCCORE_CHECK_HIP(v);
410 return false;
411 }
412
413 private:
414
415 hipEvent_t m_hip_event;
416};
417
418/*---------------------------------------------------------------------------*/
419/*---------------------------------------------------------------------------*/
420
423{
424 public:
425
426 ~HipRunnerRuntime() override = default;
427
428 public:
429
430 void notifyBeginLaunchKernel() override
431 {
432 ++m_nb_kernel_launched;
433 if (m_is_verbose)
434 std::cout << "BEGIN HIP KERNEL!\n";
435 }
436 void notifyEndLaunchKernel() override
437 {
438 ARCCORE_CHECK_HIP(hipGetLastError());
439 if (m_is_verbose)
440 std::cout << "END HIP KERNEL!\n";
441 }
442 void barrier() override
443 {
444 ARCCORE_CHECK_HIP(hipDeviceSynchronize());
445 }
446 eExecutionPolicy executionPolicy() const override
447 {
449 }
450 Impl::IRunQueueStream* createStream(const RunQueueBuildInfo& bi) override
451 {
452 return new HipRunQueueStream(this, bi);
453 }
454 Impl::IRunQueueEventImpl* createEventImpl() override
455 {
456 return new HipRunQueueEvent(false);
457 }
458 Impl::IRunQueueEventImpl* createEventImplWithTimer() override
459 {
460 return new HipRunQueueEvent(true);
461 }
462 void setMemoryAdvice(ConstMemoryView buffer, eMemoryAdvice advice, DeviceId device_id) override
463 {
464 auto v = buffer.bytes();
465 const void* ptr = v.data();
466 size_t count = v.size();
467 int device = device_id.asInt32();
468 hipMemoryAdvise hip_advise;
469
470 if (advice == eMemoryAdvice::MostlyRead)
471 hip_advise = hipMemAdviseSetReadMostly;
473 hip_advise = hipMemAdviseSetPreferredLocation;
474 else if (advice == eMemoryAdvice::AccessedByDevice)
475 hip_advise = hipMemAdviseSetAccessedBy;
476 else if (advice == eMemoryAdvice::PreferredLocationHost) {
477 hip_advise = hipMemAdviseSetPreferredLocation;
478 device = hipCpuDeviceId;
479 }
480 else if (advice == eMemoryAdvice::AccessedByHost) {
481 hip_advise = hipMemAdviseSetAccessedBy;
482 device = hipCpuDeviceId;
483 }
484 else
485 return;
486 //std::cout << "MEMADVISE p=" << ptr << " size=" << count << " advise = " << hip_advise << " id = " << device << "\n";
487 ARCCORE_CHECK_HIP(hipMemAdvise(ptr, count, hip_advise, device));
488 }
489 void unsetMemoryAdvice(ConstMemoryView buffer, eMemoryAdvice advice, DeviceId device_id) override
490 {
491 auto v = buffer.bytes();
492 const void* ptr = v.data();
493 size_t count = v.size();
494 int device = device_id.asInt32();
495 hipMemoryAdvise hip_advise;
496
497 if (advice == eMemoryAdvice::MostlyRead)
498 hip_advise = hipMemAdviseUnsetReadMostly;
500 hip_advise = hipMemAdviseUnsetPreferredLocation;
501 else if (advice == eMemoryAdvice::AccessedByDevice)
502 hip_advise = hipMemAdviseUnsetAccessedBy;
503 else if (advice == eMemoryAdvice::PreferredLocationHost) {
504 hip_advise = hipMemAdviseUnsetPreferredLocation;
505 device = hipCpuDeviceId;
506 }
507 else if (advice == eMemoryAdvice::AccessedByHost) {
508 hip_advise = hipMemAdviseUnsetAccessedBy;
509 device = hipCpuDeviceId;
510 }
511 else
512 return;
513 ARCCORE_CHECK_HIP(hipMemAdvise(ptr, count, hip_advise, device));
514 }
515
516 void setCurrentDevice(DeviceId device_id) final
517 {
518 Int32 id = device_id.asInt32();
519 ARCCORE_FATAL_IF(!device_id.isAccelerator(), "Device {0} is not an accelerator device", id);
520 ARCCORE_CHECK_HIP(hipSetDevice(id));
521 }
522 const IDeviceInfoList* deviceInfoList() override { return &m_device_info_list; }
523
524 void getPointerAttribute(PointerAttribute& attribute, const void* ptr) override
525 {
526 hipPointerAttribute_t pa;
527 hipError_t ret_value = hipPointerGetAttributes(&pa, ptr);
528 auto mem_type = ePointerMemoryType::Unregistered;
529 // If ptr has not been dynamically allocated (i.e.: it is on the stack),
530 // hipPointerGetAttribute() returns an error. In this case, we consider
531 // the memory as unregistered.
532 if (ret_value == hipSuccess) {
533#if HIP_VERSION_MAJOR >= 6
534 auto rocm_memory_type = pa.type;
535#else
536 auto rocm_memory_type = pa.memoryType;
537#endif
538 if (pa.isManaged)
539 mem_type = ePointerMemoryType::Managed;
540 else if (rocm_memory_type == hipMemoryTypeHost)
541 mem_type = ePointerMemoryType::Host;
542 else if (rocm_memory_type == hipMemoryTypeDevice)
543 mem_type = ePointerMemoryType::Device;
544 }
545
546 //std::cout << "HIP Info: hip_memory_type=" << (int)pa.memoryType << " is_managed?=" << pa.isManaged
547 // << " flags=" << pa.allocationFlags
548 // << " my_memory_type=" << (int)mem_type
549 // << "\n";
550 _fillPointerAttribute(attribute, mem_type, pa.device,
551 ptr, pa.devicePointer, pa.hostPointer);
552 }
553
554 DeviceMemoryInfo getDeviceMemoryInfo(DeviceId device_id) override
555 {
556 int d = 0;
557 int wanted_d = device_id.asInt32();
558 ARCCORE_CHECK_HIP(hipGetDevice(&d));
559 if (d != wanted_d)
560 ARCCORE_CHECK_HIP(hipSetDevice(wanted_d));
561 size_t free_mem = 0;
562 size_t total_mem = 0;
563 ARCCORE_CHECK_HIP(hipMemGetInfo(&free_mem, &total_mem));
564 if (d != wanted_d)
565 ARCCORE_CHECK_HIP(hipSetDevice(d));
567 dmi.setFreeMemory(free_mem);
568 dmi.setTotalMemory(total_mem);
569 return dmi;
570 }
571
572 void pushProfilerRange(const String& name, [[maybe_unused]] Int32 color) override
573 {
574#ifdef ARCCORE_HAS_ROCTX
575 roctxRangePush(name.localstr());
576#endif
577 }
578 void popProfilerRange() override
579 {
580#ifdef ARCCORE_HAS_ROCTX
581 roctxRangePop();
582#endif
583 }
584
585 void finalize(ITraceMng* tm) override
586 {
587 finalizeHipMemoryAllocators(tm);
588 }
589
590 KernelLaunchArgs computeKernalLaunchArgs(const KernelLaunchArgs& orig_args,
591 const void* kernel_ptr,
592 Int64 total_loop_size) override
593 {
594 Int32 shared_memory = orig_args.sharedMemorySize();
595 if (orig_args.isCooperative()) {
596 // In cooperative mode, ensure that we do not launch more blocks
597 // than the maximum that can reside on the GPU.
598 Int32 nb_thread = orig_args.nbThreadPerBlock();
599 Int32 nb_block = orig_args.nbBlockPerGrid();
600 int nb_block_per_sm = 0;
601 ARCCORE_CHECK_HIP(hipOccupancyMaxActiveBlocksPerMultiprocessor(&nb_block_per_sm, kernel_ptr, nb_thread, shared_memory));
602
603 int max_block = static_cast<int>((nb_block_per_sm * m_multi_processor_count) * m_cooperative_ratio);
604 max_block = std::max(max_block, 1);
605 if (nb_block > max_block) {
606 KernelLaunchArgs modified_args(orig_args);
607 modified_args.setNbBlockPerGrid(max_block);
608 return modified_args;
609 }
610 }
611 return orig_args;
612 }
613
614 public:
615
616 void fillDevices(bool is_verbose);
617
618 void build()
619 {
620 if (auto v = Convert::Type<Int32>::tryParseFromEnvironment("ARCANE_ACCELERATOR_COOPERATIVE_RATIO", true)) {
621 Int32 x = v.value();
622 x = std::clamp(x, 10, 100);
623 m_cooperative_ratio = x / 100.0;
624 }
625 }
626
627 private:
628
629 Int64 m_nb_kernel_launched = 0;
630 bool m_is_verbose = false;
631 Int32 m_multi_processor_count = 0;
632 double m_cooperative_ratio = 1.0;
633 Impl::DeviceInfoList m_device_info_list;
634};
635
636/*---------------------------------------------------------------------------*/
637/*---------------------------------------------------------------------------*/
638
639void HipRunnerRuntime::
640fillDevices(bool is_verbose)
641{
642 int nb_device = 0;
643 ARCCORE_CHECK_HIP(hipGetDeviceCount(&nb_device));
644 std::ostream& omain = std::cout;
645 if (is_verbose)
646 omain << "ArcaneHIP: Initialize Arcane HIP runtime nb_available_device=" << nb_device << "\n";
647 for (int i = 0; i < nb_device; ++i) {
648 std::ostringstream ostr;
649 std::ostream& o = ostr;
650
651 hipDeviceProp_t dp;
652 ARCCORE_CHECK_HIP(hipGetDeviceProperties(&dp, i));
653
654 int has_managed_memory = 0;
655 ARCCORE_CHECK_HIP(hipDeviceGetAttribute(&has_managed_memory, hipDeviceAttributeManagedMemory, i));
656
657 // The format of versions in HIP is:
658 // HIP_VERSION = (HIP_VERSION_MAJOR * 10000000 + HIP_VERSION_MINOR * 100000 + HIP_VERSION_PATCH)
659
660 int runtime_version = 0;
661 ARCCORE_CHECK_HIP(hipRuntimeGetVersion(&runtime_version));
662 //runtime_version /= 10000;
663 int runtime_major = runtime_version / 10000000;
664 int runtime_minor = (runtime_version / 100000) % 100;
665
666 int driver_version = 0;
667 ARCCORE_CHECK_HIP(hipDriverGetVersion(&driver_version));
668 //driver_version /= 10000;
669 int driver_major = driver_version / 10000000;
670 int driver_minor = (driver_version / 100000) % 100;
671
672 o << "\nDevice " << i << " name=" << dp.name << "\n";
673 o << " Driver version = " << driver_major << "." << (driver_minor) << "." << (driver_version % 100000) << "\n";
674 o << " Runtime version = " << runtime_major << "." << (runtime_minor) << "." << (runtime_version % 100000) << "\n";
675 o << " computeCapability = " << dp.major << "." << dp.minor << "\n";
676 o << " totalGlobalMem = " << dp.totalGlobalMem << "\n";
677 o << " regsPerBlock = " << dp.regsPerBlock << "\n";
678 o << " warpSize = " << dp.warpSize << "\n";
679 o << " memPitch = " << dp.memPitch << "\n";
680 o << " maxThreadsPerBlock = " << dp.maxThreadsPerBlock << "\n";
681 o << " maxBlocksPerMultiProcessor = " << dp.maxBlocksPerMultiProcessor << "\n";
682 o << " maxThreadsPerMultiProcessor = " << dp.maxThreadsPerMultiProcessor << "\n";
683 o << " totalConstMem = " << dp.totalConstMem << "\n";
684 o << " clockRate = " << dp.clockRate << "\n";
685 //o << " deviceOverlap = " << dp.deviceOverlap<< "\n";
686 o << " multiProcessorCount = " << dp.multiProcessorCount << "\n";
687 o << " kernelExecTimeoutEnabled = " << dp.kernelExecTimeoutEnabled << "\n";
688 o << " integrated = " << dp.integrated << "\n";
689 o << " canMapHostMemory = " << dp.canMapHostMemory << "\n";
690 o << " computeMode = " << dp.computeMode << "\n";
691 o << " maxThreadsDim = " << dp.maxThreadsDim[0] << " " << dp.maxThreadsDim[1]
692 << " " << dp.maxThreadsDim[2] << "\n";
693 o << " maxGridSize = " << dp.maxGridSize[0] << " " << dp.maxGridSize[1]
694 << " " << dp.maxGridSize[2] << "\n";
695 o << " concurrentManagedAccess = " << dp.concurrentManagedAccess << "\n";
696 o << " directManagedMemAccessFromHost = " << dp.directManagedMemAccessFromHost << "\n";
697 o << " gcnArchName = " << dp.gcnArchName << "\n";
698 o << " pageableMemoryAccess = " << dp.pageableMemoryAccess << "\n";
699 o << " pageableMemoryAccessUsesHostPageTables = " << dp.pageableMemoryAccessUsesHostPageTables << "\n";
700 o << " hasManagedMemory = " << has_managed_memory << "\n";
701 o << " pciInfo = " << dp.pciDomainID << " " << dp.pciBusID << " " << dp.pciDeviceID << "\n";
702 o << " memoryBusWitdh = " << dp.memoryBusWidth << " bits\n";
703
704 int clock_rate = 0;
705 ARCCORE_CHECK_HIP(hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, i));
706 o << " clockRate = " << (clock_rate / 1000) << " MHz\n";
707
708 int memory_clock_rate = 0;
709 ARCCORE_CHECK_HIP(hipDeviceGetAttribute(&memory_clock_rate, hipDeviceAttributeMemoryClockRate, i));
710 o << " memoryClockRate = " << (memory_clock_rate / 1000) << " MHz\n";
711
712 // On AMD, the frequency given for memory must be multiplied by 8
713 // to get the bandwidth of a bit of the bus (since we also have to divide by 8
714 // to get the value in bytes, we simply omit this division)
715 Real memory_bandwith = (dp.memoryBusWidth * memory_clock_rate * 2.0) / 1.0e6;
716 o << " MemoryBandwith = " << memory_bandwith << " GB/s\n";
717
718#if HIP_VERSION_MAJOR >= 6
719 o << " sharedMemPerMultiprocessor = " << dp.sharedMemPerMultiprocessor << "\n";
720 o << " sharedMemPerBlock = " << dp.sharedMemPerBlock << "\n";
721 o << " sharedMemPerBlockOptin = " << dp.sharedMemPerBlockOptin << "\n";
722 o << " gpuDirectRDMASupported = " << dp.gpuDirectRDMASupported << "\n";
723 o << " hostNativeAtomicSupported = " << dp.hostNativeAtomicSupported << "\n";
724 o << " unifiedFunctionPointers = " << dp.unifiedFunctionPointers << "\n";
725#endif
726
727 // TODO: We assume that all GPUs are the same and therefore
728 // that the number of SMs per GPU is the same. This is used to
729 // calculate the number of blocks in cooperative mode.
730 m_multi_processor_count = dp.multiProcessorCount;
731
732 std::ostringstream device_uuid_ostr;
733 {
734 hipDevice_t device;
735 ARCCORE_CHECK_HIP(hipDeviceGet(&device, i));
736 hipUUID device_uuid;
737 ARCCORE_CHECK_HIP(hipDeviceGetUuid(&device_uuid, device));
738 o << " deviceUuid=";
739 Impl::printUUID(device_uuid_ostr, device_uuid.bytes);
740 o << device_uuid_ostr.str();
741 o << "\n";
742 }
743
744 String description(ostr.str());
745 if (is_verbose)
746 omain << description;
747
748 DeviceInfo device_info;
749 device_info.setDescription(description);
750 device_info.setDeviceId(DeviceId(i));
751 device_info.setName(dp.name);
752 device_info.setWarpSize(dp.warpSize);
753 device_info.setUUIDAsString(device_uuid_ostr.str());
754 device_info.setSharedMemoryPerBlock(static_cast<Int32>(dp.sharedMemPerBlock));
755#if HIP_VERSION_MAJOR >= 6
756 device_info.setSharedMemoryPerMultiprocessor(static_cast<Int32>(dp.sharedMemPerMultiprocessor));
757 device_info.setSharedMemoryPerBlockOptin(static_cast<Int32>(dp.sharedMemPerBlockOptin));
758#endif
759 device_info.setTotalConstMemory(static_cast<Int32>(dp.totalConstMem));
760 device_info.setPCIDomainID(dp.pciDomainID);
761 device_info.setPCIBusID(dp.pciBusID);
762 device_info.setPCIDeviceID(dp.pciDeviceID);
763 m_device_info_list.addDevice(device_info);
764 }
765}
766
767/*---------------------------------------------------------------------------*/
768/*---------------------------------------------------------------------------*/
769
771: public IMemoryCopier
772{
773 void copy(ConstMemoryView from, [[maybe_unused]] eMemoryResource from_mem,
774 MutableMemoryView to, [[maybe_unused]] eMemoryResource to_mem,
775 const RunQueue* queue) override
776 {
777 if (queue) {
778 queue->copyMemory(MemoryCopyArgs(to.bytes(), from.bytes()).addAsync(queue->isAsync()));
779 return;
780 }
781 // 'hipMemcpyDefault' automatically knows what to do by only considering
782 // the value of the pointers. We should see if
783 // using from_mem and to_mem can improve performance.
784 ARCCORE_CHECK_HIP(hipMemcpy(to.data(), from.data(), from.bytes().size(), hipMemcpyDefault));
785 }
786};
787
788/*---------------------------------------------------------------------------*/
789/*---------------------------------------------------------------------------*/
790
791} // End namespace Arcane::Accelerator::Hip
792
793using namespace Arcane;
794
795namespace
796{
798Arcane::Accelerator::Hip::HipMemoryCopier global_hip_memory_copier;
799
800void _setAllocator(Accelerator::AcceleratorMemoryAllocatorBase* allocator)
801{
803 eMemoryResource mem = allocator->memoryResource();
804 mrm->setAllocator(mem, allocator);
805 mrm->setMemoryPool(mem, allocator->memoryPool());
806}
807} // namespace
808
809/*---------------------------------------------------------------------------*/
810/*---------------------------------------------------------------------------*/
811
812// This function is the entry point used when dynamically loading
813// this library
814extern "C" ARCCORE_EXPORT void
815arcaneRegisterAcceleratorRuntimehip(Arcane::Accelerator::RegisterRuntimeInfo& init_info)
816{
817 using namespace Arcane::Accelerator::Hip;
818 global_hip_runtime.build();
819 Arcane::Accelerator::Impl::setUsingHIPRuntime(true);
820 Arcane::Accelerator::Impl::setHIPRunQueueRuntime(&global_hip_runtime);
821 initializeHipMemoryAllocators();
823 MemoryUtils::setAcceleratorHostMemoryAllocator(&unified_memory_hip_memory_allocator);
824 IMemoryResourceMngInternal* mrm = MemoryUtils::getDataMemoryResourceMng()->_internal();
825 mrm->setIsAccelerator(true);
826 _setAllocator(&unified_memory_hip_memory_allocator);
827 _setAllocator(&host_pinned_hip_memory_allocator);
828 _setAllocator(&device_hip_memory_allocator);
829 mrm->setCopier(&global_hip_memory_copier);
830 global_hip_runtime.fillDevices(init_info.isVerbose());
831}
832
833/*---------------------------------------------------------------------------*/
834/*---------------------------------------------------------------------------*/
#define ARCCORE_CHECK_POINTER(ptr)
Macro that returns the pointer ptr if it is not null or throws an exception if it is null.
#define ARCCORE_FATAL_IF(cond,...)
Macro throwing a FatalErrorException if cond is true.
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.
bool isHost() const
Indicates if the instance is associated with the host.
bool isAccelerator() const
Indicates if the instance is associated with an accelerator.
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 notifyBeginLaunchKernel(Impl::RunCommandImpl &c) override
Notification before command launch.
void notifyEndLaunchKernel(Impl::RunCommandImpl &) override
Notification of command launch completion.
bool _barrierNoException() override
Barrier without exception. Returns true in case of error.
void barrier() override
Blocks until all actions associated with this queue are finished.
void prefetchMemory(const MemoryPrefetchArgs &args) override
Performs a prefetch of a memory region.
void copyMemory(const MemoryCopyArgs &args) override
Performs a copy between two memory regions.
Impl::NativeStream nativeStream() override
Pointer to the internal structure dependent on the implementation.
void freeMemory(void *ptr, Int64 size) final
Frees the block located at address address containing size bytes.
void * allocateMemory(Int64 size) final
Allocates a block for size bytes.
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
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.
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.
eExecutionPolicy
Execution policy for a Runner.
@ HIP
Execution policy using the HIP 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.
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.
Namespace of Arccore.