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