Arcane  v3.14.10.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-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/* SyclAcceleratorRuntime.cc (C) 2000-2024 */
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/FatalErrorException.h"
20#include "arcane/utils/IMemoryRessourceMng.h"
21#include "arcane/utils/internal/IMemoryRessourceMngInternal.h"
22
23#include "arcane/accelerator/core/RunQueueBuildInfo.h"
24#include "arcane/accelerator/core/Memory.h"
25#include "arcane/accelerator/core/internal/IRunnerRuntime.h"
26#include "arcane/accelerator/core/internal/RegisterRuntimeInfo.h"
27#include "arcane/accelerator/core/internal/IRunQueueStream.h"
28#include "arcane/accelerator/core/internal/IRunQueueEventImpl.h"
29#include "arcane/accelerator/core/DeviceInfoList.h"
30#include "arcane/accelerator/core/RunQueue.h"
31
32#include <iostream>
33
34namespace Arcane::Accelerator::Sycl
35{
36
37using namespace Arccore;
38
39#define ARCANE_SYCL_FUNC_NOT_HANDLED \
40 std::cout << "WARNING: SYCL: function not handled " << A_FUNCINFO << "\n"
41
42class SyclRunnerRuntime;
43
44/*---------------------------------------------------------------------------*/
45/*---------------------------------------------------------------------------*/
46
49{
50 public:
51
53 ~SyclRunQueueStream() override
54 {
55 }
56
57 public:
58
60 {
61 return m_runtime->notifyBeginLaunchKernel();
62 }
64 {
65 return m_runtime->notifyEndLaunchKernel();
66 }
67 void barrier() override
68 {
69 m_sycl_stream->wait_and_throw();
70 }
71 bool _barrierNoException() override
72 {
73 m_sycl_stream->wait();
74 return false;
75 }
76 void copyMemory(const MemoryCopyArgs& args) override
77 {
78 auto source_bytes = args.source().bytes();
79 m_sycl_stream->memcpy(args.destination().data(), source_bytes.data(),
80 source_bytes.size());
81 if (!args.isAsync())
82 this->barrier();
83 }
84 void prefetchMemory([[maybe_unused]] const MemoryPrefetchArgs& args) override
85 {
86 auto source_bytes = args.source().bytes();
87 Int64 nb_byte = source_bytes.size();
88 if (nb_byte == 0)
89 return;
90 m_sycl_stream->prefetch(source_bytes.data(), nb_byte);
91 if (!args.isAsync())
92 this->barrier();
93 }
94 void* _internalImpl() override
95 {
96 return m_sycl_stream.get();
97 }
98
100 {
101 sycl::event last_event;
102 if (sycl_event_ptr)
103 last_event = *(reinterpret_cast<sycl::event*>(sycl_event_ptr));
104 m_last_command_event = last_event;
105 }
106
107 public:
108
109 static sycl::async_handler _getAsyncHandler()
110 {
111 auto err_handler = [](const sycl::exception_list& exceptions) {
112 std::ostringstream ostr;
113 ostr << "Error in SYCL runtime\n";
114 for (const std::exception_ptr& e : exceptions) {
115 try {
116 std::rethrow_exception(e);
117 }
118 catch (const sycl::exception& e) {
119 ostr << "SYCL exception: " << e.what() << "\n";
120 }
121 }
122 ARCANE_FATAL(ostr.str());
123 };
124 return err_handler;
125 }
126
128 sycl::event lastCommandEvent() { return m_last_command_event; }
129
130 public:
131
132 sycl::queue& trueStream() const
133 {
134 return *m_sycl_stream;
135 }
136
137 private:
138
139 impl::IRunnerRuntime* m_runtime;
140 std::unique_ptr<sycl::queue> m_sycl_stream;
141 sycl::event m_last_command_event;
142};
143
144/*---------------------------------------------------------------------------*/
145/*---------------------------------------------------------------------------*/
146
149{
150 public:
151
152 explicit SyclRunQueueEvent([[maybe_unused]] bool has_timer)
153 {
154 }
155 ~SyclRunQueueEvent() override
156 {
157 }
158
159 public:
160
161 // Enregistre l'événement au sein d'une RunQueue
162 void recordQueue([[maybe_unused]] impl::IRunQueueStream* stream) final
163 {
164 ARCANE_CHECK_POINTER(stream);
165 auto* rq = static_cast<SyclRunQueueStream*>(stream);
166 m_sycl_event = rq->lastCommandEvent();
167#if defined(__ADAPTIVECPP__)
168 m_recorded_stream = stream;
169 // TODO: Vérifier s'il faut faire quelque chose
170#elif defined(__INTEL_LLVM_COMPILER)
171 //m_sycl_event = rq->trueStream().ext_oneapi_submit_barrier();
172#else
173 ARCANE_THROW(NotSupportedException, "Only supported for AdaptiveCpp and Intel DPC++ implementation");
174#endif
175 }
176
177 void wait() final
178 {
179 //ARCANE_SYCL_FUNC_NOT_HANDLED;
180 // TODO: Vérifier ce que cela signifie exactement
181 m_sycl_event.wait();
182 }
183
184 void waitForEvent([[maybe_unused]] impl::IRunQueueStream* stream) final
185 {
186#if defined(__ADAPTIVECPP__)
187 auto* rq = static_cast<SyclRunQueueStream*>(stream);
188 m_sycl_event.wait(rq->trueStream().get_wait_list());
189#elif defined(__INTEL_LLVM_COMPILER)
190 std::vector<sycl::event> events;
191 events.push_back(m_sycl_event);
192 auto* rq = static_cast<SyclRunQueueStream*>(stream);
193 rq->trueStream().ext_oneapi_submit_barrier(events);
194#else
195 ARCANE_THROW(NotSupportedException, "Only supported for AdaptiveCpp and Intel DPC++ implementation");
196#endif
197 }
198
199 Int64 elapsedTime([[maybe_unused]] IRunQueueEventImpl* start_event) final
200 {
202 // Il faut prendre l'évènement de début car on est certain qu'il contient
203 // la bonne valeur de 'sycl::event'.
204 sycl::event event = (static_cast<SyclRunQueueEvent*>(start_event))->m_sycl_event;
205 // Si pas d'évènement associé, on ne fait rien pour éviter une exception
206 if (event==sycl::event())
207 return 0;
208
209 bool is_submitted = event.get_info<sycl::info::event::command_execution_status>() == sycl::info::event_command_status::complete;
210 if (!is_submitted)
211 return 0;
212 Int64 start = event.get_profiling_info<sycl::info::event_profiling::command_start>();
213 Int64 end = event.get_profiling_info<sycl::info::event_profiling::command_end>();
214 return (end - start);
215 }
216
217 private:
218
219 sycl::event m_sycl_event;
220 impl::IRunQueueStream* m_recorded_stream = nullptr;
221};
222
223/*---------------------------------------------------------------------------*/
224/*---------------------------------------------------------------------------*/
225
228{
229 friend class SyclRunQueueStream;
230
231 public:
232
233 void notifyBeginLaunchKernel() override
234 {
235 }
236 void notifyEndLaunchKernel() override
237 {
238 }
239 void barrier() override
240 {
241 // TODO Faire le wait sur la file par défaut n'est pas strictement équivalent
242 // à la barrière en CUDA qui synchronize tout le device.
243 m_default_queue->wait();
244 }
245 eExecutionPolicy executionPolicy() const override
246 {
248 }
249 impl::IRunQueueStream* createStream(const RunQueueBuildInfo& bi) override
250 {
251 return new SyclRunQueueStream(this, bi);
252 }
253 impl::IRunQueueEventImpl* createEventImpl() override
254 {
255 return new SyclRunQueueEvent(false);
256 }
257 impl::IRunQueueEventImpl* createEventImplWithTimer() override
258 {
259 return new SyclRunQueueEvent(true);
260 }
261 void setMemoryAdvice([[maybe_unused]] ConstMemoryView buffer, [[maybe_unused]] eMemoryAdvice advice,
262 [[maybe_unused]] DeviceId device_id) override
263 {
264 }
265 void unsetMemoryAdvice([[maybe_unused]] ConstMemoryView buffer,
267 {
268 }
269
270 void setCurrentDevice([[maybe_unused]] DeviceId device_id) final
271 {
272 ARCANE_SYCL_FUNC_NOT_HANDLED;
273 }
274 const IDeviceInfoList* deviceInfoList() override { return &m_device_info_list; }
275
276 void getPointerAttribute(PointerAttribute& attribute, const void* ptr) override
277 {
278 sycl::usm::alloc sycl_mem_type = sycl::get_pointer_type(ptr, *m_default_context);
279 ePointerMemoryType mem_type = ePointerMemoryType::Unregistered;
280 const void* host_ptr = nullptr;
281 const void* device_ptr = nullptr;
282 if (sycl_mem_type == sycl::usm::alloc::host) {
283 // HostPinned. Doit être accessible depuis le device mais
284 //
285 mem_type = ePointerMemoryType::Host;
286 host_ptr = ptr;
287 // TODO: Regarder comment récupérer la valeur
288 device_ptr = ptr;
289 }
290 else if (sycl_mem_type == sycl::usm::alloc::device) {
291 mem_type = ePointerMemoryType::Device;
292 device_ptr = ptr;
293 }
294 else if (sycl_mem_type == sycl::usm::alloc::shared) {
295 mem_type = ePointerMemoryType::Managed;
296 // TODO: pour l'instant on remplit avec le pointeur car on ne sait
297 // pas comment récupérer l'info.
298 host_ptr = ptr;
299 device_ptr = ptr;
300 }
301 // TODO: à corriger
302 Int32 device_id = 0;
303 _fillPointerAttribute(attribute, mem_type, device_id, ptr, device_ptr, host_ptr);
304 }
305
306 void fillDevicesAndSetDefaultQueue(bool is_verbose);
307 sycl::queue& defaultQueue() const { return *m_default_queue; }
308 sycl::device& defaultDevice() const { return *m_default_device; }
309
310 private:
311
312 impl::DeviceInfoList m_device_info_list;
313 std::unique_ptr<sycl::device> m_default_device;
314 std::unique_ptr<sycl::context> m_default_context;
315 std::unique_ptr<sycl::queue> m_default_queue;
316
317 private:
318
319 void _init(sycl::device& device)
320 {
321 m_default_device = std::make_unique<sycl::device>(device);
322 m_default_queue = std::make_unique<sycl::queue>(device);
323 m_default_context = std::make_unique<sycl::context>(device);
324 }
325};
326
327/*---------------------------------------------------------------------------*/
328/*---------------------------------------------------------------------------*/
329
330SyclRunQueueStream::
331SyclRunQueueStream(SyclRunnerRuntime* runtime, const RunQueueBuildInfo& bi)
332: m_runtime(runtime)
333{
334 sycl::device& d = runtime->defaultDevice();
335 // Indique que les commandes lancées sont implicitement exécutées les
336 // unes derrière les autres.
337 auto queue_property = sycl::property::queue::in_order();
338 // Pour le profiling
339 auto profiling_property = sycl::property::queue::enable_profiling();
341
342 // Gestionnaire d'erreur.
343 sycl::async_handler err_handler;
344 err_handler = _getAsyncHandler();
345 if (bi.isDefault())
346 m_sycl_stream = std::make_unique<sycl::queue>(d, err_handler, queue_properties);
347 else {
348 ARCANE_SYCL_FUNC_NOT_HANDLED;
349 m_sycl_stream = std::make_unique<sycl::queue>(d, err_handler, queue_properties);
350 }
351}
352
353/*---------------------------------------------------------------------------*/
354/*---------------------------------------------------------------------------*/
355
356void SyclRunnerRuntime::
357fillDevicesAndSetDefaultQueue(bool is_verbose)
358{
359 if (is_verbose){
360 for (auto platform : sycl::platform::get_platforms()) {
361 std::cout << "Platform: "
362 << platform.get_info<sycl::info::platform::name>()
363 << std::endl;
364 }
365 }
366
367 sycl::device device{ sycl::gpu_selector_v };
368 if (is_verbose)
369 std::cout << "\nDevice: " << device.get_info<sycl::info::device::name>()
370 << "\nVersion=" << device.get_info<sycl::info::device::version>()
371 << std::endl;
372 // Pour l'instant, on prend comme file par défaut la première trouvée
373 // et on ne considère qu'un seul device accessible.
374 _init(device);
375
376 DeviceInfo device_info;
377 device_info.setDescription("No description info");
378 device_info.setDeviceId(DeviceId(0));
379 device_info.setName(device.get_info<sycl::info::device::name>());
380 m_device_info_list.addDevice(device_info);
381}
382
383/*---------------------------------------------------------------------------*/
384/*---------------------------------------------------------------------------*/
385
387: public IMemoryCopier
388{
389 void copy(ConstMemoryView from, eMemoryRessource from_mem,
391 const RunQueue* queue) override;
392};
393
394/*---------------------------------------------------------------------------*/
395/*---------------------------------------------------------------------------*/
396
397} // namespace Arcane::Accelerator::Sycl
398
399namespace
400{
403} // namespace
404
405/*---------------------------------------------------------------------------*/
406/*---------------------------------------------------------------------------*/
407
408namespace Arcane::Accelerator::Sycl
409{
410
411/*---------------------------------------------------------------------------*/
412/*---------------------------------------------------------------------------*/
413
415copy(ConstMemoryView from, [[maybe_unused]] eMemoryRessource from_mem,
417 const RunQueue* queue)
418{
419 if (queue) {
420 queue->copyMemory(MemoryCopyArgs(to.bytes(), from.bytes()).addAsync(queue->isAsync()));
421 return;
422 }
423 sycl::queue& q = global_sycl_runtime.defaultQueue();
424 q.memcpy(to.data(), from.data(), from.bytes().size()).wait();
425}
426
427} // namespace Arcane::Accelerator::Sycl
428
429/*---------------------------------------------------------------------------*/
430/*---------------------------------------------------------------------------*/
431
432// Cette fonction est le point d'entrée utilisé lors du chargement
433// dynamique de cette bibliothèque
434extern "C" ARCANE_EXPORT void
435arcaneRegisterAcceleratorRuntimesycl(Arcane::Accelerator::RegisterRuntimeInfo& init_info)
436{
437 using namespace Arcane;
438 using namespace Arcane::Accelerator::Sycl;
439 Arcane::Accelerator::impl::setUsingSYCLRuntime(true);
440 Arcane::Accelerator::impl::setSYCLRunQueueRuntime(&global_sycl_runtime);
443 mrm->setIsAccelerator(true);
444 mrm->setAllocator(eMemoryRessource::UnifiedMemory, getSyclUnifiedMemoryAllocator());
445 mrm->setAllocator(eMemoryRessource::HostPinned, getSyclHostPinnedMemoryAllocator());
446 mrm->setAllocator(eMemoryRessource::Device, getSyclDeviceMemoryAllocator());
447 mrm->setCopier(&global_sycl_memory_copier);
448 global_sycl_runtime.fillDevicesAndSetDefaultQueue(init_info.isVerbose());
449 setSyclMemoryQueue(global_sycl_runtime.defaultQueue());
450}
451
452/*---------------------------------------------------------------------------*/
453/*---------------------------------------------------------------------------*/
#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
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
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 * _internalImpl() 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é à 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
Exception lorsqu'une opération n'est pas supportée.
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 * 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.