Arcane  v3.16.0.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
GenericFilterer.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/* GenericFilterer.h (C) 2000-2025 */
9/* */
10/* Algorithme de filtrage. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCANE_ACCELERATOR_GENERICFILTERER_H
13#define ARCANE_ACCELERATOR_GENERICFILTERER_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "arcane/utils/ArrayView.h"
18#include "arcane/utils/FatalErrorException.h"
19#include "arcane/utils/NumArray.h"
20#include "arcane/utils/TraceInfo.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//#define ARCANE_USE_SCAN_ONEDPL
37
38/*---------------------------------------------------------------------------*/
39/*---------------------------------------------------------------------------*/
46class ARCANE_ACCELERATOR_EXPORT GenericFilteringBase
47{
48 template <typename DataType, typename FlagType, typename OutputDataType>
49 friend class GenericFilteringFlag;
50 friend class GenericFilteringIf;
51 friend class SyclGenericFilteringImpl;
52
53 public:
54
55
56 protected:
57
58 GenericFilteringBase();
59
60 protected:
61
62 Int32 _nbOutputElement();
63 void _allocate();
64 void _allocateTemporaryStorage(size_t size);
65 int* _getDeviceNbOutPointer();
66 void _copyDeviceNbOutToHostNbOut();
67 void _setCalled();
68 bool _checkEmpty(Int32 nb_value);
69
70 protected:
71
74 // Mémoire de travail pour l'algorithme de filtrage.
75 GenericDeviceStorage m_algo_storage;
88
90 bool m_is_already_called = false;
91};
92
93/*---------------------------------------------------------------------------*/
94/*---------------------------------------------------------------------------*/
95
96#if defined(ARCANE_COMPILING_SYCL)
98class SyclGenericFilteringImpl
99{
100 public:
101
102 template <typename SelectLambda, typename InputIterator, typename OutputIterator>
103 static void apply(GenericFilteringBase& s, Int32 nb_item, InputIterator input_iter,
104 OutputIterator output_iter, SelectLambda select_lambda)
105 {
106 RunQueue queue = s.m_queue;
107 using DataType = std::iterator_traits<OutputIterator>::value_type;
108#if defined(ARCANE_USE_SCAN_ONEDPL) && defined(__INTEL_LLVM_COMPILER)
109 sycl::queue true_queue = AcceleratorUtils::toSyclNativeStream(queue);
110 auto policy = oneapi::dpl::execution::make_device_policy(true_queue);
111 auto out_iter = oneapi::dpl::copy_if(policy, input_iter, input_iter + nb_item, output_iter, select_lambda);
112 Int32 nb_output = out_iter - output_iter;
113 s.m_host_nb_out_storage[0] = nb_output;
114#else
115 NumArray<Int32, MDDim1> scan_input_data(nb_item);
116 NumArray<Int32, MDDim1> scan_output_data(nb_item);
117 SmallSpan<Int32> in_scan_data = scan_input_data.to1DSmallSpan();
118 SmallSpan<Int32> out_scan_data = scan_output_data.to1DSmallSpan();
119 {
120 auto command = makeCommand(queue);
121 command << RUNCOMMAND_LOOP1(iter, nb_item)
122 {
123 auto [i] = iter();
124 in_scan_data[i] = select_lambda(input_iter[i]) ? 1 : 0;
125 };
126 }
127 queue.barrier();
128 SyclScanner<false /*is_exclusive*/, Int32, ScannerSumOperator<Int32>> scanner;
129 scanner.doScan(queue, in_scan_data, out_scan_data, 0);
130 // La valeur de 'out_data' pour le dernier élément (nb_item-1) contient la taille du filtre
131 Int32 nb_output = out_scan_data[nb_item - 1];
132 s.m_host_nb_out_storage[0] = nb_output;
133
134 const bool do_verbose = false;
135 if (do_verbose && nb_item < 1500)
136 for (int i = 0; i < nb_item; ++i) {
137 std::cout << "out_data i=" << i << " out_data=" << out_scan_data[i]
138 << " in_data=" << in_scan_data[i] << " value=" << input_iter[i] << "\n ";
139 }
140 // Copie depuis 'out_data' vers 'in_data' les indices correspondant au filtre
141 // Comme 'output_iter' et 'input_iter' peuvent se chevaucher, il
142 // faut faire une copie intermédiaire
143 // TODO: détecter cela et ne faire la copie que si nécessaire.
144 NumArray<DataType,MDDim1> out_copy(eMemoryRessource::Device);
145 out_copy.resize(nb_output);
146 auto out_copy_view = out_copy.to1DSpan();
147 {
148 auto command = makeCommand(queue);
149 command << RUNCOMMAND_LOOP1(iter, nb_item)
150 {
151 auto [i] = iter();
152 if (in_scan_data[i] == 1)
153 out_copy_view[out_scan_data[i] - 1] = input_iter[i];
154 };
155 }
156 {
157 auto command = makeCommand(queue);
158 command << RUNCOMMAND_LOOP1(iter, nb_output)
159 {
160 auto [i] = iter();
161 output_iter[i] = out_copy_view[i];
162 };
163 }
164 // Obligatoire à cause de 'out_copy'. On pourra le supprimer avec une
165 // allocation temporaire.
166 queue.barrier();
167#endif
168 }
169};
170#endif
171
172/*---------------------------------------------------------------------------*/
173/*---------------------------------------------------------------------------*/
181template <typename DataType, typename FlagType, typename OutputDataType>
183{
184 public:
185
188 {
189 const Int32 nb_item = input.size();
190 if (output.size() != nb_item)
191 ARCANE_FATAL("Sizes are not equals: input={0} output={1}", nb_item, output.size());
192 [[maybe_unused]] const DataType* input_data = input.data();
193 [[maybe_unused]] DataType* output_data = output.data();
194 [[maybe_unused]] const FlagType* flag_data = flag.data();
196 RunQueue queue = s.m_queue;
197 exec_policy = queue.executionPolicy();
198 switch (exec_policy) {
199#if defined(ARCANE_COMPILING_CUDA)
201 size_t temp_storage_size = 0;
202 cudaStream_t stream = AcceleratorUtils::toCudaNativeStream(queue);
203 // Premier appel pour connaitre la taille pour l'allocation
204 int* nb_out_ptr = nullptr;
205 ARCANE_CHECK_CUDA(::cub::DeviceSelect::Flagged(nullptr, temp_storage_size,
206 input_data, flag_data, output_data, nb_out_ptr, nb_item, stream));
207
208 s._allocateTemporaryStorage(temp_storage_size);
209 nb_out_ptr = s._getDeviceNbOutPointer();
210 ARCANE_CHECK_CUDA(::cub::DeviceSelect::Flagged(s.m_algo_storage.address(), temp_storage_size,
211 input_data, flag_data, output_data, nb_out_ptr, nb_item, stream));
212 s._copyDeviceNbOutToHostNbOut();
213 } break;
214#endif
215#if defined(ARCANE_COMPILING_HIP)
217 size_t temp_storage_size = 0;
218 // Premier appel pour connaitre la taille pour l'allocation
219 hipStream_t stream = AcceleratorUtils::toHipNativeStream(queue);
220 int* nb_out_ptr = nullptr;
221 ARCANE_CHECK_HIP(rocprim::select(nullptr, temp_storage_size, input_data, flag_data, output_data,
222 nb_out_ptr, nb_item, stream));
223
224 s._allocateTemporaryStorage(temp_storage_size);
225 nb_out_ptr = s._getDeviceNbOutPointer();
226
227 ARCANE_CHECK_HIP(rocprim::select(s.m_algo_storage.address(), temp_storage_size, input_data, flag_data, output_data,
228 nb_out_ptr, nb_item, stream));
229 s._copyDeviceNbOutToHostNbOut();
230 } break;
231#endif
232#if defined(ARCANE_COMPILING_SYCL)
234 impl::IndexIterator iter2(0);
235 auto filter_lambda = [=](Int32 input_index) -> bool { return flag[input_index] != 0; };
236 auto setter_lambda = [=](Int32 input_index, Int32 output_index) { output[output_index] = input[input_index]; };
237 impl::SetterLambdaIterator<decltype(setter_lambda)> out(setter_lambda);
238 SyclGenericFilteringImpl::apply(s, nb_item, iter2, out, filter_lambda);
239 } break;
240#endif
242 // Pas encore implémenté en multi-thread
243 [[fallthrough]];
245 Int32 index = 0;
246 for (Int32 i = 0; i < nb_item; ++i) {
247 if (flag[i] != 0) {
248 output[index] = input[i];
249 ++index;
250 }
251 }
252 s.m_host_nb_out_storage[0] = index;
253 } break;
254 default:
255 ARCANE_FATAL(getBadPolicyMessage(exec_policy));
256 }
257 }
258};
259
260/*---------------------------------------------------------------------------*/
261/*---------------------------------------------------------------------------*/
270{
271 public:
272
279 template <bool InPlace, typename SelectLambda, typename InputIterator, typename OutputIterator>
280 void apply(GenericFilteringBase& s, Int32 nb_item, InputIterator input_iter, OutputIterator output_iter,
281 const SelectLambda& select_lambda, const TraceInfo& trace_info)
282 {
284 RunQueue queue = s.m_queue;
285 exec_policy = queue.executionPolicy();
286 RunCommand command = makeCommand(queue);
287 command << trace_info;
288 impl::RunCommandLaunchInfo launch_info(command, nb_item);
289 launch_info.beginExecute();
290 switch (exec_policy) {
291#if defined(ARCANE_COMPILING_CUDA)
293 size_t temp_storage_size = 0;
294 cudaStream_t stream = impl::CudaUtils::toNativeStream(queue);
295 // Premier appel pour connaitre la taille pour l'allocation
296 int* nb_out_ptr = nullptr;
297 if constexpr (InPlace)
298 ARCANE_CHECK_CUDA(::cub::DeviceSelect::If(nullptr, temp_storage_size,
299 input_iter, nb_out_ptr, nb_item,
300 select_lambda, stream));
301 else
302 ARCANE_CHECK_CUDA(::cub::DeviceSelect::If(nullptr, temp_storage_size,
303 input_iter, output_iter, nb_out_ptr, nb_item,
304 select_lambda, stream));
305
306 s._allocateTemporaryStorage(temp_storage_size);
307 nb_out_ptr = s._getDeviceNbOutPointer();
308 if constexpr (InPlace)
309 ARCANE_CHECK_CUDA(::cub::DeviceSelect::If(s.m_algo_storage.address(), temp_storage_size,
310 input_iter, nb_out_ptr, nb_item,
311 select_lambda, stream));
312 else
313 ARCANE_CHECK_CUDA(::cub::DeviceSelect::If(s.m_algo_storage.address(), temp_storage_size,
314 input_iter, output_iter, nb_out_ptr, nb_item,
315 select_lambda, stream));
316
317 s._copyDeviceNbOutToHostNbOut();
318 } break;
319#endif
320#if defined(ARCANE_COMPILING_HIP)
322 size_t temp_storage_size = 0;
323 // Premier appel pour connaitre la taille pour l'allocation
324 hipStream_t stream = impl::HipUtils::toNativeStream(queue);
325 int* nb_out_ptr = nullptr;
326 // NOTE: il n'y a pas de version spécifique de 'select' en-place.
327 // A priori il est possible que \a input_iter et \a output_iter
328 // aient la même valeur.
329 ARCANE_CHECK_HIP(rocprim::select(nullptr, temp_storage_size, input_iter, output_iter,
330 nb_out_ptr, nb_item, select_lambda, stream));
331 s._allocateTemporaryStorage(temp_storage_size);
332 nb_out_ptr = s._getDeviceNbOutPointer();
333 ARCANE_CHECK_HIP(rocprim::select(s.m_algo_storage.address(), temp_storage_size, input_iter, output_iter,
334 nb_out_ptr, nb_item, select_lambda, 0));
335 s._copyDeviceNbOutToHostNbOut();
336 } break;
337#endif
338#if defined(ARCANE_COMPILING_SYCL)
340 SyclGenericFilteringImpl::apply(s, nb_item, input_iter, output_iter, select_lambda);
341 } break;
342#endif
344 if (nb_item > 500) {
345 MultiThreadAlgo scanner;
346 Int32 v = scanner.doFilter<InPlace>(launch_info.loopRunInfo(), nb_item, input_iter, output_iter, select_lambda);
347 s.m_host_nb_out_storage[0] = v;
348 break;
349 }
350 [[fallthrough]];
352 Int32 index = 0;
353 for (Int32 i = 0; i < nb_item; ++i) {
354 if (select_lambda(*input_iter)) {
355 *output_iter = *input_iter;
356 ++index;
357 ++output_iter;
358 }
359 ++input_iter;
360 }
361 s.m_host_nb_out_storage[0] = index;
362 } break;
363 default:
364 ARCANE_FATAL(getBadPolicyMessage(exec_policy));
365 }
366 launch_info.endExecute();
367 }
368};
369
370/*---------------------------------------------------------------------------*/
371/*---------------------------------------------------------------------------*/
372
373} // namespace Arcane::Accelerator::impl
374
375namespace Arcane::Accelerator
376{
377
378/*---------------------------------------------------------------------------*/
379/*---------------------------------------------------------------------------*/
385{
386
387 public:
388
394 ARCANE_DEPRECATED_REASON("Y2024: Use GenericFilterer(const RunQueue&) instead")
395 explicit GenericFilterer(RunQueue* queue)
396 {
398 m_queue = *queue;
399 _allocate();
400 }
401
407 explicit GenericFilterer(const RunQueue& queue)
408 {
409 m_queue = queue;
410 _allocate();
411 }
412
413 public:
414
438 template <typename InputDataType, typename OutputDataType, typename FlagType>
440 {
441 const Int32 nb_value = input.size();
442 if (output.size() != nb_value)
443 ARCANE_FATAL("Sizes are not equals: input={0} output={1}", nb_value, output.size());
444 if (flag.size() != nb_value)
445 ARCANE_FATAL("Sizes are not equals: input={0} flag={1}", nb_value, flag.size());
446
447 if (_checkEmpty(nb_value))
448 return;
449 _setCalled();
450 impl::GenericFilteringBase* base_ptr = this;
452 gf.apply(*base_ptr, input, output, flag);
453 }
454
490 template <typename DataType, typename SelectLambda>
492 const SelectLambda& select_lambda, const TraceInfo& trace_info = TraceInfo())
493 {
494 const Int32 nb_value = input.size();
495 if (output.size() != nb_value)
496 ARCANE_FATAL("Sizes are not equals: input={0} output={1}", nb_value, output.size());
497 if (input.data() == output.data())
498 ARCANE_FATAL("Input and Output are the same. Use in place overload instead");
499 if (_checkEmpty(nb_value))
500 return;
501 _setCalled();
502 impl::GenericFilteringBase* base_ptr = this;
504 gf.apply<false>(*base_ptr, nb_value, input.data(), output.data(), select_lambda, trace_info);
505 }
506
514 template <typename DataType, typename SelectLambda>
515 void applyIf(SmallSpan<DataType> input_output, const SelectLambda& select_lambda,
516 const TraceInfo& trace_info = TraceInfo())
517 {
518 const Int32 nb_value = input_output.size();
519 if (_checkEmpty(nb_value))
520 return;
521 _setCalled();
522 impl::GenericFilteringBase* base_ptr = this;
524 gf.apply<true>(*base_ptr, nb_value, input_output.data(), input_output.data(), select_lambda, trace_info);
525 }
526
537 template <typename InputIterator, typename OutputIterator, typename SelectLambda>
538 void applyIf(Int32 nb_value, InputIterator input_iter, OutputIterator output_iter,
539 const SelectLambda& select_lambda, const TraceInfo& trace_info = TraceInfo())
540 {
541 if (_checkEmpty(nb_value))
542 return;
543 _setCalled();
544 impl::GenericFilteringBase* base_ptr = this;
546 gf.apply<false>(*base_ptr, nb_value, input_iter, output_iter, select_lambda, trace_info);
547 }
548
582 template <typename SelectLambda, typename SetterLambda>
583 void applyWithIndex(Int32 nb_value, const SelectLambda& select_lambda,
584 const SetterLambda& setter_lambda, const TraceInfo& trace_info = TraceInfo())
585 {
586 if (_checkEmpty(nb_value))
587 return;
588 _setCalled();
589 impl::GenericFilteringBase* base_ptr = this;
591 impl::IndexIterator input_iter;
593 gf.apply<false>(*base_ptr, nb_value, input_iter, out, select_lambda, trace_info);
594 }
595
598 {
599 return _nbOutputElement();
600 }
601};
602
603/*---------------------------------------------------------------------------*/
604/*---------------------------------------------------------------------------*/
605
606} // namespace Arcane::Accelerator
607
608/*---------------------------------------------------------------------------*/
609/*---------------------------------------------------------------------------*/
610
611#endif
612
613/*---------------------------------------------------------------------------*/
614/*---------------------------------------------------------------------------*/
#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 sur accélérateur avec arguments supplémentaires pour les réductions.
void applyIf(Int32 nb_value, InputIterator input_iter, OutputIterator output_iter, const SelectLambda &select_lambda, const TraceInfo &trace_info=TraceInfo())
Applique un filtre.
void applyWithIndex(Int32 nb_value, const SelectLambda &select_lambda, const SetterLambda &setter_lambda, const TraceInfo &trace_info=TraceInfo())
Applique un filtre avec une sélection suivant un index.
GenericFilterer(RunQueue *queue)
Créé une instance.
void applyIf(SmallSpan< DataType > input_output, const SelectLambda &select_lambda, const TraceInfo &trace_info=TraceInfo())
Applique un filtre en place.
void apply(SmallSpan< const InputDataType > input, SmallSpan< OutputDataType > output, SmallSpan< const FlagType > flag)
Applique un filtre.
void applyIf(SmallSpan< const DataType > input, SmallSpan< DataType > output, const SelectLambda &select_lambda, const TraceInfo &trace_info=TraceInfo())
Applique un filtre.
Int32 nbOutputElement()
Nombre d'éléments en sortie.
GenericFilterer(const RunQueue &queue)
Créé une instance.
Gestion d'une commande sur accélérateur.
File d'exécution pour un accélérateur.
void barrier() const
Bloque tant que toutes les commandes associées à la file ne sont pas terminées.
Definition RunQueue.cc:159
eExecutionPolicy executionPolicy() const
Politique d'exécution de la file.
Definition RunQueue.cc:169
Gère l'allocation interne sur le device pour un type donné.
Gère l'allocation interne sur le device.
Definition CommonUtils.h:95
Classe de base pour effectuer un filtrage.
DeviceStorage< int > m_device_nb_out_storage
Mémoire sur le device du nombre de valeurs filtrées.
bool m_is_already_called
Indique si un appel est en cours.
NumArray< Int32, MDDim1 > m_host_nb_out_storage
Mémoire hôte pour le nombre de valeurs filtrées.
RunQueue m_queue
File d'exécution. Ne doit pas être nulle.
bool m_use_direct_host_storage
Indique quelle mémoire est utilisée pour le nombre de valeurs filtrées.
Classe pour effectuer un filtrage.
void apply(GenericFilteringBase &s, Int32 nb_item, InputIterator input_iter, OutputIterator output_iter, const SelectLambda &select_lambda, const TraceInfo &trace_info)
Applique le filtre.
Algorithmes avancée en mode multi-thread.
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.
Itérateur sur une lambda pour positionner une valeur via un index.
Tableaux multi-dimensionnels pour les types numériques accessibles sur accélérateurs.
Span< const DataType > to1DSpan() const
Vue 1D constante sur l'instance.
constexpr SmallSpan< DataType > to1DSmallSpan()
Vue 1D sur l'instance (uniquement si rank == 1)
void resize(Int32 dim1_size)
Modifie la taille du tableau en gardant pas les valeurs actuelles.
Vue d'un tableau d'éléments de type T.
Definition Span.h:673
constexpr __host__ __device__ SizeType size() const noexcept
Retourne la taille du tableau.
Definition Span.h:212
constexpr __host__ __device__ pointer data() const noexcept
Pointeur sur le début de la vue.
Definition Span.h:422
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.