xref: /petsc/src/sys/objects/tagm.c (revision ee655a11423429d4547c322471d0edfcae45c8ab)
1 
2 /*
3       Some PETSc utilites
4 */
5 #include <petsc/private/petscimpl.h>             /*I    "petscsys.h"   I*/
6 /* ---------------------------------------------------------------- */
7 /*
8    A simple way to manage tags inside a communicator.
9 
10    It uses the attributes to determine if a new communicator
11       is needed and to store the available tags.
12 
13 */
14 
15 
16 /*@C
17     PetscObjectGetNewTag - Gets a unique new tag from a PETSc object. All
18     processors that share the object MUST call this routine EXACTLY the same
19     number of times.  This tag should only be used with the current objects
20     communicator; do NOT use it with any other MPI communicator.
21 
22     Collective on PetscObject
23 
24     Input Parameter:
25 .   obj - the PETSc object; this must be cast with a (PetscObject), for example,
26          PetscObjectGetNewTag((PetscObject)mat,&tag);
27 
28     Output Parameter:
29 .   tag - the new tag
30 
31     Level: developer
32 
33     Concepts: tag^getting
34     Concepts: message tag^getting
35     Concepts: MPI message tag^getting
36 
37 .seealso: PetscCommGetNewTag()
38 @*/
39 PetscErrorCode  PetscObjectGetNewTag(PetscObject obj,PetscMPIInt *tag)
40 {
41   PetscErrorCode ierr;
42 
43   PetscFunctionBegin;
44   ierr = PetscCommGetNewTag(obj->comm,tag);CHKERRQ(ierr);
45   PetscFunctionReturn(0);
46 }
47 
48 /*@
49     PetscCommGetNewTag - Gets a unique new tag from a PETSc communicator. All
50     processors that share the communicator MUST call this routine EXACTLY the same
51     number of times.  This tag should only be used with the current objects
52     communicator; do NOT use it with any other MPI communicator.
53 
54     Collective on comm
55 
56     Input Parameter:
57 .   comm - the MPI communicator
58 
59     Output Parameter:
60 .   tag - the new tag
61 
62     Level: developer
63 
64     Concepts: tag^getting
65     Concepts: message tag^getting
66     Concepts: MPI message tag^getting
67 
68 .seealso: PetscObjectGetNewTag(), PetscCommDuplicate()
69 @*/
70 PetscErrorCode  PetscCommGetNewTag(MPI_Comm comm,PetscMPIInt *tag)
71 {
72   PetscErrorCode   ierr;
73   PetscCommCounter *counter;
74   PetscMPIInt      *maxval,flg;
75 
76   PetscFunctionBegin;
77   PetscValidIntPointer(tag,2);
78 
79   ierr = MPI_Attr_get(comm,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
80   if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Bad MPI communicator supplied; must be a PETSc communicator");
81 
82   if (counter->tag < 1) {
83     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",counter->refcount);CHKERRQ(ierr);
84     ierr = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,&maxval,&flg);CHKERRQ(ierr);
85     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
86     counter->tag = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
87   }
88 
89   *tag = counter->tag--;
90 #if defined(PETSC_USE_DEBUG)
91   /*
92      Hanging here means that some processes have called PetscCommGetNewTag() and others have not.
93   */
94   ierr = MPI_Barrier(comm);CHKERRQ(ierr);
95 #endif
96   PetscFunctionReturn(0);
97 }
98 
99 /*@C
100   PetscCommDuplicate - Duplicates the communicator only if it is not already a PETSc communicator.
101 
102   Collective on MPI_Comm
103 
104   Input Parameters:
105 . comm_in - Input communicator
106 
107   Output Parameters:
108 + comm_out - Output communicator.  May be comm_in.
109 - first_tag - Tag available that has not already been used with this communicator (you may
110               pass in NULL if you do not need a tag)
111 
112   PETSc communicators are just regular MPI communicators that keep track of which
113   tags have been used to prevent tag conflict. If you pass a non-PETSc communicator into
114   a PETSc creation routine it will attach a private communicator for use in the objects communications.
115   The internal MPI_Comm is used to perform all the MPI calls for PETSc, the outer MPI_Comm is a user
116   level MPI_Comm that may be performing communication for the user or other library and so IS NOT used by PETSc.
117 
118   Level: developer
119 
120   Concepts: communicator^duplicate
121 
122 .seealso: PetscObjectGetNewTag(), PetscCommGetNewTag(), PetscCommDestroy()
123 @*/
124 PetscErrorCode  PetscCommDuplicate(MPI_Comm comm_in,MPI_Comm *comm_out,PetscMPIInt *first_tag)
125 {
126   PetscErrorCode   ierr;
127   PetscCommCounter *counter;
128   PetscMPIInt      *maxval,flg;
129 
130   PetscFunctionBegin;
131   ierr = PetscSpinlockLock(&PetscCommSpinLock);CHKERRQ(ierr);
132   ierr = MPI_Attr_get(comm_in,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
133 
134   if (!flg) {  /* this is NOT a PETSc comm */
135     union {MPI_Comm comm; void *ptr;} ucomm;
136     /* check if this communicator has a PETSc communicator imbedded in it */
137     ierr = MPI_Attr_get(comm_in,Petsc_InnerComm_keyval,&ucomm,&flg);CHKERRQ(ierr);
138     if (!flg) {
139       /* This communicator is not yet known to this system, so we duplicate it and make an internal communicator */
140       ierr = MPI_Comm_dup(comm_in,comm_out);CHKERRQ(ierr);
141       ierr = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,&maxval,&flg);CHKERRQ(ierr);
142       if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
143       ierr = PetscNew(&counter);CHKERRQ(ierr);
144 
145       counter->tag       = *maxval;
146       counter->refcount  = 0;
147       counter->namecount = 0;
148 
149       ierr = MPI_Attr_put(*comm_out,Petsc_Counter_keyval,counter);CHKERRQ(ierr);
150       ierr = PetscInfo3(0,"Duplicating a communicator %ld %ld max tags = %d\n",(long)comm_in,(long)*comm_out,*maxval);CHKERRQ(ierr);
151 
152       /* save PETSc communicator inside user communicator, so we can get it next time */
153       ucomm.comm = *comm_out;   /* ONLY the comm part of the union is significant. */
154       ierr = MPI_Attr_put(comm_in,Petsc_InnerComm_keyval,ucomm.ptr);CHKERRQ(ierr);
155       ucomm.comm = comm_in;
156       ierr = MPI_Attr_put(*comm_out,Petsc_OuterComm_keyval,ucomm.ptr);CHKERRQ(ierr);
157     } else {
158       *comm_out = ucomm.comm;
159       /* pull out the inner MPI_Comm and hand it back to the caller */
160       ierr = MPI_Attr_get(*comm_out,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
161       if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Inner PETSc communicator does not have its tag/name counter attribute set");
162       ierr = PetscInfo2(0,"Using internal PETSc communicator %ld %ld\n",(long)comm_in,(long)*comm_out);CHKERRQ(ierr);
163     }
164   } else *comm_out = comm_in;
165 
166 #if defined(PETSC_USE_DEBUG)
167   /*
168      Hanging here means that some processes have called PetscCommDuplicate() and others have not.
169      This likley means that a subset of processes in a MPI_Comm have attempted to create a PetscObject!
170      ALL processes that share a communicator MUST shared objects created from that communicator.
171   */
172   ierr = MPI_Barrier(comm_in);CHKERRQ(ierr);
173 #endif
174 
175   if (counter->tag < 1) {
176     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",counter->refcount);CHKERRQ(ierr);
177     ierr = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,&maxval,&flg);CHKERRQ(ierr);
178     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
179     counter->tag = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
180   }
181 
182   if (first_tag) *first_tag = counter->tag--;
183 
184   counter->refcount++; /* number of references to this comm */
185   ierr = PetscSpinlockUnlock(&PetscCommSpinLock);CHKERRQ(ierr);
186   PetscFunctionReturn(0);
187 }
188 
189 /*@C
190    PetscCommDestroy - Frees communicator.  Use in conjunction with PetscCommDuplicate().
191 
192    Collective on MPI_Comm
193 
194    Input Parameter:
195 .  comm - the communicator to free
196 
197    Level: developer
198 
199    Concepts: communicator^destroy
200 
201 .seealso:   PetscCommDuplicate()
202 @*/
203 PetscErrorCode  PetscCommDestroy(MPI_Comm *comm)
204 {
205   PetscErrorCode   ierr;
206   PetscCommCounter *counter;
207   PetscMPIInt      flg;
208   MPI_Comm         icomm = *comm,ocomm;
209   union {MPI_Comm comm; void *ptr;} ucomm;
210 
211   PetscFunctionBegin;
212   if (*comm == MPI_COMM_NULL) PetscFunctionReturn(0);
213   ierr = PetscSpinlockLock(&PetscCommSpinLock);CHKERRQ(ierr);
214   ierr = MPI_Attr_get(icomm,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
215   if (!flg) { /* not a PETSc comm, check if it has an inner comm */
216     ierr = MPI_Attr_get(icomm,Petsc_InnerComm_keyval,&ucomm,&flg);CHKERRQ(ierr);
217     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"MPI_Comm does not have tag/name counter nor does it have inner MPI_Comm");
218     icomm = ucomm.comm;
219     ierr = MPI_Attr_get(icomm,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
220     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Inner MPI_Comm does not have expected tag/name counter, problem with corrupted memory");
221   }
222 
223   counter->refcount--;
224 
225   if (!counter->refcount) {
226     /* if MPI_Comm has outer comm then remove reference to inner MPI_Comm from outer MPI_Comm */
227     ierr = MPI_Attr_get(icomm,Petsc_OuterComm_keyval,&ucomm,&flg);CHKERRQ(ierr);
228     if (flg) {
229       ocomm = ucomm.comm;
230       ierr = MPI_Attr_get(ocomm,Petsc_InnerComm_keyval,&ucomm,&flg);CHKERRQ(ierr);
231       if (flg) {
232         ierr = MPI_Attr_delete(ocomm,Petsc_InnerComm_keyval);CHKERRQ(ierr);
233       } else SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Outer MPI_Comm %ld does not have expected reference to inner comm %d, problem with corrupted memory",(long int)ocomm,(long int)icomm);
234     }
235 
236     ierr = PetscInfo1(0,"Deleting PETSc MPI_Comm %ld\n",(long)icomm);CHKERRQ(ierr);
237     ierr = MPI_Comm_free(&icomm);CHKERRQ(ierr);
238   }
239   *comm = MPI_COMM_NULL;
240   ierr = PetscSpinlockUnlock(&PetscCommSpinLock);CHKERRQ(ierr);
241   PetscFunctionReturn(0);
242 }
243 
244 /*@C
245     PetscObjectsListGetGlobalNumbering - computes a global numbering
246     of PetscObjects living on subcommunicators of a given communicator.
247 
248 
249     Collective on comm.
250 
251     Input Parameters:
252 +   comm    - MPI_Comm
253 .   len     - local length of objlist
254 -   objlist - a list of PETSc objects living on subcomms of comm and containing this comm rank
255               (subcomm ordering is assumed to be deadlock-free)
256 
257     Output Parameters:
258 +   count      - global number of distinct subcommunicators on objlist (may be > len)
259 -   numbering  - global numbers of objlist entries (allocated by user)
260 
261 
262     Level: developer
263 
264     Concepts: MPI subcomm^numbering
265 
266 @*/
267 PetscErrorCode  PetscObjectsListGetGlobalNumbering(MPI_Comm comm, PetscInt len, PetscObject *objlist, PetscInt *count, PetscInt *numbering)
268 {
269   PetscErrorCode ierr;
270   PetscInt       i, roots, offset;
271   PetscMPIInt    size, rank;
272 
273   PetscFunctionBegin;
274   PetscValidPointer(objlist,3);
275   if (!count && !numbering) PetscFunctionReturn(0);
276 
277   ierr  = MPI_Comm_size(comm, &size);CHKERRQ(ierr);
278   ierr  = MPI_Comm_rank(comm, &rank);CHKERRQ(ierr);
279   roots = 0;
280   for (i = 0; i < len; ++i) {
281     PetscMPIInt srank;
282     ierr = MPI_Comm_rank(objlist[i]->comm, &srank);CHKERRQ(ierr);
283     /* Am I the root of the i-th subcomm? */
284     if (!srank) ++roots;
285   }
286   if (count) {
287     /* Obtain the sum of all roots -- the global number of distinct subcomms. */
288     ierr = MPIU_Allreduce(&roots,count,1,MPIU_INT,MPI_SUM,comm);CHKERRQ(ierr);
289   }
290   if (numbering){
291     /* Introduce a global numbering for subcomms, initially known only by subcomm roots. */
292     /*
293       At each subcomm root number all of the subcomms it owns locally
294       and make it global by calculating the shift among all of the roots.
295       The roots are ordered using the comm ordering.
296     */
297     ierr    = MPI_Scan(&roots,&offset,1,MPIU_INT,MPI_SUM,comm);CHKERRQ(ierr);
298     offset -= roots;
299     /* Now we are ready to broadcast global subcomm numbers within each subcomm.*/
300     /*
301       This is where the assumption of a deadlock-free ordering of the subcomms is assumed:
302       broadcast is collective on the subcomm.
303     */
304     roots = 0;
305     for (i = 0; i < len; ++i) {
306       PetscMPIInt srank;
307       numbering[i] = offset + roots; /* only meaningful if !srank. */
308 
309       ierr = MPI_Comm_rank(objlist[i]->comm, &srank);CHKERRQ(ierr);
310       ierr = MPI_Bcast(numbering+i,1,MPIU_INT,0,objlist[i]->comm);CHKERRQ(ierr);
311       if (!srank) ++roots;
312     }
313   }
314   PetscFunctionReturn(0);
315 }
316 
317 struct _n_PetscCommShared {
318   PetscMPIInt *ranks;    /* global ranks of each rank in this shared memory comm */
319   PetscMPIInt size;
320   MPI_Comm    comm,scomm;
321 };
322 
323 #undef __FUNCT__
324 #define __FUNCT__ "Petsc_DelShared"
325 /*
326    Private routine to delete internal tag/name shared memory communicator when a communicator is freed.
327 
328    This is called by MPI, not by users. This is called by MPI_Comm_free() when the communicator that has this  data as an attribute is freed.
329 
330    Note: this is declared extern "C" because it is passed to MPI_Keyval_create()
331 
332 */
333 PETSC_EXTERN PetscMPIInt MPIAPI Petsc_DelShared(MPI_Comm comm,PetscMPIInt keyval,void *val,void *extra_state)
334 {
335   PetscErrorCode  ierr;
336   PetscCommShared scomm = (PetscCommShared)val;
337 
338   PetscFunctionBegin;
339   ierr = PetscInfo1(0,"Deleting shared subcommunicator in a MPI_Comm %ld\n",(long)comm);if (ierr) PetscFunctionReturn((PetscMPIInt)ierr);
340   ierr = MPI_Comm_free(&scomm->scomm);if (ierr) PetscFunctionReturn((PetscMPIInt)ierr);
341   ierr = PetscFree(scomm->ranks);if (ierr) PetscFunctionReturn((PetscMPIInt)ierr);
342   ierr = PetscFree(val);if (ierr) PetscFunctionReturn((PetscMPIInt)ierr);
343   PetscFunctionReturn(MPI_SUCCESS);
344 }
345 
346 #undef  __FUNCT__
347 #define __FUNCT__ "PetscCommSharedGet"
348 /*@C
349     PetscCommSharedGet - Given a PETSc communicator returns a communicator of all ranks that shared a common memory
350 
351 
352     Collective on comm.
353 
354     Input Parameter:
355 .   comm    - MPI_Comm
356 
357     Output Parameter:
358 .   scomm - the shared memory communicator object
359 
360     Level: developer
361 
362     Notes: This should be called only with an PetscCommDuplicate() communictor
363 
364            When used with MPICH, MPICH must be configured with --download-mpich-device=ch3:nemesis
365 
366     Concepts: MPI subcomm^numbering
367 
368 @*/
369 PetscErrorCode  PetscCommSharedGet(MPI_Comm comm,PetscCommShared *scomm)
370 {
371   PetscErrorCode   ierr;
372   MPI_Group        group,sgroup;
373   PetscMPIInt      *sranks,i,flg;
374   PetscCommCounter *counter;
375 
376   PetscFunctionBegin;
377   ierr = MPI_Attr_get(comm,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
378   if (!flg) SETERRQ(comm,PETSC_ERR_ARG_CORRUPT,"Bad MPI communicator supplied; must be a PETSc communicator");
379 
380   ierr = MPI_Attr_get(comm,Petsc_Shared_keyval,&scomm,&flg);CHKERRQ(ierr);
381   if (flg) PetscFunctionReturn(0);
382 
383   ierr        = PetscNew(scomm);CHKERRQ(ierr);
384   (*scomm)->comm = comm;
385 
386   ierr = MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED,0, MPI_INFO_NULL,&(*scomm)->scomm);CHKERRQ(ierr);
387 
388   ierr = MPI_Comm_size((*scomm)->scomm,&(*scomm)->size);CHKERRQ(ierr);
389   ierr = MPI_Comm_group(comm, &group);CHKERRQ(ierr);
390   ierr = MPI_Comm_group((*scomm)->scomm, &sgroup);CHKERRQ(ierr);
391   ierr = PetscMalloc1((*scomm)->size,&sranks);CHKERRQ(ierr);
392   ierr = PetscMalloc1((*scomm)->size,&(*scomm)->ranks);CHKERRQ(ierr);
393   for (i=0; i<(*scomm)->size; i++) sranks[i] = i;
394   ierr = MPI_Group_translate_ranks(sgroup, (*scomm)->size, sranks, group, (*scomm)->ranks);CHKERRQ(ierr);
395   ierr = PetscFree(sranks);CHKERRQ(ierr);
396   for (i=0; i<(*scomm)->size; i++) {
397     ierr = PetscInfo2(NULL,"Shared memory rank %d global rank %d\n",i,(*scomm)->ranks[i]);CHKERRQ(ierr);
398   }
399   ierr = MPI_Attr_put(comm,Petsc_Shared_keyval,*scomm);CHKERRQ(ierr);
400   PetscFunctionReturn(0);
401 }
402 
403 #undef  __FUNCT__
404 #define __FUNCT__ "PetscCommSharedGlobalToLocal"
405 /*@C
406     PetscCommSharedGlobalToLocal - Given a global rank returns the local rank in the shared communicator
407 
408 
409     Collective on comm.
410 
411     Input Parameters:
412 +   scomm - the shared memory communicator object
413 -   grank - the global rank
414 
415     Output Parameter:
416 .   lrank - the local rank, or -1 if it does not exist
417 
418     Level: developer
419 
420     Notes:
421            When used with MPICH, MPICH must be configured with --download-mpich-device=ch3:nemesis
422 
423     Developer Notes: Assumes the scomm->ranks[] is sorted
424 
425     It may be better to rewrite this to map multiple global ranks to local in the same function call
426 
427     Concepts: MPI subcomm^numbering
428 
429 @*/
430 PetscErrorCode  PetscCommSharedGlobalToLocal(PetscCommShared scomm,PetscMPIInt grank,PetscMPIInt *lrank)
431 {
432   PetscMPIInt      low,high,t,i;
433 
434   PetscFunctionBegin;
435   *lrank = -1;
436   if (grank < scomm->ranks[0]) PetscFunctionReturn(0);
437   if (grank > scomm->ranks[scomm->size-1]) PetscFunctionReturn(0);
438   low  = 0;
439   high = scomm->size;
440   while (high-low > 5) {
441     t = (low+high)/2;
442     if (scomm->ranks[t] > grank) high = t;
443     else low = t;
444   }
445   for (i=low; i<high; i++) {
446     if (scomm->ranks[i] > grank) PetscFunctionReturn(0);
447     if (scomm->ranks[i] == grank) {
448       *lrank = i;
449       PetscFunctionReturn(0);
450     }
451   }
452   PetscFunctionReturn(0);
453 }
454 
455 #undef  __FUNCT__
456 #define __FUNCT__ "PetscCommSharedGetComm"
457 /*@C
458     PetscCommSharedGetComm - Returns the MPI communicator that represents all processes with common shared memory
459 
460 
461     Collective on comm.
462 
463     Input Parameter:
464 .   scomm - PetscCommShared object obtained with PetscCommSharedGet()
465 
466     Output Parameter:
467 .   comm - the MPI communicator
468 
469     Level: developer
470 
471 @*/
472 PetscErrorCode  PetscCommSharedGetComm(PetscCommShared scomm,MPI_Comm *comm)
473 {
474   PetscFunctionBegin;
475   *comm = scomm->scomm;
476   PetscFunctionReturn(0);
477 }
478