162e5d2d2SJDBetteridge #include <petsc/private/garbagecollector.h> 262e5d2d2SJDBetteridge 362e5d2d2SJDBetteridge /* Fetches garbage hashmap from communicator */ 462e5d2d2SJDBetteridge static PetscErrorCode GarbageGetHMap_Private(MPI_Comm comm, PetscGarbage *garbage) 562e5d2d2SJDBetteridge { 662e5d2d2SJDBetteridge PetscMPIInt flag; 762e5d2d2SJDBetteridge PetscHMapObj garbage_map; 862e5d2d2SJDBetteridge 962e5d2d2SJDBetteridge PetscFunctionBegin; 1062e5d2d2SJDBetteridge PetscCallMPI(MPI_Comm_get_attr(comm, Petsc_Garbage_HMap_keyval, garbage, &flag)); 1162e5d2d2SJDBetteridge if (!flag) { 1262e5d2d2SJDBetteridge /* No garbage,create one */ 1362e5d2d2SJDBetteridge PetscCall(PetscHMapObjCreate(&garbage_map)); 1462e5d2d2SJDBetteridge garbage->map = garbage_map; 1562e5d2d2SJDBetteridge PetscCallMPI(MPI_Comm_set_attr(comm, Petsc_Garbage_HMap_keyval, garbage->ptr)); 1662e5d2d2SJDBetteridge } 173ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1862e5d2d2SJDBetteridge } 1962e5d2d2SJDBetteridge 2062e5d2d2SJDBetteridge /*@C 2162e5d2d2SJDBetteridge PetscObjectDelayedDestroy - Adds an object to a data structure for 2262e5d2d2SJDBetteridge later destruction. 2362e5d2d2SJDBetteridge 2462e5d2d2SJDBetteridge Not Collective 2562e5d2d2SJDBetteridge 26*2fe279fdSBarry Smith Input Parameter: 2762e5d2d2SJDBetteridge . obj - object to be destroyed 2862e5d2d2SJDBetteridge 2962e5d2d2SJDBetteridge Notes: 3062e5d2d2SJDBetteridge Analogue to `PetscObjectDestroy()` for use in managed languages. 3162e5d2d2SJDBetteridge 3262e5d2d2SJDBetteridge A PETSc object is given a creation index at initialisation based on 3362e5d2d2SJDBetteridge the communicator it was created on and the order in which it is 3462e5d2d2SJDBetteridge created. When this function is passed a PETSc object, a pointer to 3562e5d2d2SJDBetteridge the object is stashed on a garbage dictionary (PetscHMapObj) which is 3662e5d2d2SJDBetteridge keyed by its creation index. 3762e5d2d2SJDBetteridge 3862e5d2d2SJDBetteridge Objects stashed on this garbage dictionary can later be destroyed 3962e5d2d2SJDBetteridge with a call to `PetscGarbageCleanup()`. 4062e5d2d2SJDBetteridge 4162e5d2d2SJDBetteridge This function is intended for use with managed languages such as 4262e5d2d2SJDBetteridge Python or Julia, which may not destroy objects in a deterministic 4362e5d2d2SJDBetteridge order. 4462e5d2d2SJDBetteridge 4562e5d2d2SJDBetteridge Level: developer 4662e5d2d2SJDBetteridge 4762e5d2d2SJDBetteridge .seealso: `PetscGarbageCleanup()` 4862e5d2d2SJDBetteridge @*/ 4962e5d2d2SJDBetteridge PetscErrorCode PetscObjectDelayedDestroy(PetscObject *obj) 5062e5d2d2SJDBetteridge { 5162e5d2d2SJDBetteridge MPI_Comm petsc_comm; 5262e5d2d2SJDBetteridge PetscInt count; 5362e5d2d2SJDBetteridge PetscGarbage garbage; 5462e5d2d2SJDBetteridge 5562e5d2d2SJDBetteridge PetscFunctionBegin; 5662e5d2d2SJDBetteridge PetscValidPointer(obj, 1); 5762e5d2d2SJDBetteridge /* Don't stash NULL pointers */ 5862e5d2d2SJDBetteridge if (*obj != NULL) { 5962e5d2d2SJDBetteridge /* Elaborate check for getting non-cyclic reference counts */ 6062e5d2d2SJDBetteridge if (!(*obj)->non_cyclic_references) { 6162e5d2d2SJDBetteridge count = --(*obj)->refct; 6262e5d2d2SJDBetteridge } else { 6362e5d2d2SJDBetteridge PetscCall((*obj)->non_cyclic_references(*obj, &count)); 6462e5d2d2SJDBetteridge --count; 6562e5d2d2SJDBetteridge --(*obj)->refct; 6662e5d2d2SJDBetteridge } 6762e5d2d2SJDBetteridge /* Only stash if the (non-cyclic) reference count hits 0 */ 6862e5d2d2SJDBetteridge if (count == 0) { 6962e5d2d2SJDBetteridge (*obj)->refct = 1; 7062e5d2d2SJDBetteridge PetscCall(PetscObjectGetComm(*obj, &petsc_comm)); 7162e5d2d2SJDBetteridge PetscCall(GarbageGetHMap_Private(petsc_comm, &garbage)); 7262e5d2d2SJDBetteridge PetscCall(PetscHMapObjSet(garbage.map, (*obj)->cidx, *obj)); 7362e5d2d2SJDBetteridge } 7462e5d2d2SJDBetteridge } 7562e5d2d2SJDBetteridge *obj = NULL; 763ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 7762e5d2d2SJDBetteridge } 7862e5d2d2SJDBetteridge 7962e5d2d2SJDBetteridge /* Performs the intersection of 2 sorted arrays seta and setb of lengths 8062e5d2d2SJDBetteridge lena and lenb respectively,returning the result in seta and lena 8162e5d2d2SJDBetteridge This is an O(n) operation */ 8262e5d2d2SJDBetteridge static PetscErrorCode GarbageKeySortedIntersect_Private(PetscInt64 seta[], PetscInt *lena, PetscInt64 setb[], PetscInt lenb) 8362e5d2d2SJDBetteridge { 8462e5d2d2SJDBetteridge /* The arrays seta and setb MUST be sorted! */ 85b0271995SJDBetteridge PetscInt ii, jj = 0, counter = 0; 8662e5d2d2SJDBetteridge 8762e5d2d2SJDBetteridge PetscFunctionBegin; 8862e5d2d2SJDBetteridge if (PetscDefined(USE_DEBUG)) { 8962e5d2d2SJDBetteridge PetscBool sorted = PETSC_FALSE; 9062e5d2d2SJDBetteridge /* In debug mode check whether the array are sorted */ 9162e5d2d2SJDBetteridge PetscCall(PetscSortedInt64(*lena, seta, &sorted)); 9279528a3fSJDBetteridge PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Provided array in argument 1 is not sorted"); 9362e5d2d2SJDBetteridge PetscCall(PetscSortedInt64(lenb, setb, &sorted)); 9479528a3fSJDBetteridge PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Provided array in argument 3 is not sorted"); 9562e5d2d2SJDBetteridge } 9662e5d2d2SJDBetteridge for (ii = 0; ii < *lena; ii++) { 97aa624791SPierre Jolivet while (jj < lenb && seta[ii] > setb[jj]) jj++; 98b0271995SJDBetteridge if (jj >= lenb) break; 99b0271995SJDBetteridge if (seta[ii] == setb[jj]) { 10062e5d2d2SJDBetteridge seta[counter] = seta[ii]; 10162e5d2d2SJDBetteridge counter++; 10262e5d2d2SJDBetteridge } 10362e5d2d2SJDBetteridge } 104b0271995SJDBetteridge 10562e5d2d2SJDBetteridge *lena = counter; 1063ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 10762e5d2d2SJDBetteridge } 10862e5d2d2SJDBetteridge 10962e5d2d2SJDBetteridge /* Wrapper to create MPI reduce operator for set intersection */ 11062e5d2d2SJDBetteridge void PetscGarbageKeySortedIntersect(void *inset, void *inoutset, PetscMPIInt *length, MPI_Datatype *dtype) 11162e5d2d2SJDBetteridge { 11262e5d2d2SJDBetteridge PetscInt64 *seta, *setb; 11362e5d2d2SJDBetteridge 11462e5d2d2SJDBetteridge seta = (PetscInt64 *)inoutset; 11562e5d2d2SJDBetteridge setb = (PetscInt64 *)inset; 11662e5d2d2SJDBetteridge 1173ba16761SJacob Faibussowitsch PetscCallAbort(PETSC_COMM_SELF, GarbageKeySortedIntersect_Private(&seta[1], (PetscInt *)&seta[0], &setb[1], (PetscInt)setb[0])); 11862e5d2d2SJDBetteridge } 11962e5d2d2SJDBetteridge 12062e5d2d2SJDBetteridge /* Performs a collective allreduce intersection of one array per rank */ 1218e89d99cSJDBetteridge PetscErrorCode GarbageKeyAllReduceIntersect_Private(MPI_Comm comm, PetscInt64 *set, PetscInt *entries) 12262e5d2d2SJDBetteridge { 12362e5d2d2SJDBetteridge PetscInt ii, max_entries; 12462e5d2d2SJDBetteridge PetscInt64 *sendset, *recvset; 12562e5d2d2SJDBetteridge MPI_Datatype keyset_type; 12662e5d2d2SJDBetteridge 12762e5d2d2SJDBetteridge PetscFunctionBegin; 12862e5d2d2SJDBetteridge /* Sort keys first for use with `GarbageKeySortedIntersect_Private()`*/ 12962e5d2d2SJDBetteridge PetscCall(PetscSortInt64(*entries, set)); 13062e5d2d2SJDBetteridge 13162e5d2d2SJDBetteridge /* Get the maximum size of all key sets */ 13262e5d2d2SJDBetteridge PetscCallMPI(MPI_Allreduce(entries, &max_entries, 1, MPIU_INT, MPI_MAX, comm)); 13362e5d2d2SJDBetteridge PetscCall(PetscMalloc1(max_entries + 1, &sendset)); 13462e5d2d2SJDBetteridge PetscCall(PetscMalloc1(max_entries + 1, &recvset)); 13562e5d2d2SJDBetteridge sendset[0] = (PetscInt64)*entries; 13662e5d2d2SJDBetteridge for (ii = 1; ii < *entries + 1; ii++) sendset[ii] = set[ii - 1]; 13762e5d2d2SJDBetteridge 13862e5d2d2SJDBetteridge /* Create a custom data type to hold the set */ 13962e5d2d2SJDBetteridge PetscCallMPI(MPI_Type_contiguous(max_entries + 1, MPIU_INT64, &keyset_type)); 14062e5d2d2SJDBetteridge /* PetscCallMPI(MPI_Type_set_name(keyset_type,"PETSc garbage key set type")); */ 14162e5d2d2SJDBetteridge PetscCallMPI(MPI_Type_commit(&keyset_type)); 14262e5d2d2SJDBetteridge 14362e5d2d2SJDBetteridge /* Perform custom intersect reduce operation over sets */ 14462e5d2d2SJDBetteridge PetscCallMPI(MPI_Allreduce(sendset, recvset, 1, keyset_type, Petsc_Garbage_SetIntersectOp, comm)); 14562e5d2d2SJDBetteridge 14662e5d2d2SJDBetteridge PetscCallMPI(MPI_Type_free(&keyset_type)); 14762e5d2d2SJDBetteridge 14862e5d2d2SJDBetteridge *entries = (PetscInt)recvset[0]; 14962e5d2d2SJDBetteridge for (ii = 0; ii < *entries; ii++) set[ii] = recvset[ii + 1]; 15062e5d2d2SJDBetteridge 15162e5d2d2SJDBetteridge PetscCall(PetscFree(sendset)); 15262e5d2d2SJDBetteridge PetscCall(PetscFree(recvset)); 1533ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 15462e5d2d2SJDBetteridge } 15562e5d2d2SJDBetteridge 15662e5d2d2SJDBetteridge /*@C 15762e5d2d2SJDBetteridge PetscGarbageCleanup - Destroys objects placed in the garbage by 15862e5d2d2SJDBetteridge PetscObjectDelayedDestroy(). 15962e5d2d2SJDBetteridge 16062e5d2d2SJDBetteridge Collective 16162e5d2d2SJDBetteridge 162*2fe279fdSBarry Smith Input Parameter: 16362e5d2d2SJDBetteridge . comm - communicator over which to perform collective cleanup 16462e5d2d2SJDBetteridge 16562e5d2d2SJDBetteridge Notes: 16662e5d2d2SJDBetteridge Implements a collective garbage collection. 16762e5d2d2SJDBetteridge A per- MPI communicator garbage dictionary is created to store 16862e5d2d2SJDBetteridge references to objects destroyed using PetscObjectDelayedDestroy(). 16962e5d2d2SJDBetteridge Objects that appear in this dictionary on all ranks can be destroyed 17062e5d2d2SJDBetteridge by calling PetscGarbageCleanup(). 17162e5d2d2SJDBetteridge 17262e5d2d2SJDBetteridge This is done as follows: 17362e5d2d2SJDBetteridge 1. Keys of the garbage dictionary, which correspond to the creation 17462e5d2d2SJDBetteridge indices of the objects stashed, are sorted. 17562e5d2d2SJDBetteridge 2. A collective intersection of dictionary keys is performed by all 17662e5d2d2SJDBetteridge ranks in the communicator. 17762e5d2d2SJDBetteridge 3. The intersection is broadcast back to all ranks in the 17862e5d2d2SJDBetteridge communicator. 17962e5d2d2SJDBetteridge 4. The objects on the dictionary are collectively destroyed in 18062e5d2d2SJDBetteridge creation index order using a call to PetscObjectDestroy(). 18162e5d2d2SJDBetteridge 18262e5d2d2SJDBetteridge This function is intended for use with managed languages such as 18362e5d2d2SJDBetteridge Python or Julia, which may not destroy objects in a deterministic 18462e5d2d2SJDBetteridge order. 18562e5d2d2SJDBetteridge 18662e5d2d2SJDBetteridge Level: developer 18762e5d2d2SJDBetteridge 18862e5d2d2SJDBetteridge .seealso: PetscObjectDelayedDestroy() 18962e5d2d2SJDBetteridge @*/ 19062e5d2d2SJDBetteridge PetscErrorCode PetscGarbageCleanup(MPI_Comm comm) 19162e5d2d2SJDBetteridge { 19262e5d2d2SJDBetteridge PetscInt ii, entries, offset; 19362e5d2d2SJDBetteridge PetscInt64 *keys; 19462e5d2d2SJDBetteridge PetscObject obj; 19562e5d2d2SJDBetteridge PetscGarbage garbage; 19662e5d2d2SJDBetteridge 19762e5d2d2SJDBetteridge PetscFunctionBegin; 19862e5d2d2SJDBetteridge /* Duplicate comm to prevent it being cleaned up by PetscObjectDestroy() */ 19962e5d2d2SJDBetteridge PetscCall(PetscCommDuplicate(comm, &comm, NULL)); 20062e5d2d2SJDBetteridge 20162e5d2d2SJDBetteridge /* Grab garbage from comm and remove it 20262e5d2d2SJDBetteridge this avoids calling PetscCommDestroy() and endlessly recursing */ 20362e5d2d2SJDBetteridge PetscCall(GarbageGetHMap_Private(comm, &garbage)); 20462e5d2d2SJDBetteridge PetscCallMPI(MPI_Comm_delete_attr(comm, Petsc_Garbage_HMap_keyval)); 20562e5d2d2SJDBetteridge 20662e5d2d2SJDBetteridge /* Get keys from garbage hash map */ 20762e5d2d2SJDBetteridge PetscCall(PetscHMapObjGetSize(garbage.map, &entries)); 20862e5d2d2SJDBetteridge PetscCall(PetscMalloc1(entries, &keys)); 20962e5d2d2SJDBetteridge offset = 0; 21062e5d2d2SJDBetteridge PetscCall(PetscHMapObjGetKeys(garbage.map, &offset, keys)); 21162e5d2d2SJDBetteridge 21262e5d2d2SJDBetteridge /* Gather and intersect */ 21362e5d2d2SJDBetteridge PetscCall(GarbageKeyAllReduceIntersect_Private(comm, keys, &entries)); 21462e5d2d2SJDBetteridge 21562e5d2d2SJDBetteridge /* Collectively destroy objects objects that appear in garbage in 21662e5d2d2SJDBetteridge creation index order */ 21762e5d2d2SJDBetteridge for (ii = 0; ii < entries; ii++) { 21862e5d2d2SJDBetteridge PetscCall(PetscHMapObjGet(garbage.map, keys[ii], &obj)); 21962e5d2d2SJDBetteridge PetscCall(PetscObjectDestroy(&obj)); 22062e5d2d2SJDBetteridge PetscCall(PetscFree(obj)); 22162e5d2d2SJDBetteridge PetscCall(PetscHMapObjDel(garbage.map, keys[ii])); 22262e5d2d2SJDBetteridge } 22362e5d2d2SJDBetteridge PetscCall(PetscFree(keys)); 22462e5d2d2SJDBetteridge 22562e5d2d2SJDBetteridge /* Put garbage back */ 22662e5d2d2SJDBetteridge PetscCallMPI(MPI_Comm_set_attr(comm, Petsc_Garbage_HMap_keyval, garbage.ptr)); 22762e5d2d2SJDBetteridge PetscCall(PetscCommDestroy(&comm)); 2283ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 22962e5d2d2SJDBetteridge } 23062e5d2d2SJDBetteridge 23162e5d2d2SJDBetteridge /* Utility function for printing the contents of the garbage on a given comm */ 23262e5d2d2SJDBetteridge PetscErrorCode PetscGarbageView(MPI_Comm comm, PetscViewer viewer) 23362e5d2d2SJDBetteridge { 23462e5d2d2SJDBetteridge char text[64]; 23562e5d2d2SJDBetteridge PetscInt ii, entries, offset; 23662e5d2d2SJDBetteridge PetscInt64 *keys; 23762e5d2d2SJDBetteridge PetscObject obj; 23862e5d2d2SJDBetteridge PetscGarbage garbage; 23962e5d2d2SJDBetteridge PetscMPIInt rank; 24062e5d2d2SJDBetteridge 24162e5d2d2SJDBetteridge PetscFunctionBegin; 24262e5d2d2SJDBetteridge PetscCall(PetscPrintf(comm, "PETSc garbage on ")); 24362e5d2d2SJDBetteridge if (comm == PETSC_COMM_WORLD) { 24462e5d2d2SJDBetteridge PetscCall(PetscPrintf(comm, "PETSC_COMM_WORLD\n")); 24562e5d2d2SJDBetteridge } else if (comm == PETSC_COMM_SELF) { 24662e5d2d2SJDBetteridge PetscCall(PetscPrintf(comm, "PETSC_COMM_SELF\n")); 24762e5d2d2SJDBetteridge } else { 24862e5d2d2SJDBetteridge PetscCall(PetscPrintf(comm, "UNKNOWN_COMM\n")); 24962e5d2d2SJDBetteridge } 25062e5d2d2SJDBetteridge PetscCall(PetscCommDuplicate(comm, &comm, NULL)); 25162e5d2d2SJDBetteridge PetscCall(GarbageGetHMap_Private(comm, &garbage)); 25262e5d2d2SJDBetteridge 25362e5d2d2SJDBetteridge /* Get keys from garbage hash map and sort */ 25462e5d2d2SJDBetteridge PetscCall(PetscHMapObjGetSize(garbage.map, &entries)); 25562e5d2d2SJDBetteridge PetscCall(PetscMalloc1(entries, &keys)); 25662e5d2d2SJDBetteridge offset = 0; 25762e5d2d2SJDBetteridge PetscCall(PetscHMapObjGetKeys(garbage.map, &offset, keys)); 25862e5d2d2SJDBetteridge 25962e5d2d2SJDBetteridge /* Pretty print entries in a table */ 26062e5d2d2SJDBetteridge PetscCallMPI(MPI_Comm_rank(comm, &rank)); 26162e5d2d2SJDBetteridge PetscCall(PetscSynchronizedPrintf(comm, "Rank %i:: ", rank)); 26262e5d2d2SJDBetteridge PetscCall(PetscFormatConvert("Total entries: %D\n", text)); 26362e5d2d2SJDBetteridge PetscCall(PetscSynchronizedPrintf(comm, text, entries)); 26462e5d2d2SJDBetteridge if (entries) { 26562e5d2d2SJDBetteridge PetscCall(PetscSynchronizedPrintf(comm, "| Key | Type | Name | Object ID |\n")); 26662e5d2d2SJDBetteridge PetscCall(PetscSynchronizedPrintf(comm, "|-------|------------------------|----------------------------------|-----------|\n")); 26762e5d2d2SJDBetteridge } 26862e5d2d2SJDBetteridge for (ii = 0; ii < entries; ii++) { 26962e5d2d2SJDBetteridge PetscCall(PetscHMapObjGet(garbage.map, keys[ii], &obj)); 27062e5d2d2SJDBetteridge PetscCall(PetscFormatConvert("| %5" PetscInt64_FMT " | %-22s | %-32s | %6D |\n", text)); 27162e5d2d2SJDBetteridge PetscCall(PetscSynchronizedPrintf(comm, text, keys[ii], obj->class_name, obj->description, obj->id)); 27262e5d2d2SJDBetteridge } 27362e5d2d2SJDBetteridge PetscCall(PetscSynchronizedFlush(comm, PETSC_STDOUT)); 27462e5d2d2SJDBetteridge 27562e5d2d2SJDBetteridge PetscCall(PetscFree(keys)); 27662e5d2d2SJDBetteridge PetscCall(PetscCommDestroy(&comm)); 2773ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 27862e5d2d2SJDBetteridge } 279