Arcane  v4.1.0.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
WorkGroupLoopRange.h
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/* WorkGroupLoopRange.h (C) 2000-2025 */
9/* */
10/* Boucle pour le parallélisme hiérarchique. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCANE_ACCELERATOR_WORKGROUPLOOPRANGE_H
13#define ARCANE_ACCELERATOR_WORKGROUPLOOPRANGE_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "arcane/accelerator/AcceleratorUtils.h"
18
19#if defined(ARCANE_COMPILING_CUDA)
20#include <cooperative_groups.h>
21#endif
22#if defined(ARCANE_COMPILING_HIP)
23#include <hip/hip_cooperative_groups.h>
24#endif
25
26/*---------------------------------------------------------------------------*/
27/*---------------------------------------------------------------------------*/
28
29namespace Arcane::Accelerator
30{
31namespace Impl
32{
34} // namespace Impl
35
39class SyclDeviceWorkItemBlock;
40class DeviceWorkItemBlock;
41class SyclWorkGroupLoopContext;
42
43/*---------------------------------------------------------------------------*/
44/*---------------------------------------------------------------------------*/
45/*!
46 * \brief Représente un WorkItem dans le parallélisme hiérarchique.
47 */
48class WorkItem
49{
50 friend WorkGroupLoopContext;
51 friend SyclDeviceWorkItemBlock;
52 friend DeviceWorkItemBlock;
53 friend HostWorkItemGroup;
54
55 private:
56
57 //! Constructeur pour l'hôte
58 explicit constexpr ARCCORE_HOST_DEVICE WorkItem(Int32 loop_index)
59 : m_loop_index(loop_index)
60 {}
61
62 public:
63
64 //! Index linéaire entre 0 et WorkGroupLoopRange::nbElement()
65 constexpr Int32 linearIndex() const { return m_loop_index; }
66
67 private:
68
69 Int32 m_loop_index = 0;
70};
71
72/*---------------------------------------------------------------------------*/
73/*---------------------------------------------------------------------------*/
74/*!
75 * \brief Gère un groupe de WorkItem dans un WorkGroupLoopRange pour l'hôte.
76 *
77 * Contraitement à l'exécution sur accélérateur ou un seul WorkItem est
78 * actif, l'hôte doit gérer un ensemble de WorkItem.
79 *
80 * Pour l'hôte, un bloc de WorkItem correspond toujours à l'ensemble
81 * des WorkItem d'un groupe du WorkGroupLoopRange associé. Cela signifie
82 * que nbActiveItem()==WorkGroupLoopRange::groupSize() (sauf pour le dernier
83 * élément de l'itération si le nombre total d'élément n'est pas un multiple
84 * de la taille d'un groupe).
85 */
86class HostWorkItemGroup
87{
88 friend WorkGroupLoopContext;
89 friend SyclDeviceWorkItemBlock;
90 friend DeviceWorkItemBlock;
91
92 private:
93
94 //! Constructeur pour l'hôte
95 explicit constexpr ARCCORE_HOST_DEVICE HostWorkItemGroup(Int32 loop_index, Int32 group_index, Int32 group_size, Int32 nb_active_item)
96 : m_loop_index(loop_index)
97 , m_group_size(group_size)
98 , m_group_index(group_index)
99 , m_nb_active_item(nb_active_item)
100 {}
101
102 public:
103
104 //! Rang du groupe du WorkItem dans la liste des WorkGroup.
105 constexpr Int32 groupRank() const { return m_group_index; }
106
107 //! Nombre de WorkItem dans un WorkGroup.
108 constexpr Int32 groupSize() const { return m_group_size; }
109
110 //! Rang du WorkItem actif dans son WorkGroup.
111 constexpr Int32 activeWorkItemRankInGroup() const { return 0; }
112
113 //! Indique si on s'exécute sur un accélérateur
114 static constexpr bool isDevice() { return false; }
115
116 //! Bloque tant que tous les \a WorkItem du groupe ne sont pas arrivés ici.
117 void barrier() {}
118
119 //! Nombre de \a WorkItem à gérer dans l'itération
120 constexpr Int32 nbActiveItem() const { return m_nb_active_item; }
121
122 //! Récupère le \a index-ème \a WorkItem à gérer
124 {
125 ARCANE_CHECK_AT(index, m_nb_active_item);
126 return WorkItem(m_loop_index);
127 }
128
129 private:
130
131 Int32 m_loop_index = 0;
132 Int32 m_group_size = 0;
133 Int32 m_group_index = 0;
134 Int32 m_nb_active_item = 0;
135};
136
137/*---------------------------------------------------------------------------*/
138/*---------------------------------------------------------------------------*/
139
140#if defined(ARCANE_COMPILING_CUDA) || defined(ARCANE_COMPILING_HIP)
141
142/*---------------------------------------------------------------------------*/
143/*---------------------------------------------------------------------------*/
144/*!
145 * \brief Gère un bloc de WorkItem dans un WorkGroupLoopRange pour un device CUDA ou ROCM.
146 */
147class DeviceWorkItemBlock
148{
150
151 private:
152
153 /*!
154 * \brief Constructeur pour le device.
155 *
156 * Ce constructeur n'a pas besoin d'informations spécifiques car tout est
157 * récupéré via cooperative_groups::this_thread_block()
158 */
159 explicit __device__ DeviceWorkItemBlock()
160 : m_thread_block(cooperative_groups::this_thread_block())
161 {}
162
163 public:
164
165 //! Rang du groupe du WorkItem dans la liste des WorkGroup.
166 __device__ Int32 groupRank() const { return m_thread_block.group_index().x; }
167
168 //! Nombre de WorkItem dans un WorkGroup.
169 __device__ Int32 groupSize() { return m_thread_block.group_dim().x; }
170
171 //! Rang du WorkItem actif dans son WorkGroup.
172 __device__ Int32 activeWorkItemRankInGroup() const { return m_thread_block.thread_index().x; }
173
174 //! Bloque tant que tous les \a WorkItem du groupe ne sont pas arrivés ici.
175 __device__ void barrier() { m_thread_block.sync(); }
176
177 //! Indique si on s'exécute sur un accélérateur
178 static constexpr __device__ bool isDevice() { return true; }
179
180 //! Nombre de \a WorkItem à gérer dans l'itération
181 constexpr __device__ Int32 nbActiveItem() const { return 1; }
182
183 //! Récupère le \a index-ème \a WorkItem à gérer
184 __device__ WorkItem activeItem(Int32 index)
185 {
186 // Seulement valide pour index==0
187 ARCANE_CHECK_AT(index, 1);
188 return WorkItem(blockDim.x * blockIdx.x + threadIdx.x);
189 }
190
191 private:
192
193 cooperative_groups::thread_block m_thread_block;
194};
195#endif
196
197/*---------------------------------------------------------------------------*/
198/*---------------------------------------------------------------------------*/
199/*!
200 * \brief Contexte d'exécution d'une commande sur un ensemble de blocs.
201 *
202 * Cette classe est utilisée pour l'hôte (séquentiel et multi-thread) et
203 * pour CUDA et ROCM/HIP.
204 * La méthode group() est différente sur accélérateur et sur l'hôte ce qui
205 * permet de particulariser le traitement de la commande.
206 */
207class WorkGroupLoopContext
208{
209 // Pour accéder aux constructeurs
210 friend WorkGroupLoopRange;
212
213 private:
214
215 //! Ce constructeur est utilisé dans l'implémentation hôte.
216 explicit constexpr WorkGroupLoopContext(Int32 loop_index, Int32 group_index, Int32 group_size, Int32 nb_active_item)
217 : m_loop_index(loop_index)
218 , m_group_index(group_index)
219 , m_group_size(group_size)
220 , m_nb_active_item(nb_active_item)
221 {
222 }
223
224 // Ce constructeur n'est utilisé que sur le device
225 // Il ne fait rien car les valeurs utiles sont récupérées via cooperative_groups::this_thread_block()
226 explicit constexpr ARCCORE_DEVICE WorkGroupLoopContext() {}
227
228 public:
229
230#if defined(ARCCORE_DEVICE_CODE) && !defined(ARCANE_COMPILING_SYCL)
231 //! Groupe courant. Pour CUDA/ROCM, il s'agit d'un bloc de threads.
232 __device__ DeviceWorkItemBlock group() const { return DeviceWorkItemBlock(); }
233#else
234 //! Groupe courant
235 HostWorkItemGroup group() const { return HostWorkItemGroup(m_loop_index, m_group_index, m_group_size, m_nb_active_item); }
236#endif
237
238 private:
239
240 Int32 m_loop_index = 0;
241 Int32 m_group_index = 0;
242 Int32 m_group_size = 0;
243 Int32 m_nb_active_item = 0;
244};
245
246/*---------------------------------------------------------------------------*/
247/*---------------------------------------------------------------------------*/
248/*
249 * Implémentation pour SYCL.
250 *
251 * L'équivalent de \a cooperative_groups::thread_group() avec SYCL
252 * est le \a sycl::nd_item<1>. Il est plus compliqué à utiliser pour deux
253 * raisons:
254 *
255 * - il n'y a pas dans SYCL un équivalent de
256 * \a cooperative_groups::this_thread_block(). Il faut utiliser la valeur
257 * de \a sycl::nb_item<1> passé en argument du noyau de calcul.
258 * - il n'y a pas de constructeurs par défaut pour \a sycl::nb_item<1>.
259 *
260 * Pour contourner ces deux problèmes, on utilise un type spécifique pour
261 * gérer les noyaux en SYCL. Heureusement, il est possible d'utiliser les
262 * lambda template avec SYCL. On utilise donc deux types pour gérer
263 * les noyaux selon qu'on s'exécute sur le device SYCL ou sur l'hôte.
264 *
265 * TODO: regarder si avec la macro SYCL_DEVICE_ONLY il n'est pas possible
266 * d'avoir le même type comportant des champs différents
267 */
268#if defined(ARCANE_COMPILING_SYCL)
269
270/*!
271 * \brief Gère un bloc de WorkItem dans un WorkGroupLoopRange pour un device Sycl.
272 */
273class SyclDeviceWorkItemBlock
274{
275 friend SyclWorkGroupLoopContext;
276
277 private:
278
279 explicit SyclDeviceWorkItemBlock(sycl::nd_item<1> n)
280 : m_nd_item(n)
281 {
282 }
283
284 public:
285
286 //! Rang du groupe du WorkItem dans la liste des WorkGroup.
287 Int32 groupRank() const { return static_cast<Int32>(m_nd_item.get_group(0)); }
288
289 //! Nombre de WorkItem dans un WorkGroup.
290 Int32 groupSize() { return static_cast<Int32>(m_nd_item.get_local_range(0)); }
291
292 //! Rang du WorkItem actif dans le WorkGroup.
293 Int32 activeWorkItemRankInGroup() const { return static_cast<Int32>(m_nd_item.get_local_id(0)); }
294
295 //! Bloque tant que tous les \a WorkItem du groupe ne sont pas arrivés ici.
296 void barrier() { m_nd_item.barrier(); }
297
298 //! Indique si on s'exécute sur un accélérateur
299 static constexpr bool isDevice() { return true; }
300
301 //! Nombre de \a WorkItem à gérer dans l'itération
302 constexpr Int32 nbActiveItem() const { return 1; }
303
304 //! Récupère le \a index-ème \a WorkItem à gérer
305 WorkItem activeItem(Int32 index)
306 {
307 // Seulement valide pour index==0
308 ARCANE_CHECK_AT(index, 1);
309 return WorkItem(static_cast<Int32>(m_nd_item.get_group(0) * m_nd_item.get_local_range(0) + m_nd_item.get_local_id(0)));
310 }
311
312 private:
313
314 sycl::nd_item<1> m_nd_item;
315};
316
317/*---------------------------------------------------------------------------*/
318/*---------------------------------------------------------------------------*/
319/*!
320 * \brief Contexte d'exécution d'une WorkGroupLoopRange pour Sycl.
321 *
322 * Cette classe est utilisée uniquement pour la polique d'exécution eAcceleratorPolicy::SYCL.
323 */
324class SyclWorkGroupLoopContext
325{
326 friend WorkGroupLoopRange;
327
328 private:
329
330 // Ce constructeur n'est utilisé que sur le device
331 explicit SyclWorkGroupLoopContext(sycl::nd_item<1> n)
332 : m_nd_item(n)
333 {
334 }
335
336 public:
337
338 //! Groupe courant
339 SyclDeviceWorkItemBlock group() const { return SyclDeviceWorkItemBlock(m_nd_item); }
340
341 private:
342
343 sycl::nd_item<1> m_nd_item;
344};
345
346#endif // ARCANE_COMPILING_SYCL
347
348/*---------------------------------------------------------------------------*/
349/*---------------------------------------------------------------------------*/
350/*!
351 * \brief Intervalle d'itération d'une boucle utilisant le parallélisme hiérarchique.
352 *
353 * \warning API en cours de définition. Ne pas utiliser en dehors de %Arcane.
354 *
355 * L'intervalle d'itération contient nbElement() et est décomposé en
356 * \a nbGroup() WorkGroup contenant chacun \a groupSize() WorkItem.
357 *
358 * La création de ces instances se fait via les méthodes makeWorkGroupLoopRange().
359 *
360 * \note Sur accélérateur, La valeur de \a groupSize() est dépendante de l'architecture
361 * de l'accélérateur. Afin d'être portable, cette valeur doit être comprise entre 32 et 1024
362 * et être un multiple de 32.
363 */
364class ARCANE_ACCELERATOR_EXPORT WorkGroupLoopRange
365{
366 private:
367
368 friend ARCANE_ACCELERATOR_EXPORT WorkGroupLoopRange
369 makeWorkGroupLoopRange(RunCommand& command, Int32 nb_group, Int32 group_size);
370 friend ARCANE_ACCELERATOR_EXPORT WorkGroupLoopRange
371 makeWorkGroupLoopRange(RunCommand& command, Int32 nb_element, Int32 nb_group, Int32 group_size);
372
373 public:
374
375 using LoopIndexType = WorkGroupLoopContext;
376
377 public:
378
379 WorkGroupLoopRange() = default;
380
381 private:
382
383 /*!
384 * \brief Créé un intervalle d'itération pour la commande \a command.
385 *
386 * Le nombre total d'éléments est \a total_nb_element, réparti en \a nb_group de taille \a group_size.
387 * \a total_nb_element n'est pas nécessairement un multiple de \a block_size.
388 */
389 WorkGroupLoopRange(Int32 total_nb_element, Int32 nb_group, Int32 group_size);
390
391 public:
392
393 //! Nombre d'éléments à traiter
394 constexpr Int32 nbElement() const { return m_total_size; }
395 //! Taille d'un groupe
396 constexpr Int32 groupSize() const { return m_group_size; }
397 //! Nombre de groupes
398 constexpr Int32 nbGroup() const { return m_nb_group; }
399 //! Nombre d'éléments du dernier groupe
400 constexpr Int32 lastGroupSize() const { return m_last_group_size; }
401 //! Nombre d'éléments actifs pour le i-ème groupe
402 constexpr Int32 nbActiveItem(Int32 i) const
403 {
404 return ((i + 1) != m_nb_group) ? m_group_size : m_last_group_size;
405 }
406
407 public:
408
409 //TODO rendre privé ou mettre en externe
410#if defined(ARCANE_COMPILING_CUDA) || defined(ARCANE_COMPILING_HIP)
411 constexpr ARCCORE_HOST_DEVICE WorkGroupLoopContext getIndices(Int32) const { return WorkGroupLoopContext(); }
412#endif
413
414#if defined(ARCANE_COMPILING_SYCL)
415 //TODO rendre privé ou mettre en externe
416 SyclWorkGroupLoopContext getIndices(sycl::nd_item<1> id) const
417 {
418 return SyclWorkGroupLoopContext(id);
419 }
420#endif
421
422 private:
423
424 Int32 m_total_size = 0;
425 Int32 m_nb_group = 0;
426 Int32 m_group_size = 0;
427 Int32 m_last_group_size = 0;
428};
429
430/*---------------------------------------------------------------------------*/
431/*---------------------------------------------------------------------------*/
432
433} // namespace Arcane::Accelerator
434
435/*---------------------------------------------------------------------------*/
436/*---------------------------------------------------------------------------*/
437
438#endif
439
440/*---------------------------------------------------------------------------*/
441/*---------------------------------------------------------------------------*/
Gère un groupe de WorkItem dans un WorkGroupLoopRange pour l'hôte.
WorkItem activeItem(Int32 index) const
Récupère le index-ème WorkItem à gérer.
static constexpr bool isDevice()
Indique si on s'exécute sur un accélérateur.
void barrier()
Bloque tant que tous les WorkItem du groupe ne sont pas arrivés ici.
constexpr Int32 activeWorkItemRankInGroup() const
Rang du WorkItem actif dans son WorkGroup.
constexpr Int32 groupRank() const
Rang du groupe du WorkItem dans la liste des WorkGroup.
constexpr Int32 nbActiveItem() const
Nombre de WorkItem à gérer dans l'itération.
constexpr Int32 groupSize() const
Nombre de WorkItem dans un WorkGroup.
Gestion d'une commande sur accélérateur.
Contexte d'exécution d'une commande sur un ensemble de blocs.
HostWorkItemGroup group() const
Groupe courant.
Intervalle d'itération d'une boucle utilisant le parallélisme hiérarchique.
constexpr Int32 nbActiveItem(Int32 i) const
Nombre d'éléments actifs pour le i-ème groupe.
constexpr Int32 nbGroup() const
Nombre de groupes.
constexpr Int32 lastGroupSize() const
Nombre d'éléments du dernier groupe.
constexpr Int32 nbElement() const
Nombre d'éléments à traiter.
friend WorkGroupLoopRange makeWorkGroupLoopRange(RunCommand &command, Int32 nb_group, Int32 group_size)
Créé un intervalle d'itération pour la commande command.
friend WorkGroupLoopRange makeWorkGroupLoopRange(RunCommand &command, Int32 nb_element, Int32 nb_group, Int32 group_size)
Créé un intervalle d'itération pour la commande command.
constexpr Int32 groupSize() const
Taille d'un groupe.
Représente un WorkItem dans le parallélisme hiérarchique.
constexpr Int32 linearIndex() const
Index linéaire entre 0 et WorkGroupLoopRange::nbElement()
Espace de nom pour l'utilisation des accélérateurs.
std::int32_t Int32
Type entier signé sur 32 bits.