Arcane  v4.1.4.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
RunCommandLaunchImpl.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 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/* RunCommandLaunchImpl.h (C) 2000-2026 */
9/* */
10/* Implémentation d'une RunCommand pour le parallélisme hiérarchique. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ACCELERATOR_RUNCOMMANDLAUNCHIMPL_H
13#define ARCCORE_ACCELERATOR_RUNCOMMANDLAUNCHIMPL_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "AcceleratorGlobal.h"
18#include "arccore/common/SequentialFor.h"
19#include "arccore/common/StridedLoopRanges.h"
20#include "arccore/common/accelerator/RunCommand.h"
21#include "arccore/concurrency/ParallelFor.h"
22
23#include "arccore/accelerator/WorkGroupLoopRange.h"
24#include "arccore/accelerator/CooperativeWorkGroupLoopRange.h"
25#include "arccore/accelerator/KernelLauncher.h"
26
27#include <barrier>
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32namespace Arcane::Accelerator::Impl
33{
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37/*!
38 * \brief Informations d'une boucle utilisant le parallélisme hiérarchique
39 * sur l'hôte.
40 */
41template <typename IndexType_>
42class HostLaunchLoopRangeBase
43{
44 public:
45
46 using IndexType = IndexType_;
47
48 public:
49
50 ARCCORE_ACCELERATOR_EXPORT
51 HostLaunchLoopRangeBase(IndexType total_size, Int32 nb_group, Int32 block_size);
52
53 public:
54
55 //! Nombre d'éléments à traiter
56 constexpr IndexType nbElement() const { return m_total_size; }
57 //! Taille d'un bloc
58 constexpr Int32 blockSize() const { return m_block_size; }
59 //! Nombre de groupes
60 constexpr Int32 nbBlock() const { return m_nb_block; }
61 //! Nombre d'éléments du dernier groupe
62 constexpr Int32 lastBlockSize() const { return m_last_block_size; }
63 //! Nombre d'éléments actifs pour le i-ème groupe
64 constexpr Int32 nbActiveItem(Int32 i) const
65 {
66 return ((i + 1) != m_nb_block) ? m_block_size : m_last_block_size;
67 }
68 void setThreadGridSynchronizer(ThreadGridSynchronizer* v)
69 {
70 m_thread_grid_synchronizer = v;
71 }
72 ThreadGridSynchronizer* threadGridSynchronizer() const
73 {
74 return m_thread_grid_synchronizer;
75 }
76
77 private:
78
79 IndexType m_total_size = 0;
80 Int32 m_nb_block = 0;
81 Int32 m_block_size = 0;
82 Int32 m_last_block_size = 0;
83 //! Cette instance est gérée par arcaneParallelFor(HostLaunchLoopRange<>...)
84 ThreadGridSynchronizer* m_thread_grid_synchronizer = nullptr;
85};
86
87/*---------------------------------------------------------------------------*/
88/*---------------------------------------------------------------------------*/
89
90template <typename WorkGroupLoopRangeType_>
91class HostLaunchLoopRange
92: public HostLaunchLoopRangeBase<typename WorkGroupLoopRangeType_::IndexType>
93{
94 public:
95
96 using WorkGroupLoopRangeType = WorkGroupLoopRangeType_;
97 using IndexType = typename WorkGroupLoopRangeType_::IndexType;
98 using BaseClass = HostLaunchLoopRangeBase<typename WorkGroupLoopRangeType_::IndexType>;
99
100 public:
101
102 explicit HostLaunchLoopRange(const WorkGroupLoopRangeType& bounds)
103 : BaseClass(bounds.nbElement(), bounds.nbBlock(), bounds.blockSize())
104 {
105 }
106};
107
108/*---------------------------------------------------------------------------*/
109/*---------------------------------------------------------------------------*/
110
112{
113 public:
114
115#if defined(ARCCORE_COMPILING_CUDA_OR_HIP)
116
117 template <typename IndexType_> static constexpr ARCCORE_HOST_DEVICE WorkGroupLoopContext<IndexType_>
118 build(const WorkGroupLoopRange<IndexType_>& loop_range)
119 {
120 return WorkGroupLoopContext<IndexType_>(loop_range.nbElement());
121 }
122
123 template <typename IndexType_> static constexpr ARCCORE_HOST_DEVICE CooperativeWorkGroupLoopContext<IndexType_>
124 build(const CooperativeWorkGroupLoopRange<IndexType_>& loop_range)
125 {
127 }
128
129#endif
130
131#if defined(ARCCORE_COMPILING_SYCL)
132
133 template <typename IndexType_> static SyclWorkGroupLoopContext<IndexType_>
134 build(const WorkGroupLoopRange<IndexType_>& loop_range, sycl::nd_item<1> id)
135 {
136 return SyclWorkGroupLoopContext<IndexType_>(id, loop_range.nbElement());
137 }
138
139 template <typename IndexType_> static SyclCooperativeWorkGroupLoopContext<IndexType_>
140 build(const CooperativeWorkGroupLoopRange<IndexType_>& loop_range, sycl::nd_item<1> id)
141 {
143 }
144#endif
145};
146
147#if defined(ARCCORE_COMPILING_SYCL)
148
149// Pour indiquer qu'il faut toujours utiliser sycl::nd_item (et jamais sycl::id)
150// comme argument avec 'WorkGroupLoopRange.
151template <typename IndexType_>
153: public std::true_type
154{
155};
156// Pour indiquer qu'il faut toujours utiliser sycl::nd_item (et jamais sycl::id)
157// comme argument avec 'CooperativeWorkGroupLoopRange.
158template <typename IndexType_>
159class IsAlwaysUseSyclNdItem<StridedLoopRanges<CooperativeWorkGroupLoopRange<IndexType_>>>
160: public std::true_type
161{
162};
163
164#endif
165
166/*---------------------------------------------------------------------------*/
167/*---------------------------------------------------------------------------*/
168
169/*!
170 * \internal
171 * \brief Classe pour exécuter en séquentiel sur l'hôte une partie de la boucle.
172 */
174{
175 public:
176
177 //! Applique le fonctor \a func sur une boucle séqentielle.
178 template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> static void
180 const Lambda& func, RemainingArgs... remaining_args)
181 {
182 using LoopIndexType = LoopBoundType::LoopIndexType;
184 const Int32 group_size = bounds.blockSize();
185 Int32 loop_index = begin_index * group_size;
186 for (Int32 i = begin_index; i < (begin_index + nb_loop); ++i) {
187 // Pour la dernière itération de la boucle, le nombre d'éléments actifs peut-être
188 // inférieur à la taille d'un groupe si \a total_nb_element n'est pas
189 // un multiple de \a group_size.
190 Int32 nb_active = bounds.nbActiveItem(i);
191 LoopIndexType li(loop_index, i, group_size, nb_active, bounds.nbElement(), bounds.nbBlock(), bounds.threadGridSynchronizer());
192 func(li, remaining_args...);
193 loop_index += group_size;
194 }
195
197 }
198};
199
200/*---------------------------------------------------------------------------*/
201/*---------------------------------------------------------------------------*/
202
203#if defined(ARCCORE_COMPILING_CUDA_OR_HIP)
204
205// On utilise 'Argument dependent lookup' pour trouver 'arcaneGetLoopIndexCudaHip'
206template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> __global__ static void
207doHierarchicalLaunchCudaHip(LoopBoundType bounds, Lambda func, RemainingArgs... remaining_args)
208{
209 Int32 i = blockDim.x * blockIdx.x + threadIdx.x;
210
212 // TODO: regarder s'il faut faire ce test
213 if (i < bounds.nbOriginalElement()) {
214 func(WorkGroupLoopContextBuilder::build(bounds.originalLoop()), remaining_args...);
215 }
217};
218
219#endif
220
221#if defined(ARCCORE_COMPILING_SYCL)
222
223template <typename LoopBoundType, typename Lambda, typename... RemainingArgs>
224class doHierarchicalLaunchSycl
225{
226 public:
227
228 void operator()(sycl::nd_item<1> x, SmallSpan<std::byte> shared_memory,
229 LoopBoundType bounds, Lambda func,
230 RemainingArgs... remaining_args) const
231 {
232 Int32 i = static_cast<Int32>(x.get_global_id(0));
233 SyclKernelRemainingArgsHelper::applyAtBegin(x, shared_memory, remaining_args...);
234 // TODO: regarder s'il faut faire ce test
235 if (i < bounds.nbOriginalElement()) {
236 func(WorkGroupLoopContextBuilder::build(bounds.originalLoop(), x), remaining_args...);
237 }
238 SyclKernelRemainingArgsHelper::applyAtEnd(x, shared_memory, remaining_args...);
239 }
240};
241
242#endif
243
244/*---------------------------------------------------------------------------*/
245/*---------------------------------------------------------------------------*/
246/*!
247 * \brief Applique la lambda \a func sur une boucle \a bounds.
248 *
249 * La lambda \a func est appliqué à la commande \a command.
250 * \a bound est le type de la boucle. Les types supportés sont:
251 *
252 * - WorkGroupLoopRange
253 * - CooperativeWorkGroupLoopRange
254 *
255 * Les arguments supplémentaires \a other_args sont utilisés pour supporter
256 * des fonctionnalités telles que les réductions (ReducerSum2, ReducerMax2, ...)
257 * ou la gestion de la mémoire locale (via LocalMemory).
258 */
259template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> void
260_doHierarchicalLaunch(RunCommand& command, LoopBoundType bounds,
261 const Lambda& func, const RemainingArgs&... other_args)
262{
263 Int64 nb_orig_element = bounds.nbElement();
264 if (nb_orig_element == 0)
265 return;
266 const eExecutionPolicy exec_policy = command.executionPolicy();
267 // En mode coopératif, il faut toujours appeler setBlockSize()
268 // pour être certain que la taille de bloc est cohérente sur l'hôte
269 // (en séquentiel, il ne faut qu'un seul bloc dans ce cas).
270 if ((bounds.blockSize() == 0) || bounds.isCooperativeLaunch())
271 bounds.setBlockSize(command);
272 using TrueLoopBoundType = StridedLoopRanges<LoopBoundType>;
273 TrueLoopBoundType bounds2(bounds);
274 if (isAcceleratorPolicy(exec_policy)) {
275 command.addNbThreadPerBlock(bounds.blockSize());
276 bounds2.setNbStride(command.nbStride());
277 }
278
279 using HostLoopBoundType = HostLaunchLoopRange<LoopBoundType>;
280
281 Impl::RunCommandLaunchInfo launch_info(command, bounds2.strideValue(), bounds.isCooperativeLaunch());
282 launch_info.beginExecute();
283 switch (exec_policy) {
285 ARCCORE_KERNEL_CUDA_FUNC((Impl::doHierarchicalLaunchCudaHip<TrueLoopBoundType, Lambda, RemainingArgs...>),
286 launch_info, func, bounds2, other_args...);
287 break;
289 ARCCORE_KERNEL_HIP_FUNC((Impl::doHierarchicalLaunchCudaHip<TrueLoopBoundType, Lambda, RemainingArgs...>),
290 launch_info, func, bounds2, other_args...);
291 break;
293 ARCCORE_KERNEL_SYCL_FUNC((Impl::doHierarchicalLaunchSycl<TrueLoopBoundType, Lambda, RemainingArgs...>{}),
294 launch_info, func, bounds2, other_args...);
295 break;
297 HostLoopBoundType host_bounds(bounds);
298 arccoreSequentialFor(host_bounds, func, other_args...);
299 } break;
301 HostLoopBoundType host_bounds(bounds);
302 arccoreParallelFor(host_bounds, launch_info.loopRunInfo(), func, other_args...);
303 } break;
304 default:
305 ARCCORE_FATAL("Invalid execution policy '{0}'", exec_policy);
306 }
307 launch_info.endExecute();
308}
309
310/*---------------------------------------------------------------------------*/
311/*---------------------------------------------------------------------------*/
312/*!
313 * \brief Classe pour conserver les arguments d'une RunCommand.
314 */
315template <typename LoopBoundType, typename... RemainingArgs>
316class ExtendedLaunchRunCommand
317{
318 public:
319
320 ExtendedLaunchRunCommand(RunCommand& command, const LoopBoundType& bounds)
321 : m_command(command)
322 , m_bounds(bounds)
323 {
324 }
325 ExtendedLaunchRunCommand(RunCommand& command, const LoopBoundType& bounds, const std::tuple<RemainingArgs...>& args)
326 : m_command(command)
327 , m_bounds(bounds)
328 , m_remaining_args(args)
329 {
330 }
331 RunCommand& m_command;
332 LoopBoundType m_bounds;
333 std::tuple<RemainingArgs...> m_remaining_args;
334};
335
336/*---------------------------------------------------------------------------*/
337/*---------------------------------------------------------------------------*/
338/*!
339 * \brief Classe pour gérer le lancement d'un noyau de calcul hiérarchique.
340 */
341template <typename LoopBoundType, typename... RemainingArgs>
342class ExtendedLaunchLoop
343{
344 public:
345
346 ExtendedLaunchLoop(const LoopBoundType& bounds, RemainingArgs... args)
347 : m_bounds(bounds)
348 , m_remaining_args(args...)
349 {
350 }
351 LoopBoundType m_bounds;
352 std::tuple<RemainingArgs...> m_remaining_args;
353};
354
355/*---------------------------------------------------------------------------*/
356/*---------------------------------------------------------------------------*/
357
358template <typename LoopBoundType, typename... RemainingArgs> auto
359makeLaunch(const LoopBoundType& bounds, RemainingArgs... args)
360-> ExtendedLaunchLoop<LoopBoundType, RemainingArgs...>
361{
362 return ExtendedLaunchLoop<LoopBoundType, RemainingArgs...>(bounds, args...);
363}
364
365/*---------------------------------------------------------------------------*/
366/*---------------------------------------------------------------------------*/
367
368template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> void
369operator<<(ExtendedLaunchRunCommand<LoopBoundType, RemainingArgs...>&& nr, const Lambda& f)
370{
371 if constexpr (sizeof...(RemainingArgs) > 0) {
372 std::apply([&](auto... vs) { _doHierarchicalLaunch(nr.m_command, nr.m_bounds, f, vs...); }, nr.m_remaining_args);
373 }
374 else {
375 _doHierarchicalLaunch(nr.m_command, nr.m_bounds, f);
376 }
377}
378
379/*---------------------------------------------------------------------------*/
380/*---------------------------------------------------------------------------*/
381/*!
382 * \internal
383 * \brief Applique le fonctor \a func sur une boucle séqentielle.
384 */
385template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> void
386arccoreSequentialFor(HostLaunchLoopRange<LoopBoundType> bounds, const Lambda& func, const RemainingArgs&... remaining_args)
387{
388 WorkGroupSequentialForHelper::apply(0, bounds.nbBlock(), bounds, func, remaining_args...);
389}
390
391/*---------------------------------------------------------------------------*/
392/*---------------------------------------------------------------------------*/
393/*!
394 * \internal
395 * \brief Applique le fonctor \a func sur une boucle parallèle.
396 */
397template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> void
398arccoreParallelFor(HostLaunchLoopRange<LoopBoundType> bounds, ForLoopRunInfo run_info,
399 const Lambda& func, const RemainingArgs&... remaining_args)
400{
401 Int32 nb_thread = run_info.options().value().maxThread();
402 ThreadGridSynchronizer grid_sync(nb_thread);
403 bounds.setThreadGridSynchronizer(&grid_sync);
404 auto sub_func = [=](Int32 begin_index, Int32 nb_loop) {
405 Impl::WorkGroupSequentialForHelper::apply(begin_index, nb_loop, bounds, func, remaining_args...);
406 };
407 ::Arcane::arccoreParallelFor(0, bounds.nbBlock(), run_info, sub_func);
408}
409
410/*---------------------------------------------------------------------------*/
411/*---------------------------------------------------------------------------*/
412
413} // namespace Arcane::Accelerator::Impl
414
415/*---------------------------------------------------------------------------*/
416/*---------------------------------------------------------------------------*/
417
418#endif
419
420/*---------------------------------------------------------------------------*/
421/*---------------------------------------------------------------------------*/
#define ARCCORE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Contexte d'exécution d'une commande sur un ensemble de blocs.
Intervalle d'itération d'une boucle utilisant le parallélisme hiérarchique collaboratif.
static ARCCORE_DEVICE void applyAtEnd(Int32 index, RemainingArgs &... remaining_args)
Applique les fonctors des arguments additionnels en fin de kernel.
static ARCCORE_DEVICE void applyAtBegin(Int32 index, RemainingArgs &... remaining_args)
Applique les fonctors des arguments additionnels en début de kernel.
Classe pour gérer le lancement d'un noyau de calcul hiérarchique.
constexpr Int32 blockSize() const
Taille d'un bloc.
constexpr Int32 lastBlockSize() const
Nombre d'éléments du dernier groupe.
constexpr Int32 nbActiveItem(Int32 i) const
Nombre d'éléments actifs pour le i-ème groupe.
constexpr IndexType nbElement() const
Nombre d'éléments à traiter.
constexpr Int32 nbBlock() const
Nombre de groupes.
Template pour savoir si un type utilisé comme boucle dans les kernels nécessite toujours sycl::nb_ite...
Classe pour gérer la décomposition d'une boucle en plusieurs parties.
static void apply(Int32 begin_index, Int32 nb_loop, HostLaunchLoopRange< LoopBoundType > bounds, const Lambda &func, RemainingArgs... remaining_args)
Applique le fonctor func sur une boucle séqentielle.
Contexte d'exécution d'une commande sur un ensemble de blocs.
Intervalle d'itération d'une boucle utilisant le parallélisme hiérarchique.
static void applyAtEnd(RemainingArgs &... remaining_args)
Applique les functors des arguments additionnels à la fin de l'itération.
static void applyAtBegin(RemainingArgs &... remaining_args)
Applique les functors des arguments additionnels au début de l'itération.
eExecutionPolicy
Politique d'exécution pour un Runner.
@ SYCL
Politique d'exécution utilisant l'environnement SYCL.
@ HIP
Politique d'exécution utilisant l'environnement HIP.
@ CUDA
Politique d'exécution utilisant l'environnement CUDA.
@ Sequential
Politique d'exécution séquentielle.
@ Thread
Politique d'exécution multi-thread.
bool isAcceleratorPolicy(eExecutionPolicy exec_policy)
Indique si exec_policy correspond à un accélérateur.
std::int64_t Int64
Type entier signé sur 64 bits.
void arccoreParallelFor(const ComplexForLoopRanges< RankValue > &loop_ranges, const ForLoopRunInfo &run_info, const LambdaType &lambda_function, const ReducerArgs &... reducer_args)
Applique en concurrence la fonction lambda lambda_function sur l'intervalle d'itération donné par loo...
Definition ParallelFor.h:85
std::int32_t Int32
Type entier signé sur 32 bits.