Arcane  4.2.3.0
User documentation
Loading...
Searching...
No Matches
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/* Implementation of a RunCommand for hierarchical parallelism. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ACCELERATOR_RUNCOMMANDLAUNCHIMPL_H
13#define ARCCORE_ACCELERATOR_RUNCOMMANDLAUNCHIMPL_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "arccore/common/SequentialFor.h"
18#include "arccore/common/StridedLoopRanges.h"
19#include "arccore/common/accelerator/RunCommand.h"
20#include "arccore/concurrency/ParallelFor.h"
21
22#include "arccore/accelerator/WorkGroupLoopRange.h"
23#include "arccore/accelerator/CooperativeWorkGroupLoopRange.h"
24#include "arccore/accelerator/KernelLauncher.h"
25
26/*---------------------------------------------------------------------------*/
27/*---------------------------------------------------------------------------*/
28
29namespace Arcane::Accelerator::Impl
30{
31
32/*---------------------------------------------------------------------------*/
33/*---------------------------------------------------------------------------*/
34
35/*!
36 * \brief Information of a loop using hierarchical parallelism
37 * on the host.
38 */
39template <typename IndexType_>
40class HostLaunchLoopRangeBase
41{
42 public:
43
44 using IndexType = IndexType_;
45
46 public:
47
48 ARCCORE_ACCELERATOR_EXPORT
49 HostLaunchLoopRangeBase(IndexType total_size, Int32 nb_group, IndexType block_size);
50
51 public:
52
53 //! Number of elements to process
54 constexpr IndexType nbElement() const { return m_total_size; }
55 //! Block size
56 constexpr IndexType blockSize() const { return m_block_size; }
57 //! Number of blocks
58 constexpr Int32 nbBlock() const { return m_nb_block; }
59 //! Number of elements in the last block
60 constexpr IndexType lastBlockSize() const { return m_last_block_size; }
61 //! Number of active items for the i-th block
62 constexpr IndexType nbActiveItem(Int32 i) const
63 {
64 return ((i + 1) != m_nb_block) ? m_block_size : m_last_block_size;
65 }
66 //! Grid synchronizer (non-null only in cooperative multi-threading)
68 {
69 return m_thread_grid_synchronizer;
70 }
71 void setThreadGridSynchronizer(ThreadGridSynchronizer* v)
72 {
73 m_thread_grid_synchronizer = v;
74 }
75
76 private:
77
78 //! This instance is managed by arcaneParallelFor(HostLaunchLoopRange<>...)
79 ThreadGridSynchronizer* m_thread_grid_synchronizer = nullptr;
80 IndexType m_total_size = 0;
81 IndexType m_block_size = 0;
82 IndexType m_last_block_size = 0;
83 Int32 m_nb_block = 0;
84};
85
86/*---------------------------------------------------------------------------*/
87/*---------------------------------------------------------------------------*/
88
89template <typename WorkGroupLoopRangeType_>
90class HostLaunchLoopRange
91: public HostLaunchLoopRangeBase<typename WorkGroupLoopRangeType_::IndexType>
92{
93 public:
94
95 using WorkGroupLoopRangeType = WorkGroupLoopRangeType_;
96 using IndexType = typename WorkGroupLoopRangeType_::IndexType;
97 using BaseClass = HostLaunchLoopRangeBase<typename WorkGroupLoopRangeType_::IndexType>;
98
99 public:
100
101 explicit HostLaunchLoopRange(const WorkGroupLoopRangeType& bounds)
102 : BaseClass(bounds.nbElement(), bounds.nbBlock(), bounds.blockSize())
103 {
104 }
105};
106
107/*---------------------------------------------------------------------------*/
108/*---------------------------------------------------------------------------*/
109
111{
112 public:
113
114#if defined(ARCCORE_COMPILING_CUDA_OR_HIP)
115
116 template <typename IndexType_> static constexpr ARCCORE_HOST_DEVICE WorkGroupLoopContext<IndexType_>
117 build(const WorkGroupLoopRange<IndexType_>& loop_range)
118 {
119 return WorkGroupLoopContext<IndexType_>(loop_range.nbElement());
120 }
121
122 template <typename IndexType_> static constexpr ARCCORE_HOST_DEVICE CooperativeWorkGroupLoopContext<IndexType_>
123 build(const CooperativeWorkGroupLoopRange<IndexType_>& loop_range)
124 {
126 }
127
128#endif
129
130#if defined(ARCCORE_COMPILING_SYCL)
131
132 template <typename IndexType_> static SyclWorkGroupLoopContext<IndexType_>
133 build(const WorkGroupLoopRange<IndexType_>& loop_range, sycl::nd_item<1> id)
134 {
135 return SyclWorkGroupLoopContext<IndexType_>(id, loop_range.nbElement());
136 }
137
138 template <typename IndexType_> static SyclCooperativeWorkGroupLoopContext<IndexType_>
139 build(const CooperativeWorkGroupLoopRange<IndexType_>& loop_range, sycl::nd_item<1> id)
140 {
142 }
143#endif
144};
145
146#if defined(ARCCORE_COMPILING_SYCL)
147
148// To indicate that sycl::nd_item must always be used (and never sycl::id)
149// as an argument with 'WorkGroupLoopRange.
150template <typename IndexType_>
152: public std::true_type
153{
154};
155// To indicate that sycl::nd_item must always be used (and never sycl::id)
156// as an argument with 'CooperativeWorkGroupLoopRange.
157template <typename IndexType_>
158class IsAlwaysUseSyclNdItem<StridedLoopRanges<CooperativeWorkGroupLoopRange<IndexType_>>>
159: public std::true_type
160{
161};
162
163#endif
164
165/*---------------------------------------------------------------------------*/
166/*---------------------------------------------------------------------------*/
167
168/*!
169 * \internal
170 * \brief Class to execute a portion of the loop sequentially on the host.
171 */
173{
174 public:
175
176 //! Applies the functor \a func on a sequential loop.
177 template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> static void
179 const Lambda& func, RemainingArgs... remaining_args)
180 {
181 using LoopIndexType = LoopBoundType::LoopIndexType;
183 const Int32 group_size = bounds.blockSize();
184 Int32 loop_index = begin_index * group_size;
185 for (Int32 i = begin_index; i < (begin_index + nb_loop); ++i) {
186 // For the last loop iteration, the number of active elements may be
187 // less than the group size if \a total_nb_element is not
188 // a multiple of \a group_size.
189 Int32 nb_active = bounds.nbActiveItem(i);
190 LoopIndexType li(loop_index, i, group_size, nb_active, bounds.nbElement(), bounds.nbBlock(), bounds.threadGridSynchronizer());
191 func(li, remaining_args...);
192 loop_index += group_size;
193 }
194
196 }
197};
198
199/*---------------------------------------------------------------------------*/
200/*---------------------------------------------------------------------------*/
201
202#if defined(ARCCORE_COMPILING_CUDA_OR_HIP)
203
204// We use 'Argument dependent lookup' to find 'arcaneGetLoopIndexCudaHip'
205template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> __global__ static void
206doHierarchicalLaunchCudaHip(LoopBoundType bounds, Lambda func, RemainingArgs... remaining_args)
207{
208 Int32 i = blockDim.x * blockIdx.x + threadIdx.x;
209
211 // TODO: check if this test is necessary
212 if (i < bounds.nbOriginalElement()) {
213 func(WorkGroupLoopContextBuilder::build(bounds.originalLoop()), remaining_args...);
214 }
216};
217
218#endif
219
220#if defined(ARCCORE_COMPILING_SYCL)
221
222template <typename LoopBoundType, typename Lambda, typename... RemainingArgs>
223class doHierarchicalLaunchSycl
224{
225 public:
226
227 void operator()(sycl::nd_item<1> x, SmallSpan<std::byte> shared_memory,
228 LoopBoundType bounds, Lambda func,
229 RemainingArgs... remaining_args) const
230 {
231 Int32 i = static_cast<Int32>(x.get_global_id(0));
232 SyclKernelRemainingArgsHelper::applyAtBegin(x, shared_memory, remaining_args...);
233 // TODO: check if this test is necessary
234 if (i < bounds.nbOriginalElement()) {
235 func(WorkGroupLoopContextBuilder::build(bounds.originalLoop(), x), remaining_args...);
236 }
237 SyclKernelRemainingArgsHelper::applyAtEnd(x, shared_memory, remaining_args...);
238 }
239};
240
241#endif
242
243/*---------------------------------------------------------------------------*/
244/*---------------------------------------------------------------------------*/
245
246/*!
247 * \brief Applies the lambda \a func on a loop \a bounds.
248 *
249 * The lambda \a func is applied to the \a command.
250 * \a bound is the loop type. Supported types are:
251 *
252 * - WorkGroupLoopRange
253 * - CooperativeWorkGroupLoopRange
254 *
255 * Additional arguments \a other_args are used to support
256 * features such as reductions (ReducerSum2, ReducerMax2, ...)
257 * or local memory management (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 // In cooperative mode, setBlockSize() must always be called
268 // to ensure that the block size is consistent on the host
269 // (in sequential mode, only one block is needed in this case).
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/*!
314 * \brief Class to retain the arguments of a RunCommand.
315 */
316template <typename LoopBoundType, typename... RemainingArgs>
317class ExtendedLaunchRunCommand
318{
319 public:
320
321 ExtendedLaunchRunCommand(RunCommand& command, const LoopBoundType& bounds)
322 : m_command(command)
323 , m_bounds(bounds)
324 {
325 }
326 ExtendedLaunchRunCommand(RunCommand& command, const LoopBoundType& bounds, const std::tuple<RemainingArgs...>& args)
327 : m_command(command)
328 , m_bounds(bounds)
329 , m_remaining_args(args)
330 {
331 }
332 RunCommand& m_command;
333 LoopBoundType m_bounds;
334 std::tuple<RemainingArgs...> m_remaining_args;
335};
336
337/*---------------------------------------------------------------------------*/
338/*---------------------------------------------------------------------------*/
339
340/*!
341 * \brief Class to manage the launch of a hierarchical compute kernel.
342 */
343template <typename LoopBoundType, typename... RemainingArgs>
344class ExtendedLaunchLoop
345{
346 public:
347
348 ExtendedLaunchLoop(const LoopBoundType& bounds, RemainingArgs... args)
349 : m_bounds(bounds)
350 , m_remaining_args(args...)
351 {
352 }
353 LoopBoundType m_bounds;
354 std::tuple<RemainingArgs...> m_remaining_args;
355};
356
357/*---------------------------------------------------------------------------*/
358/*---------------------------------------------------------------------------*/
359
360template <typename LoopBoundType, typename... RemainingArgs> auto
361makeLaunch(const LoopBoundType& bounds, RemainingArgs... args)
362-> ExtendedLaunchLoop<LoopBoundType, RemainingArgs...>
363{
364 return ExtendedLaunchLoop<LoopBoundType, RemainingArgs...>(bounds, args...);
365}
366
367/*---------------------------------------------------------------------------*/
368/*---------------------------------------------------------------------------*/
369
370template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> void
371operator<<(ExtendedLaunchRunCommand<LoopBoundType, RemainingArgs...>&& nr, const Lambda& f)
372{
373 if constexpr (sizeof...(RemainingArgs) > 0) {
374 std::apply([&](auto... vs) { _doHierarchicalLaunch(nr.m_command, nr.m_bounds, f, vs...); }, nr.m_remaining_args);
375 }
376 else {
377 _doHierarchicalLaunch(nr.m_command, nr.m_bounds, f);
378 }
379}
380
381/*---------------------------------------------------------------------------*/
382/*---------------------------------------------------------------------------*/
383
384/*!
385 * \internal
386 * \brief Applies the functor \a func on a sequential loop.
387 */
388template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> void
389arccoreSequentialFor(HostLaunchLoopRange<LoopBoundType> bounds, const Lambda& func, const RemainingArgs&... remaining_args)
390{
391 WorkGroupSequentialForHelper::apply(0, bounds.nbBlock(), bounds, func, remaining_args...);
392}
393
394/*---------------------------------------------------------------------------*/
395/*---------------------------------------------------------------------------*/
396
397/*!
398 * \internal
399 * \brief Applies the functor \a func on a parallel loop.
400 */
401template <typename LoopBoundType, typename Lambda, typename... RemainingArgs> void
402arccoreParallelFor(HostLaunchLoopRange<LoopBoundType> bounds, ForLoopRunInfo run_info,
403 const Lambda& func, const RemainingArgs&... remaining_args)
404{
405 Int32 nb_thread = run_info.options().value().maxThread();
406 ThreadGridSynchronizer grid_sync(nb_thread);
407 bounds.setThreadGridSynchronizer(&grid_sync);
408 auto sub_func = [=](Int32 begin_index, Int32 nb_loop) {
409 Impl::WorkGroupSequentialForHelper::apply(begin_index, nb_loop, bounds, func, remaining_args...);
410 };
411 ::Arcane::arccoreParallelFor(0, bounds.nbBlock(), run_info, sub_func);
412}
413
414/*---------------------------------------------------------------------------*/
415/*---------------------------------------------------------------------------*/
416
417} // namespace Arcane::Accelerator::Impl
418
419/*---------------------------------------------------------------------------*/
420/*---------------------------------------------------------------------------*/
421
422#endif
423
424/*---------------------------------------------------------------------------*/
425/*---------------------------------------------------------------------------*/
#define ARCCORE_FATAL(...)
Macro throwing a FatalErrorException.
Execution context for a command on a set of blocks.
Iteration range of a loop using cooperative hierarchical parallelism.
static ARCCORE_DEVICE void applyAtEnd(Int32 index, RemainingArgs &... remaining_args)
Applies the functors of additional arguments at the end of the kernel.
static ARCCORE_DEVICE void applyAtBegin(Int32 index, RemainingArgs &... remaining_args)
Applies the functors of additional arguments at the beginning of the kernel.
Class to manage the launch of a hierarchical compute kernel.
constexpr IndexType nbActiveItem(Int32 i) const
Number of active items for the i-th block.
ThreadGridSynchronizer * threadGridSynchronizer() const
Grid synchronizer (non-null only in cooperative multi-threading).
constexpr IndexType nbElement() const
Number of elements to process.
constexpr IndexType lastBlockSize() const
Number of elements in the last block.
constexpr IndexType blockSize() const
Block size.
constexpr Int32 nbBlock() const
Number of blocks.
Template to determine if a type used as a loop in kernels always requires sycl::nb_item as an argumen...
Class to manage the decomposition of a loop into multiple parts.
static void apply(Int32 begin_index, Int32 nb_loop, HostLaunchLoopRange< LoopBoundType > bounds, const Lambda &func, RemainingArgs... remaining_args)
Applies the functor func on a sequential loop.
static void applyAtEnd(RemainingArgs &... remaining_args)
Applies the functors of additional arguments at the end of the iteration.
static void applyAtBegin(RemainingArgs &... remaining_args)
Applies the functors of additional arguments at the beginning of the iteration.
eExecutionPolicy
Execution policy for a Runner.
@ SYCL
Execution policy using the SYCL environment.
@ HIP
Execution policy using the HIP environment.
@ CUDA
Execution policy using the CUDA environment.
@ Thread
Multi-threaded execution policy.
bool isAcceleratorPolicy(eExecutionPolicy exec_policy)
Indicates if exec_policy corresponds to an accelerator.
void arccoreParallelFor(const ComplexForLoopRanges< RankValue, IndexType_ > &loop_ranges, const ForLoopRunInfo &run_info, const LambdaType &lambda_function, const ReducerArgs &... reducer_args)
Applies the lambda function lambda_function concurrently over the iteration interval given by loop_ra...
Definition ParallelFor.h:86
std::int64_t Int64
Signed integer type of 64 bits.
std::int32_t Int32
Signed integer type of 32 bits.