/* 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 es.uvigo.darwin.prottest.exe.ParallelModelEstimator; import mpi.MPI; import mpi.Request; import es.uvigo.darwin.prottest.exe.RunEstimator; import es.uvigo.darwin.prottest.global.options.ApplicationOptions; import es.uvigo.darwin.prottest.model.Model; import es.uvigo.darwin.prottest.observer.ObservableModelUpdater; import es.uvigo.darwin.prottest.util.checkpoint.CheckPointManager; import es.uvigo.darwin.prottest.util.collection.ModelCollection; import es.uvigo.darwin.prottest.util.collection.SingleModelCollection; import es.uvigo.darwin.prottest.util.comparator.ModelWeightComparator; import es.uvigo.darwin.prottest.util.exception.ProtTestInternalException; /** * The Class ImprovedDynamicDistributionStrategy. */ public class HybridDistributionStrategy extends DistributionStrategy { /** The Constant TAG_SEND_REQUEST. */ private static final int TAG_SEND_REQUEST = 1; /** MPJ Tag for sending a new model. */ private static final int TAG_EXIST_MORE_MODELS = 2; /** The Constant TAG_SEND_MODEL. */ private static final int TAG_SEND_MODEL = 3; private int maxPEs; /** The number of available PEs. */ int availablePEs; /** The root model request. */ boolean rootModelRequest; /** The root model. */ Model rootModel; private ParallelModelEstimator pme; private Model[] computedModels; private MultipleDistributor distributor; /** * Instantiates a new improved dynamic distribution strategy. * * @param mpjMe the rank of the current process in MPJ * @param mpjSize the size of the MPJ communicator * @param options the application options * @param cpManager the checkpoint manager, it can be null if checkpointing is not supported */ public HybridDistributionStrategy(int mpjMe, int mpjSize, ApplicationOptions options, CheckPointManager cpManager) { this(mpjMe, mpjSize, options, cpManager, Runtime.getRuntime().availableProcessors()); } /** * Instantiates a new improved dynamic distribution strategy. * * @param mpjMe the rank of the current process in MPJ * @param mpjSize the size of the MPJ communicator * @param options the application options * @param cpManager the checkpoint manager, it can be null if checkpointing is not supported */ public HybridDistributionStrategy(int mpjMe, int mpjSize, ApplicationOptions options, CheckPointManager cpManager, int numberOfThreads) { super(mpjMe, mpjSize, options, cpManager); if (mpjSize == 1) { throw new ProtTestInternalException("Dynamic Distribution Strategy" + " requires at least 2 processors"); } itemsPerProc = new int[mpjSize]; displs = new int[mpjSize]; modelSet = new SingleModelCollection(options.getAlignment()); maxPEs = numberOfThreads; availablePEs = maxPEs; pme = new ParallelModelEstimator(maxPEs, options.getAlignment()); pme.addObserver(this); } /* (non-Javadoc) * @see es.uvigo.darwin.prottest.facade.strategy.DistributionStrategy#distribute(es.uvigo.darwin.prottest.util.collection.ModelCollection) */ public Model[] distribute(ModelCollection arrayListModel, ModelWeightComparator comparator) { distributor = new MultipleDistributor(this, arrayListModel, comparator, mpjMe, mpjSize); Thread distributorThread = new Thread(distributor); distributorThread.start(); numberOfModels = arrayListModel.size(); request(); computationDone(); return computedModels; } /* (non-Javadoc) * @see es.uvigo.darwin.prottest.facade.strategy.DistributionStrategy#request() */ public void request() { startTime = System.currentTimeMillis(); // List<RunEstimator> runenvList = new ArrayList<RunEstimator>(); while (true) { // send request to root Model[] modelToReceive = null; Model model = null; if (mpjMe > 0) { int[] sendMessage = {availablePEs}; Request modelRequest = MPI.COMM_WORLD.Isend(sendMessage, 0, 1, MPI.INT, 0, TAG_SEND_REQUEST); // prepare reception modelToReceive = new Model[1]; boolean[] notification = new boolean[1]; // wait for request modelRequest.Wait(); Request notifyRecv = MPI.COMM_WORLD.Irecv(notification, 0, 1, MPI.BOOLEAN, 0, TAG_EXIST_MORE_MODELS); notifyRecv.Wait(); if (notification[0]) { // receive model Request modelReceive = MPI.COMM_WORLD.Irecv(modelToReceive, 0, 1, MPI.OBJECT, 0, TAG_SEND_MODEL); modelReceive.Wait(); model = modelToReceive[0]; } else { break; } } else { // 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 amongst this class and Distributor, having to define two // public attributes: rootModelRequest and rootModel. rootModelRequest = true; while (rootModelRequest) { try { Thread.sleep(200); } catch (InterruptedException e) { throw new ProtTestInternalException("Thread interrupted"); } } model = rootModel; if (model == null) { break; } } if (model != null) { availablePEs -= MultipleDistributor.getPEs(model, maxPEs); // compute modelSet.add(model); RunEstimator runenv = factory.createRunEstimator(options, model, MultipleDistributor.getPEs(model, maxPEs)); // runenv.addObserver(this); pme.execute(runenv); while (availablePEs <= 0) { try { Thread.sleep(200); } catch (InterruptedException e) { throw new ProtTestInternalException("Thread interrupted"); } } } } endTime = System.currentTimeMillis(); while (pme.hasMoreTasks()) { try { Thread.sleep(400); } catch (InterruptedException e) { throw new ProtTestInternalException("Thread interrupted"); } } if (mpjMe > 0) { gather(); } else { computedModels = gather(); } } /* (non-Javadoc) * @see es.uvigo.darwin.prottest.facade.ProtTestFacade#update(es.uvigo.darwin.prottest.observer.Observable, java.lang.Object) */ @Override public void update(ObservableModelUpdater o, Model model, ApplicationOptions options) { if (options != null && model.isComputed()) { availablePEs += MultipleDistributor.getPEs(model, maxPEs); } super.update(o, model, options); } /** * Gathers the models of all processors into the root one. This method should * be called by every processor after computing likelihood value of whole model set. * * This method will return an empty array of models for every non-root processor * * @return the array of gathered models */ private Model[] gather() { Model[] allModels = new Model[numberOfModels]; if (distributor != null) { itemsPerProc = distributor.getItemsPerProc(); displs = distributor.getDispls(); } MPI.COMM_WORLD.Bcast(itemsPerProc, 0, mpjSize, MPI.INT, 0); // gathering optimized models MPI.COMM_WORLD.Gatherv(modelSet.toArray(new Model[0]), 0, modelSet.size(), MPI.OBJECT, allModels, 0, itemsPerProc, displs, MPI.OBJECT, 0); return allModels; } }