39template <
typename IdType>
41 const IdType*
const partitionCellToGlobalIds,
42 const IdType noDomains,
43 IdType*
const globalIdOffsets) {
45 globalIdOffsets[0] = 0;
48 for(IdType domainId = 1; domainId < noDomains; domainId++) {
49 globalIdOffsets[domainId] = partitionCellToGlobalIds[partitionCellOffsets[domainId]];
60template <
typename WeightType,
typename IdType>
62 IdType*
const offsets) {
63 WeightType totalWorkload = F0;
64 WeightType maxWorkload = F0;
67 for(IdType i = 0; i < noWeights; i++) {
68 prefixSum(i + 1) = prefixSum(i) + weights[i];
69 maxWorkload =
mMax(maxWorkload, weights[i]);
71 totalWorkload = prefixSum(noWeights);
72 WeightType optimalWorkload = totalWorkload / ((WeightType)noPartitions);
74 WeightType eps =
mMin(0.0001 * optimalWorkload, 0.5 * maxWorkload / optimalWorkload);
76 IdType noPartitionsOpt = 0;
77 WeightType maxPartitionWorkload = F0;
78 for(IdType i = 0; i <= noPartitions; i++) {
81 WeightType lowerVal =
mMax(maxWorkload, optimalWorkload);
82 WeightType upperVal = lowerVal +
mMax(maxWorkload, F2 * eps);
83 WeightType probeWorkload = lowerVal;
84 WeightType workloadUsed = -F1;
86 if(noWeights <= noPartitions) {
87 std::cerr <<
"Warning: less or equal number of weights than number of partitions!" << std::endl;
88 for(IdType i = 0; i <= noPartitions; i++) {
89 offsets[i] =
mMin(noPartitions, i);
91 for(IdType i = 0; i < noPartitions; i++) {
92 maxPartitionWorkload =
mMax(maxPartitionWorkload, weights[i]);
94 return maxPartitionWorkload;
96 while(upperVal - lowerVal > eps) {
99 IdType
id = noWeights / noPartitions;
101 for(IdType cpu = 1; cpu < noPartitions; cpu++) {
104 if(prefixSum(
id) - prefixSum(offsetTmp(cpu - 1)) > probeWorkload)
106 else if(!(prefixSum(
id + 1) - prefixSum(offsetTmp(cpu - 1)) > probeWorkload))
110 if(
id >= noWeights) {
116 if(cpu < noPartitions) {
117 while((noWeights -
id) < (noPartitions - cpu))
119 id =
mMax(offsetTmp[cpu - 1] + 1,
id);
122 id =
mMin(noWeights - 1, 2 * offsetTmp(cpu) - offsetTmp(cpu - 1));
125 if(prefixSum(noWeights) - prefixSum(offsetTmp(lastCpu)) > probeWorkload) {
126 lowerVal = probeWorkload;
128 std::copy(offsetTmp.
begin(), offsetTmp.
begin() + noPartitions + 1, offsets);
129 workloadUsed = probeWorkload;
130 noPartitionsOpt = lastCpu + 1;
131 upperVal = probeWorkload;
133 if(itCnt > 0 && offsets[0] == -1) {
134 upperVal += F2 * maxWorkload;
136 probeWorkload = F1B2 * (lowerVal + upperVal);
139 probeWorkload = workloadUsed;
142 for(IdType i = noPartitionsOpt; i <= noPartitions; i++) {
143 offsets[i] = noWeights;
147 for(IdType i = 1; i < noPartitionsOpt; i++) {
148 while((prefixSum[offsets[i + 1]] - prefixSum[offsets[i]] + weights[offsets[i] - 1])
149 < (prefixSum[offsets[i]] - prefixSum[offsets[i - 1]] - weights[offsets[i] - 1])
150 && offsets[i] > offsets[i - 1] + 1) {
158 if(noPartitionsOpt < noPartitions) {
160 WeightType probeSplit = 0.5;
161 IdType diff = noPartitions - noPartitionsOpt;
164 const IdType diff0 = diff;
165 for(IdType k = 0; k < diff0; k++) {
166 for(IdType i = 0; i < noPartitionsOpt; i++) {
167 if(prefixSum[offsets[i + 1]] - prefixSum[offsets[i]] > F2 * probeSplit) {
168 IdType
id = (offsets[i + 1] + offsets[i]) / 2;
170 while(!found &&
id > offsets[i] &&
id < offsets[i + 1]) {
171 if(prefixSum(
id) - prefixSum(offsets[i]) < probeSplit * probeWorkload)
173 else if(!(prefixSum(
id - 1) - prefixSum(offsets[i]) < probeSplit * probeWorkload))
181 if(
id > offsets[i] &&
id < offsets[i + 1]
182 && prefixSum(
id) - prefixSum(offsets[i]) > probeSplit * probeWorkload
183 && prefixSum(offsets[i + 1]) - prefixSum(
id) > probeSplit * probeWorkload) {
185 for(IdType j = noPartitions; j > i + 1; j--) {
186 offsets[j] = offsets[j - 1];
195 if(probeSplit <= 0) {
196 std::cout <<
"DIFF " << diff << std::endl;
197 mTerm(1, AT_,
"partition algorithm failed due to redistribution error");
202 offsets[noPartitions] = noWeights;
205 maxPartitionWorkload = F0;
206 for(IdType i = 0; i < noPartitions; i++) {
207 maxPartitionWorkload =
mMax(maxPartitionWorkload, (prefixSum[offsets[i + 1]] - prefixSum[offsets[i]]));
209 return maxPartitionWorkload;
224template <
typename WeightType,
typename IdType>
226 const MLong localOffset,
const MLong noGlobalWeights,
const WeightType totalWorkload,
227 const IdType noGlobalPartitions,
const IdType globalPartitionId,
228 const IdType noGroups,
const MPI_Comm comm, MPI_Comm& commGroup,
229 MPI_Comm& commMasters, IdType& groupId, IdType& groupLocalId,
230 MLong*
const groupOffsets) {
232 const IdType noRanksPerGroup = noGlobalPartitions / noGroups;
233 groupId = (globalPartitionId >= noGroups * noRanksPerGroup) ? noGroups - 1 : globalPartitionId / noRanksPerGroup;
234 groupLocalId = globalPartitionId - groupId * noRanksPerGroup;
235 MBool isGroupMaster = (groupLocalId == 0);
236 IdType masterTag = isGroupMaster ? 0 : MPI_UNDEFINED;
238 MPI_Comm_split(comm, groupId, globalPartitionId, &commGroup, AT_,
"commGroup");
239 MPI_Comm_split(comm, masterTag, globalPartitionId, &commMasters, AT_,
"commMaster");
242 WeightType localOffset0 = F0;
243 WeightType localWorkload = F0;
244 for(IdType i = 0; i < noLocalWeights; i++) {
245 localWorkload += localWeights[i];
247 MPI_Exscan(&localWorkload, &localOffset0, 1, MPI_DOUBLE, MPI_SUM, comm, AT_,
"localWorkload",
"localOffset0");
249 for(IdType i = 0; i < noGroups; i++)
250 groupWeight[i] = ((WeightType)(i * noRanksPerGroup)) / ((WeightType)noGlobalPartitions);
253 std::vector<std::pair<IdType, MLong>> localPartitionCellOffsets;
254 localWorkload = localOffset0;
255 for(IdType i = 0; i < noLocalWeights; i++) {
256 WeightType f0 = localWorkload / totalWorkload;
257 WeightType f1 = (localWorkload + localWeights[i]) / totalWorkload;
258 for(IdType g = 1; g < noGroups; g++) {
259 if(f0 <= groupWeight(g) && f1 > groupWeight(g)) {
261 if(fabs(groupWeight(g) - f0) < fabs(f1 - groupWeight(g)))
262 localPartitionCellOffsets.push_back(std::make_pair(g, localOffset + i));
264 localPartitionCellOffsets.push_back(std::make_pair(g, localOffset + i + 1));
267 localWorkload += localWeights[i];
271 IdType recvCount = 0;
272 IdType noOffsetsFound = (signed)localPartitionCellOffsets.
size();
273 if(noOffsetsFound > 0) {
275 for(IdType i = 0; i < noOffsetsFound; i++) {
276 sendBuf(i, 0) = localPartitionCellOffsets[i].first;
277 sendBuf(i, 1) = localPartitionCellOffsets[i].second;
279 if(globalPartitionId == 0) {
280 for(IdType i = 0; i < noOffsetsFound; i++) {
281 groupOffsets[sendBuf(i, 0)] = sendBuf(i, 1);
285 MPI_Send(&sendBuf[0], 2 * noOffsetsFound, MPI_LONG, 0, 100, comm, AT_,
"sendBuf[0]");
290 if(globalPartitionId == 0) {
291 const WeightType time0 = MPI_Wtime();
292 WeightType time1 = MPI_Wtime();
294 groupOffsets[noGroups] = noGlobalWeights;
295 while(recvCount < noGroups - 1) {
299 MPI_Iprobe(MPI_ANY_SOURCE, 100, comm, &flag, &status);
302 MInt source = status.MPI_SOURCE;
303 MInt recvOffsets = recvSize / 2;
305 MPI_Recv(&recvBuf[0], recvSize, MPI_LONG, source, 100, comm, &status, AT_,
"recvBuf[0]");
306 for(IdType i = 0; i < recvOffsets; i++) {
307 groupOffsets[recvBuf(i, 0)] = recvBuf(i, 1);
309 recvCount += recvOffsets;
311 if((
MInt)((MPI_Wtime() - time0) / 10) > (
MInt)((time1 - time0) / 10)) {
312 std::cerr <<
"Rank " << globalPartitionId <<
" already waiting " << MPI_Wtime() - time0
313 <<
" seconds for incoming group offsets..." << std::endl;
320 MPI_Bcast(&groupOffsets[0], noGroups + 1, MPI_LONG, 0, comm, AT_,
"groupOffsets[0]");
324template <
typename WeightType,
typename IdType>
326 const IdType noLocalWeights,
327 const IdType localWeightOffset,
328 const IdType noDomains,
329 const IdType domainId,
330 const MPI_Comm mpiComm,
331 IdType*
const offsets) {
333 WeightType localTotalWeight = WeightType();
334 for(IdType i = 0; i < noLocalWeights; i++) {
335 localTotalWeight += localWeights[i];
339 WeightType prefixSumOffset = WeightType();
341 "localTotalWeight",
"prefixSumOffset");
344 WeightType globalWeight = prefixSumOffset + localTotalWeight;
348 const WeightType optimalWeight = globalWeight /
static_cast<WeightType
>(noDomains);
351 std::fill_n(offsets, noDomains, IdType());
354 IdType currentDomainId = std::floor(prefixSumOffset / optimalWeight);
357 std::fill(foundDomain.
begin(), foundDomain.
end(), IdType());
360 WeightType lastPrefixSum = prefixSumOffset;
361 for(IdType i = 1; i < noLocalWeights + 1; i++) {
363 const WeightType prefixSum = localWeights[i - 1] + lastPrefixSum;
365 const WeightType splitter = (currentDomainId + 1) * optimalWeight;
367 if(!(prefixSum < splitter)) {
370 offsets[currentDomainId] = localWeightOffset + i;
373 const WeightType distance =
ABS(prefixSum - splitter);
374 const WeightType lastDistance =
ABS(lastPrefixSum - splitter);
380 if(lastDistance < distance && offsets[currentDomainId] > offsets[currentDomainId - 1] + 1) {
381 offsets[currentDomainId]--;
384 foundDomain[currentDomainId] = domainId;
386 lastPrefixSum = prefixSum;
388 if(currentDomainId == noDomains - 1) {
393 if(lastPrefixSum + 0.0001 * optimalWeight > (currentDomainId + 1) * optimalWeight) {
394 offsets[currentDomainId + 1] = localWeightOffset + noLocalWeights;
395 foundDomain[currentDomainId + 1] = domainId;
396 std::cerr <<
"DEBUG partition: set offset " << currentDomainId + 1 <<
" on domain " << domainId
397 <<
" with last local partition cell " << localWeightOffset + noLocalWeights <<
" " << lastPrefixSum <<
" "
398 << (currentDomainId + 1) * optimalWeight << std::endl;
403 "MPI_IN_PLACE",
"foundDomain");
404 for(IdType i = 0; i < noDomains; i++) {
405 if(offsets[i] > 0 && foundDomain[i] != domainId) {
406 std::cerr <<
"DEBUG partition: reset offset " << i <<
" on domain " << domainId <<
", also found on domain "
407 << foundDomain[i] << std::endl;
414 "MPI_IN_PLACE",
"offsets");
417 for(IdType i = 1; i < noDomains; i++) {
418 if(offsets[i] <= offsets[i - 1]) {
419 m_log <<
"Partition: invalid offsets " << i - 1 <<
" " << offsets[i - 1] <<
" and " << i <<
" " << offsets[i]
420 <<
"; setting offset " << i <<
" to " << offsets[i - 1] + 1 << std::endl;
421 offsets[i] = offsets[i - 1] + 1;
427 std::fill(domainWeights.
begin(), domainWeights.
end(), WeightType());
430 IdType* domainIt = std::lower_bound(&offsets[0], &offsets[0] + noDomains, localWeightOffset);
432 IdType targetDomain = (*domainIt == localWeightOffset) ? std::distance(&offsets[0], domainIt)
433 : std::distance(&offsets[0], domainIt) - 1;
436 for(IdType i = 0; i < noLocalWeights; i++) {
437 domainWeights[targetDomain] += localWeights[i];
440 if(targetDomain < noDomains - 1 && localWeightOffset + i + 1 == offsets[targetDomain + 1]) {
447 "MPI_IN_PLACE",
"domainWeights");
449 WeightType maxWeight = WeightType();
450 WeightType minWeight = domainWeights[0];
453 for(IdType i = 0; i < noDomains; i++) {
455 maxWeight = std::max(maxWeight, domainWeights[i]);
456 minWeight = std::min(minWeight, domainWeights[i]);
458 m_log <<
"maximum domain weight " << maxWeight <<
" (max/opt = " << maxWeight / optimalWeight <<
")" << std::endl;
459 m_log <<
"minimum domain weight " << minWeight <<
" (min/opt = " << minWeight / optimalWeight <<
")" << std::endl;
This class is a ScratchSpace.
void fill(T val)
fill the scratch with a given value
void mTerm(const MInt errorCode, const MString &location, const MString &message)
constexpr T mMin(const T &x, const T &y)
constexpr T mMax(const T &x, const T &y)
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status, const MString &name, const MString &varname)
same as MPI_Recv
int MPI_Get_count(const MPI_Status *status, MPI_Datatype datatype, int *count, const MString &name)
same as MPI_Get_count
int MPI_Exscan(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Exscan
int MPI_Allreduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allreduce
int MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm *newcomm, const MString &name, const MString &varname)
same as MPI_Comm_split, but updates the number of MPI communicators
int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag, MPI_Status *status, const MString &name)
Iprobe MPI to get status without actually receiving the message.
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm, const MString &name, const MString &varname)
same as MPI_Bcast
int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, const MString &name, const MString &varname)
same as MPI_Send
void partitionCellToGlobalOffsets(const IdType *const partitionCellOffsets, const IdType *const partitionCellToGlobalIds, const IdType noDomains, IdType *const globalIdOffsets)
Translates a list of partition cell offsets to global offsets.
void partitionParallelSplit(const WeightType *const localWeights, const IdType noLocalWeights, const IdType localWeightOffset, const IdType noDomains, const IdType domainId, const MPI_Comm mpiComm, IdType *const offsets)
WeightType optimalPartitioningSerial(const WeightType *const weights, const IdType noWeights, const IdType noPartitions, IdType *const offsets)
Serial algorithm to find the optimal (not ideal) partitioning with given workloads based on a probing...
void heuristicPartitioningParallel(const WeightType *const localWeights, const IdType noLocalWeights, const MLong localOffset, const MLong noGlobalWeights, const WeightType totalWorkload, const IdType noGlobalPartitions, const IdType globalPartitionId, const IdType noGroups, const MPI_Comm comm, MPI_Comm &commGroup, MPI_Comm &commMasters, IdType &groupId, IdType &groupLocalId, MLong *const groupOffsets)
Fully parallel partitioning into <noGroups> partitions based on a heuristical approach.
Namespace for auxiliary functions/classes.