Actual source code: garbage.c
1: #include <petsc/private/garbagecollector.h>
3: /* Fetches garbage hashmap from communicator */
4: static PetscErrorCode GarbageGetHMap_Private(MPI_Comm comm, PetscGarbage *garbage)
5: {
6: PetscMPIInt flag;
7: PetscHMapObj garbage_map;
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: }
20: /*@C
21: PetscObjectDelayedDestroy - Adds an object to a data structure for
22: later destruction.
24: Not Collective
26: Input Parameter:
27: . obj - object to be destroyed
29: Level: developer
31: Notes:
32: Analogue to `PetscObjectDestroy()` for use in managed languages.
34: A PETSc object is given a creation index at initialisation based on
35: the communicator it was created on and the order in which it is
36: created. When this function is passed a PETSc object, a pointer to
37: the object is stashed on a garbage dictionary (`PetscHMapObj`) which is
38: keyed by its creation index.
40: Objects stashed on this garbage dictionary can later be destroyed
41: with a call to `PetscGarbageCleanup()`.
43: This function is intended for use with managed languages such as
44: Python or Julia, which may not destroy objects in a deterministic
45: order.
47: .seealso: `PetscGarbageCleanup()`, `PetscObjectDestroy()`
48: @*/
49: PetscErrorCode PetscObjectDelayedDestroy(PetscObject *obj)
50: {
51: MPI_Comm petsc_comm;
52: PetscInt count;
53: PetscGarbage garbage;
55: PetscFunctionBegin;
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: }
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;
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: }
105: *lena = counter;
106: PetscFunctionReturn(PETSC_SUCCESS);
107: }
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;
114: seta = (PetscInt64 *)inoutset;
115: setb = (PetscInt64 *)inset;
117: PetscCallAbort(PETSC_COMM_SELF, GarbageKeySortedIntersect_Private(&seta[1], (PetscInt *)&seta[0], &setb[1], (PetscInt)setb[0]));
118: }
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;
127: PetscFunctionBegin;
128: /* Sort keys first for use with `GarbageKeySortedIntersect_Private()`*/
129: PetscCall(PetscSortInt64(*entries, set));
131: /* Get the maximum size of all key sets */
132: PetscCall(MPIU_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];
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));
143: /* Perform custom intersect reduce operation over sets */
144: PetscCallMPI(MPI_Allreduce(sendset, recvset, 1, keyset_type, Petsc_Garbage_SetIntersectOp, comm));
146: PetscCallMPI(MPI_Type_free(&keyset_type));
148: *entries = (PetscInt)recvset[0];
149: for (ii = 0; ii < *entries; ii++) set[ii] = recvset[ii + 1];
151: PetscCall(PetscFree(sendset));
152: PetscCall(PetscFree(recvset));
153: PetscFunctionReturn(PETSC_SUCCESS);
154: }
156: /*@C
157: PetscGarbageCleanup - Destroys objects placed in the garbage by
158: `PetscObjectDelayedDestroy()`.
160: Collective
162: Input Parameter:
163: . comm - MPI communicator over which to perform collective cleanup
165: Level: developer
167: Notes:
168: Implements a collective garbage collection.
169: A per- MPI communicator garbage dictionary is created to store
170: references to objects destroyed using `PetscObjectDelayedDestroy()`.
171: Objects that appear in this dictionary on all MPI processes can be destroyed
172: by calling `PetscGarbageCleanup()`.
174: This is done as follows:
175: 1. Keys of the garbage dictionary, which correspond to the creation
176: indices of the objects stashed, are sorted.
177: 2. A collective intersection of dictionary keys is performed by all
178: ranks in the communicator.
179: 3. The intersection is broadcast back to all ranks in the
180: communicator.
181: 4. The objects on the dictionary are collectively destroyed in
182: creation index order using a call to PetscObjectDestroy().
184: This function is intended for use with managed languages such as
185: Python or Julia, which may not destroy objects in a deterministic
186: order.
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;
197: PetscFunctionBegin;
198: /* Duplicate comm to prevent it being cleaned up by PetscObjectDestroy() */
199: PetscCall(PetscCommDuplicate(comm, &comm, NULL));
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));
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));
212: /* Gather and intersect */
213: PetscCall(GarbageKeyAllReduceIntersect_Private(comm, keys, &entries));
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));
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: }
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;
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));
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));
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));
275: PetscCall(PetscFree(keys));
276: PetscCall(PetscCommDestroy(&comm));
277: PetscFunctionReturn(PETSC_SUCCESS);
278: }