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: }