Arcane  v4.1.1.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
GenericPartitioner.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/* GenericPartitioner.h (C) 2000-2025 */
9/* */
10/* Algorithme de partitionnement de liste. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCANE_ACCELERATOR_GENERICPARTITIONER_H
13#define ARCANE_ACCELERATOR_GENERICPARTITIONER_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "arcane/utils/ArrayView.h"
18#include "arcane/utils/FatalErrorException.h"
19#include "arcane/utils/NotImplementedException.h"
20#include "arcane/utils/NumArray.h"
21
22#include "arcane/accelerator/AcceleratorGlobal.h"
23#include "arcane/accelerator/core/RunQueue.h"
24#include "arcane/accelerator/CommonUtils.h"
25#include "arcane/accelerator/RunCommandLaunchInfo.h"
26#if defined(ARCANE_COMPILING_SYCL)
28#endif
29
30/*---------------------------------------------------------------------------*/
31/*---------------------------------------------------------------------------*/
32
33namespace Arcane::Accelerator::impl
34{
35
36/*---------------------------------------------------------------------------*/
37/*---------------------------------------------------------------------------*/
38/*!
39 * \internal
40 * \brief Classe de base pour effectuer un filtrage.
41 *
42 * Contient les arguments nécessaires pour effectuer le filtrage.
43 */
44class ARCANE_ACCELERATOR_EXPORT GenericPartitionerBase
45{
46 friend class GenericPartitionerIf;
47
48 public:
49
50 explicit GenericPartitionerBase(const RunQueue& queue);
51
52 protected:
53
54 Int32 _nbFirstPart() const;
55 SmallSpan<const Int32> _nbParts() const;
56 void _allocate();
57 void _resetNbPart();
58
59 protected:
60
61 RunQueue m_queue;
62 GenericDeviceStorage m_algo_storage;
63 DeviceStorage<int, 2> m_device_nb_list1_storage;
64 NumArray<Int32, MDDim1> m_host_nb_list1_storage;
65};
66
67/*---------------------------------------------------------------------------*/
68/*---------------------------------------------------------------------------*/
69/*!
70 * \internal
71 * \brief Classe pour effectuer un partitionnement d'une liste.
72 */
74{
75 public:
76
77 /*!
78 * \brief Effectue le partitionnement d'une liste en deux parties.
79 */
80 template <typename SelectLambda, typename InputIterator, typename OutputIterator>
81 void apply(GenericPartitionerBase& s, Int32 nb_item, InputIterator input_iter, OutputIterator output_iter,
82 const SelectLambda& select_lambda, const TraceInfo& trace_info = TraceInfo())
83 {
84 RunQueue queue = s.m_queue;
85 eExecutionPolicy exec_policy = queue.executionPolicy();
86 RunCommand command = makeCommand(queue);
87 command << trace_info;
88 Impl::RunCommandLaunchInfo launch_info(command, nb_item);
89 launch_info.beginExecute();
90 switch (exec_policy) {
91#if defined(ARCANE_COMPILING_CUDA)
92 case eExecutionPolicy::CUDA: {
93 size_t temp_storage_size = 0;
94 cudaStream_t stream = Impl::CudaUtils::toNativeStream(&queue);
95 // Premier appel pour connaitre la taille pour l'allocation
96 int* nb_list1_ptr = nullptr;
97 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(nullptr, temp_storage_size,
98 input_iter, output_iter, nb_list1_ptr, nb_item,
99 select_lambda, stream));
100
101 s.m_algo_storage.allocate(temp_storage_size);
102 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
103 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(s.m_algo_storage.address(), temp_storage_size,
104 input_iter, output_iter, nb_list1_ptr, nb_item,
105 select_lambda, stream));
106 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
107 } break;
108#endif
109#if defined(ARCANE_COMPILING_HIP)
110 case eExecutionPolicy::HIP: {
111 size_t temp_storage_size = 0;
112 // Premier appel pour connaitre la taille pour l'allocation
113 hipStream_t stream = Impl::HipUtils::toNativeStream(&queue);
114 int* nb_list1_ptr = nullptr;
115 ARCANE_CHECK_HIP(rocprim::partition(nullptr, temp_storage_size, input_iter, output_iter,
116 nb_list1_ptr, nb_item, select_lambda, stream));
117
118 s.m_algo_storage.allocate(temp_storage_size);
119 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
120
121 ARCANE_CHECK_HIP(rocprim::partition(s.m_algo_storage.address(), temp_storage_size, input_iter, output_iter,
122 nb_list1_ptr, nb_item, select_lambda, stream));
123 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
124 } break;
125#endif
126#if defined(ARCANE_COMPILING_SYCL)
127 case eExecutionPolicy::SYCL: {
128#if defined(ARCANE_HAS_ONEDPL)
129 // Seulement implémenté pour DPC++.
130 // Actuellement (dpc++ 2025.0), il n'y a pas l'équivalent avec SYCL de
131 // la méthode de partition de cub ou rocprim.
132 // Utilise la fonction 'stable_partition'. Cependant, cette fonction
133 // ne supporte pas si la mémoire uniquement sur accélérateur. De plus,
134 // il faut que InputIterator et OutputIterator remplissent le concept
135 // std::random_access_iterator ce qui n'est pas le cas (notamment car il n'y a
136 // pas les opérateurs de copie ni de constructeur vide à cause de la lambda).
137 // Pour éviter tous ces problèmes, on alloue donc des tableaux temporaires
138 // pour l'appel à 'stable_sort' et on recopie les valeurs en sortie.
139 using InputDataType = typename InputIterator::value_type;
140 using DataType = typename OutputIterator::value_type;
141 NumArray<DataType, MDDim1> tmp_output_numarray(nb_item);
142 NumArray<bool, MDDim1> tmp_select_numarray(nb_item);
143 auto tmp_output = tmp_output_numarray.to1DSmallSpan();
144 auto tmp_select = tmp_select_numarray.to1DSmallSpan();
145 {
146 auto command = makeCommand(queue);
147 command << RUNCOMMAND_LOOP1(iter, nb_item)
148 {
149 auto [i] = iter();
150 tmp_output[i] = input_iter[i];
151 };
152 }
153 auto tmp_select_lambda = [=](Int32 i) { return tmp_select[i]; };
154 sycl::queue sycl_queue = Impl::SyclUtils::toNativeStream(queue);
155 auto policy = oneapi::dpl::execution::make_device_policy(sycl_queue);
156 auto output_after = oneapi::dpl::stable_partition(policy, tmp_output.begin(), tmp_output.end(), select_lambda);
157 queue.barrier();
158 Int32 nb_list1 = (output_after - tmp_output.begin());
159 Int32 nb_list2 = nb_item - nb_list1;
160 s.m_host_nb_list1_storage[0] = nb_list1;
161 //std::cerr << "NbList1=" << nb_list1 << " NbList2=" << nb_list2 << "\n";
162 {
163 // Recopie dans output les valeurs filtrées.
164 // Pour être cohérent avec 'cub' et 'rocprim', il faut inverser l'ordre des
165 // des valeurs de la liste pour les éléments ne remplissant pas la condition.
166 // Pour cela, on fait une boucle de taille (nb_list1 + nb_list/2) et chaque
167 // itération pour i>=nb_list1 gère deux éléments.
168 auto command = makeCommand(queue);
169 Int32 nb_iter2 = (nb_list2 / 2) + (nb_list2 % 2);
170 //std::cout << "NB_ITER2=" << nb_iter2 << "\n";
171 command << RUNCOMMAND_LOOP1(iter, (nb_list1 + nb_iter2))
172 {
173 auto [i] = iter();
174 if (i >= nb_list1) {
175 // Partie de la liste pour les valeurs ne remplissant par le critère.
176 Int32 j = i - nb_list1;
177 Int32 reverse_i = (nb_item - (j + 1));
178 auto x1 = tmp_output[i];
179 auto x2 = tmp_output[reverse_i];
180 output_iter[i] = tmp_output[reverse_i];
181 output_iter[reverse_i] = tmp_output[i];
182 }
183 else
184 output_iter[i] = tmp_output[i];
185 };
186 }
187 queue.barrier();
188#else // ARCANE_HAS_ONEDPL
189 ARCANE_THROW(NotImplementedException,"Partition is only implemented for SYCL back-end using oneDPL");
190#endif // ARCANE_HAS_ONEDPL
191 } break;
192#endif // ARCANE_COMPILING_SYCL
193 case eExecutionPolicy::Thread:
194 // Pas encore implémenté en multi-thread
195 [[fallthrough]];
196 case eExecutionPolicy::Sequential: {
197 auto saved_output_iter = output_iter;
198 auto output2_iter = output_iter + nb_item;
199 for (Int32 i = 0; i < nb_item; ++i) {
200 auto v = *input_iter;
201 if (select_lambda(v)) {
202 *output_iter = v;
203 ++output_iter;
204 }
205 else {
206 --output2_iter;
207 *output2_iter = v;
208 }
209 ++input_iter;
210 }
211 Int32 nb_list1 = static_cast<Int32>(output_iter - saved_output_iter);
212 s.m_host_nb_list1_storage[0] = nb_list1;
213 } break;
214 default:
215 ARCANE_FATAL(getBadPolicyMessage(exec_policy));
216 }
217 launch_info.endExecute();
218 }
219
220 /*!
221 * \brief Effectue le partitionnement d'une liste en trois parties.
222 */
223 template <typename Select1Lambda, typename Select2Lambda,
224 typename InputIterator, typename FirstOutputIterator,
225 typename SecondOutputIterator, typename UnselectedIterator>
227 InputIterator input_iter,
228 FirstOutputIterator first_output_iter,
229 SecondOutputIterator second_output_iter,
230 UnselectedIterator unselected_iter,
231 const Select1Lambda& select1_lambda,
232 const Select2Lambda& select2_lambda,
233 const TraceInfo& trace_info = TraceInfo())
234 {
235 RunQueue queue = s.m_queue;
236 eExecutionPolicy exec_policy = queue.executionPolicy();
237 RunCommand command = makeCommand(queue);
238 command << trace_info;
239 Impl::RunCommandLaunchInfo launch_info(command, nb_item);
240 launch_info.beginExecute();
241 switch (exec_policy) {
242#if defined(ARCANE_COMPILING_CUDA)
243 case eExecutionPolicy::CUDA: {
244 size_t temp_storage_size = 0;
245 cudaStream_t stream = Impl::CudaUtils::toNativeStream(&queue);
246 // Premier appel pour connaitre la taille pour l'allocation
247 int* nb_list1_ptr = nullptr;
248 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(nullptr, temp_storage_size,
249 input_iter, first_output_iter, second_output_iter,
250 unselected_iter, nb_list1_ptr, nb_item,
251 select1_lambda, select2_lambda, stream));
252
253 s.m_algo_storage.allocate(temp_storage_size);
254 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
255 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(s.m_algo_storage.address(), temp_storage_size,
256 input_iter, first_output_iter, second_output_iter,
257 unselected_iter, nb_list1_ptr, nb_item,
258 select1_lambda, select2_lambda, stream));
259 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
260 } break;
261#endif
262#if defined(ARCANE_COMPILING_HIP)
263 case eExecutionPolicy::HIP: {
264 size_t temp_storage_size = 0;
265 // Premier appel pour connaitre la taille pour l'allocation
266 hipStream_t stream = Impl::HipUtils::toNativeStream(&queue);
267 int* nb_list1_ptr = nullptr;
268 using namespace rocprim;
269 ARCANE_CHECK_HIP(::rocprim::partition_three_way(nullptr, temp_storage_size, input_iter, first_output_iter,
270 second_output_iter, unselected_iter,
271 nb_list1_ptr, nb_item, select1_lambda, select2_lambda, stream));
272
273 s.m_algo_storage.allocate(temp_storage_size);
274 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
275
276 ARCANE_CHECK_HIP(partition_three_way(s.m_algo_storage.address(), temp_storage_size, input_iter, first_output_iter,
277 second_output_iter, unselected_iter, nb_list1_ptr, nb_item,
278 select1_lambda, select2_lambda, stream));
279 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
280 } break;
281#endif
282#if defined(ARCANE_COMPILING_SYCL)
283 case eExecutionPolicy::SYCL: {
284 ARCANE_THROW(NotImplementedException,"3-way partition is not implemented for SYCL back-end");
285 }
286 break;
287#endif
288 case eExecutionPolicy::Thread:
289 // Pas encore implémenté en multi-thread
290 [[fallthrough]];
291 case eExecutionPolicy::Sequential: {
292 Int32 nb_first = 0;
293 Int32 nb_second = 0;
294 for (Int32 i = 0; i < nb_item; ++i) {
295 auto v = *input_iter;
296 bool is_1 = select1_lambda(v);
297 bool is_2 = select2_lambda(v);
298 if (is_1) {
299 *first_output_iter = v;
300 ++first_output_iter;
301 ++nb_first;
302 }
303 else {
304 if (is_2) {
305 *second_output_iter = v;
306 ++second_output_iter;
307 ++nb_second;
308 }
309 else {
310 *unselected_iter = v;
311 ++unselected_iter;
312 }
313 }
314 // Incrémenter l'itérateur à la fin car il est utilisé pour le positionnement
315 ++input_iter;
316 }
317 s.m_host_nb_list1_storage[0] = nb_first;
318 s.m_host_nb_list1_storage[1] = nb_second;
319 } break;
320 default:
321 ARCANE_FATAL(getBadPolicyMessage(exec_policy));
322 }
323 launch_info.endExecute();
324 }
325};
326
327/*---------------------------------------------------------------------------*/
328/*---------------------------------------------------------------------------*/
329
330} // namespace Arcane::Accelerator::impl
331
332namespace Arcane::Accelerator
333{
334
335/*---------------------------------------------------------------------------*/
336/*---------------------------------------------------------------------------*/
337/*!
338 * \brief Algorithme générique de partitionnement d'une liste.
339 *
340 * Cette classe fournit des algorithmes pour partitionner une liste en deux
341 * ou trois parties selon un critère fixé par l'utilisateur.
342 */
343class GenericPartitioner
345{
346 public:
347
348 explicit GenericPartitioner(const RunQueue& queue)
350 {
351 _allocate();
352 }
353
354 public:
355
356 /*!
357 * \brief Effectue un partitionnement d'une liste en deux parties.
358 *
359 * Le nombre de valeurs de la liste est donné par \a nb_value.
360 * Les deux fonctions lambda \a select_lambda et \a setter_lambda permettent
361 * de partitionner et de positionner les valeurs de la liste.
362 *
363 * Après exécution, il est possible de récupérer le nombre d'éléments
364 * de la première partie de la liste via la méthode \a nbFirstPart().
365 *
366 * \snippet AcceleratorPartitionerUnitTest.cc SampleListPartitionerTwoWayIndex
367 */
368 template <typename SelectLambda, typename SetterLambda>
369 void applyWithIndex(Int32 nb_value, const SetterLambda& setter_lambda,
370 const SelectLambda& select_lambda, const TraceInfo& trace_info = TraceInfo())
371 {
372 if (_checkEmpty(nb_value))
373 return;
374 _setCalled();
375 impl::GenericPartitionerBase* base_ptr = this;
377
378 impl::IndexIterator input_iter;
380 gf.apply(*base_ptr, nb_value, input_iter, out, select_lambda, trace_info);
381 }
382
383 /*!
384 * \brief Effectue un partitionnement d'une liste en deux parties.
385 *
386 * Le nombre de valeurs de la liste est donné par \a nb_value.
387 * Les valeurs en entrée sont fournies par l'itérateur \a input_iter et
388 * les valeurs en sorties par l'itérateur \a output_iterator. La fonction
389 * lambda \a select_lambda permet de sélectionner la partition utilisée :
390 * si le retour est \a true, la valeur sera dans la première partie de la liste,
391 * sinon elle sera dans la seconde. En sortie les valeurs de la deuxième
392 * partie sont rangées en ordre inverse de la liste d'entrée.
393 *
394 * Après exécution, il est possible de récupérer le nombre d'éléments
395 * de la première partie de la liste via la méthode \a nbFirstPart().
396 *
397 * \snippet AcceleratorPartitionerUnitTest.cc SampleListPartitionerTwoWayIf
398 */
399 template <typename InputIterator, typename OutputIterator, typename SelectLambda>
400 void applyIf(Int32 nb_value, InputIterator input_iter, OutputIterator output_iter,
401 const SelectLambda& select_lambda, const TraceInfo& trace_info = TraceInfo())
402 {
403 if (_checkEmpty(nb_value))
404 return;
405 _setCalled();
406 impl::GenericPartitionerBase* base_ptr = this;
408 gf.apply(*base_ptr, nb_value, input_iter, output_iter, select_lambda, trace_info);
409 }
410
411 /*!
412 * \brief Effectue un partitionnement d'une liste en trois parties.
413 *
414 * Le nombre de valeurs de la liste est donné par \a nb_value.
415 * Les deux fonctions lambda \a select1_lambda et \a select2_lambda permettent
416 * de partitionner la liste avec l'algorithme suivant:
417 * - si select1_lambda() est vrai, la valeur sera positionnée via \a setter1_lambda,
418 * - sinon si select2_lambda() est vrai, la valeur sera positionnée via \a setter2_lambda,
419 * - sinon la valeur sera positionnée via \a unselected_setter_lambda.
420 *
421 * Les listes en sortie sont dans le même ordre qu'en entrée.
422 *
423 * Après exécution, il est possible de récupérer le nombre d'éléments
424 * de la première et de la deuxième liste la méthode \a nbParts().
425 *
426 * \snippet AcceleratorPartitionerUnitTest.cc SampleListPartitionerThreeWayIndex
427 */
428 template <typename Setter1Lambda, typename Setter2Lambda, typename UnselectedSetterLambda,
429 typename Select1Lambda, typename Select2Lambda>
430 void applyWithIndex(Int32 nb_value,
431 const Setter1Lambda setter1_lambda,
432 const Setter2Lambda setter2_lambda,
433 const UnselectedSetterLambda& unselected_setter_lambda,
434 const Select1Lambda& select1_lambda,
435 const Select2Lambda& select2_lambda,
436 const TraceInfo& trace_info = TraceInfo())
437 {
438 if (_checkEmpty(nb_value))
439 return;
440 _setCalled();
441 impl::GenericPartitionerBase* base_ptr = this;
443 impl::IndexIterator input_iter;
444 impl::SetterLambdaIterator<Setter1Lambda> setter1_wrapper(setter1_lambda);
445 impl::SetterLambdaIterator<Setter2Lambda> setter2_wrapper(setter2_lambda);
446 impl::SetterLambdaIterator<UnselectedSetterLambda> unselected_setter_wrapper(unselected_setter_lambda);
447 gf.apply3(*base_ptr, nb_value, input_iter, setter1_wrapper, setter2_wrapper,
448 unselected_setter_wrapper, select1_lambda, select2_lambda, trace_info);
449 }
450
451 /*!
452 * \brief Effectue un partitionnement d'une liste en trois parties.
453 *
454 * Le nombre de valeurs de la liste est donné par \a nb_value.
455 * Les deux fonctions lambda \a select1_lambda et \a select2_lambda permettent
456 * de partitionner la liste avec l'algorithme suivant:
457 * - si select1_lambda() est vrai, la valeur ajoutée \a first_output_iter,
458 * - sinon si select2_lambda() est vrai, la valeur sera ajoutée à \a second_output_iter,
459 * - sinon la valeur sera ajoutée à \a unselected_iter.
460 *
461 * Les listes en sortie sont dans le même ordre qu'en entrée.
462 *
463 * Après exécution, il est possible de récupérer le nombre d'éléments
464 * de la première et de la deuxième liste la méthode \a nbParts().
465 *
466 * \snippet AcceleratorPartitionerUnitTest.cc SampleListPartitionerThreeWayIf
467 */
468 template <typename InputIterator, typename FirstOutputIterator,
469 typename SecondOutputIterator, typename UnselectedIterator,
470 typename Select1Lambda, typename Select2Lambda>
471 void applyIf(Int32 nb_value, InputIterator input_iter,
472 FirstOutputIterator first_output_iter,
473 SecondOutputIterator second_output_iter,
474 UnselectedIterator unselected_iter,
475 const Select1Lambda& select1_lambda,
476 const Select2Lambda& select2_lambda,
477 const TraceInfo& trace_info = TraceInfo())
478 {
479 if (_checkEmpty(nb_value))
480 return;
481 _setCalled();
482 impl::GenericPartitionerBase* base_ptr = this;
484 gf.apply3(*base_ptr, nb_value, input_iter, first_output_iter, second_output_iter,
485 unselected_iter, select1_lambda, select2_lambda, trace_info);
486 }
487
488 /*!
489 * \brief Nombre d'éléments de la première partie de la liste.
490 */
492 {
493 m_is_already_called = false;
494 return _nbFirstPart();
495 }
496
497 /*!
498 * \brief Nombre d'éléments de la première et deuxième partie de la liste.
499 *
500 * Retourne une vue de deux valeurs. La première valeur contient le nombre
501 * d'éléments de la première liste et la seconde valeur le
502 * nombre d'éléments de la deuxième liste.
503 *
504 * Cette méthode n'est valide qu'après avoir appelé une méthode de partitionnement
505 * en trois parties.
506 */
508 {
509 m_is_already_called = false;
510 return _nbParts();
511 }
512
513 private:
514
515 bool m_is_already_called = false;
516
517 private:
518
519 void _setCalled()
520 {
521 if (m_is_already_called)
522 ARCANE_FATAL("apply() has already been called for this instance");
523 m_is_already_called = true;
524 }
525 bool _checkEmpty(Int32 nb_value)
526 {
527 if (nb_value == 0) {
528 _resetNbPart();
529 return true;
530 }
531 return false;
532 }
533};
534
535/*---------------------------------------------------------------------------*/
536/*---------------------------------------------------------------------------*/
537
538} // namespace Arcane::Accelerator
539
540/*---------------------------------------------------------------------------*/
541/*---------------------------------------------------------------------------*/
542
543#endif
544
545/*---------------------------------------------------------------------------*/
546/*---------------------------------------------------------------------------*/
#define ARCANE_THROW(exception_class,...)
Macro pour envoyer une exception avec formattage.
#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.
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.
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.
Itérateur sur une lambda pour positionner une valeur via un index.
Vue d'un tableau d'éléments de type T.
Definition Span.h:801
std::int32_t Int32
Type entier signé sur 32 bits.