Arcane  v4.1.0.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
SyclAcceleratorRuntime.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/* SyclAcceleratorRuntime.cc (C) 2000-2025 */
9/* */
10/* Runtime pour 'SYCL'. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/accelerator/sycl/SyclAccelerator.h"
15#include "arcane/accelerator/sycl/internal/SyclAcceleratorInternal.h"
16
17#include "arcane/utils/PlatformUtils.h"
18#include "arcane/utils/NotSupportedException.h"
19#include "arcane/utils/NotImplementedException.h"
20#include "arcane/utils/FatalErrorException.h"
21#include "arcane/utils/IMemoryRessourceMng.h"
22#include "arcane/utils/internal/MemoryUtilsInternal.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
36#include <iostream>
37
38namespace Arcane::Accelerator::Sycl
39{
40
41using namespace Arccore;
42
43#define ARCANE_SYCL_FUNC_NOT_HANDLED \
44 std::cout << "WARNING: SYCL: function not handled " << A_FUNCINFO << "\n"
45
47
48/*---------------------------------------------------------------------------*/
49/*---------------------------------------------------------------------------*/
50
51class SyclRunQueueStream
53{
54 public:
55
56 SyclRunQueueStream(SyclRunnerRuntime* runtime, const RunQueueBuildInfo& bi);
57 ~SyclRunQueueStream() override
58 {
59 }
60
61 public:
62
63 void notifyBeginLaunchKernel([[maybe_unused]] impl::RunCommandImpl& c) override
64 {
65 return m_runtime->notifyBeginLaunchKernel();
66 }
68 {
69 return m_runtime->notifyEndLaunchKernel();
70 }
71 void barrier() override
72 {
73 m_sycl_stream->wait_and_throw();
74 }
75 bool _barrierNoException() override
76 {
77 m_sycl_stream->wait();
78 return false;
79 }
80 void copyMemory(const MemoryCopyArgs& args) override
81 {
82 auto source_bytes = args.source().bytes();
83 m_sycl_stream->memcpy(args.destination().data(), source_bytes.data(),
84 source_bytes.size());
85 if (!args.isAsync())
86 this->barrier();
87 }
88 void prefetchMemory([[maybe_unused]] const MemoryPrefetchArgs& args) override
89 {
90 auto source_bytes = args.source().bytes();
91 Int64 nb_byte = source_bytes.size();
92 if (nb_byte == 0)
93 return;
94 m_sycl_stream->prefetch(source_bytes.data(), nb_byte);
95 if (!args.isAsync())
96 this->barrier();
97 }
99 {
100 return impl::NativeStream(m_sycl_stream.get());
101 }
102
103 void _setSyclLastCommandEvent([[maybe_unused]] void* sycl_event_ptr) override
104 {
105 sycl::event last_event;
106 if (sycl_event_ptr)
107 last_event = *(reinterpret_cast<sycl::event*>(sycl_event_ptr));
108 m_last_command_event = last_event;
109 }
110
111 public:
112
113 static sycl::async_handler _getAsyncHandler()
114 {
115 auto err_handler = [](const sycl::exception_list& exceptions) {
116 std::ostringstream ostr;
117 ostr << "Error in SYCL runtime\n";
118 for (const std::exception_ptr& e : exceptions) {
119 try {
120 std::rethrow_exception(e);
121 }
122 catch (const sycl::exception& e) {
123 ostr << "SYCL exception: " << e.what() << "\n";
124 }
125 }
126 ARCANE_FATAL(ostr.str());
127 };
128 return err_handler;
129 }
130
132 sycl::event lastCommandEvent() { return m_last_command_event; }
133
134 public:
135
136 sycl::queue& trueStream() const
137 {
138 return *m_sycl_stream;
139 }
140
141 private:
142
143 impl::IRunnerRuntime* m_runtime;
144 std::unique_ptr<sycl::queue> m_sycl_stream;
145 sycl::event m_last_command_event;
146};
147
148/*---------------------------------------------------------------------------*/
149/*---------------------------------------------------------------------------*/
150
151class SyclRunQueueEvent
153{
154 public:
155
156 explicit SyclRunQueueEvent([[maybe_unused]] bool has_timer)
157 {
158 }
159 ~SyclRunQueueEvent() override
160 {
161 }
162
163 public:
164
165 // Enregistre l'événement au sein d'une RunQueue
166 void recordQueue([[maybe_unused]] impl::IRunQueueStream* stream) final
167 {
168 ARCANE_CHECK_POINTER(stream);
169 auto* rq = static_cast<SyclRunQueueStream*>(stream);
170 m_sycl_event = rq->lastCommandEvent();
171#if defined(__ADAPTIVECPP__)
172 m_recorded_stream = stream;
173 // TODO: Vérifier s'il faut faire quelque chose
174#elif defined(__INTEL_LLVM_COMPILER)
175 //m_sycl_event = rq->trueStream().ext_oneapi_submit_barrier();
176#else
177 ARCANE_THROW(NotSupportedException, "Only supported for AdaptiveCpp and Intel DPC++ implementation");
178#endif
179 }
180
181 void wait() final
182 {
183 //ARCANE_SYCL_FUNC_NOT_HANDLED;
184 // TODO: Vérifier ce que cela signifie exactement
185 m_sycl_event.wait();
186 }
187
188 void waitForEvent([[maybe_unused]] impl::IRunQueueStream* stream) final
189 {
190#if defined(__ADAPTIVECPP__)
191 auto* rq = static_cast<SyclRunQueueStream*>(stream);
192 m_sycl_event.wait(rq->trueStream().get_wait_list());
193#elif defined(__INTEL_LLVM_COMPILER)
194 std::vector<sycl::event> events;
195 events.push_back(m_sycl_event);
196 auto* rq = static_cast<SyclRunQueueStream*>(stream);
197 rq->trueStream().ext_oneapi_submit_barrier(events);
198#else
199 ARCANE_THROW(NotSupportedException, "Only supported for AdaptiveCpp and Intel DPC++ implementation");
200#endif
201 }
202
203 Int64 elapsedTime([[maybe_unused]] IRunQueueEventImpl* start_event) final
204 {
205 ARCANE_CHECK_POINTER(start_event);
206 // Il faut prendre l'évènement de début car on est certain qu'il contient
207 // la bonne valeur de 'sycl::event'.
208 sycl::event event = (static_cast<SyclRunQueueEvent*>(start_event))->m_sycl_event;
209 // Si pas d'évènement associé, on ne fait rien pour éviter une exception
210 if (event==sycl::event())
211 return 0;
212
213 bool is_submitted = event.get_info<sycl::info::event::command_execution_status>() == sycl::info::event_command_status::complete;
214 if (!is_submitted)
215 return 0;
216 Int64 start = event.get_profiling_info<sycl::info::event_profiling::command_start>();
217 Int64 end = event.get_profiling_info<sycl::info::event_profiling::command_end>();
218 return (end - start);
219 }
220
221 bool hasPendingWork() final
222 {
223 ARCANE_THROW(NotImplementedException,"hasPendingWork()");
224 }
225
226 private:
227
228 sycl::event m_sycl_event;
229 impl::IRunQueueStream* m_recorded_stream = nullptr;
230};
231
232/*---------------------------------------------------------------------------*/
233/*---------------------------------------------------------------------------*/
234
237{
238 friend class SyclRunQueueStream;
239
240 public:
241
242 void notifyBeginLaunchKernel() override
243 {
244 }
245 void notifyEndLaunchKernel() override
246 {
247 }
248 void barrier() override
249 {
250 // TODO Faire le wait sur la file par défaut n'est pas strictement équivalent
251 // à la barrière en CUDA qui synchronize tout le device.
252 m_default_queue->wait();
253 }
254 eExecutionPolicy executionPolicy() const override
255 {
257 }
258 impl::IRunQueueStream* createStream(const RunQueueBuildInfo& bi) override
259 {
260 return new SyclRunQueueStream(this, bi);
261 }
262 impl::IRunQueueEventImpl* createEventImpl() override
263 {
264 return new SyclRunQueueEvent(false);
265 }
266 impl::IRunQueueEventImpl* createEventImplWithTimer() override
267 {
268 return new SyclRunQueueEvent(true);
269 }
270 void setMemoryAdvice([[maybe_unused]] ConstMemoryView buffer, [[maybe_unused]] eMemoryAdvice advice,
271 [[maybe_unused]] DeviceId device_id) override
272 {
273 }
274 void unsetMemoryAdvice([[maybe_unused]] ConstMemoryView buffer,
275 [[maybe_unused]] eMemoryAdvice advice, [[maybe_unused]] DeviceId device_id) override
276 {
277 }
278
279 void setCurrentDevice([[maybe_unused]] DeviceId device_id) final
280 {
281 ARCANE_SYCL_FUNC_NOT_HANDLED;
282 }
283 const IDeviceInfoList* deviceInfoList() override { return &m_device_info_list; }
284
285 void getPointerAttribute(PointerAttribute& attribute, const void* ptr) override
286 {
287 sycl::usm::alloc sycl_mem_type = sycl::get_pointer_type(ptr, *m_default_context);
288 ePointerMemoryType mem_type = ePointerMemoryType::Unregistered;
289 const void* host_ptr = nullptr;
290 const void* device_ptr = nullptr;
291 if (sycl_mem_type == sycl::usm::alloc::host) {
292 // HostPinned. Doit être accessible depuis le device mais
293 //
294 mem_type = ePointerMemoryType::Host;
295 host_ptr = ptr;
296 // TODO: Regarder comment récupérer la valeur
297 device_ptr = ptr;
298 }
299 else if (sycl_mem_type == sycl::usm::alloc::device) {
300 mem_type = ePointerMemoryType::Device;
301 device_ptr = ptr;
302 }
303 else if (sycl_mem_type == sycl::usm::alloc::shared) {
304 mem_type = ePointerMemoryType::Managed;
305 // TODO: pour l'instant on remplit avec le pointeur car on ne sait
306 // pas comment récupérer l'info.
307 host_ptr = ptr;
308 device_ptr = ptr;
309 }
310 // TODO: à corriger
311 Int32 device_id = 0;
312 _fillPointerAttribute(attribute, mem_type, device_id, ptr, device_ptr, host_ptr);
313 }
314
315 DeviceMemoryInfo getDeviceMemoryInfo([[maybe_unused]] DeviceId device_id) override
316 {
317 return {};
318 }
319
320 void fillDevicesAndSetDefaultQueue(bool is_verbose);
321 sycl::queue& defaultQueue() const { return *m_default_queue; }
322 sycl::device& defaultDevice() const { return *m_default_device; }
323
324 private:
325
326 impl::DeviceInfoList m_device_info_list;
327 std::unique_ptr<sycl::device> m_default_device;
328 std::unique_ptr<sycl::context> m_default_context;
329 std::unique_ptr<sycl::queue> m_default_queue;
330
331 private:
332
333 void _init(sycl::device& device)
334 {
335 m_default_device = std::make_unique<sycl::device>(device);
336 m_default_queue = std::make_unique<sycl::queue>(device);
337 m_default_context = std::make_unique<sycl::context>(device);
338 }
339};
340
341/*---------------------------------------------------------------------------*/
342/*---------------------------------------------------------------------------*/
343
344SyclRunQueueStream::
345SyclRunQueueStream(SyclRunnerRuntime* runtime, const RunQueueBuildInfo& bi)
346: m_runtime(runtime)
347{
348 sycl::device& d = runtime->defaultDevice();
349 // Indique que les commandes lancées sont implicitement exécutées les
350 // unes derrière les autres.
351 auto queue_property = sycl::property::queue::in_order();
352 // Pour le profiling
353 auto profiling_property = sycl::property::queue::enable_profiling();
354 sycl::property_list queue_properties(queue_property, profiling_property);
355
356 // Gestionnaire d'erreur.
357 sycl::async_handler err_handler;
358 err_handler = _getAsyncHandler();
359 if (bi.isDefault())
360 m_sycl_stream = std::make_unique<sycl::queue>(d, err_handler, queue_properties);
361 else {
362 ARCANE_SYCL_FUNC_NOT_HANDLED;
363 m_sycl_stream = std::make_unique<sycl::queue>(d, err_handler, queue_properties);
364 }
365}
366
367/*---------------------------------------------------------------------------*/
368/*---------------------------------------------------------------------------*/
369
370void SyclRunnerRuntime::
371fillDevicesAndSetDefaultQueue(bool is_verbose)
372{
373 if (is_verbose){
374 for (auto platform : sycl::platform::get_platforms()) {
375 std::cout << "Platform: "
376 << platform.get_info<sycl::info::platform::name>()
377 << std::endl;
378 }
379 }
380
381 sycl::device device{ sycl::gpu_selector_v };
382 if (is_verbose)
383 std::cout << "\nDevice: " << device.get_info<sycl::info::device::name>()
384 << "\nVersion=" << device.get_info<sycl::info::device::version>()
385 << std::endl;
386 // Pour l'instant, on prend comme file par défaut la première trouvée
387 // et on ne considère qu'un seul device accessible.
388 _init(device);
389
390 DeviceInfo device_info;
391 device_info.setDescription("No description info");
392 device_info.setDeviceId(DeviceId(0));
393 device_info.setName(device.get_info<sycl::info::device::name>());
394 m_device_info_list.addDevice(device_info);
395}
396
397/*---------------------------------------------------------------------------*/
398/*---------------------------------------------------------------------------*/
399
401: public IMemoryCopier
402{
403 void copy(ConstMemoryView from, eMemoryRessource from_mem,
405 const RunQueue* queue) override;
406};
407
408/*---------------------------------------------------------------------------*/
409/*---------------------------------------------------------------------------*/
410
411} // namespace Arcane::Accelerator::Sycl
412
413namespace
414{
416Arcane::Accelerator::Sycl::SyclMemoryCopier global_sycl_memory_copier;
417} // namespace
418
419/*---------------------------------------------------------------------------*/
420/*---------------------------------------------------------------------------*/
421
422namespace Arcane::Accelerator::Sycl
423{
424
425/*---------------------------------------------------------------------------*/
426/*---------------------------------------------------------------------------*/
427
428void SyclMemoryCopier::
429copy(ConstMemoryView from, [[maybe_unused]] eMemoryRessource from_mem,
430 MutableMemoryView to, [[maybe_unused]] eMemoryRessource to_mem,
431 const RunQueue* queue)
432{
433 if (queue) {
434 queue->copyMemory(MemoryCopyArgs(to.bytes(), from.bytes()).addAsync(queue->isAsync()));
435 return;
436 }
437 sycl::queue& q = global_sycl_runtime.defaultQueue();
438 q.memcpy(to.data(), from.data(), from.bytes().size()).wait();
439}
440
441} // namespace Arcane::Accelerator::Sycl
442
443/*---------------------------------------------------------------------------*/
444/*---------------------------------------------------------------------------*/
445
446// Cette fonction est le point d'entrée utilisé lors du chargement
447// dynamique de cette bibliothèque
448extern "C" ARCANE_EXPORT void
449arcaneRegisterAcceleratorRuntimesycl(Arcane::Accelerator::RegisterRuntimeInfo& init_info)
450{
451 using namespace Arcane;
452 using namespace Arcane::Accelerator::Sycl;
453 Arcane::Accelerator::impl::setUsingSYCLRuntime(true);
454 Arcane::Accelerator::impl::setSYCLRunQueueRuntime(&global_sycl_runtime);
455 MemoryUtils::setAcceleratorHostMemoryAllocator(getSyclMemoryAllocator());
458 mrm->setIsAccelerator(true);
459 mrm->setAllocator(eMemoryRessource::UnifiedMemory, getSyclUnifiedMemoryAllocator());
460 mrm->setAllocator(eMemoryRessource::HostPinned, getSyclHostPinnedMemoryAllocator());
461 mrm->setAllocator(eMemoryRessource::Device, getSyclDeviceMemoryAllocator());
462 mrm->setCopier(&global_sycl_memory_copier);
463 global_sycl_runtime.fillDevicesAndSetDefaultQueue(init_info.isVerbose());
464 setSyclMemoryQueue(global_sycl_runtime.defaultQueue());
465}
466
467/*---------------------------------------------------------------------------*/
468/*---------------------------------------------------------------------------*/
#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_THROW(exception_class,...)
Macro pour envoyer une exception avec formattage.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Identifiant d'un composant du système.
Definition DeviceId.h:33
Information mémoire d'un accélérateur.
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
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.
impl::NativeStream nativeStream() override
Pointeur sur la structure interne dépendante de l'implémentation.
void barrier() override
Bloque jusqu'à ce que toutes les actions associées à cette file soient terminées.
sycl::event lastCommandEvent()
Évènement correspondant à la dernière 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.
bool _barrierNoException() override
Barrière sans exception. Retourne true en cas d'erreur.
void _setSyclLastCommandEvent(void *sycl_event_ptr) override
Pour SYCL, positionne l'évènement associé à la dernière commande exécutée.
void notifyEndLaunchKernel(impl::RunCommandImpl &) override
Notification de fin de lancement de la commande.
void notifyBeginLaunchKernel(impl::RunCommandImpl &c) override
Notification avant le lancement de la commande.
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.
Vue constante sur une zone mémoire contigue contenant des éléments de taille fixe.
Definition MemoryView.h:38
constexpr SpanType bytes() const
Vue sous forme d'octets.
Definition MemoryView.h:107
constexpr const std::byte * data() const
Pointeur sur la zone mémoire.
Definition MemoryView.h:110
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.
Vue modifiable sur une zone mémoire contigue contenant des éléments de taille fixe.
Definition MemoryView.h:156
constexpr std::byte * data() const
Pointeur sur la zone mémoire.
Definition MemoryView.h:218
constexpr SpanType bytes() const
Vue sous forme d'octets.
Definition MemoryView.h:215
Exception lorsqu'une fonction n'est pas implémentée.
Exception lorsqu'une opération n'est pas supportée.
constexpr __host__ __device__ SizeType size() const noexcept
Retourne la taille du tableau.
Definition Span.h:305
eMemoryAdvice
Conseils pour la gestion mémoire.
Definition Memory.h:36
ePointerMemoryType
Type de mémoire pour un pointeur.
eExecutionPolicy
Politique d'exécution pour un Runner.
@ SYCL
Politique d'exécution utilisant l'environnement SYCL.
IMemoryRessourceMng * getDataMemoryResourceMng()
Gestionnaire de ressource mémoire pour les données.
IMemoryAllocator * setAcceleratorHostMemoryAllocator(IMemoryAllocator *a)
Positionne l'allocateur spécifique pour les accélérateurs.
void setDefaultDataMemoryResource(eMemoryResource mem_resource)
Positionne la ressource mémoire utilisée pour l'allocateur mémoire des données.
Espace de nom pour les fonctions dépendant de la plateforme.
-*- 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.
Arcane::eMemoryResource eMemoryRessource
Typedef pour la version Arcane historique (avec 2's')