Arcane  v3.16.0.0
Documentation développeur
Tout Classes Espaces de nommage Fichiers Fonctions Variables Définitions de type Énumérations Valeurs énumérées Amis Macros Groupes Pages Concepts
HipAcceleratorRuntime.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2025 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-2025 */
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/DeviceInfoList.h"
28#include "arcane/accelerator/core/RunQueue.h"
29#include "arcane/accelerator/core/DeviceMemoryInfo.h"
30#include "arcane/accelerator/core/NativeStream.h"
31#include "arcane/accelerator/core/internal/IRunnerRuntime.h"
32#include "arcane/accelerator/core/internal/RegisterRuntimeInfo.h"
33#include "arcane/accelerator/core/internal/IRunQueueStream.h"
34#include "arcane/accelerator/core/internal/IRunQueueEventImpl.h"
35#include "arcane/accelerator/core/internal/RunCommandImpl.h"
36
37#include <iostream>
38
39#ifdef ARCANE_HAS_ROCTX
40#include <roctx.h>
41#endif
42
43using namespace Arccore;
44
45namespace Arcane::Accelerator::Hip
46{
47
48/*---------------------------------------------------------------------------*/
49/*---------------------------------------------------------------------------*/
50
51class HipRunQueueStream
53{
54 public:
55
56 HipRunQueueStream(impl::IRunnerRuntime* runtime, const RunQueueBuildInfo& bi)
57 : m_runtime(runtime)
58 {
59 if (bi.isDefault())
60 ARCANE_CHECK_HIP(hipStreamCreate(&m_hip_stream));
61 else {
62 int priority = bi.priority();
63 ARCANE_CHECK_HIP(hipStreamCreateWithPriority(&m_hip_stream, hipStreamDefault, priority));
64 }
65 }
66 ~HipRunQueueStream() override
67 {
68 ARCANE_CHECK_HIP_NOTHROW(hipStreamDestroy(m_hip_stream));
69 }
70
71 public:
72
73 void notifyBeginLaunchKernel([[maybe_unused]] impl::RunCommandImpl& c) override
74 {
75#ifdef ARCANE_HAS_ROCTX
76 auto kname = c.kernelName();
77 if (kname.empty())
78 roctxRangePush(c.traceInfo().name());
79 else
80 roctxRangePush(kname.localstr());
81#endif
82 return m_runtime->notifyBeginLaunchKernel();
83 }
85 {
86#ifdef ARCANE_HAS_ROCTX
87 roctxRangePop();
88#endif
89 return m_runtime->notifyEndLaunchKernel();
90 }
91 void barrier() override
92 {
93 ARCANE_CHECK_HIP(hipStreamSynchronize(m_hip_stream));
94 }
95 bool _barrierNoException() override
96 {
97 return hipStreamSynchronize(m_hip_stream) != hipSuccess;
98 }
99 void copyMemory(const MemoryCopyArgs& args) override
100 {
101 auto r = hipMemcpyAsync(args.destination().data(), args.source().data(),
102 args.source().bytes().size(), hipMemcpyDefault, m_hip_stream);
103 ARCANE_CHECK_HIP(r);
104 if (!args.isAsync())
105 barrier();
106 }
107 void prefetchMemory(const MemoryPrefetchArgs& args) override
108 {
109 auto src = args.source().bytes();
110 if (src.size()==0)
111 return;
112 DeviceId d = args.deviceId();
113 int device = hipCpuDeviceId;
114 if (!d.isHost())
115 device = d.asInt32();
116 auto r = hipMemPrefetchAsync(src.data(), src.size(), device, m_hip_stream);
117 ARCANE_CHECK_HIP(r);
118 if (!args.isAsync())
119 barrier();
120 }
122 {
123 return impl::NativeStream(&m_hip_stream);
124 }
125
126 public:
127
128 hipStream_t trueStream() const
129 {
130 return m_hip_stream;
131 }
132
133 private:
134
135 impl::IRunnerRuntime* m_runtime;
136 hipStream_t m_hip_stream;
137};
138
139/*---------------------------------------------------------------------------*/
140/*---------------------------------------------------------------------------*/
141
142class HipRunQueueEvent
144{
145 public:
146
147 explicit HipRunQueueEvent(bool has_timer)
148 {
149 if (has_timer)
150 ARCANE_CHECK_HIP(hipEventCreate(&m_hip_event));
151 else
152 ARCANE_CHECK_HIP(hipEventCreateWithFlags(&m_hip_event, hipEventDisableTiming));
153 }
154 ~HipRunQueueEvent() override
155 {
156 ARCANE_CHECK_HIP_NOTHROW(hipEventDestroy(m_hip_event));
157 }
158
159 public:
160
161 // Enregistre l'événement au sein d'une RunQueue
162 void recordQueue(impl::IRunQueueStream* stream) final
163 {
164 auto* rq = static_cast<HipRunQueueStream*>(stream);
165 ARCANE_CHECK_HIP(hipEventRecord(m_hip_event, rq->trueStream()));
166 }
167
168 void wait() final
169 {
170 ARCANE_CHECK_HIP(hipEventSynchronize(m_hip_event));
171 }
172
173 void waitForEvent(impl::IRunQueueStream* stream) final
174 {
175 auto* rq = static_cast<HipRunQueueStream*>(stream);
176 ARCANE_CHECK_HIP(hipStreamWaitEvent(rq->trueStream(), m_hip_event, 0));
177 }
178
179 Int64 elapsedTime(IRunQueueEventImpl* from_event) final
180 {
181 auto* true_from_event = static_cast<HipRunQueueEvent*>(from_event);
182 ARCANE_CHECK_POINTER(true_from_event);
183 float time_in_ms = 0.0;
184 ARCANE_CHECK_HIP(hipEventElapsedTime(&time_in_ms, true_from_event->m_hip_event, m_hip_event));
185 double x = time_in_ms * 1.0e6;
186 Int64 nano_time = static_cast<Int64>(x);
187 return nano_time;
188 }
189
190 bool hasPendingWork() final
191 {
192 hipError_t v = hipEventQuery(m_hip_event);
193 if (v == hipErrorNotReady)
194 return true;
195 ARCANE_CHECK_HIP(v);
196 return false;
197 }
198
199 private:
200
201 hipEvent_t m_hip_event;
202};
203
204/*---------------------------------------------------------------------------*/
205/*---------------------------------------------------------------------------*/
206
209{
210 public:
211
212 ~HipRunnerRuntime() override = default;
213
214 public:
215
216 void notifyBeginLaunchKernel() override
217 {
218 ++m_nb_kernel_launched;
219 if (m_is_verbose)
220 std::cout << "BEGIN HIP KERNEL!\n";
221 }
222 void notifyEndLaunchKernel() override
223 {
224 ARCANE_CHECK_HIP(hipGetLastError());
225 if (m_is_verbose)
226 std::cout << "END HIP KERNEL!\n";
227 }
228 void barrier() override
229 {
230 ARCANE_CHECK_HIP(hipDeviceSynchronize());
231 }
232 eExecutionPolicy executionPolicy() const override
233 {
235 }
236 impl::IRunQueueStream* createStream(const RunQueueBuildInfo& bi) override
237 {
238 return new HipRunQueueStream(this, bi);
239 }
240 impl::IRunQueueEventImpl* createEventImpl() override
241 {
242 return new HipRunQueueEvent(false);
243 }
244 impl::IRunQueueEventImpl* createEventImplWithTimer() override
245 {
246 return new HipRunQueueEvent(true);
247 }
248 void setMemoryAdvice(ConstMemoryView buffer, eMemoryAdvice advice, DeviceId device_id) override
249 {
250 auto v = buffer.bytes();
251 const void* ptr = v.data();
252 size_t count = v.size();
253 int device = device_id.asInt32();
254 hipMemoryAdvise hip_advise;
255
256 if (advice == eMemoryAdvice::MostlyRead)
257 hip_advise = hipMemAdviseSetReadMostly;
259 hip_advise = hipMemAdviseSetPreferredLocation;
260 else if (advice == eMemoryAdvice::AccessedByDevice)
261 hip_advise = hipMemAdviseSetAccessedBy;
262 else if (advice == eMemoryAdvice::PreferredLocationHost) {
263 hip_advise = hipMemAdviseSetPreferredLocation;
264 device = hipCpuDeviceId;
265 }
266 else if (advice == eMemoryAdvice::AccessedByHost) {
267 hip_advise = hipMemAdviseSetAccessedBy;
268 device = hipCpuDeviceId;
269 }
270 else
271 return;
272 //std::cout << "MEMADVISE p=" << ptr << " size=" << count << " advise = " << hip_advise << " id = " << device << "\n";
273 ARCANE_CHECK_HIP(hipMemAdvise(ptr, count, hip_advise, device));
274 }
275 void unsetMemoryAdvice(ConstMemoryView buffer, eMemoryAdvice advice, DeviceId device_id) override
276 {
277 auto v = buffer.bytes();
278 const void* ptr = v.data();
279 size_t count = v.size();
280 int device = device_id.asInt32();
281 hipMemoryAdvise hip_advise;
282
283 if (advice == eMemoryAdvice::MostlyRead)
284 hip_advise = hipMemAdviseUnsetReadMostly;
286 hip_advise = hipMemAdviseUnsetPreferredLocation;
287 else if (advice == eMemoryAdvice::AccessedByDevice)
288 hip_advise = hipMemAdviseUnsetAccessedBy;
289 else if (advice == eMemoryAdvice::PreferredLocationHost) {
290 hip_advise = hipMemAdviseUnsetPreferredLocation;
291 device = hipCpuDeviceId;
292 }
293 else if (advice == eMemoryAdvice::AccessedByHost) {
294 hip_advise = hipMemAdviseUnsetAccessedBy;
295 device = hipCpuDeviceId;
296 }
297 else
298 return;
299 ARCANE_CHECK_HIP(hipMemAdvise(ptr, count, hip_advise, device));
300 }
301
302 void setCurrentDevice(DeviceId device_id) final
303 {
304 Int32 id = device_id.asInt32();
305 if (!device_id.isAccelerator())
306 ARCANE_FATAL("Device {0} is not an accelerator device", id);
307 ARCANE_CHECK_HIP(hipSetDevice(id));
308 }
309 const IDeviceInfoList* deviceInfoList() override { return &m_device_info_list; }
310
311 void getPointerAttribute(PointerAttribute& attribute, const void* ptr) override
312 {
313 hipPointerAttribute_t pa;
314 hipError_t ret_value = hipPointerGetAttributes(&pa, ptr);
315 auto mem_type = ePointerMemoryType::Unregistered;
316 // Si \a ptr n'a pas été alloué dynamiquement (i.e: il est sur la pile),
317 // hipPointerGetAttribute() retourne une erreur. Dans ce cas on considère
318 // la mémoire comme non enregistrée.
319 if (ret_value==hipSuccess){
320#if HIP_VERSION_MAJOR >= 6
321 auto rocm_memory_type = pa.type;
322#else
323 auto rocm_memory_type = pa.memoryType;
324#endif
325 if (pa.isManaged)
326 mem_type = ePointerMemoryType::Managed;
327 else if (rocm_memory_type == hipMemoryTypeHost)
328 mem_type = ePointerMemoryType::Host;
329 else if (rocm_memory_type == hipMemoryTypeDevice)
330 mem_type = ePointerMemoryType::Device;
331 }
332
333 //std::cout << "HIP Info: hip_memory_type=" << (int)pa.memoryType << " is_managed?=" << pa.isManaged
334 // << " flags=" << pa.allocationFlags
335 // << " my_memory_type=" << (int)mem_type
336 // << "\n";
337 _fillPointerAttribute(attribute, mem_type, pa.device,
338 ptr, pa.devicePointer, pa.hostPointer);
339 }
340
341 DeviceMemoryInfo getDeviceMemoryInfo(DeviceId device_id) override
342 {
343 int d = 0;
344 int wanted_d = device_id.asInt32();
345 ARCANE_CHECK_HIP(hipGetDevice(&d));
346 if (d != wanted_d)
347 ARCANE_CHECK_HIP(hipSetDevice(wanted_d));
348 size_t free_mem = 0;
349 size_t total_mem = 0;
350 ARCANE_CHECK_HIP(hipMemGetInfo(&free_mem, &total_mem));
351 if (d != wanted_d)
352 ARCANE_CHECK_HIP(hipSetDevice(d));
354 dmi.setFreeMemory(free_mem);
355 dmi.setTotalMemory(total_mem);
356 return dmi;
357 }
358
359 void pushProfilerRange(const String& name, [[maybe_unused]] Int32 color) override
360 {
361#ifdef ARCANE_HAS_ROCTX
362 roctxRangePush(name.localstr());
363#endif
364 }
365 void popProfilerRange() override
366 {
367#ifdef ARCANE_HAS_ROCTX
368 roctxRangePop();
369#endif
370 }
371
372 public:
373
374 void fillDevices(bool is_verbose);
375
376 private:
377
378 Int64 m_nb_kernel_launched = 0;
379 bool m_is_verbose = false;
380 impl::DeviceInfoList m_device_info_list;
381};
382
383/*---------------------------------------------------------------------------*/
384/*---------------------------------------------------------------------------*/
385
386void HipRunnerRuntime::
387fillDevices(bool is_verbose)
388{
389 int nb_device = 0;
390 ARCANE_CHECK_HIP(hipGetDeviceCount(&nb_device));
391 std::ostream& omain = std::cout;
392 if (is_verbose)
393 omain << "ArcaneHIP: Initialize Arcane HIP runtime nb_available_device=" << nb_device << "\n";
394 for (int i = 0; i < nb_device; ++i) {
395 OStringStream ostr;
396 std::ostream& o = ostr.stream();
397
398 hipDeviceProp_t dp;
399 ARCANE_CHECK_HIP(hipGetDeviceProperties(&dp, i));
400
401 int has_managed_memory = 0;
402 ARCANE_CHECK_HIP(hipDeviceGetAttribute(&has_managed_memory, hipDeviceAttributeManagedMemory, i));
403
404 o << "\nDevice " << i << " name=" << dp.name << "\n";
405 o << " computeCapability = " << dp.major << "." << dp.minor << "\n";
406 o << " totalGlobalMem = " << dp.totalGlobalMem << "\n";
407 o << " sharedMemPerBlock = " << dp.sharedMemPerBlock << "\n";
408 o << " regsPerBlock = " << dp.regsPerBlock << "\n";
409 o << " warpSize = " << dp.warpSize << "\n";
410 o << " memPitch = " << dp.memPitch << "\n";
411 o << " maxThreadsPerBlock = " << dp.maxThreadsPerBlock << "\n";
412 o << " totalConstMem = " << dp.totalConstMem << "\n";
413 o << " clockRate = " << dp.clockRate << "\n";
414 //o << " deviceOverlap = " << dp.deviceOverlap<< "\n";
415 o << " multiProcessorCount = " << dp.multiProcessorCount << "\n";
416 o << " kernelExecTimeoutEnabled = " << dp.kernelExecTimeoutEnabled << "\n";
417 o << " integrated = " << dp.integrated << "\n";
418 o << " canMapHostMemory = " << dp.canMapHostMemory << "\n";
419 o << " computeMode = " << dp.computeMode << "\n";
420 o << " maxThreadsDim = " << dp.maxThreadsDim[0] << " " << dp.maxThreadsDim[1]
421 << " " << dp.maxThreadsDim[2] << "\n";
422 o << " maxGridSize = " << dp.maxGridSize[0] << " " << dp.maxGridSize[1]
423 << " " << dp.maxGridSize[2] << "\n";
424 o << " concurrentManagedAccess = " << dp.concurrentManagedAccess << "\n";
425 o << " directManagedMemAccessFromHost = " << dp.directManagedMemAccessFromHost << "\n";
426 o << " gcnArchName = " << dp.gcnArchName << "\n";
427 o << " pageableMemoryAccess = " << dp.pageableMemoryAccess << "\n";
428 o << " pageableMemoryAccessUsesHostPageTables = " << dp.pageableMemoryAccessUsesHostPageTables << "\n";
429 o << " hasManagedMemory = " << has_managed_memory << "\n";
430#if HIP_VERSION_MAJOR >= 6
431 o << " gpuDirectRDMASupported = " << dp.gpuDirectRDMASupported << "\n";
432 o << " hostNativeAtomicSupported = " << dp.hostNativeAtomicSupported << "\n";
433 o << " unifiedFunctionPointers = " << dp.unifiedFunctionPointers << "\n";
434#endif
435 {
436 hipDevice_t device;
437 ARCANE_CHECK_HIP(hipDeviceGet(&device, i));
438 hipUUID device_uuid;
439 ARCANE_CHECK_HIP(hipDeviceGetUuid(&device_uuid, device));
440 o << " deviceUuid=";
441 impl::printUUID(o, device_uuid.bytes);
442 o << "\n";
443 }
444
445 String description(ostr.str());
446 if (is_verbose)
447 omain << description;
448
449 DeviceInfo device_info;
450 device_info.setDescription(description);
451 device_info.setDeviceId(DeviceId(i));
452 device_info.setName(dp.name);
453 m_device_info_list.addDevice(device_info);
454 }
455}
456
457/*---------------------------------------------------------------------------*/
458/*---------------------------------------------------------------------------*/
459
461: public IMemoryCopier
462{
463 void copy(ConstMemoryView from, [[maybe_unused]] eMemoryRessource from_mem,
464 MutableMemoryView to, [[maybe_unused]] eMemoryRessource to_mem,
465 const RunQueue* queue) override
466 {
467 if (queue) {
468 queue->copyMemory(MemoryCopyArgs(to.bytes(), from.bytes()).addAsync(queue->isAsync()));
469 return;
470 }
471 // 'hipMemcpyDefault' sait automatiquement ce qu'il faut faire en tenant
472 // uniquement compte de la valeur des pointeurs. Il faudrait voir si
473 // utiliser \a from_mem et \a to_mem peut améliorer les performances.
474 ARCANE_CHECK_HIP(hipMemcpy(to.data(), from.data(), from.bytes().size(), hipMemcpyDefault));
475 }
476};
477
478/*---------------------------------------------------------------------------*/
479/*---------------------------------------------------------------------------*/
480
481} // End namespace Arcane::Accelerator::Hip
482
483namespace
484{
486Arcane::Accelerator::Hip::HipMemoryCopier global_hip_memory_copier;
487}
488
489/*---------------------------------------------------------------------------*/
490/*---------------------------------------------------------------------------*/
491
492// Cette fonction est le point d'entrée utilisé lors du chargement
493// dynamique de cette bibliothèque
494extern "C" ARCANE_EXPORT void
495arcaneRegisterAcceleratorRuntimehip(Arcane::Accelerator::RegisterRuntimeInfo& init_info)
496{
497 using namespace Arcane;
498 using namespace Arcane::Accelerator::Hip;
499 Arcane::Accelerator::impl::setUsingHIPRuntime(true);
500 Arcane::Accelerator::impl::setHIPRunQueueRuntime(&global_hip_runtime);
503 mrm->setIsAccelerator(true);
504 mrm->setAllocator(eMemoryRessource::UnifiedMemory, getHipUnifiedMemoryAllocator());
505 mrm->setAllocator(eMemoryRessource::HostPinned, getHipHostPinnedMemoryAllocator());
506 mrm->setAllocator(eMemoryRessource::Device, getHipDeviceMemoryAllocator());
507 mrm->setCopier(&global_hip_memory_copier);
508 global_hip_runtime.fillDevices(init_info.isVerbose());
509}
510
511/*---------------------------------------------------------------------------*/
512/*---------------------------------------------------------------------------*/
#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
bool isAccelerator() const
Indique si l'instance est associée à un accélérateur.
Definition DeviceId.h:66
Information sur un device.
Definition DeviceInfo.h:32
Information mémoire d'un accélérateur.
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 copyMemory(const MemoryCopyArgs &args) override
Effectue une copie entre deux zones mémoire.
impl::NativeStream nativeStream() override
Pointeur sur la structure interne dépendante de l'implémentation.
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.
bool isDefault() const
Indique si l'instance a uniquement les valeurs par défaut.
File d'exécution pour un accélérateur.
bool isAsync() const
Indique si la file d'exécution est asynchrone.
Definition RunQueue.cc:320
void copyMemory(const MemoryCopyArgs &args) const
Copie des informations entre deux zones mémoires.
Definition RunQueue.cc:237
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é à un accélérateur.
Type opaque pour encapsuler une 'stream' native.
Implémentation d'une commande pour accélérateur.
Interface pour les copies mémoire avec support des accélérateurs.
virtual IMemoryRessourceMngInternal * _internal()=0
Interface interne.
Partie interne à Arcane de 'IMemoryRessourceMng'.
virtual void setAllocator(eMemoryRessource r, IMemoryAllocator *allocator)=0
Positionne l'allocateur pour la ressource r.
virtual void setCopier(IMemoryCopier *copier)=0
Positionne l'instance gérant les copies.
virtual void setIsAccelerator(bool v)=0
Indique si un accélérateur est disponible.
Flot de sortie lié à une String.
Chaîne de caractères unicode.
const char * localstr() const
Retourne la conversion de l'instance dans l'encodage UTF-8.
Definition String.cc:227
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.
Definition Memory.h:48
@ PreferredLocationDevice
Privilégié le positionnement de la mémoire sur l'accélérateur.
Definition Memory.h:42
@ MostlyRead
Indique que la zone mémoire est principalement en lecture seule.
Definition Memory.h:40
@ PreferredLocationHost
Privilégié le positionnement de la mémoire sur l'hôte.
Definition Memory.h:44
@ AccessedByDevice
Indique que la zone mémoire est accédée par l'accélérateur.
Definition Memory.h:46
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 -*-
@ 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.
Arcane::eMemoryResource eMemoryRessource
Typedef pour la version Arcane historique (avec 2's')