Arcane  v3.14.10.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
Partitioner.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/* Partitioner.h (C) 2000-2024 */
9/* */
10/* Algorithme de partitionnement de liste. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCANE_ACCELERATOR_PARTITIONER_H
13#define ARCANE_ACCELERATOR_PARTITIONER_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "arcane/utils/ArrayView.h"
18#include "arcane/utils/FatalErrorException.h"
19#include "arcane/utils/NumArray.h"
20
21#include "arcane/accelerator/AcceleratorGlobal.h"
22#include "arcane/accelerator/core/RunQueue.h"
23#include "arcane/accelerator/CommonUtils.h"
24#include "arcane/accelerator/RunCommandLaunchInfo.h"
25
26/*---------------------------------------------------------------------------*/
27/*---------------------------------------------------------------------------*/
28
29namespace Arcane::Accelerator::impl
30{
31
32/*---------------------------------------------------------------------------*/
33/*---------------------------------------------------------------------------*/
34/*!
35 * \internal
36 * \brief Classe de base pour effectuer un filtrage.
37 *
38 * Contient les arguments nécessaires pour effectuer le filtrage.
39 */
40class ARCANE_ACCELERATOR_EXPORT GenericPartitionerBase
41{
42 friend class GenericPartitionerIf;
43
44 public:
45
46 explicit GenericPartitionerBase(const RunQueue& queue);
47
48 protected:
49
50 Int32 _nbFirstPart() const;
51 SmallSpan<const Int32> _nbParts() const;
52 void _allocate();
53
54 protected:
55
56 RunQueue m_queue;
57 GenericDeviceStorage m_algo_storage;
58 DeviceStorage<int, 2> m_device_nb_list1_storage;
59 NumArray<Int32, MDDim1> m_host_nb_list1_storage;
60};
61
62/*---------------------------------------------------------------------------*/
63/*---------------------------------------------------------------------------*/
64/*!
65 * \internal
66 * \brief Classe pour effectuer un partitionnement d'une liste.
67 */
69{
70 public:
71
72 /*!
73 * \brief Effectue le partitionnement d'une liste en deux parties.
74 */
75 template <typename SelectLambda, typename InputIterator, typename OutputIterator>
76 void apply(GenericPartitionerBase& s, Int32 nb_item, InputIterator input_iter, OutputIterator output_iter,
77 const SelectLambda& select_lambda, const TraceInfo& trace_info = TraceInfo())
78 {
79 RunQueue queue = s.m_queue;
80 eExecutionPolicy exec_policy = queue.executionPolicy();
81 RunCommand command = makeCommand(queue);
82 command << trace_info;
83 impl::RunCommandLaunchInfo launch_info(command, nb_item);
84 launch_info.beginExecute();
85 switch (exec_policy) {
86#if defined(ARCANE_COMPILING_CUDA)
88 size_t temp_storage_size = 0;
89 cudaStream_t stream = impl::CudaUtils::toNativeStream(&queue);
90 // Premier appel pour connaitre la taille pour l'allocation
91 int* nb_list1_ptr = nullptr;
92 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(nullptr, temp_storage_size,
93 input_iter, output_iter, nb_list1_ptr, nb_item,
94 select_lambda, stream));
95
96 s.m_algo_storage.allocate(temp_storage_size);
97 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
98 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(s.m_algo_storage.address(), temp_storage_size,
99 input_iter, output_iter, nb_list1_ptr, nb_item,
100 select_lambda, stream));
101 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
102 } break;
103#endif
104#if defined(ARCANE_COMPILING_HIP)
106 size_t temp_storage_size = 0;
107 // Premier appel pour connaitre la taille pour l'allocation
108 hipStream_t stream = impl::HipUtils::toNativeStream(&queue);
109 int* nb_list1_ptr = nullptr;
110 ARCANE_CHECK_HIP(rocprim::partition(nullptr, temp_storage_size, input_iter, output_iter,
111 nb_list1_ptr, nb_item, select_lambda, stream));
112
113 s.m_algo_storage.allocate(temp_storage_size);
114 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
115
116 ARCANE_CHECK_HIP(rocprim::partition(s.m_algo_storage.address(), temp_storage_size, input_iter, output_iter,
117 nb_list1_ptr, nb_item, select_lambda, stream));
118 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
119 } break;
120#endif
122 // Pas encore implémenté en multi-thread
123 [[fallthrough]];
125 UniqueArray<bool> filter_index(nb_item);
126 auto saved_output_iter = output_iter;
127 auto output2_iter = output_iter + nb_item;
128 for (Int32 i = 0; i < nb_item; ++i) {
129 auto v = *input_iter;
130 if (select_lambda(v)) {
131 *output_iter = v;
132 ++output_iter;
133 }
134 else {
135 --output2_iter;
136 *output2_iter = v;
137 }
138 ++input_iter;
139 }
140 Int32 nb_list1 = static_cast<Int32>(output_iter - saved_output_iter);
141 s.m_host_nb_list1_storage[0] = nb_list1;
142 } break;
143 default:
144 ARCANE_FATAL(getBadPolicyMessage(exec_policy));
145 }
146 launch_info.endExecute();
147 }
148
149 /*!
150 * \brief Effectue le partitionnement d'une liste en trois parties.
151 */
152 template <typename Select1Lambda, typename Select2Lambda,
153 typename InputIterator, typename FirstOutputIterator,
154 typename SecondOutputIterator, typename UnselectedIterator>
156 InputIterator input_iter,
157 FirstOutputIterator first_output_iter,
158 SecondOutputIterator second_output_iter,
159 UnselectedIterator unselected_iter,
160 const Select1Lambda& select1_lambda,
161 const Select2Lambda& select2_lambda,
162 const TraceInfo& trace_info = TraceInfo())
163 {
164 RunQueue queue = s.m_queue;
165 eExecutionPolicy exec_policy = queue.executionPolicy();
166 RunCommand command = makeCommand(queue);
167 command << trace_info;
168 impl::RunCommandLaunchInfo launch_info(command, nb_item);
169 launch_info.beginExecute();
170 switch (exec_policy) {
171#if defined(ARCANE_COMPILING_CUDA)
173 size_t temp_storage_size = 0;
174 cudaStream_t stream = impl::CudaUtils::toNativeStream(&queue);
175 // Premier appel pour connaitre la taille pour l'allocation
176 int* nb_list1_ptr = nullptr;
177 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(nullptr, temp_storage_size,
178 input_iter, first_output_iter, second_output_iter,
179 unselected_iter, nb_list1_ptr, nb_item,
180 select1_lambda, select2_lambda, stream));
181
182 s.m_algo_storage.allocate(temp_storage_size);
183 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
184 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(s.m_algo_storage.address(), temp_storage_size,
185 input_iter, first_output_iter, second_output_iter,
186 unselected_iter, nb_list1_ptr, nb_item,
187 select1_lambda, select2_lambda, stream));
188 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
189 } break;
190#endif
191#if defined(ARCANE_COMPILING_HIP)
193 size_t temp_storage_size = 0;
194 // Premier appel pour connaitre la taille pour l'allocation
195 hipStream_t stream = impl::HipUtils::toNativeStream(&queue);
196 int* nb_list1_ptr = nullptr;
197 using namespace rocprim;
198 ARCANE_CHECK_HIP(::rocprim::partition_three_way(nullptr, temp_storage_size, input_iter, first_output_iter,
199 second_output_iter, unselected_iter,
200 nb_list1_ptr, nb_item, select1_lambda, select2_lambda, stream));
201
202 s.m_algo_storage.allocate(temp_storage_size);
203 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
204
205 ARCANE_CHECK_HIP(partition_three_way(s.m_algo_storage.address(), temp_storage_size, input_iter, first_output_iter,
206 second_output_iter, unselected_iter, nb_list1_ptr, nb_item,
207 select1_lambda, select2_lambda, stream));
208 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
209 } break;
210#endif
212 // Pas encore implémenté en multi-thread
213 [[fallthrough]];
215 UniqueArray<bool> filter_index(nb_item);
216 Int32 nb_first = 0;
217 Int32 nb_second = 0;
218 for (Int32 i = 0; i < nb_item; ++i) {
219 auto v = *input_iter;
220 bool is_1 = select1_lambda(v);
221 bool is_2 = select2_lambda(v);
222 if (is_1) {
223 *first_output_iter = v;
224 ++first_output_iter;
225 ++nb_first;
226 }
227 else {
228 if (is_2) {
229 *second_output_iter = v;
230 ++second_output_iter;
231 ++nb_second;
232 }
233 else {
234 *unselected_iter = v;
235 ++unselected_iter;
236 }
237 }
238 // Incrémenter l'itérateur à la fin car il est utilisé pour le positionnement
239 ++input_iter;
240 }
241 s.m_host_nb_list1_storage[0] = nb_first;
242 s.m_host_nb_list1_storage[1] = nb_second;
243 } break;
244 default:
245 ARCANE_FATAL(getBadPolicyMessage(exec_policy));
246 }
247 launch_info.endExecute();
248 }
249};
250
251/*---------------------------------------------------------------------------*/
252/*---------------------------------------------------------------------------*/
253
254} // namespace Arcane::Accelerator::impl
255
256namespace Arcane::Accelerator
257{
258
259/*---------------------------------------------------------------------------*/
260/*---------------------------------------------------------------------------*/
261/*!
262 * \brief Algorithme générique de partitionnement d'une liste.
263 *
264 * Cette classe fournit des algorithmes pour partitionner une liste en deux
265 * ou trois parties selon un critère fixé par l'utilisateur.
266 */
269{
270 public:
271
272 explicit GenericPartitioner(const RunQueue& queue)
274 {
275 _allocate();
276 }
277
278 public:
279
280 /*!
281 * \brief Effectue un partitionnement d'une liste en deux parties.
282 *
283 * Le nombre de valeurs de la liste est donné par \a nb_value.
284 * Les deux fonctions lambda \a select_lambda et \a setter_lambda permettent
285 * de partitionner et de positionner les valeurs de la liste.
286 *
287 * Après exécution, il est possible de récupérer le nombre d'éléments
288 * de la première partie de la liste via la méthode \a nbFirstPart().
289 *
290 * \snippet AcceleratorPartitionerUnitTest.cc SampleListPartitionerTwoWayIndex
291 */
292 template <typename SelectLambda, typename SetterLambda>
293 void applyWithIndex(Int32 nb_value, const SetterLambda& setter_lambda,
294 const SelectLambda& select_lambda, const TraceInfo& trace_info = TraceInfo())
295 {
296 if (nb_value == 0)
297 return;
298 _setCalled();
299 impl::GenericPartitionerBase* base_ptr = this;
301
302 impl::IndexIterator input_iter;
304 gf.apply(*base_ptr, nb_value, input_iter, out, select_lambda, trace_info);
305 }
306
307 /*!
308 * \brief Effectue un partitionnement d'une liste en deux parties.
309 *
310 * Le nombre de valeurs de la liste est donné par \a nb_value.
311 * Les valeurs en entrée sont fournies par l'itérateur \a input_iter et
312 * les valeurs en sorties par l'itérateur \a output_iterator. La fonction
313 * lambda \a select_lambda permet de sélectionner la partition utilisée :
314 * si le retour est \a true, la valeur sera dans la première partie de la liste,
315 * sinon elle sera dans la seconde. En sortie les valeurs de la deuxième
316 * partie sont rangées en ordre inverse de la liste d'entrée.
317 *
318 * Après exécution, il est possible de récupérer le nombre d'éléments
319 * de la première partie de la liste via la méthode \a nbFirstPart().
320 *
321 * \snippet AcceleratorPartitionerUnitTest.cc SampleListPartitionerTwoWayIf
322 */
323 template <typename InputIterator, typename OutputIterator, typename SelectLambda>
324 void applyIf(Int32 nb_value, InputIterator input_iter, OutputIterator output_iter,
325 const SelectLambda& select_lambda, const TraceInfo& trace_info = TraceInfo())
326 {
327 if (nb_value == 0)
328 return;
329 _setCalled();
330 impl::GenericPartitionerBase* base_ptr = this;
332 gf.apply(*base_ptr, nb_value, input_iter, output_iter, select_lambda, trace_info);
333 }
334
335 /*!
336 * \brief Effectue un partitionnement d'une liste en trois parties.
337 *
338 * Le nombre de valeurs de la liste est donné par \a nb_value.
339 * Les deux fonctions lambda \a select1_lambda et \a select2_lambda permettent
340 * de partitionner la liste avec l'algorithme suivant:
341 * - si select1_lambda() est vrai, la valeur sera positionnée via \a setter1_lambda,
342 * - sinon si select2_lambda() est vrai, la valeur sera positionnée via \a setter2_lambda,
343 * - sinon la valeur sera positionnée via \a unselected_setter_lambda.
344 *
345 * Les listes en sortie sont dans le même ordre qu'en entrée.
346 *
347 * Après exécution, il est possible de récupérer le nombre d'éléments
348 * de la première et de la deuxième liste la méthode \a nbParts().
349 *
350 * \snippet AcceleratorPartitionerUnitTest.cc SampleListPartitionerThreeWayIndex
351 */
352 template <typename Setter1Lambda, typename Setter2Lambda, typename UnselectedSetterLambda,
353 typename Select1Lambda, typename Select2Lambda>
354 void applyWithIndex(Int32 nb_value,
355 const Setter1Lambda setter1_lambda,
356 const Setter2Lambda setter2_lambda,
357 const UnselectedSetterLambda& unselected_setter_lambda,
358 const Select1Lambda& select1_lambda,
359 const Select2Lambda& select2_lambda,
360 const TraceInfo& trace_info = TraceInfo())
361 {
362 if (nb_value == 0)
363 return;
364 _setCalled();
365 impl::GenericPartitionerBase* base_ptr = this;
367 impl::IndexIterator input_iter;
368 impl::SetterLambdaIterator<Setter1Lambda> setter1_wrapper(setter1_lambda);
369 impl::SetterLambdaIterator<Setter2Lambda> setter2_wrapper(setter2_lambda);
370 impl::SetterLambdaIterator<UnselectedSetterLambda> unselected_setter_wrapper(unselected_setter_lambda);
371 gf.apply3(*base_ptr, nb_value, input_iter, setter1_wrapper, setter2_wrapper,
372 unselected_setter_wrapper, select1_lambda, select2_lambda, trace_info);
373 }
374
375 /*!
376 * \brief Effectue un partitionnement d'une liste en trois parties.
377 *
378 * Le nombre de valeurs de la liste est donné par \a nb_value.
379 * Les deux fonctions lambda \a select1_lambda et \a select2_lambda permettent
380 * de partitionner la liste avec l'algorithme suivant:
381 * - si select1_lambda() est vrai, la valeur ajoutée \a first_output_iter,
382 * - sinon si select2_lambda() est vrai, la valeur sera ajoutée à \a second_output_iter,
383 * - sinon la valeur sera ajoutée à \a unselected_iter.
384 *
385 * Les listes en sortie sont dans le même ordre qu'en entrée.
386 *
387 * Après exécution, il est possible de récupérer le nombre d'éléments
388 * de la première et de la deuxième liste la méthode \a nbParts().
389 *
390 * \snippet AcceleratorPartitionerUnitTest.cc SampleListPartitionerThreeWayIf
391 */
392 template <typename InputIterator, typename FirstOutputIterator,
393 typename SecondOutputIterator, typename UnselectedIterator,
394 typename Select1Lambda, typename Select2Lambda>
395 void applyIf(Int32 nb_value, InputIterator input_iter,
396 FirstOutputIterator first_output_iter,
397 SecondOutputIterator second_output_iter,
398 UnselectedIterator unselected_iter,
399 const Select1Lambda& select1_lambda,
400 const Select2Lambda& select2_lambda,
401 const TraceInfo& trace_info = TraceInfo())
402 {
403 if (nb_value == 0)
404 return;
405 _setCalled();
406 impl::GenericPartitionerBase* base_ptr = this;
408 gf.apply3(*base_ptr, nb_value, input_iter, first_output_iter, second_output_iter,
409 unselected_iter, select1_lambda, select2_lambda, trace_info);
410 }
411
412 /*!
413 * \brief Nombre d'éléments de la première partie de la liste.
414 */
416 {
417 m_is_already_called = false;
418 return _nbFirstPart();
419 }
420
421 /*!
422 * \brief Nombre d'éléments de la première et deuxième partie de la liste.
423 *
424 * Retourne une vue de deux valeurs. La première valeur contient le nombre
425 * d'éléments de la première liste et la seconde valeur le
426 * nombre d'éléments de la deuxième liste.
427 *
428 * Cette méthode n'est valide qu'après avoir appelé une méthode de partitionnement
429 * en trois parties.
430 */
432 {
433 m_is_already_called = false;
434 return _nbParts();
435 }
436
437 private:
438
439 bool m_is_already_called = false;
440
441 private:
442
443 void _setCalled()
444 {
445 if (m_is_already_called)
446 ARCANE_FATAL("apply() has already been called for this instance");
447 m_is_already_called = true;
448 }
449};
450
451/*---------------------------------------------------------------------------*/
452/*---------------------------------------------------------------------------*/
453
454} // namespace Arcane::Accelerator
455
456/*---------------------------------------------------------------------------*/
457/*---------------------------------------------------------------------------*/
458
459#endif
460
461/*---------------------------------------------------------------------------*/
462/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Algorithme générique de partitionnement d'une liste.
SmallSpan< const Int32 > nbParts()
Nombre d'éléments de la première et deuxième partie de la liste.
Int32 nbFirstPart()
Nombre d'éléments de la première partie de la liste.
void applyWithIndex(Int32 nb_value, const Setter1Lambda setter1_lambda, const Setter2Lambda setter2_lambda, const UnselectedSetterLambda &unselected_setter_lambda, const Select1Lambda &select1_lambda, const Select2Lambda &select2_lambda, const TraceInfo &trace_info=TraceInfo())
Effectue un partitionnement d'une liste en trois parties.
void applyIf(Int32 nb_value, InputIterator input_iter, OutputIterator output_iter, const SelectLambda &select_lambda, const TraceInfo &trace_info=TraceInfo())
Effectue un partitionnement d'une liste en deux parties.
void applyWithIndex(Int32 nb_value, const SetterLambda &setter_lambda, const SelectLambda &select_lambda, const TraceInfo &trace_info=TraceInfo())
Effectue un partitionnement d'une liste en deux parties.
void applyIf(Int32 nb_value, InputIterator input_iter, FirstOutputIterator first_output_iter, SecondOutputIterator second_output_iter, UnselectedIterator unselected_iter, const Select1Lambda &select1_lambda, const Select2Lambda &select2_lambda, const TraceInfo &trace_info=TraceInfo())
Effectue un partitionnement d'une liste en trois parties.
Gestion d'une commande sur accélérateur.
File d'exécution pour un accélérateur.
eExecutionPolicy executionPolicy() const
Politique d'exécution de la file.
Definition RunQueue.cc:168
void copyToAsync(SmallSpan< DataType > dest_ptr, const RunQueue &queue)
Copie l'instance dans dest_ptr.
void apply3(GenericPartitionerBase &s, Int32 nb_item, InputIterator input_iter, FirstOutputIterator first_output_iter, SecondOutputIterator second_output_iter, UnselectedIterator unselected_iter, const Select1Lambda &select1_lambda, const Select2Lambda &select2_lambda, const TraceInfo &trace_info=TraceInfo())
Effectue le partitionnement d'une liste en trois parties.
void apply(GenericPartitionerBase &s, Int32 nb_item, InputIterator input_iter, OutputIterator output_iter, const SelectLambda &select_lambda, const TraceInfo &trace_info=TraceInfo())
Effectue le partitionnement d'une liste en deux parties.
Definition Partitioner.h:76
void beginExecute()
Indique qu'on commence l'exécution de la commande.
void endExecute()
Signale la fin de l'exécution.
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.
Vue d'un tableau d'éléments de type T.
Definition Span.h:670
Vecteur 1D de données avec sémantique par valeur (style STL).
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.
@ 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.