xref: /petsc/src/sys/objects/tagm.c (revision 848cd4f18f500d2ab14b6bcb001aa20532e9cebe)
1 
2 /*
3       Some PETSc utilites
4 */
5 #include <petscsys.h>             /*I    "petscsys.h"   I*/
6 #if defined(PETSC_HAVE_STDLIB_H)
7 #include <stdlib.h>
8 #endif
9 
10 #include <petsc-private/threadcommimpl.h>
11 /* ---------------------------------------------------------------- */
12 /*
13    A simple way to manage tags inside a communicator.
14 
15    It uses the attributes to determine if a new communicator
16       is needed and to store the available tags.
17 
18 */
19 
20 
21 #undef __FUNCT__
22 #define __FUNCT__ "PetscObjectGetNewTag"
23 /*@C
24     PetscObjectGetNewTag - Gets a unique new tag from a PETSc object. All
25     processors that share the object MUST call this routine EXACTLY the same
26     number of times.  This tag should only be used with the current objects
27     communicator; do NOT use it with any other MPI communicator.
28 
29     Collective on PetscObject
30 
31     Input Parameter:
32 .   obj - the PETSc object; this must be cast with a (PetscObject), for example,
33          PetscObjectGetNewTag((PetscObject)mat,&tag);
34 
35     Output Parameter:
36 .   tag - the new tag
37 
38     Level: developer
39 
40     Concepts: tag^getting
41     Concepts: message tag^getting
42     Concepts: MPI message tag^getting
43 
44 .seealso: PetscCommGetNewTag()
45 @*/
46 PetscErrorCode  PetscObjectGetNewTag(PetscObject obj,PetscMPIInt *tag)
47 {
48   PetscErrorCode ierr;
49 
50   PetscFunctionBegin;
51   ierr = PetscCommGetNewTag(obj->comm,tag);CHKERRQ(ierr);
52   PetscFunctionReturn(0);
53 }
54 
55 #undef __FUNCT__
56 #define __FUNCT__ "PetscCommGetNewTag"
57 /*@
58     PetscCommGetNewTag - Gets a unique new tag from a PETSc communicator. All
59     processors that share the communicator MUST call this routine EXACTLY the same
60     number of times.  This tag should only be used with the current objects
61     communicator; do NOT use it with any other MPI communicator.
62 
63     Collective on comm
64 
65     Input Parameter:
66 .   comm - the MPI communicator
67 
68     Output Parameter:
69 .   tag - the new tag
70 
71     Level: developer
72 
73     Concepts: tag^getting
74     Concepts: message tag^getting
75     Concepts: MPI message tag^getting
76 
77 .seealso: PetscObjectGetNewTag(), PetscCommDuplicate()
78 @*/
79 PetscErrorCode  PetscCommGetNewTag(MPI_Comm comm,PetscMPIInt *tag)
80 {
81   PetscErrorCode   ierr;
82   PetscCommCounter *counter;
83   PetscMPIInt      *maxval,flg;
84 
85   PetscFunctionBegin;
86   PetscValidIntPointer(tag,2);
87 
88   ierr = MPI_Attr_get(comm,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
89   if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Bad MPI communicator supplied; must be a PETSc communicator");
90 
91   if (counter->tag < 1) {
92     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",counter->refcount);CHKERRQ(ierr);
93     ierr = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,&maxval,&flg);CHKERRQ(ierr);
94     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
95     counter->tag = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
96   }
97 
98   *tag = counter->tag--;
99 #if defined(PETSC_USE_DEBUG)
100   /*
101      Hanging here means that some processes have called PetscCommGetNewTag() and others have not.
102   */
103   ierr = MPI_Barrier(comm);CHKERRQ(ierr);
104 #endif
105   PetscFunctionReturn(0);
106 }
107 
108 #undef __FUNCT__
109 #define __FUNCT__ "PetscCommDuplicate"
110 /*@C
111   PetscCommDuplicate - Duplicates the communicator only if it is not already a PETSc communicator.
112 
113   Collective on MPI_Comm
114 
115   Input Parameters:
116 . comm_in - Input communicator
117 
118   Output Parameters:
119 + comm_out - Output communicator.  May be comm_in.
120 - first_tag - Tag available that has not already been used with this communicator (you may
121               pass in PETSC_NULL if you do not need a tag)
122 
123   PETSc communicators are just regular MPI communicators that keep track of which
124   tags have been used to prevent tag conflict. If you pass a non-PETSc communicator into
125   a PETSc creation routine it will attach a private communicator for use in the objects communications.
126   The internal MPI_Comm is used to perform all the MPI calls for PETSc, the outer MPI_Comm is a user
127   level MPI_Comm that may be performing communication for the user or other library and so IS NOT used by PETSc.
128 
129   Level: developer
130 
131   Concepts: communicator^duplicate
132 
133 .seealso: PetscObjectGetNewTag(), PetscCommGetNewTag(), PetscCommDestroy()
134 @*/
135 PetscErrorCode  PetscCommDuplicate(MPI_Comm comm_in,MPI_Comm *comm_out,PetscMPIInt* first_tag)
136 {
137   PetscErrorCode   ierr;
138   PetscCommCounter *counter;
139   PetscMPIInt      *maxval,flg;
140 #if defined(PETSC_THREADCOMM_ACTIVE)
141   PetscThreadComm  tcomm;
142 #endif
143 
144   PetscFunctionBegin;
145   ierr = MPI_Attr_get(comm_in,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
146 
147   if (!flg) {  /* this is NOT a PETSc comm */
148     void *ptr;
149     /* check if this communicator has a PETSc communicator imbedded in it */
150     ierr = MPI_Attr_get(comm_in,Petsc_InnerComm_keyval,&ptr,&flg);CHKERRQ(ierr);
151     if (!flg) {
152       /* This communicator is not yet known to this system, so we duplicate it and make an internal communicator */
153       ierr       = MPI_Comm_dup(comm_in,comm_out);CHKERRQ(ierr);
154       ierr       = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,&maxval,&flg);CHKERRQ(ierr);
155       if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
156       ierr = PetscMalloc(sizeof(PetscCommCounter),&counter);CHKERRQ(ierr);
157       counter->tag       = *maxval;
158       counter->refcount  = 0;
159       counter->namecount = 0;
160       ierr = MPI_Attr_put(*comm_out,Petsc_Counter_keyval,counter);CHKERRQ(ierr);
161       ierr = PetscInfo3(0,"Duplicating a communicator %ld %ld max tags = %d\n",(long)comm_in,(long)*comm_out,*maxval);CHKERRQ(ierr);
162 
163       /* save PETSc communicator inside user communicator, so we can get it next time */
164       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
165       ierr = PetscMemcpy(&ptr,comm_out,sizeof(MPI_Comm));CHKERRQ(ierr);
166       ierr = MPI_Attr_put(comm_in,Petsc_InnerComm_keyval,ptr);CHKERRQ(ierr);
167       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
168       ierr = PetscMemcpy(&ptr,&comm_in,sizeof(MPI_Comm));CHKERRQ(ierr);
169       ierr = MPI_Attr_put(*comm_out,Petsc_OuterComm_keyval,ptr);CHKERRQ(ierr);
170     } else {
171       /* pull out the inner MPI_Comm and hand it back to the caller */
172       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
173       ierr = PetscMemcpy(comm_out,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
174       ierr = MPI_Attr_get(*comm_out,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
175       if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Inner PETSc communicator does not have its tag/name counter attribute set");
176       ierr = PetscInfo2(0,"Using internal PETSc communicator %ld %ld\n",(long)comm_in,(long)*comm_out);CHKERRQ(ierr);
177     }
178   } else {
179     *comm_out = comm_in;
180   }
181 
182 #if defined(PETSC_USE_DEBUG)
183   /*
184      Hanging here means that some processes have called PetscCommDuplicate() and others have not.
185      This likley means that a subset of processes in a MPI_Comm have attempted to create a PetscObject!
186      ALL processes that share a communicator MUST shared objects created from that communicator.
187   */
188   ierr = MPI_Barrier(comm_in);CHKERRQ(ierr);
189 #endif
190 
191   if (counter->tag < 1) {
192     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",counter->refcount);CHKERRQ(ierr);
193     ierr = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,&maxval,&flg);CHKERRQ(ierr);
194     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
195     counter->tag = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
196   }
197 
198   if (first_tag) {
199     *first_tag = counter->tag--;
200   }
201 
202 #if defined(PETSC_THREADCOMM_ACTIVE)
203   /* Only the main thread updates counter->refcount */
204   ierr = MPI_Attr_get(*comm_out,Petsc_ThreadComm_keyval,(PetscThreadComm*)&tcomm,&flg);CHKERRQ(ierr);
205   if (flg) {
206     PetscInt trank;
207     trank = PetscThreadCommGetRank(tcomm);
208     if (!trank) counter->refcount++; /* number of references to this comm */
209   } else counter->refcount++;
210 #else
211   counter->refcount++;
212 #endif
213 
214   PetscFunctionReturn(0);
215 }
216 
217 #undef __FUNCT__
218 #define __FUNCT__ "PetscCommDestroy"
219 /*@C
220    PetscCommDestroy - Frees communicator.  Use in conjunction with PetscCommDuplicate().
221 
222    Collective on MPI_Comm
223 
224    Input Parameter:
225 .  comm - the communicator to free
226 
227    Level: developer
228 
229    Concepts: communicator^destroy
230 
231 .seealso:   PetscCommDuplicate()
232 @*/
233 PetscErrorCode  PetscCommDestroy(MPI_Comm *comm)
234 {
235   PetscErrorCode   ierr;
236   PetscCommCounter *counter;
237   PetscMPIInt      flg;
238   MPI_Comm         icomm = *comm,ocomm;
239   void             *ptr;
240 #if defined(PETSC_THREADCOMM_ACTIVE)
241   PetscThreadComm  tcomm;
242 #endif
243 
244   PetscFunctionBegin;
245   ierr = MPI_Attr_get(icomm,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
246   if (!flg) { /* not a PETSc comm, check if it has an inner comm */
247     ierr  = MPI_Attr_get(icomm,Petsc_InnerComm_keyval,&ptr,&flg);CHKERRQ(ierr);
248     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");
249     /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
250     ierr = PetscMemcpy(&icomm,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
251     ierr = MPI_Attr_get(icomm,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
252     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Inner MPI_Comm does not have expected tag/name counter, problem with corrupted memory");
253   }
254 
255 #if defined(PETSC_THREADCOMM_ACTIVE)
256   /* Only the main thread updates counter->refcount */
257   ierr = MPI_Attr_get(icomm,Petsc_ThreadComm_keyval,(PetscThreadComm*)&tcomm,&flg);CHKERRQ(ierr);
258   if(flg) {
259     PetscInt trank;
260     trank = PetscThreadCommGetRank(tcomm);
261     /* Only thread rank 0 updates the counter */
262     if(!trank) counter->refcount--;
263   } else counter->refcount--;
264 #else
265   counter->refcount--;
266 #endif
267 
268   if (!counter->refcount) {
269     /* if MPI_Comm has outer comm then remove reference to inner MPI_Comm from outer MPI_Comm */
270     ierr  = MPI_Attr_get(icomm,Petsc_OuterComm_keyval,&ptr,&flg);CHKERRQ(ierr);
271     if (flg) {
272       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
273       ierr = PetscMemcpy(&ocomm,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
274       ierr  = MPI_Attr_get(ocomm,Petsc_InnerComm_keyval,&ptr,&flg);CHKERRQ(ierr);
275       if (flg) {
276         ierr = MPI_Attr_delete(ocomm,Petsc_InnerComm_keyval);CHKERRQ(ierr);
277       } 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);
278     }
279 
280     ierr = PetscInfo1(0,"Deleting PETSc MPI_Comm %ld\n",(long)icomm);CHKERRQ(ierr);
281     ierr = MPI_Comm_free(&icomm);CHKERRQ(ierr);
282   }
283   *comm = 0;
284   PetscFunctionReturn(0);
285 }
286 
287 #undef  __FUNCT__
288 #define __FUNCT__ "PetscObjectsGetGlobalNumbering"
289 /*@C
290     PetscObjectsGetGlobalNumbering - computes a global numbering
291     of PetscObjects living on subcommunicators of a given communicator.
292     This results in a deadlock-free ordering of the subcommunicators
293     and, hence, the objects.
294 
295 
296     Collective on comm.
297 
298     Input Parameters:
299 +   comm    - MPI_Comm
300 .   len     - length of objlist
301 -   objlist - a list of PETSc objects living on subcommunicators of comm
302                 (subcommunicator ordering is assumed to be deadlock-free)
303 
304     Output Parameters:
305 +   count      - number of globally-distinct subcommunicators on objlist
306 .   numbering  - global numbers of objlist entries (allocated by user)
307 
308 
309     Level: developer
310 
311     Concepts: MPI subcomm^numbering
312 
313 @*/
314 PetscErrorCode  PetscObjectsGetGlobalNumbering(MPI_Comm comm, PetscInt len, PetscObject *objlist, PetscInt *count, PetscInt *numbering)
315 {
316   PetscErrorCode ierr;
317   PetscInt i, roots, offset;
318   PetscMPIInt size, rank, r0 = 0, r;
319   MPI_Group group, subgroup;
320   PetscFunctionBegin;
321   PetscValidPointer(objlist,3);
322   PetscValidPointer(count,4);
323   PetscValidPointer(numbering,5);
324   /* Identify comm ranks of subcomm roots.  What makes it work is that MPI_Group_translate_ranks is not collective. */
325   ierr = MPI_Comm_size(comm, &size);                   CHKERRQ(ierr);
326   ierr = MPI_Comm_rank(comm, &rank);                   CHKERRQ(ierr);
327   ierr = MPI_Comm_group(comm, &group);                 CHKERRQ(ierr);
328   roots = 0;
329   for(i = 0; i < len; ++i) {
330     ierr = MPI_Comm_group(objlist[i]->comm, &subgroup);         CHKERRQ(ierr);
331     ierr = MPI_Group_translate_ranks(subgroup, 1,&r0,group,&r); CHKERRQ(ierr);
332     if(r == MPI_UNDEFINED) SETERRQ1(objlist[i]->comm, PETSC_ERR_ARG_WRONG, "Cannot determine global rank of the root of local subcomm %D", i); CHKERRQ(ierr);
333     /* Am I the root of the i-th subcomm? */
334     if(r == rank) ++roots;
335   }
336   /* Obtain the sum of all roots -- the global number of distinct subcomms. */
337     ierr   = MPI_Allreduce((void*)&roots,(void*)count,1,MPIU_INT,MPI_SUM,comm); CHKERRQ(ierr);
338   /* Now introduce a global numbering for subcomms, initially known only by subcomm roots. */
339   /*
340    At the subcomm roots number the subcomms in the subcomm-root local manner,
341    and make it global by calculating the shift.
342    */
343   ierr = MPI_Scan((PetscMPIInt*)&roots,(PetscMPIInt*)&offset,1,MPI_INT,MPI_SUM,comm); CHKERRQ(ierr);
344   offset -= roots;
345   /* Now we are ready to communicate global subcomm numbers from subcomm roots to the other subcomm ranks.*/
346   /*
347    Communication proceeds one subcomm at a time: here the deadlock-free ordering assumption is used.
348    The reason for this is that getting a tag on each subcomm is collective.  An alternative would be to use
349    one sided primitives, but only with MPI-3.
350    */
351   for(i = 0; i < len; ++i) {
352     PetscInt num = offset + i;
353     PetscMPIInt srank, ssize, tag, j;
354     MPI_Request *sreq, rreq;
355     /* Subcomm rank and size. */
356     ierr = MPI_Comm_size(objlist[i]->comm, &ssize); CHKERRQ(ierr);
357     ierr = MPI_Comm_rank(objlist[i]->comm, &srank); CHKERRQ(ierr);
358     /* Obtain a subcomm tag.  */
359     ierr = PetscCommGetNewTag(objlist[i]->comm, &tag); CHKERRQ(ierr);
360     /* Post the receive first. */
361     ierr = MPI_Irecv((PetscMPIInt*)(numbering+i),1,MPI_INT,0,tag,objlist[i]->comm, &rreq); CHKERRQ(ierr);
362     /* Only the subcomm root posts the sends. */
363     if(!srank) {
364       ierr = PetscMalloc(sizeof(MPI_Request)*ssize, &sreq); CHKERRQ(ierr);
365       for(j = 0; j < ssize; ++j) {
366         ierr = MPI_Isend((PetscMPIInt*)&num,1,MPI_INT,j,tag,objlist[i]->comm,sreq+j); CHKERRQ(ierr);
367       }
368     }
369     /* Now we wait on receives. */
370     ierr = MPI_Wait(&rreq, MPI_STATUS_IGNORE); CHKERRQ(ierr);
371     /* And finally we wait on the sends. */
372     if(!srank) {
373       ierr = MPI_Waitall(ssize,sreq,MPI_STATUSES_IGNORE); CHKERRQ(ierr);
374       ierr = PetscFree(sreq); CHKERRQ(ierr);
375     }
376   }
377 
378   PetscFunctionReturn(0);
379 }
380