/*
Copyright (C) 2009 Diego Darriba
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package es.uvigo.darwin.prottest.facade.strategy;
import java.util.Collections;
import mpi.MPI;
import mpi.Request;
import mpi.Status;
import es.uvigo.darwin.prottest.model.Model;
import es.uvigo.darwin.prottest.util.collection.ModelCollection;
import es.uvigo.darwin.prottest.util.comparator.ModelWeightComparator;
import es.uvigo.darwin.prottest.util.exception.ProtTestInternalException;
/**
* This class distributes wrok load in an MPJ-Express environment.
* Its behavior is inherently coupled to
* ImprovedDynamicDistributionStrategy's, providing the root process with
* the possibility of distribute the models and also computing likelihood
* values asynchronously.
*
* @see ImprovedDynamicDistributionStrategy
*/
public class Distributor implements Runnable {
/** MPJ Tag for requesting a new model. */
private static final int TAG_SEND_REQUEST = 1;
/** MPJ Tag for sending a new model. */
private static final int TAG_SEND_MODEL = 2;
/** MPJ Rank of the processor. */
private int mpjMe;
/** MPJ Size of the communicator. */
private int mpjSize;
/** The improved dynamic distribution strategy who calls this instance. */
private ImprovedDynamicDistributionStrategy caller;
/** The collection of all models to distribute. */
private ModelCollection modelCollection;
/** The heuristic algorithm to compare models. */
private ModelWeightComparator comparator;
/** The number of models per processor. It will be necessary
* by the root process to do the non-uniform gathering */
private int[] itemsPerProc;
/** The array of displacements after the distribution.
* It will be necessary by the root process to do the non-uniform gathering */
private int[] displs;
/**
* Gets the array of items per processor.
*
* @return the array of items per processor
*/
public int[] getItemsPerProc() { return itemsPerProc; }
/**
* Gets the array of displacements. The root process needs this attribute
* in order to do the non-uniform gathering.
*
* @return the array of displacements
*/
public int[] getDispls() { return displs; }
/**
* Instantiates a new distributor. The root process needs this attribute
* in order to do the non-uniform gathering.
*
* @param caller the ImprovedDynamicDistributionStrategy instance which calls this constructor
* @param modelCollection the models to distribute amongst processors
* @param mpjMe the rank of the current process in MPJ
* @param mpjSize the size of the MPJ communicator
*/
public Distributor(ImprovedDynamicDistributionStrategy caller,
ModelCollection modelCollection, ModelWeightComparator comparator,
int mpjMe, int mpjSize) {
this.comparator = comparator;
this.modelCollection = modelCollection;
this.mpjMe = mpjMe;
this.mpjSize = mpjSize;
this.caller = caller;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
distribute(modelCollection, comparator);
}
/**
* Distributes the whole model collection amongst the processors in
* the communicator. This method should be synchronized with the
* request method in the Improved Dynamic Distribution Strategy.
*
* @see es.uvigo.darwin.prottest.facade.strategy.ImprovedDynamicDistributionStrategy#request()
*
* @param modelSet the model collection to distribute amongst processors
* @param comparator implementation of the heuristic algorithm for sort and distribute models
*/
private void distribute(ModelCollection modelSet, ModelWeightComparator comparator) {
itemsPerProc = new int[mpjSize];
displs = new int[mpjSize];
Collections.sort(modelSet, comparator);
for (Model model : modelSet) {
// check root processor
//
// This strategy is an easy way to avoid the problem of thread-safety
// in MPJ-Express. It works correctly, but it also causes to introduce
// coupling between this class and ImprovedDynamicDistributionStrategy,
// having to define two public attributes: rootModelRequest and rootModel.
//
if (caller.rootModelRequest) {
if (caller.rootModel != null) {
caller.setCheckpoint(modelSet);
}
caller.rootModel = model;
caller.rootModelRequest = false;
itemsPerProc[mpjMe]++;
} else {
Model[] computedModel = new Model[1];
// getModel request
Request modelRequest = MPI.COMM_WORLD.Irecv(computedModel, 0, 1, MPI.OBJECT, MPI.ANY_SOURCE, TAG_SEND_REQUEST);
// prepare model
Model[] modelToSend = new Model[1];
modelToSend[0] = model;
// wait for request
Status requestStatus = modelRequest.Wait();
if (computedModel[0] != null) {
// set checkpoint
int index = modelSet.indexOf(computedModel[0]);
modelSet.set(index, computedModel[0]);
caller.setCheckpoint(modelSet);
}
// send model
Request modelSend = MPI.COMM_WORLD.Isend(modelToSend, 0, 1, MPI.OBJECT, requestStatus.source, TAG_SEND_MODEL);
// update structures
itemsPerProc[requestStatus.source]++;
// wait for send
modelSend.Wait();
}
}
displs[0] = 0;
for (int i = 1; i < mpjSize; i++)
displs[i] = displs[i-1] + itemsPerProc[i-1];
// finalize
for (int i = 1; i < mpjSize; i++) {
Model[] computedModel = new Model[1];
// getModel request
Request modelRequest = MPI.COMM_WORLD.Irecv(computedModel, 0, 1, MPI.OBJECT, MPI.ANY_SOURCE, TAG_SEND_REQUEST);
Model[] modelToSend = { null };
// wait for request
Status requestStatus = modelRequest.Wait();
if (computedModel[0] != null) {
// set checkpoint
int index = modelSet.indexOf(computedModel[0]);
modelSet.set(index, computedModel[0]);
caller.setCheckpoint(modelSet);
}
// send null model
Request modelSend = MPI.COMM_WORLD.Isend(modelToSend, 0, 1, MPI.OBJECT, requestStatus.source, TAG_SEND_MODEL);
modelSend.Wait();
}
// check root
while (!caller.rootModelRequest) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new ProtTestInternalException("Thread interrupted");
}
}
caller.rootModel = null;
caller.rootModelRequest = false;
}
}