diff --git a/eo/src/mpi/eoMpi.cpp b/eo/src/mpi/eoMpi.cpp index 9ffc84bda..ab7543284 100644 --- a/eo/src/mpi/eoMpi.cpp +++ b/eo/src/mpi/eoMpi.cpp @@ -1,6 +1,5 @@ # include "eoMpi.h" -// MpiNode* MpiNodeStore::singleton; namespace eo { namespace mpi diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index d4e0349e3..230399040 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -37,6 +37,7 @@ namespace eo /** * @ingroup Parallel * @defgroup MPI Message Passing Interface parallelization + * @{ */ /** @@ -790,6 +791,10 @@ namespace eo } }; } + + /** + * @} + */ } # endif // __EO_MPI_H__ diff --git a/eo/src/mpi/eoMpiAssignmentAlgorithm.h b/eo/src/mpi/eoMpiAssignmentAlgorithm.h index 85177ea08..d42ec723d 100644 --- a/eo/src/mpi/eoMpiAssignmentAlgorithm.h +++ b/eo/src/mpi/eoMpiAssignmentAlgorithm.h @@ -1,28 +1,118 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __MPI_ASSIGNMENT_ALGORITHM_H__ # define __MPI_ASSIGNMENT_ALGORITHM_H__ -# include +# include // std::vector # include "eoMpiNode.h" namespace eo { namespace mpi { + /** + * @brief Constant indicating to use all the resting available workers, in assignment algorithms constructor + * using an interval. + */ const int REST_OF_THE_WORLD = -1; + /** + * @brief Contains informations on the available workers and allows to find assignees for jobs. + * + * Available workers are workers who aren't processing anything. When they've received an order, workers switch + * from the state "available" to the state "busy", and the master has to wait for their response for considering + * them available again. + * + * @ingroup Parallel + */ struct AssignmentAlgorithm { + /** + * @brief Gets the rank of an available worker, so as to send it a task. + * + * @return The MPI rank of an available worker, or -1 if there is no available worker. + */ virtual int get( ) = 0; + + /** + * @brief Gets the number of total available workers. + * + * Before the first call, it is equal to the total number of present workers, as specified in the + * specific assignment algorithm constructor. It allows the Job class to know when all the responses have + * been received, by comparing this number to the total number of workers. + * + * @return Integer indicating how many workers are available. + */ virtual int availableWorkers( ) = 0; + + /** + * @brief Reinject the worker of indicated rank in the available state. + * + * @param wrkRank The MPI rank of the worker who has finished its job. + */ virtual void confirm( int wrkRank ) = 0; + + /** + * @brief Indicates who are the workers which do nothing. + * + * At the end of the algorithm, the master has to warn all the workers that it's done. All the workers mean, + * the workers which are currently processing data, and the other ones who could be waiting : the idles. + * This function indicates to the master which worker aren't doing anything. + * + * @param A std::vector containing all the MPI ranks of the idles workers. + */ virtual std::vector idles( ) = 0; + + /** + * @brief Reinitializes the assignment algorithm with the right number of runs. + * + * In fact, this is only useful for static assignment algorithm, which has to be reinitialized every time + * it's used, in the case of a Multi Job. It's the user's responsability to call this function. + * + * @todo Not really clean. Find a better way to do it. + */ virtual void reinit( int runs ) = 0; }; + /** + * @brief Assignment (scheduling) algorithm which handles workers in a queue. + * + * With this assignment algorithm, workers are put in a queue and may be called an unlimited number of times. + * Whenever a worker returns, it is added to the queue, and it becomes available for the next call to get(). + * The available workers are all located in the queue at any time, so the number of available workers is + * directly equal to the size of the queue. + * + * This kind of assignment is adapted for tasks whose execution time is stochastic or unknown, but without any + * warranty to be faster than other assignments. + * + * @ingroup Parallel + */ struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm { public: + /** + * @brief Uses all the hosts whose rank is higher to 1, inclusive, as workers. + */ DynamicAssignmentAlgorithm( ) { for(int i = 1; i < Node::comm().size(); ++i) @@ -31,16 +121,33 @@ namespace eo } } + /** + * @brief Uses the unique host with given rank as a worker. + * + * @param unique MPI rank of the unique worker. + */ DynamicAssignmentAlgorithm( int unique ) { availableWrk.push_back( unique ); } + /** + * @brief Uses the workers whose ranks are present in the argument as workers. + * + * @param workers std::vector containing MPI ranks of workers. + */ DynamicAssignmentAlgorithm( const std::vector & workers ) { availableWrk = workers; } + /** + * @brief Uses a range of ranks as workers. + * + * @param first The first worker to be included (inclusive) + * @param last The last worker to be included (inclusive). If last == eo::mpi::REST_OF_THE_WORLD, all + * hosts whose rank is higher than first are taken. + */ DynamicAssignmentAlgorithm( int first, int last ) { if( last == REST_OF_THE_WORLD ) @@ -90,14 +197,43 @@ namespace eo std::vector< int > availableWrk; }; + /** + * @brief Assignment algorithm which gives to each worker a precise number of tasks to do, in a round robin + * fashion. + * + * This scheduling algorithm attributes, at initialization or when calling reinit(), a fixed amount of runs to + * distribute to the workers. The amount of runs is then equally distributed between all workers ; if total + * number of runs is not a direct multiple of workers number, then remainding unaffected runs are distributed to + * workers from the first to the last, in a round-robin fashion. + * + * This scheduling should be used when the amount of runs can be computed or is fixed and when we guess that the + * duration of processing task will be the same for each run. There is no warranty that this algorithm is more + * or less efficient that another one. When having a doubt, use DynamicAssignmentAlgorithm. + * + * @ingroup Parallel + */ struct StaticAssignmentAlgorithm : public AssignmentAlgorithm { public: + /** + * @brief Uses a given precise set of workers. + * + * @param workers std::vector of MPI ranks of workers which will be used. + * @param runs Fixed amount of runs, strictly positive. + */ StaticAssignmentAlgorithm( std::vector& workers, int runs ) { init( workers, runs ); } + /** + * @brief Uses a range of workers. + * + * @param first The first MPI rank of worker to use + * @param last The last MPI rank of worker to use. If it's equal to REST_OF_THE_WORLD, then all the + * workers from the first one are taken as workers. + * @param runs Fixed amount of runs, strictly positive. + */ StaticAssignmentAlgorithm( int first, int last, int runs ) { std::vector workers; @@ -114,6 +250,12 @@ namespace eo init( workers, runs ); } + /** + * @brief Uses all the hosts whose rank is higher than 1 (inclusive) as workers. + * + * @param runs Fixed amount of runs, strictly positive. If it's not set, you'll have to call reinit() + * later. + */ StaticAssignmentAlgorithm( int runs = 0 ) { std::vector workers; @@ -125,6 +267,12 @@ namespace eo init( workers, runs ); } + /** + * @brief Uses an unique host as worker. + * + * @param unique The MPI rank of the host which will be the worker. + * @param runs Fixed amount of runs, strictly positive. + */ StaticAssignmentAlgorithm( int unique, int runs ) { std::vector workers; @@ -133,15 +281,31 @@ namespace eo } private: + /** + * @brief Initializes the static scheduling. + * + * Gives to each worker an equal attribution number, equal to runs / workers.size(), eventually plus one + * if number of workers is not a divisor of runs. + * + * @param workers Vector of hosts' ranks + * @param runs Fixed amount of runs, strictly positive. + */ void init( std::vector & workers, int runs ) { unsigned int nbWorkers = workers.size(); freeWorkers = nbWorkers; - attributions.clear(); - attributions.reserve( nbWorkers ); busy.clear(); busy.resize( nbWorkers, false ); + realRank = workers; + + if( runs <= 0 ) + { + return; + } + + attributions.clear(); + attributions.reserve( nbWorkers ); // Let be the euclidean division of runs by nbWorkers : // runs == q * nbWorkers + r, 0 <= r < nbWorkers @@ -151,8 +315,6 @@ namespace eo // r requests to workers, in ascending order unsigned int diff = runs - (runs / nbWorkers) * nbWorkers; for (unsigned int i = 0; i < diff; ++attributions[i++]); - - realRank = workers; } public: diff --git a/eo/src/mpi/eoMpiNode.h b/eo/src/mpi/eoMpiNode.h index 9f1ea7b53..e836637f5 100644 --- a/eo/src/mpi/eoMpiNode.h +++ b/eo/src/mpi/eoMpiNode.h @@ -1,3 +1,24 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __MPI_NODE_H__ # define __MPI_NODE_H__ @@ -8,15 +29,34 @@ namespace eo { namespace mpi { + /** + * @brief Global object used to reach boost::mpi::communicator everywhere. + * + * boost::mpi::communicator is the main object used to send and receive messages between the different hosts of + * a MPI algorithm. + * + * @ingroup Parallel + */ class Node { public: + /** + * @brief Initializes the MPI environment with argc and argv. + * + * Should be called at the beginning of every parallel program. + * + * @param argc Main's argc + * @param argv Main's argv + */ static void init( int argc, char** argv ) { static bmpi::environment env( argc, argv ); } + /** + * @brief Returns the global boost::mpi::communicator + */ static bmpi::communicator& comm() { return _comm; diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index d0435cead..6f5120f81 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -1,28 +1,100 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EO_PARALLEL_APPLY_H__ # define __EO_PARALLEL_APPLY_H__ # include "eoMpi.h" -# include -# include +# include // eoUF +# include // std::vector population + +/** + * @file eoParallelApply.h + * + * @brief Applies a functor with single parameter to elements of a table, in a parallel fashion. + * + * This file contains all the required classes to do a parallel apply of a table, in a parallel fashion. This can be + * very useful when applying the function can be made without any dependances within the data. In EO, it occurs in + * particular during the evaluation: the number of individuals to evaluate can be really high, and the evaluation of one + * individual is independant from the evaluation of other individuals. + * + * Elements in the table are directly replaced, as the table is given by reference. No new table is made during the + * process. + * + * User can tune this job, indicating how many elements of the table should be sent and evaluated by a worker, at a + * time: this is called the "packet size", as individuals are groupped into a packet of individuals which are sent to + * the worker before evaluation. The problem of choosing the optimal packet size is beyond the purposes of this documentation + * and deserves a theoritical study. + * + * This job is the parallel equivalent to the function apply, defined in apply.h. It just applies the function to + * every element of a table. In Python or Javascript, it's the equivalent of the function Map. + */ namespace eo { namespace mpi { + /** + * @brief Structure used to save assignment to a worker, i.e which slice of the table it has to process. + * + * This slice is defined by the index of the first evaluated argument and the number of processed elements. + */ struct ParallelApplyAssignment { int index; int size; }; + /** + * @brief Data useful for a parallel apply (map). + * + * A parallel apply needs at least the functor to apply to every element of the table, and the table itself, + * whereas it can be set later with the function init(). Master rank is also needed, to send it informations and + * receive informations from it, inside the functors (the job knows these values, but the functors don't). The + * size of a packet can be tuned here. + * + * Internal attributes contain: + * - (useful for master) the index of the next element to be evaluated. + * - (useful for master) a map containing links between MPI ranks and slices of the table which the worker with + * this rank has evaluated. Without this map, when receiving results from a worker, the master couldn't be + * able to replace the right elements in the table. + * + * @ingroup Parallel + */ template struct ParallelApplyData { + /** + * @brief Ctor for Parallel apply (map) data. + * + * @param _proc The functor to apply on each element in the table + * @param _masterRank The MPI rank of the master + * @param _packetSize The number of elements on which the function will be applied by the worker, at a time. + * @param _pop The table to apply. If this value is NULL, user will have to call init() before launching the + * job. + */ ParallelApplyData( eoUF & _proc, int _masterRank, - // long _maxTime = 0, int _packetSize, std::vector * _pop = 0 ) : @@ -39,6 +111,9 @@ namespace eo } } + /** + * @brief Reinitializes the data for a new table to evaluate. + */ void init( std::vector& _pop ) { index = 0; @@ -52,11 +127,12 @@ namespace eo return *_data; } + // All elements are public since functors will often use them. std::vector * _data; eoUF & func; int index; int size; - std::map< int /* worker rank */, ParallelApplyAssignment /* min indexes in vector */> assignedTasks; + std::map< int /* worker rank */, ParallelApplyAssignment /* last assignment */> assignedTasks; int packetSize; std::vector tempArray; @@ -64,6 +140,15 @@ namespace eo bmpi::communicator& comm; }; + /** + * @brief Send task functor implementation for the parallel apply (map) job. + * + * Master side: Sends a slice of the table to evaluate to the worker. + * + * Implementation details: + * Finds the next slice of data to send to the worker, sends first the size and then the data, and memorizes + * that this slice has been distributed to the worker, then updates the next position of element to evaluate. + */ template< class EOT > class SendTaskParallelApply : public SendTaskFunction< ParallelApplyData > { @@ -100,6 +185,11 @@ namespace eo } }; + /** + * @brief Handle response functor implementation for the parallel apply (map) job. + * + * Master side: Replaces the slice of data attributed to the worker in the table. + */ template< class EOT > class HandleResponseParallelApply : public HandleResponseFunction< ParallelApplyData > { @@ -117,6 +207,14 @@ namespace eo } }; + /** + * @brief Process task functor implementation for the parallel apply (map) job. + * + * Worker side: apply the function to the given slice of data. + * + * Implementation details: retrieves the number of elements to evaluate, retrieves them, applies the function + * and then returns the results. + */ template< class EOT > class ProcessTaskParallelApply : public ProcessTaskFunction< ParallelApplyData > { @@ -145,6 +243,12 @@ namespace eo } }; + /** + * @brief Is finished functor implementation for the parallel apply (map) job. + * + * Master side: returns true if and only if the whole table has been evaluated. The job is also terminated only + * when the whole table has been evaluated. + */ template< class EOT > class IsFinishedParallelApply : public IsFinishedFunction< ParallelApplyData > { @@ -162,6 +266,14 @@ namespace eo } }; + /** + * @brief Store containing all the datas and the functors for the parallel apply (map) job. + * + * User can tune functors when constructing the object. For each functor which is not given, a default one is + * generated. + * + * @ingroup Parallel + */ template< class EOT > struct ParallelApplyStore : public JobStore< ParallelApplyData > { @@ -170,6 +282,17 @@ namespace eo using JobStore< ParallelApplyData >::_ptf; using JobStore< ParallelApplyData >::_iff; + /** + * @brief Main constructor for the parallel apply (map) job. + * + * @param _proc The procedure to apply to each element of the table. + * @param _masterRank The rank of the master process. + * @param _packetSize The number of elements of the table to be evaluated at a time, by the worker. + * @param stpa Pointer to Send Task parallel apply functor descendant. If null, a default one is used. + * @param hrpa Pointer to Handle Response parallel apply functor descendant. If null, a default one is used. + * @param ptpa Pointer to Process Task parallel apply functor descendant. If null, a default one is used. + * @param ifpa Pointer to Is Finished parallel apply functor descendant. If null, a default one is used. + */ ParallelApplyStore( eoUF & _proc, int _masterRank, @@ -210,12 +333,17 @@ namespace eo ParallelApplyData* data() { return &_data; } + /** + * @brief Reinits the store with a new table to evaluate. + * + * @param _pop The table of elements to be evaluated. + */ void data( std::vector& _pop ) { _data.init( _pop ); } - virtual ~ParallelApplyStore() + virtual ~ParallelApplyStore() // for inheritance purposes only { } @@ -223,8 +351,15 @@ namespace eo ParallelApplyData _data; }; - // TODO commentaire : impossible de faire un typedef sur un template sans passer - // par un traits => complique la tâche de l'utilisateur pour rien. + /** + * @brief Parallel apply job. Present for convenience only. + * + * A typedef wouldn't have been working, as typedef on templates don't work in C++. Traits would be a + * disgraceful overload for the user. + * + * @ingroup Parallel + * @see eoParallelApply.h + */ template< typename EOT > class ParallelApply : public MultiJob< ParallelApplyData > { diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h index ed45ca052..ccbd3521e 100644 --- a/eo/src/mpi/eoTerminateJob.h +++ b/eo/src/mpi/eoTerminateJob.h @@ -1,3 +1,24 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EO_TERMINATE_H__ # define __EO_TERMINATE_H__ @@ -7,6 +28,14 @@ namespace eo { namespace mpi { + /** + * @ingroup Parallel + * @{ + */ + + /** + * @brief Send task functor which does nothing. + */ struct DummySendTaskFunction : public SendTaskFunction { void operator()( int _ ) @@ -15,6 +44,9 @@ namespace eo } }; + /** + * @brief Handle response functor which does nothing. + */ struct DummyHandleResponseFunction : public HandleResponseFunction { void operator()( int _ ) @@ -23,6 +55,9 @@ namespace eo } }; + /** + * @brief Process task functor which does nothing. + */ struct DummyProcessTaskFunction : public ProcessTaskFunction { void operator()() @@ -31,6 +66,9 @@ namespace eo } }; + /** + * @brief Is finished functor which returns true everytime. + */ struct DummyIsFinishedFunction : public IsFinishedFunction { bool operator()() @@ -39,6 +77,9 @@ namespace eo } }; + /** + * @brief Job store containing all dummy functors and containing no data. + */ struct DummyJobStore : public JobStore { using JobStore::_stf; @@ -61,11 +102,20 @@ namespace eo void* data() { return 0; } }; + /** + * @brief Job to run after a Multi Job, so as to indicate that every workers should terminate. + */ struct EmptyJob : public OneShotJob { + /** + * @brief Main EmptyJob ctor + * + * @param algo Assignment (scheduling) algorithm used. + * @param masterRank The rank of the master process. + */ EmptyJob( AssignmentAlgorithm& algo, int masterRank ) : OneShotJob( algo, masterRank, *(new DummyJobStore) ) - // FIXME memory leak => will be corrected by using const correctness + // FIXME memory leak, meaningless but present { // empty } @@ -79,6 +129,10 @@ namespace eo } } }; + + /** + * @} + */ } }