Arcane  v3.15.0.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
GenericPartitioner.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/* GenericPartitioner.h (C) 2000-2024 */
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/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#if defined(ARCANE_COMPILING_SYCL)
27#endif
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32namespace Arcane::Accelerator::impl
33{
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
43class ARCANE_ACCELERATOR_EXPORT GenericPartitionerBase
44{
45 friend class GenericPartitionerIf;
46
47 public:
48
49 explicit GenericPartitionerBase(const RunQueue& queue);
50
51 protected:
52
53 Int32 _nbFirstPart() const;
54 SmallSpan<const Int32> _nbParts() const;
55 void _allocate();
56
57 protected:
58
59 RunQueue m_queue;
60 GenericDeviceStorage m_algo_storage;
61 DeviceStorage<int, 2> m_device_nb_list1_storage;
62 NumArray<Int32, MDDim1> m_host_nb_list1_storage;
63};
64
65/*---------------------------------------------------------------------------*/
66/*---------------------------------------------------------------------------*/
72{
73 public:
74
78 template <typename SelectLambda, typename InputIterator, typename OutputIterator>
81 {
82 RunQueue queue = s.m_queue;
84 RunCommand command = makeCommand(queue);
85 command << trace_info;
87 launch_info.beginExecute();
88 switch (exec_policy) {
89#if defined(ARCANE_COMPILING_CUDA)
91 size_t temp_storage_size = 0;
92 cudaStream_t stream = impl::CudaUtils::toNativeStream(&queue);
93 // Premier appel pour connaitre la taille pour l'allocation
94 int* nb_list1_ptr = nullptr;
95 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(nullptr, temp_storage_size,
97 select_lambda, stream));
98
99 s.m_algo_storage.allocate(temp_storage_size);
100 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
101 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(s.m_algo_storage.address(), temp_storage_size,
103 select_lambda, stream));
104 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
105 } break;
106#endif
107#if defined(ARCANE_COMPILING_HIP)
109 size_t temp_storage_size = 0;
110 // Premier appel pour connaitre la taille pour l'allocation
111 hipStream_t stream = impl::HipUtils::toNativeStream(&queue);
112 int* nb_list1_ptr = nullptr;
113 ARCANE_CHECK_HIP(rocprim::partition(nullptr, temp_storage_size, input_iter, output_iter,
115
116 s.m_algo_storage.allocate(temp_storage_size);
117 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
118
119 ARCANE_CHECK_HIP(rocprim::partition(s.m_algo_storage.address(), temp_storage_size, input_iter, output_iter,
121 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
122 } break;
123#endif
124#if defined(ARCANE_COMPILING_SYCL) && defined(__INTEL_LLVM_COMPILER)
126 // Seulement implémenté pour DPC++.
127 // Actuellement (dpc++ 2025.0), il n'y a pas l'équivalent avec SYCL de
128 // la méthode de partition de cub ou rocprim.
129 // Utilise la fonction 'stable_partition'. Cependant, cette fonction
130 // ne supporte pas si la mémoire uniquement sur accélérateur. De plus,
131 // il faut que InputIterator et OutputIterator remplissent le concept
132 // std::random_access_iterator ce qui n'est pas (notamment car il n'y a
133 // pas les opérateurs de copie ni de constructeur vide à cause de la lambda).
134 // Pour éviter tous ces problèmes, on alloue donc des tableaux temporaires
135 // pour l'appel à 'stable_sort' et on recopie les valerus en sortie.
136 using InputDataType = typename InputIterator::value_type;
137 using DataType = typename OutputIterator::value_type;
140 auto tmp_output = tmp_output_numarray.to1DSmallSpan();
141 auto tmp_select = tmp_select_numarray.to1DSmallSpan();
142 {
143 auto command = makeCommand(queue);
144 command << RUNCOMMAND_LOOP1(iter, nb_item)
145 {
146 auto [i] = iter();
147 tmp_output[i] = input_iter[i];
148 };
149 }
150 auto tmp_select_lambda = [=](Int32 i) { return tmp_select[i]; };
151 sycl::queue sycl_queue = impl::SyclUtils::toNativeStream(queue);
152 auto policy = oneapi::dpl::execution::make_device_policy(sycl_queue);
153 auto output_after = oneapi::dpl::stable_partition(policy, tmp_output.begin(), tmp_output.end(), select_lambda);
154 queue.barrier();
157 s.m_host_nb_list1_storage[0] = nb_list1;
158 //std::cerr << "NbList1=" << nb_list1 << " NbList2=" << nb_list2 << "\n";
159 {
160 // Recopie dans output les valeurs filtrées.
161 // Pour être cohérent avec 'cub' et 'rocprim', il faut inverser l'ordre des
162 // des valeurs de la liste pour les éléments ne remplissant pas la condition.
163 // Pour cela, on fait une boucle de taille (nb_list1 + nb_list/2) et chaque
164 // itération pour i>=nb_list1 gère deux éléments.
165 auto command = makeCommand(queue);
166 Int32 nb_iter2 = (nb_list2 / 2) + (nb_list2 % 2);
167 //std::cout << "NB_ITER2=" << nb_iter2 << "\n";
168 command << RUNCOMMAND_LOOP1(iter, (nb_list1 + nb_iter2))
169 {
170 auto [i] = iter();
171 if (i >= nb_list1) {
172 // Partie de la liste pour les valeurs ne remplissant par le critère.
173 Int32 j = i - nb_list1;
174 Int32 reverse_i = (nb_item - (j + 1));
175 auto x1 = tmp_output[i];
176 auto x2 = tmp_output[reverse_i];
179 }
180 else
181 output_iter[i] = tmp_output[i];
182 };
183 }
184 queue.barrier();
185 } break;
186#endif
188 // Pas encore implémenté en multi-thread
189 [[fallthrough]];
193 for (Int32 i = 0; i < nb_item; ++i) {
194 auto v = *input_iter;
195 if (select_lambda(v)) {
196 *output_iter = v;
197 ++output_iter;
198 }
199 else {
200 --output2_iter;
201 *output2_iter = v;
202 }
203 ++input_iter;
204 }
206 s.m_host_nb_list1_storage[0] = nb_list1;
207 } break;
208 default:
209 ARCANE_FATAL(getBadPolicyMessage(exec_policy));
210 }
211 launch_info.endExecute();
212 }
213
217 template <typename Select1Lambda, typename Select2Lambda,
218 typename InputIterator, typename FirstOutputIterator,
219 typename SecondOutputIterator, typename UnselectedIterator>
227 const TraceInfo& trace_info = TraceInfo())
228 {
229 RunQueue queue = s.m_queue;
231 RunCommand command = makeCommand(queue);
232 command << trace_info;
234 launch_info.beginExecute();
235 switch (exec_policy) {
236#if defined(ARCANE_COMPILING_CUDA)
238 size_t temp_storage_size = 0;
239 cudaStream_t stream = impl::CudaUtils::toNativeStream(&queue);
240 // Premier appel pour connaitre la taille pour l'allocation
241 int* nb_list1_ptr = nullptr;
242 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(nullptr, temp_storage_size,
246
247 s.m_algo_storage.allocate(temp_storage_size);
248 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
249 ARCANE_CHECK_CUDA(::cub::DevicePartition::If(s.m_algo_storage.address(), temp_storage_size,
253 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
254 } break;
255#endif
256#if defined(ARCANE_COMPILING_HIP)
258 size_t temp_storage_size = 0;
259 // Premier appel pour connaitre la taille pour l'allocation
260 hipStream_t stream = impl::HipUtils::toNativeStream(&queue);
261 int* nb_list1_ptr = nullptr;
262 using namespace rocprim;
263 ARCANE_CHECK_HIP(::rocprim::partition_three_way(nullptr, temp_storage_size, input_iter, first_output_iter,
266
267 s.m_algo_storage.allocate(temp_storage_size);
268 nb_list1_ptr = s.m_device_nb_list1_storage.allocate();
269
270 ARCANE_CHECK_HIP(partition_three_way(s.m_algo_storage.address(), temp_storage_size, input_iter, first_output_iter,
273 s.m_device_nb_list1_storage.copyToAsync(s.m_host_nb_list1_storage, queue);
274 } break;
275#endif
277 // Pas encore implémenté en multi-thread
278 [[fallthrough]];
280 Int32 nb_first = 0;
281 Int32 nb_second = 0;
282 for (Int32 i = 0; i < nb_item; ++i) {
283 auto v = *input_iter;
284 bool is_1 = select1_lambda(v);
285 bool is_2 = select2_lambda(v);
286 if (is_1) {
289 ++nb_first;
290 }
291 else {
292 if (is_2) {
295 ++nb_second;
296 }
297 else {
298 *unselected_iter = v;
300 }
301 }
302 // Incrémenter l'itérateur à la fin car il est utilisé pour le positionnement
303 ++input_iter;
304 }
305 s.m_host_nb_list1_storage[0] = nb_first;
306 s.m_host_nb_list1_storage[1] = nb_second;
307 } break;
308 default:
309 ARCANE_FATAL(getBadPolicyMessage(exec_policy));
310 }
311 launch_info.endExecute();
312 }
313};
314
315/*---------------------------------------------------------------------------*/
316/*---------------------------------------------------------------------------*/
317
318} // namespace Arcane::Accelerator::impl
319
320namespace Arcane::Accelerator
321{
322
323/*---------------------------------------------------------------------------*/
324/*---------------------------------------------------------------------------*/
333{
334 public:
335
336 explicit GenericPartitioner(const RunQueue& queue)
338 {
339 _allocate();
340 }
341
342 public:
343
356 template <typename SelectLambda, typename SetterLambda>
370
387 template <typename InputIterator, typename OutputIterator, typename SelectLambda>
398
416 template <typename Setter1Lambda, typename Setter2Lambda, typename UnselectedSetterLambda,
417 typename Select1Lambda, typename Select2Lambda>
438
456 template <typename InputIterator, typename FirstOutputIterator,
457 typename SecondOutputIterator, typename UnselectedIterator,
458 typename Select1Lambda, typename Select2Lambda>
475
480 {
481 m_is_already_called = false;
482 return _nbFirstPart();
483 }
484
496 {
497 m_is_already_called = false;
498 return _nbParts();
499 }
500
501 private:
502
503 bool m_is_already_called = false;
504
505 private:
506
507 void _setCalled()
508 {
509 if (m_is_already_called)
510 ARCANE_FATAL("apply() has already been called for this instance");
511 m_is_already_called = true;
512 }
513};
514
515/*---------------------------------------------------------------------------*/
516/*---------------------------------------------------------------------------*/
517
518} // namespace Arcane::Accelerator
519
520/*---------------------------------------------------------------------------*/
521/*---------------------------------------------------------------------------*/
522
523#endif
524
525/*---------------------------------------------------------------------------*/
526/*---------------------------------------------------------------------------*/
#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.
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.
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
void copyToAsync(SmallSpan< DataType > dest_ptr, const RunQueue &queue)
Copie l'instance dans dest_ptr.
Gère l'allocation interne sur le device.
Definition CommonUtils.h:95
Classe de base pour effectuer un filtrage.
Classe pour effectuer un partitionnement d'une liste.
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.
Object temporaire pour conserver les informations d'exécution d'une commande et regrouper les tests.
Itérateur sur une lambda pour positionner une valeur via un index.
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:149
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.