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