Arcane  v4.1.1.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
GenericScanner.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2025 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/* GenericScanner.h (C) 2000-2025 */
9/* */
10/* Algorithme de 'scan' pour les accélérateurs. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCANE_ACCELERATOR_GENERICSCANNER_H
13#define ARCANE_ACCELERATOR_GENERICSCANNER_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "arcane/utils/ArrayView.h"
18#include "arcane/utils/FatalErrorException.h"
19
20#include "arcane/utils/NumArray.h"
21
22#include "arcane/accelerator/core/RunQueue.h"
23
24#include "arcane/accelerator/AcceleratorGlobal.h"
25#include "arcane/accelerator/CommonUtils.h"
26#include "arcane/accelerator/RunCommandLaunchInfo.h"
28#include "arcane/accelerator/ScanImpl.h"
29#include "arcane/accelerator/MultiThreadAlgo.h"
30
31/*---------------------------------------------------------------------------*/
32/*---------------------------------------------------------------------------*/
33
34namespace Arcane::Accelerator::impl
35{
36
37/*---------------------------------------------------------------------------*/
38/*---------------------------------------------------------------------------*/
43class ScannerImpl
44{
45 public:
46
47 explicit ScannerImpl(const RunQueue& queue)
48 : m_queue(queue)
49 {}
50
51 public:
52
53 template <bool IsExclusive, typename InputIterator, typename OutputIterator,
54 typename Operator, typename DataType>
55 void apply(Int32 nb_item, InputIterator input_data, OutputIterator output_data,
56 DataType init_value, Operator op, const TraceInfo& trace_info)
57 {
58 RunCommand command = makeCommand(m_queue);
59 command << trace_info;
60 Impl::RunCommandLaunchInfo launch_info(command, nb_item);
61 launch_info.beginExecute();
62 eExecutionPolicy exec_policy = m_queue.executionPolicy();
63 switch (exec_policy) {
64#if defined(ARCANE_COMPILING_CUDA)
66 size_t temp_storage_size = 0;
67 cudaStream_t stream = Impl::CudaUtils::toNativeStream(&m_queue);
68 // Premier appel pour connaitre la taille pour l'allocation
69 if constexpr (IsExclusive)
70 ARCANE_CHECK_CUDA(::cub::DeviceScan::ExclusiveScan(nullptr, temp_storage_size,
71 input_data, output_data, op, init_value, nb_item, stream));
72 else
73 ARCANE_CHECK_CUDA(::cub::DeviceScan::InclusiveScan(nullptr, temp_storage_size,
74 input_data, output_data, op, nb_item, stream));
75 void* temp_storage = m_storage.allocate(temp_storage_size);
76 if constexpr (IsExclusive)
77 ARCANE_CHECK_CUDA(::cub::DeviceScan::ExclusiveScan(temp_storage, temp_storage_size,
78 input_data, output_data, op, init_value, nb_item, stream));
79 else
80 ARCANE_CHECK_CUDA(::cub::DeviceScan::InclusiveScan(temp_storage, temp_storage_size,
81 input_data, output_data, op, nb_item, stream));
82 } break;
83#endif
84#if defined(ARCANE_COMPILING_HIP)
86 size_t temp_storage_size = 0;
87 // Premier appel pour connaitre la taille pour l'allocation
88 hipStream_t stream = Impl::HipUtils::toNativeStream(&m_queue);
89 if constexpr (IsExclusive)
90 ARCANE_CHECK_HIP(rocprim::exclusive_scan(nullptr, temp_storage_size, input_data, output_data,
91 init_value, nb_item, op, stream));
92 else
93 ARCANE_CHECK_HIP(rocprim::inclusive_scan(nullptr, temp_storage_size, input_data, output_data,
94 nb_item, op, stream));
95 void* temp_storage = m_storage.allocate(temp_storage_size);
96 if constexpr (IsExclusive)
97 ARCANE_CHECK_HIP(rocprim::exclusive_scan(temp_storage, temp_storage_size, input_data, output_data,
98 init_value, nb_item, op, stream));
99 else
100 ARCANE_CHECK_HIP(rocprim::inclusive_scan(temp_storage, temp_storage_size, input_data, output_data,
101 nb_item, op, stream));
102 } break;
103#endif
104#if defined(ARCANE_COMPILING_SYCL)
106#if defined(ARCANE_USE_SCAN_ONEDPL) && defined(__INTEL_LLVM_COMPILER)
107 sycl::queue queue = impl::SyclUtils::toNativeStream(&m_queue);
108 auto policy = oneapi::dpl::execution::make_device_policy(queue);
109 if constexpr (IsExclusive) {
110 oneapi::dpl::exclusive_scan(policy, input_data, input_data + nb_item, output_data, init_value, op);
111 }
112 else {
113 oneapi::dpl::inclusive_scan(policy, input_data, input_data + nb_item, output_data, op);
114 }
115#else
117 copy_input_data(nb_item);
118 NumArray<DataType, MDDim1> copy_output_data(nb_item);
119 SmallSpan<DataType> in_data = copy_input_data.to1DSmallSpan();
120 SmallSpan<DataType> out_data = copy_output_data.to1DSmallSpan();
121 {
122 auto command = makeCommand(m_queue);
123 command << RUNCOMMAND_LOOP1(iter, nb_item)
124 {
125 auto [i] = iter();
126 in_data[i] = input_data[i];
127 };
128 }
129 m_queue.barrier();
130 SyclScanner<IsExclusive, DataType, Operator> scanner;
131 scanner.doScan(m_queue, in_data, out_data, init_value);
132 {
133 auto command = makeCommand(m_queue);
134 command << RUNCOMMAND_LOOP1(iter, nb_item)
135 {
136 auto [i] = iter();
137 output_data[i] = out_data[i];
138 };
139 }
140 m_queue.barrier();
141#endif
142 } break;
143#endif
145 // Si le nombre de valeurs est 1 on utilise la version séquentielle.
146 // TODO: il serait judicieux de faire cela aussi pour des valeurs plus importantes
147 // car en général sur les petites boucles le multi-threading est contre productif.
148 if (nb_item > 1) {
149 MultiThreadAlgo scanner;
150 scanner.doScan<IsExclusive, DataType>(launch_info.loopRunInfo(), nb_item, input_data, output_data, init_value, op);
151 break;
152 }
153 [[fallthrough]];
155 DataType sum = init_value;
156 for (Int32 i = 0; i < nb_item; ++i) {
157 DataType v = *input_data;
158 if constexpr (IsExclusive) {
159 *output_data = sum;
160 sum = op(v, sum);
161 }
162 else {
163 sum = op(v, sum);
164 *output_data = sum;
165 }
166 ++input_data;
167 ++output_data;
168 }
169 } break;
170 default:
171 ARCANE_FATAL(getBadPolicyMessage(exec_policy));
172 }
173 launch_info.endExecute();
174 }
175
176 private:
177
178 RunQueue m_queue;
179 GenericDeviceStorage m_storage;
180};
181
182/*---------------------------------------------------------------------------*/
183/*---------------------------------------------------------------------------*/
184
185} // namespace Arcane::Accelerator::impl
186
187namespace Arcane::Accelerator
188{
189
190/*---------------------------------------------------------------------------*/
191/*---------------------------------------------------------------------------*/
199template <typename DataType>
201{
202 public:
203
206 {
207 _applyArray<true>(queue, input, output, ScannerSumOperator<DataType>{});
208 }
209
211 {
212 _applyArray<true>(queue, input, output, ScannerMinOperator<DataType>{});
213 }
214
216 {
217 _applyArray<true>(queue, input, output, ScannerMaxOperator<DataType>{});
218 }
219
221 {
222 _applyArray<false>(queue, input, output, ScannerSumOperator<DataType>{});
223 }
224
226 {
227 _applyArray<false>(queue, input, output, ScannerMinOperator<DataType>{});
228 }
229
231 {
232 _applyArray<false>(queue, input, output, ScannerMaxOperator<DataType>{});
233 }
234
235 private:
236
237 template <bool IsExclusive, typename Operator>
238 static void _applyArray(RunQueue* queue, SmallSpan<const DataType> input, SmallSpan<DataType> output, const Operator& op)
239 {
241 impl::ScannerImpl scanner(*queue);
242 const Int32 nb_item = input.size();
243 if (output.size() != nb_item)
244 ARCANE_FATAL("Sizes are not equals: input={0} output={1}", nb_item, output.size());
245 const DataType* input_data = input.data();
246 DataType* output_data = output.data();
247 DataType init_value = op.defaultValue();
248 scanner.apply<IsExclusive>(nb_item, input_data, output_data, init_value, op, TraceInfo{});
249 if (!queue->isAsync())
250 queue->barrier();
251 }
252};
253
254/*---------------------------------------------------------------------------*/
255/*---------------------------------------------------------------------------*/
264class GenericScanner
265{
266 public:
267
271 template <typename DataType, typename SetterLambda>
272 class SetterLambdaIterator
273 {
274 public:
275
277 class Setter
278 {
279 public:
280
281 ARCCORE_HOST_DEVICE explicit Setter(const SetterLambda& s, Int32 index)
282 : m_index(index)
283 , m_lambda(s)
284 {}
285 ARCCORE_HOST_DEVICE void operator=(const DataType& value)
286 {
287 m_lambda(m_index, value);
288 }
289
290 public:
291
292 Int32 m_index = 0;
293 SetterLambda m_lambda;
294 };
295
296 using value_type = DataType;
297 using iterator_category = std::random_access_iterator_tag;
298 using reference = Setter;
299 using difference_type = ptrdiff_t;
300 using pointer = void;
301 using ThatClass = SetterLambdaIterator<DataType, SetterLambda>;
302
303 public:
304
305 ARCCORE_HOST_DEVICE SetterLambdaIterator(const SetterLambda& s)
306 : m_lambda(s)
307 {}
308 ARCCORE_HOST_DEVICE explicit SetterLambdaIterator(const SetterLambda& s, Int32 v)
309 : m_index(v)
310 , m_lambda(s)
311 {}
312
313 public:
314
315 ARCCORE_HOST_DEVICE ThatClass& operator++()
316 {
317 ++m_index;
318 return (*this);
319 }
320 ARCCORE_HOST_DEVICE friend ThatClass operator+(const ThatClass& iter, Int32 x)
321 {
322 return ThatClass(iter.m_lambda, iter.m_index + x);
323 }
324 ARCCORE_HOST_DEVICE friend ThatClass operator+(Int32 x, const ThatClass& iter)
325 {
326 return ThatClass(iter.m_lambda, iter.m_index + x);
327 }
328 ARCCORE_HOST_DEVICE friend bool operator<(const ThatClass& iter1, const ThatClass& iter2)
329 {
330 return iter1.m_index < iter2.m_index;
331 }
332 ARCCORE_HOST_DEVICE ThatClass operator-(Int32 x)
333 {
334 return ThatClass(m_lambda, m_index - x);
335 }
336 ARCCORE_HOST_DEVICE Int32 operator-(const ThatClass& x) const
337 {
338 return m_index - x.m_index;
339 }
340 ARCCORE_HOST_DEVICE reference operator*() const
341 {
342 return Setter(m_lambda, m_index);
343 }
344 ARCCORE_HOST_DEVICE reference operator[](Int32 x) const { return Setter(m_lambda, m_index + x); }
345 ARCCORE_HOST_DEVICE friend bool operator!=(const ThatClass& a, const ThatClass& b)
346 {
347 return a.m_index != b.m_index;
348 }
349
350 private:
351
352 Int32 m_index = 0;
353 SetterLambda m_lambda;
354 };
355
356 public:
357
358 explicit GenericScanner(const RunQueue& queue)
359 : m_queue(queue)
360 {}
361
362 public:
363
364 template <typename DataType, typename GetterLambda, typename SetterLambda, typename Operator>
365 void applyWithIndexExclusive(Int32 nb_value, const DataType& initial_value,
366 const GetterLambda& getter_lambda,
367 const SetterLambda& setter_lambda,
368 const Operator& op_lambda,
369 const TraceInfo& trace_info = TraceInfo())
370 {
371 _applyWithIndex<true>(nb_value, initial_value, getter_lambda, setter_lambda, op_lambda, trace_info);
372 }
373
374 template <typename DataType, typename GetterLambda, typename SetterLambda, typename Operator>
375 void applyWithIndexInclusive(Int32 nb_value, const DataType& initial_value,
376 const GetterLambda& getter_lambda,
377 const SetterLambda& setter_lambda,
378 const Operator& op_lambda,
379 const TraceInfo& trace_info = TraceInfo())
380 {
381 _applyWithIndex<false>(nb_value, initial_value, getter_lambda, setter_lambda, op_lambda, trace_info);
382 }
383
384 template <typename InputDataType, typename OutputDataType, typename Operator>
385 void applyExclusive(const OutputDataType& initial_value,
386 SmallSpan<const InputDataType> input,
387 SmallSpan<OutputDataType> output,
388 const Operator& op_lambda,
389 const TraceInfo& trace_info = TraceInfo())
390 {
391 _apply<true>(initial_value, input, output, op_lambda, trace_info);
392 }
393
394 template <typename InputDataType, typename OutputDataType, typename Operator>
395 void applyInclusive(const OutputDataType& initial_value,
396 SmallSpan<const InputDataType> input,
397 SmallSpan<OutputDataType> output,
398 const Operator& op_lambda,
399 const TraceInfo& trace_info = TraceInfo())
400 {
401 _apply<false>(initial_value, input, output, op_lambda, trace_info);
402 }
403
404 private:
405
406 template <bool IsExclusive, typename DataType, typename GetterLambda, typename SetterLambda, typename Operator>
407 void _applyWithIndex(Int32 nb_value, const DataType& initial_value,
408 const GetterLambda& getter_lambda,
409 const SetterLambda& setter_lambda,
410 const Operator& op_lambda,
411 const TraceInfo& trace_info)
412 {
413 impl::GetterLambdaIterator<DataType, GetterLambda> input_iter(getter_lambda);
414 SetterLambdaIterator<DataType, SetterLambda> output_iter(setter_lambda);
415 impl::ScannerImpl scanner(m_queue);
416 scanner.apply<IsExclusive>(nb_value, input_iter, output_iter, initial_value, op_lambda, trace_info);
417 _checkBarrier();
418 }
419
420 template <bool IsExclusive, typename InputDataType, typename OutputDataType, typename Operator>
421 void _apply(const OutputDataType& initial_value,
422 SmallSpan<const InputDataType> input,
423 SmallSpan<OutputDataType> output,
424 const Operator& op,
425 const TraceInfo& trace_info = TraceInfo())
426 {
427 const Int32 nb_item = input.size();
428 if (output.size() != nb_item)
429 ARCANE_FATAL("Sizes are not equals: input={0} output={1}", nb_item, output.size());
430 auto* input_data = input.data();
431 auto* output_data = output.data();
432 impl::ScannerImpl scanner(m_queue);
433 scanner.apply<IsExclusive>(nb_item, input_data, output_data, initial_value, op, trace_info);
434 _checkBarrier();
435 }
436
437 void _checkBarrier()
438 {
439 // Les fonctions cub ou rocprim pour le scan sont asynchrones par défaut.
440 // Si on a une RunQueue synchrone, alors on fait une barrière.
441 if (!m_queue.isAsync())
442 m_queue.barrier();
443 }
444
445 private:
446
447 RunQueue m_queue;
448};
449
450/*---------------------------------------------------------------------------*/
451/*---------------------------------------------------------------------------*/
452
453} // namespace Arcane::Accelerator
454
455/*---------------------------------------------------------------------------*/
456/*---------------------------------------------------------------------------*/
457
458#endif
459
460/*---------------------------------------------------------------------------*/
461/*---------------------------------------------------------------------------*/
#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_FATAL(...)
Macro envoyant une exception FatalErrorException.
Types et macros pour gérer les boucles sur les accélérateurs.
#define RUNCOMMAND_LOOP1(iter_name, x1,...)
Boucle 1D sur accélérateur avec arguments supplémentaires.
Permet de positionner un élément de l'itérateur de sortie.
Itérateur sur une lambda pour positionner une valeur via un index.
Object temporaire pour conserver les informations d'exécution d'une commande et regrouper les tests.
void beginExecute()
Indique qu'on commence l'exécution de la commande.
void endExecute()
Signale la fin de l'exécution.
const ForLoopRunInfo & loopRunInfo() const
Informations d'exécution de la boucle.
bool isAsync() const
Indique si la file d'exécution est asynchrone.
Definition RunQueue.cc:320
void barrier() const
Bloque tant que toutes les commandes associées à la file ne sont pas terminées.
Definition RunQueue.cc:159
Algorithmes de scan exclusif ou inclusif sur accélérateurs.
static void exclusiveMax(RunQueue *queue, SmallSpan< const DataType > input, SmallSpan< DataType > output)
Maximum exclusif.
static void inclusiveMax(RunQueue *queue, SmallSpan< const DataType > input, SmallSpan< DataType > output)
Maximum inclusif.
static void exclusiveMin(RunQueue *queue, SmallSpan< const DataType > input, SmallSpan< DataType > output)
Minimum exclusif.
static void inclusiveSum(RunQueue *queue, SmallSpan< const DataType > input, SmallSpan< DataType > output)
Somme inclusive.
static void inclusiveMin(RunQueue *queue, SmallSpan< const DataType > input, SmallSpan< DataType > output)
Minimum inclusif.
static void exclusiveSum(RunQueue *queue, SmallSpan< const DataType > input, SmallSpan< DataType > output)
Somme exclusive.
Gère l'allocation interne sur le device.
Definition CommonUtils.h:98
Algorithmes avancée en mode multi-thread.
void doScan(ForLoopRunInfo run_info, Int32 nb_value, InputIterator input, OutputIterator output, DataType init_value, Operator op)
Algorithme de scan multi-thread.
Classe pour effectuer un scan exlusif ou inclusif avec un opérateur spécifique.
Tableaux multi-dimensionnels pour les types numériques accessibles sur accélérateurs.
constexpr SmallSpan< DataType > to1DSmallSpan()
Vue 1D sur l'instance (uniquement si rank == 1)
Vue d'un tableau d'éléments de type T.
Definition Span.h:801
constexpr __host__ __device__ pointer data() const noexcept
Pointeur sur le début de la vue.
Definition Span.h:537
constexpr __host__ __device__ SizeType size() const noexcept
Retourne la taille du tableau.
Definition Span.h:325
Espace de nom pour l'utilisation des accélérateurs.
RunCommand makeCommand(const RunQueue &run_queue)
Créé une commande associée à la file run_queue.
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.
std::int32_t Int32
Type entier signé sur 32 bits.