Arcane  v3.14.10.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
BitonicSortT.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/* BitonicSortT.H (C) 2000-2024 */
9/* */
10/* Algorithme de tri bitonique parallèle */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/TraceAccessor.h"
15#include "arcane/utils/FatalErrorException.h"
16
17#include "arcane/core/IParallelMng.h"
18#include "arcane/core/parallel/BitonicSort.h"
19
20/*---------------------------------------------------------------------------*/
21/*---------------------------------------------------------------------------*/
22
23namespace Arcane::Parallel
24{
25
26/*---------------------------------------------------------------------------*/
27/*---------------------------------------------------------------------------*/
28
29template <typename KeyType, typename KeyTypeTraits> BitonicSort<KeyType, KeyTypeTraits>::
30BitonicSort(IParallelMng* parallel_mng)
31: TraceAccessor(parallel_mng->traceMng())
32, m_parallel_mng(parallel_mng)
33{
34}
35
36/*---------------------------------------------------------------------------*/
37/*---------------------------------------------------------------------------*/
38
39template <typename KeyType, typename KeyTypeTraits> void BitonicSort<KeyType, KeyTypeTraits>::
40_init(ConstArrayView<KeyType> keys)
41{
42 m_init_size = keys.size();
43
44 // Il est indispensable que tout les procs aient la même valeur
45 // de m_size
46 m_size = m_parallel_mng->reduce(Parallel::ReduceMax, m_init_size);
47
48 m_keys.resize(m_size);
49 m_key_ranks.resize(m_size);
50 m_key_indexes.resize(m_size);
51
52 Int32 rank = m_parallel_mng->commRank();
53
54 // Les valeurs de la variable aux mailles tab sont stockées
55 // dans le tableau m_key
56 for (Integer i = 0; i < m_init_size; ++i) {
57 m_keys[i] = keys[i];
58 m_key_indexes[i] = i;
59 m_key_ranks[i] = rank;
60 }
61
62 // Remplit la fin du tableau avec des valeurs non significatives
63 // Afin que chaque processeur ait le meme nombre d'elements
64 KeyType max_value = KeyTypeTraits::maxValue();
65 for (Int64 i = m_init_size; i < m_size; i++) {
66 m_keys[i] = max_value;
67 m_key_indexes[i] = -1;
68 m_key_ranks[i] = rank;
69 }
70}
71
72/*---------------------------------------------------------------------------*/
73/*---------------------------------------------------------------------------*/
74/*!
75 \brief Tri bitonique de la variable \a m_key
76
77 En sortie de l'appel, la variable key est entièrement classée
78 par ordre croissant : les premiers éléments sont situés
79 par ordre croissant sur le processeur 0, les suivants sur
80 le processeur 1, etc...
81 Le rangement se fait 'en place', de sorte que la taille
82 des tableaux sur les différents processeurs n'est pas modifiée.
83
84*/
85template <typename KeyType, typename KeyTypeTraits> void
88{
89 _init(keys);
90
91 m_nb_merge = 0;
92
93 info() << "BITONIC_SORT (64bit) want_rank?=" << m_want_index_and_rank
94 << " size=" << m_size
95 << " memsize=" << sizeof(KeyType) * m_size
96 << " structsize=" << sizeof(KeyType);
97
98 // Appel au tri local des cles
99 _localHeapSort();
100
101 // Définition du nombre de niveaux pour l'algorithme
102 Int32 nb_level = 0;
103 Int32 test = 1;
104 Int32 nb_rank = m_parallel_mng->commSize();
105 while (nb_rank > test) {
106 nb_level++;
107 test *= 2;
108 }
109 Int32 size = 1;
110
111 // Boucle sur les niveaux de profondeurs de l'algorithme
112 for (Int32 ilevel = 0; ilevel < nb_level; ++ilevel) {
113 size *= 2;
114
115 // Boucle sur les groupes de processeurs à traiter
116 for (Int32 jproc = 0; jproc < nb_rank; jproc += size) {
117 _mergeLevels(jproc, size);
118 }
119 }
120
121 // Le nombre d'éléments valides de la liste peut-être inférieur
122 // au nombre d'éléments alloués. Dès qu'une valeur est invalide,
123 // on arrête la liste.
124 // TODO: partir de la fin
125 for (Integer i = 0; i < m_size; ++i) {
126 if (!KeyTypeTraits::isValid(m_keys[i])) {
127 m_size = i;
128 break;
129 }
130 }
131
132 info() << "END_BITONIC_SORT nb_merge=" << m_nb_merge;
133
134 m_keys.resize(m_size);
135 m_key_ranks.resize(m_size);
136 m_key_indexes.resize(m_size);
137}
138
139/*---------------------------------------------------------------------------*/
140/*---------------------------------------------------------------------------*/
141/*!
142 \brief Tri bitonique de la variable \a key.
143
144 En sortie de l'appel, la variable key est entièrement classée
145 par ordre croissant : les premiers éléments sont situés
146 par ordre croissant sur le processeur 0, les suivants sur
147 le processeur 1, etc...
148 Le rangement se fait 'en place', de sorte que la taille
149 des tableaux sur les différents processeurs n'est pas modifiée.
150*/
151template <typename KeyType, typename KeyTypeTraits> void
153_mergeLevels(Int32 begin, Int32 size)
154{
155 //etage separateur
156 for (Int32 i = 0; i < size / 2; ++i)
157 _mergeProcessors(begin + i, begin + size - 1 - i);
158
159 if (size <= 2)
160 return;
161
162 // Application de la trieuse bitonique de niveau n/2
163 Int32 div_size = size;
164 while (div_size > 1) {
165 div_size /= 2;
166 for (Int32 iproc = 0; iproc < size; iproc += div_size)
167 _separator(begin + iproc, div_size);
168 }
169}
170
171/*---------------------------------------------------------------------------*/
172/*---------------------------------------------------------------------------*/
173/*!
174 * \brief Etage séparateur de l'algorithme de tri bitonique
175 */
176template <typename KeyType, typename KeyTypeTraits> void BitonicSort<KeyType, KeyTypeTraits>::
177_separator(Int32 begin, Int32 size)
178{
179 for (Int32 i = 0; i < size / 2; ++i)
180 _mergeProcessors(begin + i, begin + i + size / 2);
181}
182
183/*---------------------------------------------------------------------------*/
184/*---------------------------------------------------------------------------*/
185/*!
186 * \brief Fusion des listes de deux processeurs .
187 *
188 \param iproc1 processeur de plus faible Id dont on fusionne la liste
189 \param iproc2 processeur de plus fort Id dont on fusionne la liste
190
191 * Cette opération est la brique élémentaire de
192 * l'algorithme de tri fusion bitonique. En entrée, elle
193 * prend les numéros de deux processeurs avec iproc1<iproc2.
194 * En sortie, les tableaux m_key de ces deux processeurs
195 * sont triés : les plus petits éléments sont triés par ordre croissant
196 * sur iproc1, les plus grands éléments sont triés par ordre croissant
197 * sur iproc2.
198 */
199template <typename KeyType, typename KeyTypeTraits> void BitonicSort<KeyType, KeyTypeTraits>::
200_mergeProcessors(Int32 iproc1, Int32 iproc2)
201{
202 if (iproc1 >= iproc2)
203 ARCANE_FATAL("Invalid merge iproc1={0} iproc2={1}", iproc1, iproc2);
204
205 Int32 my_rank = m_parallel_mng->commRank();
206
207 if (iproc2 >= m_parallel_mng->commSize())
208 return;
209
210 bool is_proc2 = (iproc2 == my_rank);
211 bool is_proc1 = (iproc1 == my_rank);
212
213 if (!is_proc1 && !is_proc2)
214 return;
215
216 info() << "SORT iproc1=" << iproc1 << " iproc2=" << iproc2;
217 ++m_nb_merge;
218
219 const Int64 buf_size = m_size;
220 const bool is_want_index_rank = m_want_index_and_rank;
221 ////////////////////////////////////////////
222 //traitement du processeur 2
223 ////////////////////////////////////////////
224
225 if (is_proc2) {
226 //envoi de la liste au proc 1
227 UniqueArray<KeyType> send_keys(m_keys);
228 UniqueArray<Request> requests;
229 requests.add(KeyTypeTraits::send(m_parallel_mng, iproc1, send_keys));
230 if (is_want_index_rank) {
231 requests.add(m_parallel_mng->send(m_key_indexes, iproc1, false));
232 requests.add(m_parallel_mng->send(m_key_ranks, iproc1, false));
233 }
234 //reception de la fin de la liste fusionnee par le proc1
235 requests.add(KeyTypeTraits::recv(m_parallel_mng, iproc1, m_keys));
236 if (is_want_index_rank) {
237 requests.add(m_parallel_mng->recv(m_key_indexes, iproc1, false));
238 requests.add(m_parallel_mng->recv(m_key_ranks, iproc1, false));
239 }
240 m_parallel_mng->waitAllRequests(requests);
241 }
242
243 //////////////////////////////////////
244 //traitement du processeur 1
245 //////////////////////////////////////
246 if (is_proc1) {
247 //reception de la liste du proc2
248
249 UniqueArray<KeyType> buf2(buf_size);
250 Int32UniqueArray buf2_proc;
251 Int32UniqueArray buf2_local_id;
252 //on alloue buf2 a la taille+1 afin de placer une sentinelle en
253 //buf2[size]
254 buf2.resize(buf_size);
255 if (is_want_index_rank) {
256 buf2_proc.resize(buf_size);
257 buf2_local_id.resize(buf_size);
258 }
259
260 Request recv_request = KeyTypeTraits::recv(m_parallel_mng, iproc2, buf2);
261 m_parallel_mng->waitAllRequests(ArrayView<Request>(1, &recv_request));
262 if (is_want_index_rank) {
263 m_parallel_mng->recv(buf2_local_id, iproc2);
264 m_parallel_mng->recv(buf2_proc, iproc2);
265 }
266
267 //à remplacer par le plus grand nombre representable par un Integer
268 buf2.add(KeyTypeTraits::maxValue());
269
270 // allocation d'un buffer de travail pour la fusion des deux listes
271 Int64 total_size = m_size + buf_size;
272 UniqueArray<KeyType> buf_merge(total_size);
273 UniqueArray<Int32> buf_proc_merge;
274 UniqueArray<Int32> buf_local_id_merge;
275 if (is_want_index_rank) {
276 buf_proc_merge.resize(total_size);
277 buf_local_id_merge.resize(total_size);
278 }
279
280#if 0
281 info() << "SIZE=" << buf_size << " total=" << total_size << " m_size=" << m_size
282 << " proc2=" << iproc2 << " init-size=" << m_init_size;
283#endif
284
285 // Fusion des deux listes en partant du principe que chacune est
286 // déjà triée.
287 Int64 cursor1 = 0;
288 Int64 cursor2 = 0;
289 for (Int64 i = 0; i < total_size; ++i) {
290 if (cursor1 >= m_size || (KeyTypeTraits::compareLess(buf2[cursor2], m_keys[cursor1]) && cursor2 < buf_size)) {
291 buf_merge[i] = buf2[cursor2];
292 if (is_want_index_rank) {
293 buf_local_id_merge[i] = buf2_local_id[cursor2];
294 buf_proc_merge[i] = buf2_proc[cursor2];
295 }
296 ++cursor2;
297 }
298 else {
299 buf_merge[i] = m_keys[cursor1];
300 if (is_want_index_rank) {
301 buf_local_id_merge[i] = m_key_indexes[cursor1];
302 buf_proc_merge[i] = m_key_ranks[cursor1];
303 }
304 ++cursor1;
305 }
306 }
307 buf2.resize(buf_size);
308
309 // Recopie et envoi de la fin de la liste vers le proc2
310 for (Integer ii = 0; ii < buf_size; ii++) {
311 buf2[ii] = buf_merge[m_size + ii];
312 if (is_want_index_rank) {
313 buf2_local_id[ii] = buf_local_id_merge[m_size + ii];
314 buf2_proc[ii] = buf_proc_merge[m_size + ii];
315 }
316 }
317 Request send_request = KeyTypeTraits::send(m_parallel_mng, iproc2, buf2);
318 m_parallel_mng->waitAllRequests(ArrayView<Request>(1, &send_request));
319 if (is_want_index_rank) {
320 m_parallel_mng->send(buf2_local_id, iproc2);
321 m_parallel_mng->send(buf2_proc, iproc2);
322 }
323
324 // recopie du debut de la liste dans la variable m_work_unique_id
325 for (Integer i = 0; i < buf_size; i++) {
326 m_keys[i] = buf_merge[i];
327 if (is_want_index_rank) {
328 m_key_indexes[i] = buf_local_id_merge[i];
329 m_key_ranks[i] = buf_proc_merge[i];
330 }
331 }
332 }
333}
334
335/*---------------------------------------------------------------------------*/
336/*---------------------------------------------------------------------------*/
337/*!
338 \brief Tri par tas en local de la variable \a m_key.
339
340 En sortie de l'appel, la variable est classée
341 par ordre croissant à l'intérieur de chaque processeur
342 */
343template <typename KeyType, typename KeyTypeTraits> void BitonicSort<KeyType, KeyTypeTraits>::
344_localHeapSort()
345{
346 // Cas particulier d'une liste réduite à zero ou un element
347 if (m_size < 2)
348 return;
349
350 // Suivant du parent du dernier element
351 Int64 l = m_size / 2;
352 Int64 ir = m_size - 1;
353 KeyType rra;
354 Int64 i = 0;
355 Int64 j = 0;
356 Integer tmp_local_id = NULL_ITEM_LOCAL_ID;
357 Integer tmp_proc = A_NULL_RANK;
358
359 for (;;) {
360 if (l > 0) {
361 --l;
362 rra = m_keys[l];
363 tmp_local_id = m_key_indexes[l];
364 tmp_proc = m_key_ranks[l];
365 }
366 else {
367 rra = m_keys[ir];
368 tmp_local_id = m_key_indexes[ir];
369 tmp_proc = m_key_ranks[ir];
370
371 m_keys[ir] = m_keys[0];
372 m_key_indexes[ir] = m_key_indexes[0];
373 m_key_ranks[ir] = m_key_ranks[0];
374
375 if (--ir == 0) {
376 m_keys[0] = rra;
377 m_key_indexes[0] = tmp_local_id;
378 m_key_ranks[0] = tmp_proc;
379 break;
380 }
381 }
382 i = l;
383 j = l + l + 1;
384 while (j <= ir) {
385 if (j < ir && KeyTypeTraits::compareLess(m_keys[j], m_keys[j + 1]))
386 ++j;
387 if (KeyTypeTraits::compareLess(rra, m_keys[j])) {
388 m_keys[i] = m_keys[j];
389 m_key_indexes[i] = m_key_indexes[j];
390 m_key_ranks[i] = m_key_ranks[j];
391 i = j;
392 //enfant a gauche
393 j = j * 2 + 1;
394 }
395 else
396 j = ir + 1;
397 }
398 m_keys[i] = rra;
399 m_key_indexes[i] = tmp_local_id;
400 m_key_ranks[i] = tmp_proc;
401 }
402}
403
404/*---------------------------------------------------------------------------*/
405/*---------------------------------------------------------------------------*/
406
407} // namespace Arcane::Parallel
408
409/*---------------------------------------------------------------------------*/
410/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Algorithme de tri bitonique parallèle.
void resize(Int64 s)
Change le nombre d'éléments du tableau à s.
Vue constante d'un tableau de type T.
Implémentation de la concurrence.
UniqueArray< Int32 > Int32UniqueArray
Tableau dynamique à une dimension d'entiers 32 bits.
Definition UtilsTypes.h:515
Int32 Integer
Type représentant un entier.