xref: /petsc/src/sys/objects/garbage.c (revision 21e3ffae2f3b73c0bd738cf6d0a809700fc04bb0)
1 #include <petsc/private/garbagecollector.h>
2 
3 /* Fetches garbage hashmap from communicator */
4 static PetscErrorCode GarbageGetHMap_Private(MPI_Comm comm, PetscGarbage *garbage)
5 {
6   PetscMPIInt  flag;
7   PetscHMapObj garbage_map;
8 
9   PetscFunctionBegin;
10   PetscCallMPI(MPI_Comm_get_attr(comm, Petsc_Garbage_HMap_keyval, garbage, &flag));
11   if (!flag) {
12     /* No garbage,create one */
13     PetscCall(PetscHMapObjCreate(&garbage_map));
14     garbage->map = garbage_map;
15     PetscCallMPI(MPI_Comm_set_attr(comm, Petsc_Garbage_HMap_keyval, garbage->ptr));
16   }
17   PetscFunctionReturn(PETSC_SUCCESS);
18 }
19 
20 /*@C
21     PetscObjectDelayedDestroy - Adds an object to a data structure for
22     later destruction.
23 
24     Not Collective
25 
26     Input Parameters:
27 .   obj - object to be destroyed
28 
29     Notes:
30     Analogue to `PetscObjectDestroy()` for use in managed languages.
31 
32     A PETSc object is given a creation index at initialisation based on
33     the communicator it was created on and the order in which it is
34     created. When this function is passed a PETSc object, a pointer to
35     the object is stashed on a garbage dictionary (PetscHMapObj) which is
36     keyed by its creation index.
37 
38     Objects stashed on this garbage dictionary can later be destroyed
39     with a call to `PetscGarbageCleanup()`.
40 
41     This function is intended for use with managed languages such as
42     Python or Julia, which may not destroy objects in a deterministic
43     order.
44 
45     Level: developer
46 
47 .seealso: `PetscGarbageCleanup()`
48 @*/
49 PetscErrorCode PetscObjectDelayedDestroy(PetscObject *obj)
50 {
51   MPI_Comm     petsc_comm;
52   PetscInt     count;
53   PetscGarbage garbage;
54 
55   PetscFunctionBegin;
56   PetscValidPointer(obj, 1);
57   /* Don't stash NULL pointers */
58   if (*obj != NULL) {
59     /* Elaborate check for getting non-cyclic reference counts */
60     if (!(*obj)->non_cyclic_references) {
61       count = --(*obj)->refct;
62     } else {
63       PetscCall((*obj)->non_cyclic_references(*obj, &count));
64       --count;
65       --(*obj)->refct;
66     }
67     /* Only stash if the (non-cyclic) reference count hits 0 */
68     if (count == 0) {
69       (*obj)->refct = 1;
70       PetscCall(PetscObjectGetComm(*obj, &petsc_comm));
71       PetscCall(GarbageGetHMap_Private(petsc_comm, &garbage));
72       PetscCall(PetscHMapObjSet(garbage.map, (*obj)->cidx, *obj));
73     }
74   }
75   *obj = NULL;
76   PetscFunctionReturn(PETSC_SUCCESS);
77 }
78 
79 /* Performs the intersection of 2 sorted arrays seta and setb of lengths
80    lena and lenb respectively,returning the result in seta and lena
81    This is an O(n) operation */
82 static PetscErrorCode GarbageKeySortedIntersect_Private(PetscInt64 seta[], PetscInt *lena, PetscInt64 setb[], PetscInt lenb)
83 {
84   /* The arrays seta and setb MUST be sorted! */
85   PetscInt ii, jj = 0, counter = 0;
86 
87   PetscFunctionBegin;
88   if (PetscDefined(USE_DEBUG)) {
89     PetscBool sorted = PETSC_FALSE;
90     /* In debug mode check whether the array are sorted */
91     PetscCall(PetscSortedInt64(*lena, seta, &sorted));
92     PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Provided array in argument 1 is not sorted");
93     PetscCall(PetscSortedInt64(lenb, setb, &sorted));
94     PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Provided array in argument 3 is not sorted");
95   }
96   for (ii = 0; ii < *lena; ii++) {
97     while (jj < lenb && seta[ii] > setb[jj]) { jj++; }
98     if (jj >= lenb) break;
99     if (seta[ii] == setb[jj]) {
100       seta[counter] = seta[ii];
101       counter++;
102     }
103   }
104 
105   *lena = counter;
106   PetscFunctionReturn(PETSC_SUCCESS);
107 }
108 
109 /* Wrapper to create MPI reduce operator for set intersection */
110 void PetscGarbageKeySortedIntersect(void *inset, void *inoutset, PetscMPIInt *length, MPI_Datatype *dtype)
111 {
112   PetscInt64 *seta, *setb;
113 
114   seta = (PetscInt64 *)inoutset;
115   setb = (PetscInt64 *)inset;
116 
117   PetscCallAbort(PETSC_COMM_SELF, GarbageKeySortedIntersect_Private(&seta[1], (PetscInt *)&seta[0], &setb[1], (PetscInt)setb[0]));
118 }
119 
120 /* Performs a collective allreduce intersection of one array per rank */
121 PetscErrorCode GarbageKeyAllReduceIntersect_Private(MPI_Comm comm, PetscInt64 *set, PetscInt *entries)
122 {
123   PetscInt     ii, max_entries;
124   PetscInt64  *sendset, *recvset;
125   MPI_Datatype keyset_type;
126 
127   PetscFunctionBegin;
128   /* Sort keys first for use with `GarbageKeySortedIntersect_Private()`*/
129   PetscCall(PetscSortInt64(*entries, set));
130 
131   /* Get the maximum size of all key sets */
132   PetscCallMPI(MPI_Allreduce(entries, &max_entries, 1, MPIU_INT, MPI_MAX, comm));
133   PetscCall(PetscMalloc1(max_entries + 1, &sendset));
134   PetscCall(PetscMalloc1(max_entries + 1, &recvset));
135   sendset[0] = (PetscInt64)*entries;
136   for (ii = 1; ii < *entries + 1; ii++) sendset[ii] = set[ii - 1];
137 
138   /* Create a custom data type to hold the set */
139   PetscCallMPI(MPI_Type_contiguous(max_entries + 1, MPIU_INT64, &keyset_type));
140   /* PetscCallMPI(MPI_Type_set_name(keyset_type,"PETSc garbage key set type")); */
141   PetscCallMPI(MPI_Type_commit(&keyset_type));
142 
143   /* Perform custom intersect reduce operation over sets */
144   PetscCallMPI(MPI_Allreduce(sendset, recvset, 1, keyset_type, Petsc_Garbage_SetIntersectOp, comm));
145 
146   PetscCallMPI(MPI_Type_free(&keyset_type));
147 
148   *entries = (PetscInt)recvset[0];
149   for (ii = 0; ii < *entries; ii++) set[ii] = recvset[ii + 1];
150 
151   PetscCall(PetscFree(sendset));
152   PetscCall(PetscFree(recvset));
153   PetscFunctionReturn(PETSC_SUCCESS);
154 }
155 
156 /*@C
157     PetscGarbageCleanup - Destroys objects placed in the garbage by
158     PetscObjectDelayedDestroy().
159 
160     Collective
161 
162     Input Parameters:
163 .   comm      - communicator over which to perform collective cleanup
164 
165     Notes:
166     Implements a collective garbage collection.
167     A per- MPI communicator garbage dictionary is created to store
168     references to objects destroyed using PetscObjectDelayedDestroy().
169     Objects that appear in this dictionary on all ranks can be destroyed
170     by calling PetscGarbageCleanup().
171 
172     This is done as follows:
173     1.  Keys of the garbage dictionary, which correspond to the creation
174         indices of the objects stashed, are sorted.
175     2.  A collective intersection of dictionary keys is performed by all
176         ranks in the communicator.
177     3.  The intersection is broadcast back to all ranks in the
178         communicator.
179     4.  The objects on the dictionary are collectively destroyed in
180         creation index order using a call to PetscObjectDestroy().
181 
182     This function is intended for use with managed languages such as
183     Python or Julia, which may not destroy objects in a deterministic
184     order.
185 
186     Level: developer
187 
188 .seealso: PetscObjectDelayedDestroy()
189 @*/
190 PetscErrorCode PetscGarbageCleanup(MPI_Comm comm)
191 {
192   PetscInt     ii, entries, offset;
193   PetscInt64  *keys;
194   PetscObject  obj;
195   PetscGarbage garbage;
196 
197   PetscFunctionBegin;
198   /* Duplicate comm to prevent it being cleaned up by PetscObjectDestroy() */
199   PetscCall(PetscCommDuplicate(comm, &comm, NULL));
200 
201   /* Grab garbage from comm and remove it
202    this avoids calling PetscCommDestroy() and endlessly recursing */
203   PetscCall(GarbageGetHMap_Private(comm, &garbage));
204   PetscCallMPI(MPI_Comm_delete_attr(comm, Petsc_Garbage_HMap_keyval));
205 
206   /* Get keys from garbage hash map */
207   PetscCall(PetscHMapObjGetSize(garbage.map, &entries));
208   PetscCall(PetscMalloc1(entries, &keys));
209   offset = 0;
210   PetscCall(PetscHMapObjGetKeys(garbage.map, &offset, keys));
211 
212   /* Gather and intersect */
213   PetscCall(GarbageKeyAllReduceIntersect_Private(comm, keys, &entries));
214 
215   /* Collectively destroy objects objects that appear in garbage in
216      creation index order */
217   for (ii = 0; ii < entries; ii++) {
218     PetscCall(PetscHMapObjGet(garbage.map, keys[ii], &obj));
219     PetscCall(PetscObjectDestroy(&obj));
220     PetscCall(PetscFree(obj));
221     PetscCall(PetscHMapObjDel(garbage.map, keys[ii]));
222   }
223   PetscCall(PetscFree(keys));
224 
225   /* Put garbage back */
226   PetscCallMPI(MPI_Comm_set_attr(comm, Petsc_Garbage_HMap_keyval, garbage.ptr));
227   PetscCall(PetscCommDestroy(&comm));
228   PetscFunctionReturn(PETSC_SUCCESS);
229 }
230 
231 /* Utility function for printing the contents of the garbage on a given comm */
232 PetscErrorCode PetscGarbageView(MPI_Comm comm, PetscViewer viewer)
233 {
234   char         text[64];
235   PetscInt     ii, entries, offset;
236   PetscInt64  *keys;
237   PetscObject  obj;
238   PetscGarbage garbage;
239   PetscMPIInt  rank;
240 
241   PetscFunctionBegin;
242   PetscCall(PetscPrintf(comm, "PETSc garbage on "));
243   if (comm == PETSC_COMM_WORLD) {
244     PetscCall(PetscPrintf(comm, "PETSC_COMM_WORLD\n"));
245   } else if (comm == PETSC_COMM_SELF) {
246     PetscCall(PetscPrintf(comm, "PETSC_COMM_SELF\n"));
247   } else {
248     PetscCall(PetscPrintf(comm, "UNKNOWN_COMM\n"));
249   }
250   PetscCall(PetscCommDuplicate(comm, &comm, NULL));
251   PetscCall(GarbageGetHMap_Private(comm, &garbage));
252 
253   /* Get keys from garbage hash map and sort */
254   PetscCall(PetscHMapObjGetSize(garbage.map, &entries));
255   PetscCall(PetscMalloc1(entries, &keys));
256   offset = 0;
257   PetscCall(PetscHMapObjGetKeys(garbage.map, &offset, keys));
258 
259   /* Pretty print entries in a table */
260   PetscCallMPI(MPI_Comm_rank(comm, &rank));
261   PetscCall(PetscSynchronizedPrintf(comm, "Rank %i:: ", rank));
262   PetscCall(PetscFormatConvert("Total entries: %D\n", text));
263   PetscCall(PetscSynchronizedPrintf(comm, text, entries));
264   if (entries) {
265     PetscCall(PetscSynchronizedPrintf(comm, "| Key   | Type                   | Name                             | Object ID |\n"));
266     PetscCall(PetscSynchronizedPrintf(comm, "|-------|------------------------|----------------------------------|-----------|\n"));
267   }
268   for (ii = 0; ii < entries; ii++) {
269     PetscCall(PetscHMapObjGet(garbage.map, keys[ii], &obj));
270     PetscCall(PetscFormatConvert("| %5" PetscInt64_FMT " | %-22s | %-32s | %6D    |\n", text));
271     PetscCall(PetscSynchronizedPrintf(comm, text, keys[ii], obj->class_name, obj->description, obj->id));
272   }
273   PetscCall(PetscSynchronizedFlush(comm, PETSC_STDOUT));
274 
275   PetscCall(PetscFree(keys));
276   PetscCall(PetscCommDestroy(&comm));
277   PetscFunctionReturn(PETSC_SUCCESS);
278 }
279