source: XIOS/dev/dev_ym/XIOS_COUPLING/extern/remap/src/mpi_routing.cpp @ 2269

Last change on this file since 2269 was 2269, checked in by ymipsl, 3 years ago
  • Solve memory leak from remapper.
  • shared_ptr add add for manage nodes.

YM

File size: 18.2 KB
Line 
1#include "mpi_routing.hpp"
2#include "mpi.hpp"
3#include "node.hpp"
4#include "elt.hpp"
5#include "timerRemap.hpp"
6#include <iostream>
7
8namespace sphereRemap {
9
10const int verbose = 0;
11
12CMPIRouting::CMPIRouting(MPI_Comm comm) : communicator(comm)
13{
14        MPI_Comm_rank(comm, &mpiRank);
15        MPI_Comm_size(comm, &mpiSize);
16}
17
18/* sparse alltoallv when it is known that only a subset of ranks will communicate,
19    but message lengths are *known* to receiver */
20template <typename T>
21void alltoalls_known(const vector<vector<T> >& send, vector<vector<T> >& recv, const vector<int>& ranks, MPI_Comm communicator)
22{
23        vector<MPI_Request> request(ranks.size() * 2);
24        vector<MPI_Status>  status(ranks.size() * 2);
25
26        // communicate data
27        int nbRequest = 0;
28        for (int i = 0; i < ranks.size(); i++)
29                if (recv[i].size())
30                        MPI_Irecv(&recv[i][0], recv[i].size()*sizeof(T), MPI_CHAR, ranks[i], 0, communicator, &request[nbRequest++]);
31        for (int i = 0; i < ranks.size(); i++)
32                if (send[i].size())
33                        MPI_Isend((void *) &send[i][0], send[i].size()*sizeof(T), MPI_CHAR, ranks[i], 0, communicator, &request[nbRequest++]);
34        MPI_Waitall(nbRequest, &request[0], &status[0]);
35}
36
37/* sparse alltoallv when it is known that only a subset of ranks will communicate,
38    but message lengths are *unknown* to receiver */
39template <typename T>
40void alltoalls_unknown(const vector<vector<T> >& send, vector<vector<T> >& recv, const vector<int>& ranks, MPI_Comm communicator)
41{
42        vector<MPI_Request> request(ranks.size() * 2);
43        vector<MPI_Status>  status(ranks.size() * 2);
44
45        // communicate sizes
46        int nbRequest = 0;
47        vector<int> sendSizes(ranks.size());
48        vector<int> recvSizes(ranks.size());
49        for (int i = 0; i < ranks.size(); i++)
50                sendSizes[i] = send[i].size();
51        for (int i = 0; i < ranks.size(); i++)
52                MPI_Irecv(&recvSizes[i], 1, MPI_INT, ranks[i], 0, communicator, &request[nbRequest++]);
53        for (int i = 0; i < ranks.size(); i++)
54                MPI_Isend(&sendSizes[i], 1, MPI_INT, ranks[i], 0, communicator, &request[nbRequest++]);
55        MPI_Waitall(nbRequest, &request[0], &status[0]);
56
57        // allocate
58        for (int i = 0; i < ranks.size(); i++)
59                if (recvSizes[i]) recv[i].resize(recvSizes[i]);
60        // communicate data
61        alltoalls_known(send, recv, ranks, communicator);
62}
63
64void setElementsSendCnt(int route, vector<int>& sendCnt)
65{
66        sendCnt[route]++;
67}
68
69void setElementsSendCnt(const vector<int>& route, vector<int>& sendCnt)
70{
71        for (int i = 0; i < route.size(); i++)
72                sendCnt[route[i]]++;
73}
74
75void setTargetElementIndex(int route, vector<int>& indices, const int *rankToIndex)
76{
77        int index = rankToIndex[route];
78        indices.push_back(index);
79}
80
81void setTargetElementIndex(const vector<int>& route, vector<int>& indices, const int *rankToIndex)
82{
83        for (int i = 0; i < route.size(); i++)
84        {
85                int index = rankToIndex[route[i]];
86                indices.push_back(index);
87        }
88}
89
90template<typename T>
91void CMPIRouting::init(const vector<T>& route, CMPICascade *cascade)
92{
93        vector<int> nbElementSend(mpiSize);
94        int *toSend = new int[mpiSize];
95        int *recvCount = new int[mpiSize];
96        int *targetRankToIndex;
97
98        for (int i = 0; i < route.size(); i++)
99                setElementsSendCnt(route[i], nbElementSend);
100
101        nbTarget = 0;
102vector<int> destRanks;
103        for (int i = 0; i < mpiSize; i++)
104        {
105                if (nbElementSend[i] == 0)
106                        toSend[i] = 0;
107                else
108                {
109destRanks.push_back(i);
110                        toSend[i] = 1;
111                        nbTarget++;
112                }
113                recvCount[i] = 1;
114        }
115//int recvCntDeb = (stree) ? stree->scatter_reduce(destRanks) : -1;
116
117        MPI_Barrier(communicator);
118        CTimer::get("CMPIRouting::init(reduce_scatter)").reset();
119        CTimer::get("CMPIRouting::init(reduce_scatter)").resume();
120        MPI_Reduce_scatter(toSend, &nbSource, recvCount, MPI_INT, MPI_SUM, communicator);
121        CTimer::get("CMPIRouting::init(reduce_scatter)").suspend();
122        CTimer::get("CMPIRouting::init(reduce_scatter)").print();
123
124        MPI_Alloc_mem(nbTarget *sizeof(int), MPI_INFO_NULL, &targetRank);
125        MPI_Alloc_mem(nbSource *sizeof(int), MPI_INFO_NULL, &sourceRank);
126
127        targetRankToIndex = new int[mpiSize];
128        int index = 0;
129        for (int i = 0; i < mpiSize; i++)
130        {
131                if (toSend[i] == 1)
132                {
133                        targetRank[index] = i;
134                        targetRankToIndex[i] = index;
135                        index++;
136                }
137        }
138
139        MPI_Barrier(communicator);
140        CTimer::get("CMPIRouting::init(get_source)").reset();
141        CTimer::get("CMPIRouting::init(get_source)").resume();
142
143        MPI_Request *request = new MPI_Request[nbSource + nbTarget];
144        MPI_Status  *status = new MPI_Status[nbSource + nbTarget];
145
146        int indexRequest = 0;
147        if (verbose) cout << "CMPIRouting::init     nbSource : " << nbSource << " nbTarget : " << nbTarget << endl;
148//      assert(recvCntDeb == -1 || recvCntDeb == nbSource);
149//cout << mpiRank <<  "DEB : " << recvCntDeb << "    nbSource " << nbSource << " nbTarget : " << nbTarget << endl;
150        for (int i = 0; i < nbSource; i++)
151        {
152                MPI_Irecv(&sourceRank[i], 1, MPI_INT, MPI_ANY_SOURCE, 0, communicator, &request[indexRequest]);
153                indexRequest++;
154        }
155        MPI_Barrier(communicator);
156        for (int i = 0; i < nbTarget; i++)
157        {
158                MPI_Isend(&mpiRank, 1, MPI_INT, targetRank[i], 0, communicator, &request[indexRequest]);
159                indexRequest++;
160        }
161        MPI_Waitall(indexRequest, request, status);
162        MPI_Barrier(communicator);  //needed
163        CTimer::get("CMPIRouting::init(get_source)").suspend();
164        CTimer::get("CMPIRouting::init(get_source)").print();
165
166        CTimer::get("CMPIRouting::init(get_source)").reset();
167        CTimer::get("CMPIRouting::init(get_source)").resume();
168
169        indexRequest = 0;
170        for (int i = 0; i < nbSource; i++)
171        {
172                MPI_Irecv(&sourceRank[i], 1, MPI_INT, MPI_ANY_SOURCE, 0, communicator, &request[indexRequest]);
173                indexRequest++;
174        }
175
176        for (int i = 0; i < nbTarget; i++)
177        {
178                MPI_Isend(&mpiRank, 1, MPI_INT, targetRank[i], 0, communicator, &request[indexRequest]);
179                indexRequest++;
180        }
181        MPI_Waitall(indexRequest, request, status);
182        MPI_Barrier(communicator);
183        CTimer::get("CMPIRouting::init(get_source)").suspend();
184        CTimer::get("CMPIRouting::init(get_source)").print();
185
186        CTimer::get("CMPIRouting::init(send_element)").reset();
187        CTimer::get("CMPIRouting::init(send_element)").resume();
188
189        nbTargetElement.resize(nbTarget);
190        nbSourceElement.resize(nbSource);
191
192        for (int i = 0; i < route.size(); i++)
193                setTargetElementIndex(route[i], targetElementIndex, targetRankToIndex);
194
195        for (int i = 0; i < targetElementIndex.size(); i++)
196                nbTargetElement[targetElementIndex[i]]++;
197
198        indexRequest = 0;
199        totalSourceElement = 0;
200        totalTargetElement = 0;
201        for (int i = 0; i < nbSource; i++)
202        {
203                MPI_Irecv(&nbSourceElement[i], 1, MPI_INT, sourceRank[i], 0, communicator, &request[indexRequest]);
204                indexRequest++;
205        }
206
207        for (int i = 0; i < nbTarget; i++)
208        {
209                totalTargetElement += nbTargetElement[i];
210                MPI_Isend(&nbTargetElement[i], 1, MPI_INT, targetRank[i], 0, communicator, &request[indexRequest]);
211                indexRequest++;
212        }
213
214        MPI_Waitall(indexRequest, request, status);
215
216        CTimer::get("CMPIRouting::init(send_element)").suspend();
217        CTimer::get("CMPIRouting::init(send_element)").print();
218
219        totalSourceElement = 0;
220        for (int i = 0; i < nbSource; i++)
221                totalSourceElement += nbSourceElement[i];
222
223        sourceElementIndex.resize(totalSourceElement);
224
225        totalSourceElement = 0;
226        for (int i = 0; i < nbSource; i++)
227        {
228                for (int j = 0; j < nbSourceElement[i]; j++)
229                {
230                        sourceElementIndex[totalSourceElement] = i;
231                        totalSourceElement++;
232                }
233        }
234
235        delete[]  toSend;
236        delete[]  recvCount;
237        delete[]  targetRankToIndex;
238        delete[]  request;
239        delete[]  status;
240}
241
242
243int CMPIRouting::getTotalSourceElement(void)
244{
245        return totalSourceElement;
246}
247
248template<typename T>
249void CMPIRouting::transferToTarget(T* targetElements, T* sourceElements)
250{
251        char** targetBuffer = new char*[nbTarget];
252        int* indexTargetBuffer = new int[nbTarget];
253
254        for(int i=0;i<nbTarget; i++)
255        {
256                targetBuffer[i] = new char[sizeof(T)*nbTargetElement[i]];
257                indexTargetBuffer[i]= 0;
258        }
259
260        char** sourceBuffer = new char*[nbSource];
261        int* indexSourceBuffer = new int[nbSource];
262
263        for(int i=0;i<nbSource; i++)
264        {
265                sourceBuffer[i] = new char[sizeof(T)*nbSourceElement[i]];
266                indexSourceBuffer[i]= 0;
267        }
268
269        // pack the data
270        int index;
271        for(int i=0;i<totalTargetElement;i++)
272        {
273                index=targetElementIndex[i];
274                *((T*) &(targetBuffer[index][indexTargetBuffer[index]])) = targetElements[i];
275                indexTargetBuffer[index]+=sizeof(T);
276        }
277
278
279        MPI_Request* request=new MPI_Request[nbSource+nbTarget];
280        MPI_Status*  status=new MPI_Status[nbSource+nbTarget];
281        int indexRequest=0;
282
283        MPI_Barrier(communicator);
284        CTimer::get("CMPIRouting::transferToTarget").reset();
285        CTimer::get("CMPIRouting::transferToTarget").resume();
286
287        for(int i=0; i<nbSource; i++)
288        {
289                MPI_Irecv(sourceBuffer[i],nbSourceElement[i]*sizeof(T),MPI_CHAR, sourceRank[i], 0, communicator, &request[indexRequest]);
290                indexRequest++;
291        }
292
293        for(int i=0;i<nbTarget; i++)
294        {
295                MPI_Isend(targetBuffer[i],nbTargetElement[i]*sizeof(T), MPI_CHAR, targetRank[i], 0, communicator, &request[indexRequest]);
296                indexRequest++;
297        }
298
299        MPI_Waitall(indexRequest,request,status);
300
301        CTimer::get("CMPIRouting::transferToTarget").suspend();
302        CTimer::get("CMPIRouting::transferToTarget").print();
303        MPI_Barrier(communicator);
304
305        // unpack the data
306        for(int i=0;i<totalSourceElement;i++)
307        {
308                index=sourceElementIndex[i];
309                sourceElements[i] = *((T*) &(sourceBuffer[index][indexSourceBuffer[index]]));
310                indexSourceBuffer[index]+=sizeof(T);
311        }
312
313        for(int i=0;i<nbTarget; i++) delete[] targetBuffer[i];
314        for(int i=0;i<nbSource; i++) delete[] sourceBuffer[i];
315        delete[] targetBuffer;
316        delete[] indexTargetBuffer;
317        delete[] sourceBuffer;
318        delete[] indexSourceBuffer;
319        delete[] request;
320        delete[] status;
321}
322
323
324template<typename T, typename t_pack, typename t_unpack>
325void CMPIRouting::transferToTarget(T* targetElements, T* sourceElements, t_pack pack, t_unpack unpack)
326{
327        char** targetBuffer = new char*[nbTarget];
328        int* indexTargetBuffer = new int[nbTarget];
329        int* targetMessageSize = new int[nbTarget];
330        int* sourceMessageSize = new int[nbSource];
331        int index;
332
333        // compute target buffer size
334        for (int i = 0; i < nbTarget; i++)
335                targetMessageSize[i] = 0;
336
337        for (int i = 0; i < totalTargetElement; i++)
338        {
339                index = targetElementIndex[i];
340                pack(targetElements[i], NULL, targetMessageSize[index]);
341        }
342
343        MPI_Request *request = new MPI_Request[nbSource + nbTarget];
344        MPI_Status  *status = new MPI_Status[nbSource + nbTarget];
345        int indexRequest = 0;
346
347        MPI_Barrier(communicator);
348        CTimer::get("CMPIRouting::transferToTarget(messageSize)").reset();
349        CTimer::get("CMPIRouting::transferToTarget(messageSize)").resume();
350
351        for(int i=0; i<nbSource; i++)
352        {
353                MPI_Irecv(&sourceMessageSize[i],1,MPI_INT, sourceRank[i], 0, communicator, &request[indexRequest]);
354                indexRequest++;
355        }
356
357        for(int i=0; i<nbTarget; i++)
358        {
359                MPI_Isend(&targetMessageSize[i],1, MPI_INT, targetRank[i], 0, communicator, &request[indexRequest]);
360                indexRequest++;
361        }
362
363        MPI_Waitall(indexRequest,request,status);
364
365        MPI_Barrier(communicator);
366        CTimer::get("CMPIRouting::transferToTarget(messageSize)").suspend();
367        CTimer::get("CMPIRouting::transferToTarget(messageSize)").print();
368
369        for(int i=0; i<nbTarget; i++)
370        {
371                targetBuffer[i] = new char[targetMessageSize[i]];
372                indexTargetBuffer[i] = 0;
373        }
374
375        char** sourceBuffer = new char*[nbSource];
376        int* indexSourceBuffer = new int[nbSource];
377
378        for(int i=0;i<nbSource; i++)
379        {
380                sourceBuffer[i] = new char[sourceMessageSize[i]];
381                indexSourceBuffer[i]= 0;
382        }
383
384        // pack the data
385        for(int i=0; i<totalTargetElement; i++)
386        {
387                index=targetElementIndex[i];
388                pack(targetElements[i], targetBuffer[index], indexTargetBuffer[index] );
389        }
390
391        indexRequest=0;
392
393        MPI_Barrier(communicator);
394        CTimer::get("CMPIRouting::transferToTarget(data)").reset();
395        CTimer::get("CMPIRouting::transferToTarget(data)").resume();
396        for(int i=0; i<nbSource; i++)
397        {
398                MPI_Irecv(sourceBuffer[i],sourceMessageSize[i],MPI_CHAR, sourceRank[i], 0, communicator, &request[indexRequest]);
399                indexRequest++;
400        }
401
402        for(int i=0;i<nbTarget; i++)
403        {
404                MPI_Isend(targetBuffer[i],targetMessageSize[i], MPI_CHAR, targetRank[i], 0, communicator, &request[indexRequest]);
405                indexRequest++;
406        }
407
408        MPI_Waitall(indexRequest,request,status);
409
410        MPI_Barrier(communicator);
411        CTimer::get("CMPIRouting::transferToTarget(data)").suspend();
412        CTimer::get("CMPIRouting::transferToTarget(data)").print();
413
414        // unpack the data
415        for(int i=0; i<totalSourceElement; i++)
416        {
417                index=sourceElementIndex[i];
418                unpack(sourceElements[i], sourceBuffer[index], indexSourceBuffer[index]);
419        }
420
421        for (int i=0; i<nbTarget; i++) delete[] targetBuffer[i];
422        for (int i=0; i<nbSource; i++) delete[] sourceBuffer[i];
423        delete[] targetBuffer;
424        delete[] indexTargetBuffer;
425        delete[] targetMessageSize;
426        delete[] sourceBuffer;
427        delete[] indexSourceBuffer;
428        delete[] sourceMessageSize;
429        delete[] request;
430        delete[] status;
431}
432
433template<typename T>
434void CMPIRouting::transferFromSource(T* targetElements, T* sourceElements)
435{
436        char** targetBuffer = new char*[nbTarget];
437        int* indexTargetBuffer = new int[nbTarget];
438
439        for (int i = 0; i < nbTarget; i++)
440        {
441                targetBuffer[i] = new char[sizeof(T)*nbTargetElement[i]];
442                indexTargetBuffer[i] = 0;
443        }
444
445        char** sourceBuffer = new char*[nbSource];
446        int* indexSourceBuffer = new int[nbSource];
447
448        for (int i = 0; i < nbSource; i++)
449        {
450                sourceBuffer[i] = new char[sizeof(T)*nbSourceElement[i]];
451                indexSourceBuffer[i] = 0;
452        }
453
454        // pack the data
455        int index;
456        for (int i = 0; i < totalSourceElement; i++)
457        {
458                index = sourceElementIndex[i];
459                *((T*) &(sourceBuffer[index][indexSourceBuffer[index]])) = sourceElements[i];
460                indexSourceBuffer[index] += sizeof(T);
461        }
462
463        MPI_Request* request=new MPI_Request[nbSource+nbTarget];
464        MPI_Status*  status=new MPI_Status[nbSource+nbTarget];
465        int indexRequest=0;
466
467        for(int i=0; i<nbSource; i++)
468        {
469                MPI_Isend(sourceBuffer[i],nbSourceElement[i]*sizeof(T),MPI_CHAR, sourceRank[i], 0, communicator, &request[indexRequest]);
470                indexRequest++;
471        }
472
473        for(int i=0;i<nbTarget; i++)
474        {
475                MPI_Irecv(targetBuffer[i],nbTargetElement[i]*sizeof(T), MPI_CHAR, targetRank[i], 0, communicator, &request[indexRequest]);
476                indexRequest++;
477        }
478
479        MPI_Waitall(indexRequest,request,status);
480
481        // unpack the data
482        for(int i=0;i<totalTargetElement;i++)
483        {
484                index=targetElementIndex[i];
485                targetElements[i] = *((T*) &(targetBuffer[index][indexTargetBuffer[index]]));
486                indexTargetBuffer[index]+=sizeof(T);
487        }
488
489        for(int i=0;i<nbTarget; i++) delete[] targetBuffer[i];
490        for(int i=0;i<nbSource; i++) delete[] sourceBuffer[i];
491        delete[] targetBuffer;
492        delete[] indexTargetBuffer;
493        delete[] sourceBuffer;
494        delete[] indexSourceBuffer;
495        delete[] request;
496        delete[] status;
497}
498
499
500/* number of source and target elements is known from previous call to init() */
501template<typename T, typename t_pack, typename t_unpack>
502void CMPIRouting::transferFromSource(T *targetElements, T *sourceElements, t_pack pack, t_unpack unpack)
503{
504        char **targetBuffer = new char*[nbTarget];
505        int *indexTargetBuffer = new int[nbTarget];
506        int *targetMessageSize = new int[nbTarget];
507        int *sourceMessageSize = new int[nbSource];
508        int index;
509
510        // compute target buffer size
511        for (int i = 0; i < nbSource; i++)  sourceMessageSize[i] = 0;
512
513        for (int i = 0; i < totalSourceElement; i++)
514        {
515                index = sourceElementIndex[i];
516                pack(sourceElements[i], NULL, sourceMessageSize[index]);
517        }
518
519        MPI_Request *request = new MPI_Request[nbSource + nbTarget];
520        MPI_Status  *status = new MPI_Status[nbSource + nbTarget];
521        int indexRequest = 0;
522        for (int i = 0; i < nbSource; i++)
523        {
524                MPI_Isend(&sourceMessageSize[i], 1, MPI_INT, sourceRank[i], 0, communicator, &request[indexRequest]);
525                indexRequest++;
526        }
527        for (int i = 0; i < nbTarget; i++)
528        {
529                MPI_Irecv(&targetMessageSize[i], 1, MPI_INT, targetRank[i], 0, communicator, &request[indexRequest]);
530                indexRequest++;
531        }
532        MPI_Waitall(indexRequest, request, status);
533
534        for (int i = 0; i < nbTarget; i++)
535        {
536                targetBuffer[i] = new char[targetMessageSize[i]];
537                indexTargetBuffer[i] = 0;
538        }
539
540        char **sourceBuffer = new char*[nbSource];
541        int *indexSourceBuffer = new int[nbSource];
542
543        for (int i = 0; i < nbSource; i++)
544        {
545                sourceBuffer[i] = new char[sourceMessageSize[i]];
546                indexSourceBuffer[i] = 0;
547        }
548
549
550        // pack the data
551        for (int i = 0; i < totalSourceElement; i++)
552        {
553                index = sourceElementIndex[i];
554                pack(sourceElements[i], sourceBuffer[index], indexSourceBuffer[index] );
555        }
556
557        indexRequest = 0;
558        for (int i = 0; i < nbSource; i++)
559        {
560                MPI_Isend(sourceBuffer[i], sourceMessageSize[i], MPI_CHAR, sourceRank[i], 0, communicator, &request[indexRequest]);
561                indexRequest++;
562        }
563        for (int i = 0; i < nbTarget; i++)
564        {
565                MPI_Irecv(targetBuffer[i], targetMessageSize[i], MPI_CHAR, targetRank[i], 0, communicator, &request[indexRequest]);
566                indexRequest++;
567        }
568        MPI_Waitall(indexRequest, request, status);
569
570        // unpack the data
571        for (int i = 0; i < totalTargetElement; i++)
572        {
573                index = targetElementIndex[i];
574                unpack(targetElements[i], targetBuffer[index], indexTargetBuffer[index]);
575        }
576
577        for (int i = 0; i < nbTarget; i++) delete[] targetBuffer[i];
578        for (int i = 0; i < nbSource; i++) delete[] sourceBuffer[i];
579        delete[] targetBuffer;
580        delete[] indexTargetBuffer;
581        delete[] targetMessageSize;
582        delete[] sourceBuffer;
583        delete[] indexSourceBuffer;
584        delete[] sourceMessageSize;
585        delete[] request;
586        delete[] status;
587}
588
589CMPIRouting::~CMPIRouting()
590{
591};
592
593template void CMPIRouting::init(const std::vector<int>& route, CMPICascade *cascade);
594template void CMPIRouting::init(const std::vector<vector<int> >& route, CMPICascade *cascade);
595
596template void CMPIRouting::transferToTarget(int *targetElements, int *sourceElements);
597template void CMPIRouting::transferToTarget(int *targetElements, int *sourceElements, void (*pack)(int&, char*, int&), void (* unpack)(int&, char *, int&));
598template void CMPIRouting::transferFromSource(int *targetElements, int *sourceElements);
599template void CMPIRouting::transferFromSource(int *targetElements, int *sourceElements, void (*pack)(int&, char*, int&), void (* unpack)(int&, char *, int&));
600
601template void CMPIRouting::transferToTarget(NodePtr* targetElements, NodePtr* sourceElements, void (*pack)(NodePtr, char*, int&), void (* unpack)(NodePtr, char *, int&));
602template void CMPIRouting::transferToTarget(Elt **targetElements, Elt **sourceElements, void (*pack)(Elt *, char*, int&), void (* unpack)(Elt *, char *, int&));
603template void CMPIRouting::transferFromSource(std::vector<int> *targetElements, std::vector<int> *sourceElements, void (*pack)(const std::vector<int>&, char*, int&), void (* unpack)(std::vector<int>&, const char *, int&));
604
605struct NES { int cnt; int risc; int rank; };
606
607template void alltoalls_unknown(const std::vector<std::vector<NES> >& send, std::vector<std::vector<NES> >& recv,
608                                const std::vector<int>& ranks, MPI_Comm communicator);
609
610template void alltoalls_known(const std::vector<std::vector<int> >& send, std::vector<std::vector<int> >& recv,
611                              const std::vector<int>& ranks, MPI_Comm communicator);
612
613}
Note: See TracBrowser for help on using the repository browser.