/* Copyright (C) 2011 Diego Darriba, David Posada 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 3 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.jmodeltest.exe; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Observable; import javax.management.RuntimeErrorException; import mpi.MPI; import mpi.Request; import mpi.Status; import es.uvigo.darwin.jmodeltest.exception.InternalException; import es.uvigo.darwin.jmodeltest.model.Model; import es.uvigo.darwin.jmodeltest.observer.ProgressInfo; import es.uvigo.darwin.jmodeltest.utilities.Utilities; public class Distributor extends Observable implements Runnable { /** MPJ Tag for requesting a new model. */ public static final int TAG_SEND_REQUEST = 1; /** MPJ Tag for sending a new model. */ public static final int TAG_SEND_MODEL = 2; private List<Model> models; private RunPhymlMPJ caller; /** MPJ Rank of the processor. */ private int mpjMe; /** MPJ Size of the communicator. */ private int mpjSize; /** * 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; public Distributor(List<Model> models, RunPhymlMPJ caller, int mpjMe, int mpjSize) { this.mpjMe = mpjMe; this.mpjSize = mpjSize; this.models = models; this.caller = caller; this.itemsPerProc = new int[mpjSize]; this.displs = new int[mpjSize]; Collections.sort(this.models, new ModelComparator()); } public void distribute(List<Model> models) throws InterruptedException { int index = 0; for (Model model : models) { // 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 RunPhymlMPJ having to define two volatile attributes: // rootModelRequest and rootModel. if (caller.rootModelRequest || mpjSize == 1) { while (!caller.rootModelRequest) { try { Thread.sleep(200); } catch (InterruptedException e) { throw new InternalException( "Thread interrupted"); } } if (caller.rootModel != null) { // finalized model optimization notifyObservers(ProgressInfo.SINGLE_OPTIMIZATION_COMPLETED, index, caller.rootModel, Utilities.displayRuntime(caller.rootModel .getComputationTime())); } notifyObservers(ProgressInfo.SINGLE_OPTIMIZATION_INIT, index, model, null); 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) { // finalized model optimization int recvIndex = models.indexOf(computedModel[0]); notifyObservers(ProgressInfo.SINGLE_OPTIMIZATION_COMPLETED, index, computedModel[0], Utilities.displayRuntime(computedModel[0] .getComputationTime())); models.set(recvIndex, computedModel[0]); } // send model Request modelSend = MPI.COMM_WORLD.Isend(modelToSend, 0, 1, MPI.OBJECT, requestStatus.source, TAG_SEND_MODEL); notifyObservers(ProgressInfo.SINGLE_OPTIMIZATION_INIT, index, model, null); // update structures itemsPerProc[requestStatus.source]++; // wait for send modelSend.Wait(); } index++; } 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) { int recvIndex = models.indexOf(computedModel[0]); notifyObservers(ProgressInfo.SINGLE_OPTIMIZATION_COMPLETED, index, computedModel[0], Utilities.displayRuntime(computedModel[0] .getComputationTime())); models.set(recvIndex, computedModel[0]); } // 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 e; } } if (caller.rootModel != null) { notifyObservers(ProgressInfo.SINGLE_OPTIMIZATION_COMPLETED, index, caller.rootModel, Utilities.displayRuntime(caller.rootModel .getComputationTime())); } caller.rootModel = null; caller.rootModelRequest = false; } public int[] getItemsPerProc() { return itemsPerProc; } public int[] getDispls() { return displs; } @Override public void run() { try { distribute(models); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeErrorException(new Error(e)); } } private class ModelComparator implements Comparator<Model> { /* * (non-Javadoc) * * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ public int compare(Model model1, Model model2) { int value = 0; if (model1 != null && model2 != null) value = getWeight(model2) - getWeight(model1); return value; } private int getWeight(Model model) { int weight = 0; if (model.ispG()) weight += 4; if (model.ispI()) weight += 2; if (model.ispF()) weight += 1; return weight; } } private void notifyObservers(int type, int value, Model model, String message) { setChanged(); notifyObservers(new ProgressInfo(type, value, model, message)); } }