Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
HipAcceleratorRuntime.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2024 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-2024 */
9/* */
10/* Runtime pour 'HIP'. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/accelerator/hip/HipAccelerator.h"
15
16#include "arcane/utils/PlatformUtils.h"
17#include "arcane/utils/Array.h"
18#include "arcane/utils/TraceInfo.h"
19#include "arcane/utils/FatalErrorException.h"
20#include "arcane/utils/NotImplementedException.h"
21#include "arcane/utils/IMemoryRessourceMng.h"
22#include "arcane/utils/OStringStream.h"
23#include "arcane/utils/internal/IMemoryRessourceMngInternal.h"
24
25#include "arcane/accelerator/core/RunQueueBuildInfo.h"
26#include "arcane/accelerator/core/Memory.h"
27#include "arcane/accelerator/core/internal/IRunnerRuntime.h"
28#include "arcane/accelerator/core/internal/RegisterRuntimeInfo.h"
29#include "arcane/accelerator/core/internal/IRunQueueStream.h"
30#include "arcane/accelerator/core/internal/IRunQueueEventImpl.h"
31#include "arcane/accelerator/core/DeviceInfoList.h"
32#include "arcane/accelerator/core/RunQueue.h"
33#include "arcane/accelerator/core/internal/RunCommandImpl.h"
34
35#include <iostream>
36
37#ifdef ARCANE_HAS_ROCTX
38#include <roctx.h>
39#endif
40
41using namespace Arccore;
42
43namespace Arcane::Accelerator::Hip
44{
45
46/*---------------------------------------------------------------------------*/
47/*---------------------------------------------------------------------------*/
48
51{
52 public:
53
55 : m_runtime(runtime)
56 {
57 if (bi.isDefault())
58 ARCANE_CHECK_HIP(hipStreamCreate(&m_hip_stream));
59 else {
60 int priority = bi.priority();
61 ARCANE_CHECK_HIP(hipStreamCreateWithPriority(&m_hip_stream, hipStreamDefault, priority));
62 }
63 }
64 ~HipRunQueueStream() override
65 {
66 ARCANE_CHECK_HIP_NOTHROW(hipStreamDestroy(m_hip_stream));
67 }
68
69 public:
70
72 {
73#ifdef ARCANE_HAS_ROCTX
74 auto kname = c.kernelName();
75 if (kname.empty())
76 roctxRangePush(c.traceInfo().name());
77 else
78 roctxRangePush(kname.localstr());
79#endif
80 return m_runtime->notifyBeginLaunchKernel();
81 }
83 {
84#ifdef ARCANE_HAS_ROCTX
86#endif
87 return m_runtime->notifyEndLaunchKernel();
88 }
89 void barrier() override
90 {
91 ARCANE_CHECK_HIP(hipStreamSynchronize(m_hip_stream));
92 }
93 bool _barrierNoException() override
94 {
95 return hipStreamSynchronize(m_hip_stream) != hipSuccess;
96 }
97 void copyMemory(const MemoryCopyArgs& args) override
98 {
99 auto r = hipMemcpyAsync(args.destination().data(), args.source().data(),
100 args.source().bytes().size(), hipMemcpyDefault, m_hip_stream);
101 ARCANE_CHECK_HIP(r);
102 if (!args.isAsync())
103 barrier();
104 }
105 void prefetchMemory(const MemoryPrefetchArgs& args) override
106 {
107 auto src = args.source().bytes();
108 if (src.size()==0)
109 return;
110 DeviceId d = args.deviceId();
111 int device = hipCpuDeviceId;
112 if (!d.isHost())
113 device = d.asInt32();
114 auto r = hipMemPrefetchAsync(src.data(), src.size(), device, m_hip_stream);
115 ARCANE_CHECK_HIP(r);
116 if (!args.isAsync())
117 barrier();
118 }
119 void* _internalImpl() override
120 {
121 return &m_hip_stream;
122 }
123
124 public:
125
126 hipStream_t trueStream() const
127 {
128 return m_hip_stream;
129 }
130
131 private:
132
133 impl::IRunnerRuntime* m_runtime;
134 hipStream_t m_hip_stream;
135};
136
137/*---------------------------------------------------------------------------*/
138/*---------------------------------------------------------------------------*/
139
142{
143 public:
144
145 explicit HipRunQueueEvent(bool has_timer)
146 {
147 if (has_timer)
148 ARCANE_CHECK_HIP(hipEventCreate(&m_hip_event));
149 else
150 ARCANE_CHECK_HIP(hipEventCreateWithFlags(&m_hip_event, hipEventDisableTiming));
151 }
152 ~HipRunQueueEvent() override
153 {
154 ARCANE_CHECK_HIP_NOTHROW(hipEventDestroy(m_hip_event));
155 }
156
157 public:
158
159 // Enregistre l'événement au sein d'une RunQueue
160 void recordQueue(impl::IRunQueueStream* stream) final
161 {
162 auto* rq = static_cast<HipRunQueueStream*>(stream);
163 ARCANE_CHECK_HIP(hipEventRecord(m_hip_event, rq->trueStream()));
164 }
165
166 void wait() final
167 {
168 ARCANE_CHECK_HIP(hipEventSynchronize(m_hip_event));
169 }
170
171 void waitForEvent(impl::IRunQueueStream* stream) final
172 {
173 auto* rq = static_cast<HipRunQueueStream*>(stream);
174 ARCANE_CHECK_HIP(hipStreamWaitEvent(rq->trueStream(), m_hip_event, 0));
175 }
176
177 Int64 elapsedTime(IRunQueueEventImpl* from_event) final
178 {
179 auto* true_from_event = static_cast<HipRunQueueEvent*>(from_event);
181 float time_in_ms = 0.0;
182 ARCANE_CHECK_HIP(hipEventElapsedTime(&time_in_ms, true_from_event->m_hip_event, m_hip_event));
183 double x = time_in_ms * 1.0e6;
184 Int64 nano_time = static_cast<Int64>(x);
185 return nano_time;
186 }
187
188 private:
189
190 hipEvent_t m_hip_event;
191};
192
193/*---------------------------------------------------------------------------*/
194/*---------------------------------------------------------------------------*/
195
198{
199 public:
200
201 ~HipRunnerRuntime() override = default;
202
203 public:
204
205 void notifyBeginLaunchKernel() override
206 {
207 ++m_nb_kernel_launched;
208 if (m_is_verbose)
209 std::cout << "BEGIN HIP KERNEL!\n";
210 }
211 void notifyEndLaunchKernel() override
212 {
213 ARCANE_CHECK_HIP(hipGetLastError());
214 if (m_is_verbose)
215 std::cout << "END HIP KERNEL!\n";
216 }
217 void barrier() override
218 {
219 ARCANE_CHECK_HIP(hipDeviceSynchronize());
220 }
221 eExecutionPolicy executionPolicy() const override
222 {
224 }
225 impl::IRunQueueStream* createStream(const RunQueueBuildInfo& bi) override
226 {
227 return new HipRunQueueStream(this, bi);
228 }
229 impl::IRunQueueEventImpl* createEventImpl() override
230 {
231 return new HipRunQueueEvent(false);
232 }
233 impl::IRunQueueEventImpl* createEventImplWithTimer() override
234 {
235 return new HipRunQueueEvent(true);
236 }
237 void setMemoryAdvice(ConstMemoryView buffer, eMemoryAdvice advice, DeviceId device_id) override
238 {
239 auto v = buffer.bytes();
240 const void* ptr = v.data();
241 size_t count = v.size();
242 int device = device_id.asInt32();
244
253 device = hipCpuDeviceId;
254 }
257 device = hipCpuDeviceId;
258 }
259 else
260 return;
261 //std::cout << "MEMADVISE p=" << ptr << " size=" << count << " advise = " << hip_advise << " id = " << device << "\n";
262 ARCANE_CHECK_HIP(hipMemAdvise(ptr, count, hip_advise, device));
263 }
264 void unsetMemoryAdvice(ConstMemoryView buffer, eMemoryAdvice advice, DeviceId device_id) override
265 {
266 auto v = buffer.bytes();
267 const void* ptr = v.data();
268 size_t count = v.size();
269 int device = device_id.asInt32();
271
280 device = hipCpuDeviceId;
281 }
284 device = hipCpuDeviceId;
285 }
286 else
287 return;
288 ARCANE_CHECK_HIP(hipMemAdvise(ptr, count, hip_advise, device));
289 }
290
291 void setCurrentDevice(DeviceId device_id) final
292 {
293 Int32 id = device_id.asInt32();
294 if (!device_id.isAccelerator())
295 ARCANE_FATAL("Device {0} is not an accelerator device", id);
296 ARCANE_CHECK_HIP(hipSetDevice(id));
297 }
298 const IDeviceInfoList* deviceInfoList() override { return &m_device_info_list; }
299
300 void getPointerAttribute(PointerAttribute& attribute, const void* ptr) override
301 {
304 auto mem_type = ePointerMemoryType::Unregistered;
305 // Si \a ptr n'a pas été alloué dynamiquement (i.e: il est sur la pile),
306 // hipPointerGetAttribute() retourne une erreur. Dans ce cas on considère
307 // la mémoire comme non enregistrée.
308 if (ret_value==hipSuccess){
309#if HIP_VERSION_MAJOR >= 6
310 auto rocm_memory_type = pa.type;
311#else
312 auto rocm_memory_type = pa.memoryType;
313#endif
314 if (pa.isManaged)
315 mem_type = ePointerMemoryType::Managed;
317 mem_type = ePointerMemoryType::Host;
319 mem_type = ePointerMemoryType::Device;
320 }
321
322 //std::cout << "HIP Info: hip_memory_type=" << (int)pa.memoryType << " is_managed?=" << pa.isManaged
323 // << " flags=" << pa.allocationFlags
324 // << " my_memory_type=" << (int)mem_type
325 // << "\n";
326 _fillPointerAttribute(attribute, mem_type, pa.device,
327 ptr, pa.devicePointer, pa.hostPointer);
328 }
329
330 void pushProfilerRange(const String& name, [[maybe_unused]] Int32 color) override
331 {
332#ifdef ARCANE_HAS_ROCTX
333 roctxRangePush(name.localstr());
334#endif
335 }
336 void popProfilerRange() override
337 {
338#ifdef ARCANE_HAS_ROCTX
340#endif
341 }
342
343 public:
344
345 void fillDevices(bool is_verbose);
346
347 private:
348
349 Int64 m_nb_kernel_launched = 0;
350 bool m_is_verbose = false;
351 impl::DeviceInfoList m_device_info_list;
352};
353
354/*---------------------------------------------------------------------------*/
355/*---------------------------------------------------------------------------*/
356
357void HipRunnerRuntime::
358fillDevices(bool is_verbose)
359{
360 int nb_device = 0;
361 ARCANE_CHECK_HIP(hipGetDeviceCount(&nb_device));
362 std::ostream& omain = std::cout;
363 if (is_verbose)
364 omain << "ArcaneHIP: Initialize Arcane HIP runtime nb_available_device=" << nb_device << "\n";
365 for (int i = 0; i < nb_device; ++i) {
367 std::ostream& o = ostr.stream();
368
370 ARCANE_CHECK_HIP(hipGetDeviceProperties(&dp, i));
371
372 int has_managed_memory = 0;
374
375 o << "\nDevice " << i << " name=" << dp.name << "\n";
376 o << " computeCapability = " << dp.major << "." << dp.minor << "\n";
377 o << " totalGlobalMem = " << dp.totalGlobalMem << "\n";
378 o << " sharedMemPerBlock = " << dp.sharedMemPerBlock << "\n";
379 o << " regsPerBlock = " << dp.regsPerBlock << "\n";
380 o << " warpSize = " << dp.warpSize << "\n";
381 o << " memPitch = " << dp.memPitch << "\n";
382 o << " maxThreadsPerBlock = " << dp.maxThreadsPerBlock << "\n";
383 o << " totalConstMem = " << dp.totalConstMem << "\n";
384 o << " clockRate = " << dp.clockRate << "\n";
385 //o << " deviceOverlap = " << dp.deviceOverlap<< "\n";
386 o << " multiProcessorCount = " << dp.multiProcessorCount << "\n";
387 o << " kernelExecTimeoutEnabled = " << dp.kernelExecTimeoutEnabled << "\n";
388 o << " integrated = " << dp.integrated << "\n";
389 o << " canMapHostMemory = " << dp.canMapHostMemory << "\n";
390 o << " computeMode = " << dp.computeMode << "\n";
391 o << " maxThreadsDim = " << dp.maxThreadsDim[0] << " " << dp.maxThreadsDim[1]
392 << " " << dp.maxThreadsDim[2] << "\n";
393 o << " maxGridSize = " << dp.maxGridSize[0] << " " << dp.maxGridSize[1]
394 << " " << dp.maxGridSize[2] << "\n";
395 o << " hasManagedMemory = " << has_managed_memory << "\n";
396
397 String description(ostr.str());
398 if (is_verbose)
399 omain << description;
400
402 device_info.setDescription(description);
403 device_info.setDeviceId(DeviceId(i));
404 device_info.setName(dp.name);
405 m_device_info_list.addDevice(device_info);
406 }
407}
408
409/*---------------------------------------------------------------------------*/
410/*---------------------------------------------------------------------------*/
411
413: public IMemoryCopier
414{
415 void copy(ConstMemoryView from, [[maybe_unused]] eMemoryRessource from_mem,
417 const RunQueue* queue) override
418 {
419 if (queue){
420 queue->copyMemory(MemoryCopyArgs(to.bytes(),from.bytes()).addAsync(queue->isAsync()));
421 return;
422 }
423 // 'hipMemcpyDefault' sait automatiquement ce qu'il faut faire en tenant
424 // uniquement compte de la valeur des pointeurs. Il faudrait voir si
425 // utiliser \a from_mem et \a to_mem peut améliorer les performances.
426 ARCANE_CHECK_HIP(hipMemcpy(to.data(), from.data(), from.bytes().size(), hipMemcpyDefault));
427 }
428};
429
430/*---------------------------------------------------------------------------*/
431/*---------------------------------------------------------------------------*/
432
433} // End namespace Arcane::Accelerator::Hip
434
435namespace
436{
439}
440
441/*---------------------------------------------------------------------------*/
442/*---------------------------------------------------------------------------*/
443
444// Cette fonction est le point d'entrée utilisé lors du chargement
445// dynamique de cette bibliothèque
446extern "C" ARCANE_EXPORT void
447arcaneRegisterAcceleratorRuntimehip(Arcane::Accelerator::RegisterRuntimeInfo& init_info)
448{
449 using namespace Arcane;
450 using namespace Arcane::Accelerator::Hip;
451 Arcane::Accelerator::impl::setUsingHIPRuntime(true);
452 Arcane::Accelerator::impl::setHIPRunQueueRuntime(&global_hip_runtime);
455 mrm->setIsAccelerator(true);
456 mrm->setAllocator(eMemoryRessource::UnifiedMemory, getHipUnifiedMemoryAllocator());
457 mrm->setAllocator(eMemoryRessource::HostPinned, getHipHostPinnedMemoryAllocator());
458 mrm->setAllocator(eMemoryRessource::Device, getHipDeviceMemoryAllocator());
459 mrm->setCopier(&global_hip_memory_copier);
460 global_hip_runtime.fillDevices(init_info.isVerbose());
461}
462
463/*---------------------------------------------------------------------------*/
464/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro retournant le pointeur ptr s'il est non nul ou lancant une exception s'il est nul.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Identifiant d'un composant du système.
Definition DeviceId.h:33
bool isHost() const
Indique si l'instance est associée à l'hôte.
Definition DeviceId.h:60
Int32 asInt32() const
Valeur numérique du device.
Definition DeviceId.h:69
Information sur un device.
Definition DeviceInfo.h:32
void copy(ConstMemoryView from, eMemoryRessource from_mem, MutableMemoryView to, eMemoryRessource to_mem, const RunQueue *queue) override
Copie les données de from vers to avec la queue queue.
void notifyBeginLaunchKernel(impl::RunCommandImpl &c) override
Notification avant le lancement de la commande.
bool _barrierNoException() override
Barrière sans exception. Retourne true en cas d'erreur.
void barrier() override
Bloque jusqu'à ce que toutes les actions associées à cette file soient terminées.
void notifyEndLaunchKernel(impl::RunCommandImpl &) override
Notification de fin de lancement de la commande.
void prefetchMemory(const MemoryPrefetchArgs &args) override
Effectue un pré-chargement d'une zone mémoire.
void * _internalImpl() override
Pointeur sur la structure interne dépendante de l'implémentation.
void copyMemory(const MemoryCopyArgs &args) override
Effectue une copie entre deux zones mémoire.
Interface d'une liste de devices.
Arguments pour la copie mémoire.
Definition Memory.h:63
Arguments pour le préfetching mémoire.
Definition Memory.h:125
Informations sur une adresse mémoire.
Informations pour initialiser le runtime accélérateur.
Informations pour créer une RunQueue.
File d'exécution pour un accélérateur.
bool isAsync() const
Indique si la file d'exécution est asynchrone.
Definition RunQueue.cc:313
void copyMemory(const MemoryCopyArgs &args) const
Copie des informations entre deux zones mémoires.
Definition RunQueue.cc:230
Interface d'une liste de devices.
Interface de l'implémentation d'un évènement.
Interface d'un flux d'exécution pour une RunQueue.
Interface du runtime associé à une RunQueue.
Implémentation d'une commande pour accélérateur.
Interface pour les copies mémoire avec support des accélérateurs.
Partie interne à Arcane de 'IMemoryRessourceMng'.
virtual IMemoryRessourceMngInternal * _internal()=0
Interface interne.
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:120
Flot de sortie lié à une String.
Chaîne de caractères unicode.
eMemoryAdvice
Conseils pour la gestion mémoire.
Definition Memory.h:36
@ AccessedByHost
Indique que la zone mémoire est accédée par l'hôte.
@ PreferredLocationDevice
Privilégié le positionnement de la mémoire sur l'accélérateur.
@ MostlyRead
Indique que la zone mémoire est principalement en lecture seule.
@ PreferredLocationHost
Privilégié le positionnement de la mémoire sur l'hôte.
@ AccessedByDevice
Indique que la zone mémoire est accédée par l'accélérateur.
eExecutionPolicy
Politique d'exécution pour un Runner.
@ HIP
Politique d'exécution utilisant l'environnement HIP.
IMemoryRessourceMng * getDataMemoryRessourceMng()
Gestionnaire de ressource mémoire pour les données.
IMemoryAllocator * setAcceleratorHostMemoryAllocator(IMemoryAllocator *a)
Positionne l'allocateur spécifique pour les accélérateurs.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
eMemoryRessource
Liste des ressources mémoire disponibles.
@ HostPinned
Alloue sur l'hôte.
@ UnifiedMemory
Alloue en utilisant la mémoire unifiée.
@ Device
Alloue sur le device.
Espace de nom de Arccore.
Definition ArcaneTypes.h:24
std::int64_t Int64
Type entier signé sur 64 bits.
std::int32_t Int32
Type entier signé sur 32 bits.