Actual source code: sfalltoall.c

  1: #include <../src/vec/is/sf/impls/basic/allgatherv/sfallgatherv.h>
  2: #include <../src/vec/is/sf/impls/basic/allgather/sfallgather.h>
  3: #include <../src/vec/is/sf/impls/basic/gatherv/sfgatherv.h>

  5: /* Reuse the type. The difference is some fields (i.e., displs, recvcounts) are not used, which is not a big deal */
  6: typedef PetscSF_Allgatherv PetscSF_Alltoall;

  8: static PetscErrorCode PetscSFLinkStartCommunication_Alltoall(PetscSF sf, PetscSFLink link, PetscSFDirection direction)
  9: {
 10:   MPI_Comm     comm    = MPI_COMM_NULL;
 11:   void        *rootbuf = NULL, *leafbuf = NULL;
 12:   MPI_Request *req  = NULL;
 13:   MPI_Datatype unit = link->unit;

 15:   PetscFunctionBegin;
 16:   if (direction == PETSCSF_ROOT2LEAF) {
 17:     PetscCall(PetscSFLinkCopyRootBufferInCaseNotUseGpuAwareMPI(sf, link, PETSC_TRUE /* device2host before sending */));
 18:   } else {
 19:     PetscCall(PetscSFLinkCopyLeafBufferInCaseNotUseGpuAwareMPI(sf, link, PETSC_TRUE /* device2host */));
 20:   }
 21:   PetscCall(PetscObjectGetComm((PetscObject)sf, &comm));
 22:   PetscCall(PetscSFLinkGetMPIBuffersAndRequests(sf, link, direction, &rootbuf, &leafbuf, &req, NULL));
 23:   PetscCall(PetscSFLinkSyncStreamBeforeCallMPI(sf, link));

 25:   if (direction == PETSCSF_ROOT2LEAF) {
 26:     PetscCallMPI(MPIU_Ialltoall(rootbuf, 1, unit, leafbuf, 1, unit, comm, req));
 27:   } else {
 28:     PetscCallMPI(MPIU_Ialltoall(leafbuf, 1, unit, rootbuf, 1, unit, comm, req));
 29:   }
 30:   PetscFunctionReturn(PETSC_SUCCESS);
 31: }

 33: static PetscErrorCode PetscSFSetCommunicationOps_Alltoall(PetscSF sf, PetscSFLink link)
 34: {
 35:   PetscFunctionBegin;
 36:   link->StartCommunication = PetscSFLinkStartCommunication_Alltoall;
 37:   PetscFunctionReturn(PETSC_SUCCESS);
 38: }

 40: /*===================================================================================*/
 41: /*              Implementations of SF public APIs                                    */
 42: /*===================================================================================*/
 43: static PetscErrorCode PetscSFGetGraph_Alltoall(PetscSF sf, PetscInt *nroots, PetscInt *nleaves, const PetscInt **ilocal, const PetscSFNode **iremote)
 44: {
 45:   PetscInt i;

 47:   PetscFunctionBegin;
 48:   if (nroots) *nroots = sf->nroots;
 49:   if (nleaves) *nleaves = sf->nleaves;
 50:   if (ilocal) *ilocal = NULL; /* Contiguous local indices */
 51:   if (iremote) {
 52:     if (!sf->remote) {
 53:       PetscCall(PetscMalloc1(sf->nleaves, &sf->remote));
 54:       sf->remote_alloc = sf->remote;
 55:       for (i = 0; i < sf->nleaves; i++) {
 56:         sf->remote[i].rank  = (PetscMPIInt)i; /* this is nonsense, cannot be larger than size */
 57:         sf->remote[i].index = i;
 58:       }
 59:     }
 60:     *iremote = sf->remote;
 61:   }
 62:   PetscFunctionReturn(PETSC_SUCCESS);
 63: }

 65: static PetscErrorCode PetscSFCreateLocalSF_Alltoall(PetscSF sf, PetscSF *out)
 66: {
 67:   PetscInt     nroots = 1, nleaves = 1, *ilocal;
 68:   PetscSFNode *iremote = NULL;
 69:   PetscSF      lsf;
 70:   PetscMPIInt  rank;

 72:   PetscFunctionBegin;
 73:   nroots  = 1;
 74:   nleaves = 1;
 75:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)sf), &rank));
 76:   PetscCall(PetscMalloc1(nleaves, &ilocal));
 77:   PetscCall(PetscMalloc1(nleaves, &iremote));
 78:   ilocal[0]        = rank;
 79:   iremote[0].rank  = 0;    /* rank in PETSC_COMM_SELF */
 80:   iremote[0].index = rank; /* LocalSF is an embedded SF. Indices are not remapped */

 82:   PetscCall(PetscSFCreate(PETSC_COMM_SELF, &lsf));
 83:   PetscCall(PetscSFSetGraph(lsf, nroots, nleaves, NULL /*contiguous leaves*/, PETSC_OWN_POINTER, iremote, PETSC_OWN_POINTER));
 84:   PetscCall(PetscSFSetUp(lsf));
 85:   *out = lsf;
 86:   PetscFunctionReturn(PETSC_SUCCESS);
 87: }

 89: static PetscErrorCode PetscSFCreateEmbeddedRootSF_Alltoall(PetscSF sf, PetscInt nselected, const PetscInt *selected, PetscSF *newsf)
 90: {
 91:   PetscInt       i, *tmproots, *ilocal;
 92:   PetscSFNode   *iremote;
 93:   PetscMPIInt    nroots, *roots, nleaves, *leaves, rank, ndiranks, ndranks;
 94:   MPI_Comm       comm;
 95:   PetscSF_Basic *bas;
 96:   PetscSF        esf;

 98:   PetscFunctionBegin;
 99:   PetscCall(PetscObjectGetComm((PetscObject)sf, &comm));
100:   PetscCallMPI(MPI_Comm_rank(comm, &rank));

102:   /* Uniq selected[] and store the result in roots[] */
103:   PetscCall(PetscMalloc1(nselected, &tmproots));
104:   PetscCall(PetscArraycpy(tmproots, selected, nselected));
105:   PetscCall(PetscSortRemoveDupsInt(&nselected, tmproots)); /* nselected might be changed */
106:   PetscCheck(tmproots[0] >= 0 && tmproots[nselected - 1] < sf->nroots, comm, PETSC_ERR_ARG_OUTOFRANGE, "Min/Max root indices %" PetscInt_FMT "/%" PetscInt_FMT " are not in [0,%" PetscInt_FMT ")", tmproots[0], tmproots[nselected - 1], sf->nroots);
107:   PetscCall(PetscMPIIntCast(nselected, &nroots));
108:   PetscCall(PetscMalloc1(nselected, &roots));
109:   for (PetscMPIInt i = 0; i < nroots; i++) PetscCall(PetscMPIIntCast(tmproots[i], &roots[i]));
110:   PetscCall(PetscFree(tmproots));

112:   /* Find out which leaves are still connected to roots in the embedded sf. Expect PetscCommBuildTwoSided is more scalable than MPI_Alltoall */
113:   PetscCall(PetscCommBuildTwoSided(comm, 0 /*empty msg*/, MPI_INT /*fake*/, nroots, roots, NULL /*todata*/, &nleaves, &leaves, NULL /*fromdata*/));

115:   /* Move myself ahead if rank is in leaves[], since I am a distinguished rank */
116:   ndranks = 0;
117:   for (i = 0; i < nleaves; i++) {
118:     if (leaves[i] == rank) {
119:       leaves[i] = -rank;
120:       ndranks   = 1;
121:       break;
122:     }
123:   }
124:   PetscCall(PetscSortMPIInt(nleaves, leaves));
125:   if (nleaves && leaves[0] < 0) leaves[0] = rank;

127:   /* Build esf and fill its fields manually (without calling PetscSFSetUp) */
128:   PetscCall(PetscMalloc1(nleaves, &ilocal));
129:   PetscCall(PetscMalloc1(nleaves, &iremote));
130:   for (i = 0; i < nleaves; i++) { /* 1:1 map from roots to leaves */
131:     ilocal[i]        = leaves[i];
132:     iremote[i].rank  = leaves[i];
133:     iremote[i].index = leaves[i];
134:   }
135:   PetscCall(PetscSFCreate(comm, &esf));
136:   PetscCall(PetscSFSetType(esf, PETSCSFBASIC)); /* This optimized routine can only create a basic sf */
137:   PetscCall(PetscSFSetGraph(esf, sf->nleaves, nleaves, ilocal, PETSC_OWN_POINTER, iremote, PETSC_OWN_POINTER));

139:   /* As if we are calling PetscSFSetUpRanks(esf,self's group) */
140:   PetscCall(PetscMalloc4(nleaves, &esf->ranks, nleaves + 1, &esf->roffset, nleaves, &esf->rmine, nleaves, &esf->rremote));
141:   esf->nranks     = nleaves;
142:   esf->ndranks    = ndranks;
143:   esf->roffset[0] = 0;
144:   for (i = 0; i < nleaves; i++) {
145:     esf->ranks[i]       = leaves[i];
146:     esf->roffset[i + 1] = i + 1;
147:     esf->rmine[i]       = leaves[i];
148:     esf->rremote[i]     = leaves[i];
149:   }

151:   /* Set up esf->data, the incoming communication (i.e., recv info), which is usually done by PetscSFSetUp_Basic */
152:   bas = (PetscSF_Basic *)esf->data;
153:   PetscCall(PetscMalloc2(nroots, &bas->iranks, nroots + 1, &bas->ioffset));
154:   PetscCall(PetscMalloc1(nroots, &bas->irootloc));
155:   /* Move myself ahead if rank is in roots[], since I am a distinguished irank */
156:   ndiranks = 0;
157:   for (i = 0; i < nroots; i++) {
158:     if (roots[i] == rank) {
159:       roots[i] = -rank;
160:       ndiranks = 1;
161:       break;
162:     }
163:   }
164:   PetscCall(PetscSortMPIInt(nroots, roots));
165:   if (nroots && roots[0] < 0) roots[0] = rank;

167:   bas->niranks    = nroots;
168:   bas->ndiranks   = ndiranks;
169:   bas->ioffset[0] = 0;
170:   bas->itotal     = nroots;
171:   for (i = 0; i < nroots; i++) {
172:     bas->iranks[i]      = roots[i];
173:     bas->ioffset[i + 1] = i + 1;
174:     bas->irootloc[i]    = roots[i];
175:   }

177:   /* See PetscSFCreateEmbeddedRootSF_Basic */
178:   esf->nleafreqs  = esf->nranks - esf->ndranks;
179:   bas->nrootreqs  = bas->niranks - bas->ndiranks;
180:   esf->persistent = PETSC_TRUE;
181:   /* Setup packing related fields */
182:   PetscCall(PetscSFSetUpPackFields(esf));

184:   esf->setupcalled = PETSC_TRUE; /* We have done setup ourselves! */
185:   *newsf           = esf;
186:   PetscFunctionReturn(PETSC_SUCCESS);
187: }

189: PETSC_INTERN PetscErrorCode PetscSFCreate_Alltoall(PetscSF sf)
190: {
191:   PetscSF_Alltoall *dat = (PetscSF_Alltoall *)sf->data;

193:   PetscFunctionBegin;
194:   sf->ops->BcastBegin  = PetscSFBcastBegin_Basic;
195:   sf->ops->BcastEnd    = PetscSFBcastEnd_Basic;
196:   sf->ops->ReduceBegin = PetscSFReduceBegin_Basic;
197:   sf->ops->ReduceEnd   = PetscSFReduceEnd_Basic;

199:   /* Inherit from Allgatherv. It is astonishing Alltoall can inherit so much from Allgather(v) */
200:   sf->ops->Destroy       = PetscSFDestroy_Allgatherv;
201:   sf->ops->Reset         = PetscSFReset_Allgatherv;
202:   sf->ops->FetchAndOpEnd = PetscSFFetchAndOpEnd_Allgatherv;
203:   sf->ops->GetRootRanks  = PetscSFGetRootRanks_Allgatherv;

205:   /* Inherit from Allgather. Every process gathers equal-sized data from others, which enables this inheritance. */
206:   sf->ops->GetLeafRanks = PetscSFGetLeafRanks_Allgatherv;
207:   sf->ops->SetUp        = PetscSFSetUp_Allgather;

209:   /* Inherit from Gatherv. Each root has only one leaf connected, which enables this inheritance */
210:   sf->ops->FetchAndOpBegin = PetscSFFetchAndOpBegin_Gatherv;

212:   /* Alltoall stuff */
213:   sf->ops->GetGraph             = PetscSFGetGraph_Alltoall;
214:   sf->ops->CreateLocalSF        = PetscSFCreateLocalSF_Alltoall;
215:   sf->ops->CreateEmbeddedRootSF = PetscSFCreateEmbeddedRootSF_Alltoall;

217:   sf->ops->SetCommunicationOps = PetscSFSetCommunicationOps_Alltoall;

219:   sf->collective = PETSC_TRUE;

221:   PetscCall(PetscNew(&dat));
222:   sf->data = (void *)dat;
223:   PetscFunctionReturn(PETSC_SUCCESS);
224: }