manual merge on apply.h

This commit is contained in:
Johann Dreo 2012-07-20 15:05:08 +02:00
commit 20d06df7d1
65 changed files with 7559 additions and 65 deletions

5
eo/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
*.swp
debug/
release/
*CMakeFiles*
*Makefile

View file

@ -45,10 +45,12 @@ ENABLE_LANGUAGE(C)
### 2) Include required modules / configuration files
#####################################################################################
FIND_PACKAGE(OpenMP)
IF(OPENMP_FOUND)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
IF(WITH_OMP)
FIND_PACKAGE(OpenMP)
IF(OPENMP_FOUND)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
ENDIF()
ENDIF()
INCLUDE(CMakeBackwardCompatibilityCXX)
@ -119,6 +121,11 @@ ENDIF (ENABLE_CMAKE_TESTING)
ADD_SUBDIRECTORY(doc)
ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(test)
IF(WITH_MPI)
ADD_SUBDIRECTORY(test/mpi)
ENDIF()
ADD_SUBDIRECTORY(tutorial)
######################################################################################

View file

@ -6,3 +6,13 @@ SET(PROJECT_VERSION_PATCH 0)
SET(PROJECT_VERSION_MISC "-edge")
# ADD_DEFINITIONS(-DDEPRECATED_MESSAGES) # disable warning deprecated function messages
# If you plan to use OpenMP, put the following boolean to true :
SET(WITH_OMP FALSE CACHE BOOL "Use OpenMP ?" FORCE)
# If you plan to use MPI, precise here where are the static libraries from
# openmpi and boost::mpi.
SET(WITH_MPI FALSE CACHE BOOL "Use mpi ?" FORCE)
SET(MPI_DIR "/mpi/directory" CACHE PATH "OpenMPI directory" FORCE)
SET(BOOST_DIR "/boost/directory" CACHE PATH "Boost directory" FORCE)

View file

@ -5,7 +5,32 @@
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
######################################################################################
### 2) Define the eo target
### 2) Optional: add MPI and Boost MPI dependencies.
######################################################################################
IF(WITH_MPI)
MESSAGE("[EO] Compilation with MPI and BoostMPI.")
SET(CMAKE_CXX_COMPILER "${MPI_DIR}/bin/mpicxx")
# headers location
INCLUDE_DIRECTORIES(${MPI_DIR}/include)
INCLUDE_DIRECTORIES(${BOOST_DIR}/include)
# lib location
LINK_DIRECTORIES(${MPI_DIR}/lib)
LINK_DIRECTORIES(${BOOST_DIR}/lib)
# for conditional compilation in code
ADD_DEFINITIONS(-DWITH_MPI)
LINK_LIBRARIES(boost_mpi boost_serialization)
ADD_SUBDIRECTORY(mpi)
ENDIF()
######################################################################################
### 3) Define the eo target
######################################################################################
SET(EO_LIB_OUTPUT_PATH ${EO_BINARY_DIR}/lib)
@ -27,14 +52,14 @@ FILE(GLOB HDRS *.h eo)
INSTALL(FILES ${HDRS} DESTINATION include/eo COMPONENT headers)
######################################################################################
### 3) Optionnal: define your target(s)'s version: no effect for windows
### 4) Optionnal: define your target(s)'s version: no effect for windows
######################################################################################
SET(EO_VERSION ${GLOBAL_VERSION})
SET_TARGET_PROPERTIES(eo PROPERTIES VERSION "${EO_VERSION}")
######################################################################################
### 4) Where must cmake go now ?
### 5) Where must cmake go now ?
######################################################################################
ADD_SUBDIRECTORY(do)
@ -43,6 +68,7 @@ ADD_SUBDIRECTORY(ga)
ADD_SUBDIRECTORY(gp)
ADD_SUBDIRECTORY(other)
ADD_SUBDIRECTORY(utils)
ADD_SUBDIRECTORY(serial)
IF(ENABLE_PYEO)
ADD_SUBDIRECTORY(pyeo)

View file

@ -19,7 +19,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact: todos@geneura.ugr.es, http://geneura.ugr.es
mak@dhi.dk
mak@dhi.dk
*/
//-----------------------------------------------------------------------------
@ -51,41 +51,41 @@ void apply(eoUF<EOT&, void>& _proc, std::vector<EOT>& _pop)
double t1 = 0;
if ( eo::parallel.enableResults() )
{
t1 = omp_get_wtime();
}
{
t1 = omp_get_wtime();
}
if (!eo::parallel.isDynamic())
{
{
#pragma omp parallel for if(eo::parallel.isEnabled()) //default(none) shared(_proc, _pop, size)
#ifdef _MSC_VER
//Visual Studio supports only OpenMP version 2.0 in which
//an index variable must be of a signed integral type
for (long long i = 0; i < size; ++i) { _proc(_pop[i]); }
//Visual Studio supports only OpenMP version 2.0 in which
//an index variable must be of a signed integral type
for (long long i = 0; i < size; ++i) { _proc(_pop[i]); }
#else // _MSC_VER
for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); }
for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); }
#endif
}
}
else
{
{
#pragma omp parallel for schedule(dynamic) if(eo::parallel.isEnabled())
#ifdef _MSC_VER
//Visual Studio supports only OpenMP version 2.0 in which
//an index variable must be of a signed integral type
for (long long i = 0; i < size; ++i) { _proc(_pop[i]); }
//Visual Studio supports only OpenMP version 2.0 in which
//an index variable must be of a signed integral type
for (long long i = 0; i < size; ++i) { _proc(_pop[i]); }
#else // _MSC_VER
//doesnot work with gcc 4.1.2
//default(none) shared(_proc, _pop, size)
for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); }
//doesnot work with gcc 4.1.2
//default(none) shared(_proc, _pop, size)
for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); }
#endif
}
}
if ( eo::parallel.enableResults() )
{
double t2 = omp_get_wtime();
eoLogger log;
log << eo::file(eo::parallel.prefix()) << t2 - t1 << ' ';
}
{
double t2 = omp_get_wtime();
eoLogger log;
log << eo::file(eo::parallel.prefix()) << t2 - t1 << ' ';
}
#else // _OPENMP
@ -109,7 +109,7 @@ void apply(eoUF<EOT&, void>& _proc, std::vector<EOT>& _pop)
// //default(none) shared(_proc, _pop, size)
// for (size_t i = 0; i < size; ++i)
// {
// _proc(_pop[i]);
// _proc(_pop[i]);
// }
// }
@ -127,7 +127,7 @@ void apply(eoUF<EOT&, void>& _proc, std::vector<EOT>& _pop)
// //default(none) shared(_proc, _pop, size)
// for (size_t i = 0; i < size; ++i)
// {
// _proc(_pop[i]);
// _proc(_pop[i]);
// }
// }

View file

@ -142,6 +142,9 @@
#include <utils/eoRealVectorBounds.h> // includes eoRealBounds.h
#include <utils/eoIntBounds.h> // no eoIntVectorBounds
// Serialization stuff
#include <serial/eoSerial.h>
// aliens
#include <other/external_eo>
#include <eoCounter.h>

View file

@ -37,8 +37,6 @@
#include <eoMergeReduce.h>
#include <eoReplacement.h>
template <class EOT> class eoIslandsEasyEA ;
template <class EOT> class eoDistEvalEasyEA ;
@ -102,6 +100,33 @@ template<class EOT> class eoEasyEA: public eoAlgo<EOT>
offspring.reserve(_offspringSize); // This line avoids an incremental resize of offsprings.
}
/**
* @brief Ctor allowing to specify which pop eval function we're going to use.
*
* Ctor taking a breed and merge, an overload of ctor to define an offspring size, and
* the pop eval function used. This allows to precise if we would like to use the
* parallel evaluation, for instance.
*/
eoEasyEA(
eoContinue<EOT>& _continuator,
eoEvalFunc<EOT>& _eval,
eoPopEvalFunc<EOT>& _pop_eval,
eoBreed<EOT>& _breed,
eoReplacement<EOT>& _replace,
unsigned _offspringSize
) : continuator(_continuator),
eval (_eval),
loopEval(_eval),
popEval(_pop_eval),
selectTransform(dummySelect, dummyTransform),
breed(_breed),
mergeReduce(dummyMerge, dummyReduce),
replace(_replace),
isFirstCall(true)
{
offspring.reserve(_offspringSize); // This line avoids an incremental resize of offsprings.
}
/*
eoEasyEA(eoContinue <EOT> & _continuator,
eoPopEvalFunc <EOT> & _pop_eval,
@ -219,45 +244,44 @@ template<class EOT> class eoEasyEA: public eoAlgo<EOT>
/// Apply a few generation of evolution to the population.
virtual void operator()(eoPop<EOT>& _pop)
{
if (isFirstCall)
{
size_t total_capacity = _pop.capacity() + offspring.capacity();
_pop.reserve(total_capacity);
offspring.reserve(total_capacity);
isFirstCall = false;
}
eoPop<EOT> empty_pop;
popEval(empty_pop, _pop); // A first eval of pop.
do
if (isFirstCall)
{
try
size_t total_capacity = _pop.capacity() + offspring.capacity();
_pop.reserve(total_capacity);
offspring.reserve(total_capacity);
isFirstCall = false;
}
eoPop<EOT> empty_pop;
do
{
try
{
unsigned pSize = _pop.size();
offspring.clear(); // new offspring
unsigned pSize = _pop.size();
breed(_pop, offspring);
offspring.clear(); // new offspring
popEval(_pop, offspring); // eval of parents + offspring if necessary
breed(_pop, offspring);
replace(_pop, offspring); // after replace, the new pop. is in _pop
popEval(_pop, offspring); // eval of parents + offspring if necessary
if (pSize > _pop.size())
throw std::runtime_error("Population shrinking!");
else if (pSize < _pop.size())
throw std::runtime_error("Population growing!");
replace(_pop, offspring); // after replace, the new pop. is in _pop
if (pSize > _pop.size())
throw std::runtime_error("Population shrinking!");
else if (pSize < _pop.size())
throw std::runtime_error("Population growing!");
}
catch (std::exception& e)
catch (std::exception& e)
{
std::string s = e.what();
s.append( " in eoEasyEA");
throw std::runtime_error( s );
std::string s = e.what();
s.append( " in eoEasyEA");
throw std::runtime_error( s );
}
}
while ( continuator( _pop ) );
while ( continuator( _pop ) );
}
protected :

View file

@ -30,6 +30,16 @@
#include <eoEvalFunc.h>
#include <apply.h>
# ifdef WITH_MPI
#include <mpi/eoMpi.h>
#include <mpi/eoTerminateJob.h>
#include <mpi/eoMpiAssignmentAlgorithm.h>
#include <mpi/eoParallelApply.h>
#include <utils/eoParallel.h>
#include <cmath> // ceil
# endif // WITH_MPI
/** eoPopEvalFunc: This abstract class is for GLOBAL evaluators
* of a population after variation.
* It takes 2 populations (typically the parents and the offspring)
@ -77,6 +87,142 @@ private:
eoEvalFunc<EOT> & eval;
};
#ifdef WITH_MPI
/**
* @brief Evaluator of a population of EOT which uses parallelization to evaluate individuals.
*
* This class implements an instance of eoPopEvalFunc that applies a private eoEvalFunc to
* all offspring, but in a parallel way. The original process becomes the central host from a network ("master"), and
* other machines disponible in the MPI network ("slaves") are used as evaluators. Population to evaluate is splitted in
* little packets of individuals, which are sent to the evaluators, that process the effective call to eval. Once all
* the individuals have been evaluated, they are returned to the master. The whole process is entirely invisible to the
* eyes of the user, who just has to launch a certain number of processes in MPI so as to have a result.
*
* The eoEvalFunc is no more directly given, but it is stored in the eo::mpi::ParallelApplyStore, which can be
* instanciated if no one is given at construction.
*
* The use of this class requires the user to have called the eo::mpi::Node::init function, at the beginning of its
* program.
*
* @ingroup Evaluation Parallel
*
* @author Benjamin Bouvier <benjamin.bouvier@gmail.com>
*/
template<class EOT>
class eoParallelPopLoopEval : public eoPopEvalFunc<EOT>
{
public:
/**
* @brief Constructor which creates the job store for the user.
*
* This constructor is the simplest to use, as it creates the store used by the parallel job, for the user.
* The user just precises the scheduling algorithm, the rank of the master and then gives its eval function and
* the size of a packet (how many individuals should be in a single message to evaluator).
*
* @param _assignAlgo The scheduling algorithm used to give orders to evaluators.
* @param _masterRank The MPI rank of the master.
* @param _eval The evaluation functor used to evaluate each individual in the population.
* @param _packetSize The number of individuals to send in one message to evaluator, and which are evaluated at
* a time.
*/
eoParallelPopLoopEval(
// Job parameters
eo::mpi::AssignmentAlgorithm& _assignAlgo,
int _masterRank,
// Default parameters for store
eoEvalFunc<EOT> & _eval,
int _packetSize = 1
) :
assignAlgo( _assignAlgo ),
masterRank( _masterRank ),
needToDeleteStore( true ) // we used new, we'll have to use delete (RAII)
{
store = new eo::mpi::ParallelApplyStore<EOT>( _eval, _masterRank, _packetSize );
}
/**
* @brief Constructor which allows the user to customize its job store.
*
* This constructor allows the user to customize the store, for instance by adding wrappers and other
* functionnalities, before using it for the parallelized evaluation.
*
* @param _assignAlgo The scheduling algorithm used to give orders to evaluators.
* @param _masterRank The MPI rank of the master.
* @param _store Pointer to a parallel eval store given by the user.
*/
eoParallelPopLoopEval(
// Job parameters
eo::mpi::AssignmentAlgorithm& _assignAlgo,
int _masterRank,
eo::mpi::ParallelApplyStore<EOT>* _store
) :
assignAlgo( _assignAlgo ),
masterRank( _masterRank ),
store( _store ),
needToDeleteStore( false ) // we haven't used new for creating store, we don't care if we have to delete it (RAII).
{
// empty
}
/**
* @brief Default destructor. Sends a message to all evaluators indicating that the global loop (eoEasyEA, for
* instance) is over.
*/
~eoParallelPopLoopEval()
{
// Only the master has to send the termination message
if( eo::mpi::Node::comm().rank() == masterRank )
{
eo::mpi::EmptyJob job( assignAlgo, masterRank );
job.run();
}
// RAII
if( needToDeleteStore )
{
delete store;
}
}
/**
* @brief Parallel implementation of the operator().
*
* @param _parents Population of parents (ignored).
* @param _offspring Population of children, which will be evaluated.
*/
void operator()( eoPop<EOT> & _parents, eoPop<EOT> & _offspring )
{
(void)_parents;
// Reinits the store and the scheduling algorithm
store->data( _offspring );
// For static scheduling, it's mandatory to reinit attributions
int nbWorkers = assignAlgo.availableWorkers();
assignAlgo.reinit( nbWorkers );
if( ! eo::parallel.isDynamic() ) {
store->data()->packetSize = ceil( static_cast<double>( _offspring.size() ) / nbWorkers );
}
// Effectively launches the job.
eo::mpi::ParallelApply<EOT> job( assignAlgo, masterRank, *store );
job.run();
}
private:
// Scheduling algorithm
eo::mpi::AssignmentAlgorithm & assignAlgo;
// Master MPI rank
int masterRank;
// Store
eo::mpi::ParallelApplyStore<EOT>* store;
// Do we have to delete the store by ourselves ?
bool needToDeleteStore;
};
/**
* @example t-mpi-eval.cpp
*/
#endif
/////////////////////////////////////////////////////////////
// eoTimeVaryingLoopEval
/////////////////////////////////////////////////////////////

32
eo/src/mpi/CMakeLists.txt Normal file
View file

@ -0,0 +1,32 @@
######################################################################################
### 1) Include the sources
######################################################################################
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
######################################################################################
### 2) Define the eompi target
######################################################################################
SET(EOMPI_LIB_OUTPUT_PATH ${EO_BINARY_DIR}/lib)
SET(LIBRARY_OUTPUT_PATH ${EOMPI_LIB_OUTPUT_PATH})
SET(EOMPI_SOURCES
eoMpi.cpp
)
ADD_LIBRARY(eompi STATIC ${EOMPI_SOURCES})
INSTALL(TARGETS eompi ARCHIVE DESTINATION lib COMPONENT libraries)
FILE(GLOB HDRS *.h)
INSTALL(FILES ${HDRS} DESTINATION include/eo/mpi COMPONENT headers)
######################################################################################
### 3) Optionnal
######################################################################################
SET(EOMPI_VERSION ${GLOBAL_VERSION})
SET_TARGET_PROPERTIES(eompi PROPERTIES VERSION "${EOMPI_VERSION}")
######################################################################################

11
eo/src/mpi/eoMpi.cpp Normal file
View file

@ -0,0 +1,11 @@
# include "eoMpi.h"
namespace eo
{
namespace mpi
{
bmpi::communicator Node::_comm;
eoTimerStat timerStat;
}
}

836
eo/src/mpi/eoMpi.h Normal file
View file

@ -0,0 +1,836 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EO_MPI_H__
# define __EO_MPI_H__
# include <vector> // std::vector
# include <utils/eoLogger.h>
# include <utils/eoTimer.h>
# include <eoFunctor.h>
# include <eoExceptions.h>
# include "eoMpiNode.h"
# include "eoMpiAssignmentAlgorithm.h"
namespace eo
{
/**
* @ingroup Parallel
* @defgroup MPI Message Passing Interface
* @brief See namespace eo::mpi to have all explanations about this module.
* @{
*/
/**
* @brief MPI parallelization helpers for EO.
*
* This namespace contains parallelization functions which help to parallelize computations in EO. It is based on a
* generic algorithm, which is then customized with functors, corresponding to the algorithm main steps. These
* computations are centralized, i.e there is one central host whose role is to handle the steps of the algorithm ;
* we call it the "master". The other hosts just have to perform a "dummy" computation, which may be any kind of
* processing ; we call them, the "slaves", or less pejoratively, the "workers". Workers can communicate to each
* other, but they receive their orders from the Master and send him back some results. A worker can also be the
* master of a different parallelization process, as soon as it is a part of its work. Machines of the network, also
* called hosts, are identified by an unique number: their rank. At any time during the execution of the program,
* all the hosts know the total number of hosts.
*
* A parallelized Job is a set of tasks which are independant (i.e can be executed in random order without
* modifiying the result) and take a data input and compute a data output to be sent to the Master. The data can be
* of any type, however they have to be serialized to be sent over a network. It is sufficient that they can be
* serialized through boost.
*
* @todo For serialization purposes, don't depend upon boost. It would be easy to use only eoserial and send strings
* via mpi.
*
* The main steps of the algorithm are the following:
* - For the master:
* - Have we done with the treatment we are doing ?
* - If this is the case, we can quit.
* - Otherwise, send an input data to some available worker.
* - If there's no available worker, wait for a worker to be free.
* - When receiving the response, handle it (eventually compute something on the output data, store it...).
* - Go back to the first step.
* - For the worker, it is even easier:
* - Wait for an order.
* - If there's nothing to do, just quit.
* - Otherwise, eventually retrieve data and do the work.
* - Go back to the first step.
*
* There is of course some network adjustements to do and precisions to give there, but the main ideas are present. As the
* job is fully centralized, this is the master who tells the workers when to quit and when to work.
*
* The idea behind these MPI helpers is to be the most generic possible. If we look back at the steps of the
* algorithm, we found that the steps can be splitted into 2 parts: the first consists in the steps of any
* parallelization algorithm and the other consists in the specific parts of the algorithm. Ideally, the user should
* just have to implement the specific parts of the algorithm. We identified these parts to be:
* - For the master:
* - What does mean to have terminated ? There are only two alternatives, in our binary world: either it is
* terminated, or it is not. Hence we only need a function returning a boolean to know if we're done with the
* computation : we'll call it IsFinished.
* - What do we have to do when we send a task ? We don't have any a priori on the form of the sent data, or
* the number of sent data. Moreover, as the tasks are all independant, we don't care of who will do the
* computation, as soon as it's done. Knowing the rank of the worker will be sufficient to send him data. We
* have identified another function, taking a single argument which is the rank of the worker: we'll call it
* SendTask.
* - What do we have to do when we receive a response from a worker ? One more time, we don't know which form
* or structure can have the receive data, only the user can know. Also we let the user the charge to retrieve
* the data ; he just has to know from who the master will retrieve the data. Here is another function, taking
* a rank (the sender's one) as a function argument : this will be HandleResponse.
* - For the worker:
* - What is the processing ? It can have any nature. We just need to be sure that a data is sent back to the
* master, but it seems difficult to check that: it will be the role of the user to assert that data is sent by
* the worker at the end of an execution. We've got identified our last function: ProcessTask.
*
* In term of implementation, it would be annoying to have only abstract classes with these 4 methods to implement. It
* would mean that if you want to alter just one of these 4 functions, you have to implement a new sub class, with a
* new constructor which could have the same signature. Besides, this fashion doesn't allow you to add dynamic
* functionalities, using the design pattern Decorator for instance, without implement a class for each type of
* decoration you want to add. For these reasons, we decided to transform function into functors ; the user can then
* wrap the existing, basic comportments into more sophisticated computations, whenever he wants, and without the
* notion of order. We retrieve here the power of extension given by the design pattern Decorator.
*
* Our 4 functors could have a big amount of data in common (see eoParallelApply to have an idea).
* So as to make it easy for the user to implement these 4 functors, we consider that these functors
* have to share a common data structure. This data structure is referenced (as a pointer) in the 4 functors, so the
* user doesn't need to pass a lot of parameters to each functor constructor.
*
* There are two kinds of jobs:
* - The job which are launched a fixed and well known amount of times, i.e both master and workers know how many
* times they will be launched. They are "one shot jobs".
* - The job which are launched an unknown amount of times, for instance embedded in a while loop for which we don't
* know the amount of repetitions (typically, eoEasyEA loop is a good example, as we don't know the continuator
* condition). They are called "multi job".
* As the master tells the workers to quit, we have to differentiate these two kinds of jobs. When the job is of the
* kind "multi job", the workers would have to perform a while(true) loop so as to receive the orders ; but even if
* the master tells them to quit, they would begin another job and wait for another order, while the master would
* have quit: this would cause a deadlock and workers processes would be blocked, waiting for an order.
*/
namespace mpi
{
/**
* @brief A timer which allows user to generate statistics about computation times.
*/
extern eoTimerStat timerStat;
/**
* @brief Tags used in MPI messages for framework communication
*
* These tags are used for framework communication and fits "channels", so as to differentiate when we're
* sending an order to a worker (Commands) or data (Messages). They are not reserved by the framework and can be
* used by the user, but he is not bound to.
*
* @ingroup MPI
*/
namespace Channel
{
const int Commands = 0;
const int Messages = 1;
}
/**
* @brief Simple orders used by the framework.
*
* These orders are sent by the master to the workers, to indicate to them if they should receive another task
* to do (Continue), if an one shot job is done (Finish) or if a multi job is done (Kill).
*
* @ingroup MPI
*/
namespace Message
{
const int Continue = 0;
const int Finish = 1;
const int Kill = 2;
}
/**
* @brief If the job only has one master, the user can use this constant, so as not to worry with integer ids.
*
* @ingroup MPI
*/
const int DEFAULT_MASTER = 0;
/**
* @brief Base class for the 4 algorithm functors.
*
* This class can embed a data (JobData) and a wrapper, so as to make all the 4 functors wrappable.
* We can add a wrapper at initialization or at any time when executing the program.
*
* According to RAII, the boolean needDelete helps to know if we have to use the operator delete on the wrapper
* or not. Hence, if any functor is wrapped, user has just to put this boolean to true, to indicate to wrapper
* that it should call delete. This allows to mix wrapper initialized in the heap (with new) or in the stack.
*
* @param JobData a Data type, which can have any form. It can a struct, a single int, anything.
*
* @param Wrapped the type of the functor, which will be stored as a pointer under the name _wrapped.
* This allows to wrap directly the functor in functors of the same type
* here, instead of dealing with SharedDataFunction* that we would have to cast all the time.
* Doing also allows to handle the wrapped functor as the functor we're writing, when coding the wrappers,
* instead of doing some static_cast. For instance, if there are 2 functors subclasses, fA and fB, fA
* implementing doFa() and fB implementing doFb(), we could have the following code:
* @code
* struct fA_wrapper
* {
* // some code
* void doFa()
* {
* _wrapped->doFa();
* std::cout << "I'm a fA wrapper!" << std::endl;
* // if we didn't have the second template parameter, but a SharedDataFunction, we would have to do this:
* static_cast<fA*>(_wrapped)->doFa();
* // do other things (it's a wrapper)
* }
* };
*
* struct fB_wrapper
* {
* // some code
* void doFb()
* {
* _wrapped->doFb(); // and not: static_cast<fB*>(_wrapped)->doFb();
* }
* };
* @endcode
* This makes the code easier to write for the user.
*
* @ingroup MPI
*/
template< typename JobData, typename Wrapped >
struct SharedDataFunction
{
/**
* @brief Default constructor.
*
* The user is not bound to give a wrapped functor.
*/
SharedDataFunction( Wrapped * w = 0 ) : _data( 0 ), _wrapped( w ), _needDelete( false )
{
// empty
}
/**
* @brief Destructor.
*
* Calls delete on the wrapped function, only if necessary.
*/
virtual ~SharedDataFunction()
{
if( _wrapped && _wrapped->needDelete() )
{
delete _wrapped;
}
}
/**
* @brief Setter for the wrapped function.
*
* It doesn't do anything on the current wrapped function, like deleting it.
*/
void wrapped( Wrapped * w )
{
_wrapped = w;
}
/**
* @brief Setter for the data present in the functor.
*
* Calls the setter on the functor and on the wrapped functors, in a Composite pattern fashion.
*/
void data( JobData* d )
{
_data = d;
if( _wrapped )
{
_wrapped->data( d );
}
}
/**
* @brief Returns true if we need to use operator delete on this wrapper, false otherwise.
*
* Allows the user to reject delete responsability to the framework, by setting this value to true.
**/
bool needDelete() { return _needDelete; }
void needDelete( bool b ) { _needDelete = b; }
protected:
JobData* _data;
Wrapped* _wrapped; // Pointer and not a reference so as to be set at any time and to avoid affectation
bool _needDelete;
};
/**
* @brief Functor (master side) used to send a task to the worker.
*
* The user doesn't have to know which worker will receive a task, so we just indicate to master the rank of the
* worker. The data used for computation have to be explicitly sent by the master to the worker, with indicated
* rank. Once this functor has been called, the worker is considered busy until it sends a return message to the
* master.
*
* This is a functor implementing void operator()(int), and also a shared data function, containing wrapper on its
* own type.
*
* @ingroup MPI
*/
template< typename JobData >
struct SendTaskFunction : public eoUF<int, void>, public SharedDataFunction< JobData, SendTaskFunction<JobData> >
{
public:
SendTaskFunction( SendTaskFunction<JobData>* w = 0 ) : SharedDataFunction<JobData, SendTaskFunction<JobData> >( w )
{
// empty
}
virtual ~SendTaskFunction() {} // for inherited classes
};
/**
* @brief Functor (master side) used to indicate what to do when receiving a response.
*
* The master calls this function as soon as it receives some data, in some channel. Thanks to MPI, we retrieve
* the rank of the data's sender. This functor is then called with this rank. There is no memoization of a link
* between sent data and rank, so the user has to implement it, if he needs it.
*
* This is a functor implementing void operator()(int), and also a shared data function, containing wrapper on
* its own type.
*
* @ingroup MPI
*/
template< typename JobData >
struct HandleResponseFunction : public eoUF<int, void>, public SharedDataFunction< JobData, HandleResponseFunction<JobData> >
{
public:
HandleResponseFunction( HandleResponseFunction<JobData>* w = 0 ) : SharedDataFunction<JobData, HandleResponseFunction<JobData> >( w )
{
// empty
}
virtual ~HandleResponseFunction() {} // for inherited classes
};
/**
* @brief Functor (worker side) implementing the processing to do.
*
* This is where the real computation happen.
* Whenever the master sends the command "Continue" to workers, which indicates the worker will receive a task,
* the worker calls this functor. The user has to explicitly retrieve the data, handle it and transmit it,
* processed, back to the master. If the worker does not send any data back to the master, the latter will
* consider the worker isn't done and a deadlock could occur.
*
* This is a functor implementing void operator()(), and also a shared data function, containing wrapper on its
* own type.
*
* @ingroup MPI
*/
template< typename JobData >
struct ProcessTaskFunction : public eoF<void>, public SharedDataFunction< JobData, ProcessTaskFunction<JobData> >
{
public:
ProcessTaskFunction( ProcessTaskFunction<JobData>* w = 0 ) : SharedDataFunction<JobData, ProcessTaskFunction<JobData> >( w )
{
// empty
}
virtual ~ProcessTaskFunction() {} // for inherited classes
};
/**
* @brief Functor (master side) indicating whether the job is done or not.
*
* The master loops on this functor to know when to stop. When this functor returns true, the master will wait
* for the last responses and properly stops the job. Whenever this functor returns false, the master will send
* tasks, until this functor returns true.
*
* This is a functor implementing bool operator()(), and also a shared function, containing wrapper on its own
* type.
*
* @ingroup MPI
*/
template< typename JobData >
struct IsFinishedFunction : public eoF<bool>, public SharedDataFunction< JobData, IsFinishedFunction<JobData> >
{
public:
IsFinishedFunction( IsFinishedFunction<JobData>* w = 0 ) : SharedDataFunction<JobData, IsFinishedFunction<JobData> >( w )
{
// empty
}
virtual ~IsFinishedFunction() {} // for inherited classes
};
/**
* @brief Contains all the required data and the functors to launch a job.
*
* Splitting the functors and data from the job in itself allows to use the same functors and data for multiples
* instances of the same job. You define your store once and can use it a lot of times during your program. If
* the store was included in the job, you'd have to give again all the functors and all the datas to each
* invokation of the job.
*
* Job store contains the 4 functors (pointers, so as to be able to wrap them ; references couldn't have
* permitted that) described above and the JobData used by all these functors. It contains
* also helpers to easily wrap the functors, getters and setters on all of them.
*
* The user has to implement data(), which is the getter for retrieving JobData. We don't have any idea of who
* owns the data, moreover it is impossible to initialize it in this generic JobStore, as we don't know its
* form. As a matter of fact, the user has to define this in the JobStore subclasses.
*
* @ingroup MPI
*/
template< typename JobData >
struct JobStore
{
/**
* @brief Default ctor with the 4 functors.
*/
JobStore(
SendTaskFunction<JobData>* stf,
HandleResponseFunction<JobData>* hrf,
ProcessTaskFunction<JobData>* ptf,
IsFinishedFunction<JobData>* iff
) :
_stf( stf ), _hrf( hrf ), _ptf( ptf ), _iff( iff )
{
// empty
}
/**
* @brief Empty ctor, useful for not forcing users to call the other constructor.
*
* When using this constructor, the user have to care about the 4 functors pointers, otherwise null pointer
* segfaults have to be expected.
*/
JobStore()
{
// empty
}
/**
* @brief Default destructor.
*
* JobStore is the highest layer which calls needDelete on its functors.
*/
~JobStore()
{
if( _stf->needDelete() ) delete _stf;
if( _hrf->needDelete() ) delete _hrf;
if( _ptf->needDelete() ) delete _ptf;
if( _iff->needDelete() ) delete _iff;
}
// Getters
SendTaskFunction<JobData> & sendTask() { return *_stf; }
HandleResponseFunction<JobData> & handleResponse() { return *_hrf; }
ProcessTaskFunction<JobData> & processTask() { return *_ptf; }
IsFinishedFunction<JobData> & isFinished() { return *_iff; }
// Setters
void sendTask( SendTaskFunction<JobData>* stf ) { _stf = stf; }
void handleResponse( HandleResponseFunction<JobData>* hrf ) { _hrf = hrf; }
void processTask( ProcessTaskFunction<JobData>* ptf ) { _ptf = ptf; }
void isFinished( IsFinishedFunction<JobData>* iff ) { _iff = iff; }
/**
* @brief Helpers for wrapping send task functor.
*/
void wrapSendTask( SendTaskFunction<JobData>* stf )
{
if( stf )
{
stf->wrapped( _stf );
_stf = stf;
}
}
/**
* @brief Helpers for wrapping handle response functor.
*/
void wrapHandleResponse( HandleResponseFunction<JobData>* hrf )
{
if( hrf )
{
hrf->wrapped( _hrf );
_hrf = hrf;
}
}
/**
* @brief Helpers for wrapping process task functor.
*/
void wrapProcessTask( ProcessTaskFunction<JobData>* ptf )
{
if( ptf )
{
ptf->wrapped( _ptf );
_ptf = ptf;
}
}
/**
* @brief Helpers for wrapping is finished functor.
*/
void wrapIsFinished( IsFinishedFunction<JobData>* iff )
{
if( iff )
{
iff->wrapped( _iff );
_iff = iff;
}
}
virtual JobData* data() = 0;
protected:
SendTaskFunction< JobData >* _stf;
HandleResponseFunction< JobData >* _hrf;
ProcessTaskFunction< JobData >* _ptf;
IsFinishedFunction< JobData >* _iff;
};
/**
* @example t-mpi-wrapper.cpp
*/
/**
* @brief Class implementing the centralized job algorithm.
*
* This class handles all the job algorithm. With its store and its assignment (scheduling) algorithm, it
* executes the general algorithm described above, adding some networking, so as to make the global process
* work. It initializes all the functors with the data, then launches the main loop, indicating to workers when
* they will have to work and when they will finish, by sending them a termination message (integer that can be
* customized). As the algorithm is centralized, it is also mandatory to indicate what is the MPI rank of the
* master process, hence the workers will know from who they should receive their commands.
*
* Any of the 3 master functors can launch exception, it will be catched and rethrown as a std::runtime_exception
* to the higher layers.
*
* @ingroup MPI
*/
template< class JobData >
class Job
{
public:
/**
* @brief Main constructor for Job.
*
* @param _algo The used assignment (scheduling) algorithm. It has to be initialized, with its maximum
* possible number of workers (some workers referenced in this algorithm shouldn't be busy). See
* AssignmentAlgorithm for more details.
*
* @param _masterRank The MPI rank of the master.
*
* @param _workerStopCondition Number of the message which will cause the workers to terminate. It could
* be one of the constants defined in eo::mpi::Commands, or any other integer. The user has to be sure
* that a message containing this integer will be sent to each worker on the Commands channel, otherwise
* deadlock will happen. Master sends Finish messages at the end of a simple job, but as a job can
* happen multiples times (multi job), workers don't have to really finish on these messages but on
* another message. This is here where you can configurate it. See also OneShotJob and MultiJob.
*
* @param store The JobStore containing functors and data for this job.
*/
Job( AssignmentAlgorithm& _algo,
int _masterRank,
int _workerStopCondition,
JobStore<JobData> & _store
) :
assignmentAlgo( _algo ),
masterRank( _masterRank ),
workerStopCondition( _workerStopCondition ),
comm( Node::comm() ),
// Functors
store( _store ),
sendTask( _store.sendTask() ),
handleResponse( _store.handleResponse() ),
processTask( _store.processTask() ),
isFinished( _store.isFinished() )
{
_isMaster = Node::comm().rank() == _masterRank;
sendTask.data( _store.data() );
handleResponse.data( _store.data() );
processTask.data( _store.data() );
isFinished.data( _store.data() );
}
protected:
/**
* @brief Finally block of the main algorithm
*
* Herb Sutter's trick for having a finally block, in a try/catch section: invoke a class at the
* beginning of the try, its destructor will be called in every cases.
*
* This implements the end of the master algorithm:
* - sends to all available workers that they are free,
* - waits for last responses, handles them and sends termination messages to last workers.
*/
struct FinallyBlock
{
FinallyBlock(
int _totalWorkers,
AssignmentAlgorithm& _algo,
Job< JobData > & _that
) :
totalWorkers( _totalWorkers ),
assignmentAlgo( _algo ),
that( _that ),
// global field
comm( Node::comm() )
{
// empty
}
~FinallyBlock()
{
# ifndef NDEBUG
eo::log << eo::debug;
eo::log << "[M" << comm.rank() << "] Frees all the idle." << std::endl;
# endif
// frees all the idle workers
timerStat.start("master_wait_for_idles");
std::vector<int> idles = assignmentAlgo.idles();
for(unsigned int i = 0; i < idles.size(); ++i)
{
comm.send( idles[i], Channel::Commands, Message::Finish );
}
timerStat.stop("master_wait_for_idles");
# ifndef NDEBUG
eo::log << "[M" << comm.rank() << "] Waits for all responses." << std::endl;
# endif
// wait for all responses
timerStat.start("master_wait_for_all_responses");
while( assignmentAlgo.availableWorkers() != totalWorkers )
{
bmpi::status status = comm.probe( bmpi::any_source, bmpi::any_tag );
int wrkRank = status.source();
that.handleResponse( wrkRank );
comm.send( wrkRank, Channel::Commands, Message::Finish );
assignmentAlgo.confirm( wrkRank );
}
timerStat.stop("master_wait_for_all_responses");
# ifndef NDEBUG
eo::log << "[M" << comm.rank() << "] Leaving master task." << std::endl;
# endif
}
protected:
int totalWorkers;
AssignmentAlgorithm& assignmentAlgo;
Job< JobData > & that;
bmpi::communicator & comm;
};
/**
* @brief Master part of the job.
*
* Launches the parallelized job algorithm : while there is something to do (! IsFinished ), get a
* worker who will be the assignee ; if no worker is available, wait for a response, handle it and reask
* for an assignee. Then send the command and the task.
* Once there is no more to do (IsFinished), indicate to all available workers that they're free, wait
* for all the responses and send termination messages (see also FinallyBlock).
*/
void master( )
{
int totalWorkers = assignmentAlgo.availableWorkers();
# ifndef NDEBUG
eo::log << eo::debug;
eo::log << "[M" << comm.rank() << "] Have " << totalWorkers << " workers." << std::endl;
# endif
try {
FinallyBlock finally( totalWorkers, assignmentAlgo, *this );
while( ! isFinished() )
{
timerStat.start("master_wait_for_assignee");
int assignee = assignmentAlgo.get( );
while( assignee <= 0 )
{
# ifndef NDEBUG
eo::log << "[M" << comm.rank() << "] Waitin' for node..." << std::endl;
# endif
bmpi::status status = comm.probe( bmpi::any_source, bmpi::any_tag );
int wrkRank = status.source();
# ifndef NDEBUG
eo::log << "[M" << comm.rank() << "] Node " << wrkRank << " just terminated." << std::endl;
# endif
handleResponse( wrkRank );
assignmentAlgo.confirm( wrkRank );
assignee = assignmentAlgo.get( );
}
timerStat.stop("master_wait_for_assignee");
# ifndef NDEBUG
eo::log << "[M" << comm.rank() << "] Assignee : " << assignee << std::endl;
# endif
timerStat.start("master_wait_for_send");
comm.send( assignee, Channel::Commands, Message::Continue );
sendTask( assignee );
timerStat.stop("master_wait_for_send");
}
} catch( const std::exception & e )
{
std::string s = e.what();
s.append( " in eoMpi loop");
throw std::runtime_error( s );
}
}
/**
* @brief Worker part of the algorithm.
*
* The algorithm is more much simpler: wait for an order; if it's termination message, leave. Otherwise,
* prepare to work.
*/
void worker( )
{
int order;
# ifndef NDEBUG
eo::log << eo::debug;
# endif
timerStat.start("worker_wait_for_order");
comm.recv( masterRank, Channel::Commands, order );
timerStat.stop("worker_wait_for_order");
while( true )
{
# ifndef NDEBUG
eo::log << "[W" << comm.rank() << "] Waiting for an order..." << std::endl;
# endif
if ( order == workerStopCondition )
{
# ifndef NDEBUG
eo::log << "[W" << comm.rank() << "] Leaving worker task." << std::endl;
# endif
return;
} else if( order == Message::Continue )
{
# ifndef NDEBUG
eo::log << "[W" << comm.rank() << "] Processing task..." << std::endl;
# endif
processTask( );
}
timerStat.start("worker_wait_for_order");
comm.recv( masterRank, Channel::Commands, order );
timerStat.stop("worker_wait_for_order");
}
}
public:
/**
* @brief Launches the job algorithm, according to the role of the host (roles are deduced from the
* master rank indicated in the constructor).
*/
void run( )
{
( _isMaster ) ? master( ) : worker( );
}
/**
* @brief Returns true if the current host is the master, false otherwise.
*/
bool isMaster( )
{
return _isMaster;
}
protected:
AssignmentAlgorithm& assignmentAlgo;
int masterRank;
const int workerStopCondition;
bmpi::communicator& comm;
JobStore<JobData>& store;
SendTaskFunction<JobData> & sendTask;
HandleResponseFunction<JobData> & handleResponse;
ProcessTaskFunction<JobData> & processTask;
IsFinishedFunction<JobData> & isFinished;
bool _isMaster;
};
/**
* @brief Job that will be launched only once.
*
* As explained in eo::mpi documentation, jobs can happen either a well known amount of times or an unknown
* amount of times. This class implements the general case when the job is launched a well known amount of
* times. The job will be terminated on both sides (master and worker) once the master would have said it.
*
* It uses the message Message::Finish as the termination message.
*
* @ingroup MPI
*/
template< class JobData >
class OneShotJob : public Job< JobData >
{
public:
OneShotJob( AssignmentAlgorithm& algo,
int masterRank,
JobStore<JobData> & store )
: Job<JobData>( algo, masterRank, Message::Finish, store )
{
// empty
}
};
/**
* @brief Job that will be launched an unknown amount of times, in worker side.
*
* As explained in eo::mpi documentation, jobs can happen either a well known amount of times or an unknown
* amount of times. This class implements the general case when the job is launched an unknown amount of times, for
* instance in a while loop. The master will run many jobs (or the same job many times), but the workers will
* launch it only once.
*
* It uses the message Message::Kill as the termination message. This message can be launched with an EmptyJob,
* launched only by the master. If no Message::Kill is sent on the Channels::Commands, the worker will wait
* forever, which will cause a deadlock.
*
* @ingroup MPI
*/
template< class JobData >
class MultiJob : public Job< JobData >
{
public:
MultiJob ( AssignmentAlgorithm& algo,
int masterRank,
JobStore<JobData> & store )
: Job<JobData>( algo, masterRank, Message::Kill, store )
{
// empty
}
};
}
/**
* @}
*/
}
# endif // __EO_MPI_H__

View file

@ -0,0 +1,387 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __MPI_ASSIGNMENT_ALGORITHM_H__
# define __MPI_ASSIGNMENT_ALGORITHM_H__
# include <vector> // 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.
*
* @ingroup MPI
*/
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 MPI
*/
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.
*
* @return A std::vector containing all the MPI ranks of the idles workers.
*/
virtual std::vector<int> 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 MPI
*/
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)
{
availableWrk.push_back( i );
}
}
/**
* @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<int> & 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 )
{
last = Node::comm().size() - 1;
}
for( int i = first; i <= last; ++i)
{
availableWrk.push_back( i );
}
}
virtual int get( )
{
int assignee = -1;
if (! availableWrk.empty() )
{
assignee = availableWrk.back();
availableWrk.pop_back();
}
return assignee;
}
int availableWorkers()
{
return availableWrk.size();
}
void confirm( int rank )
{
availableWrk.push_back( rank );
}
std::vector<int> idles( )
{
return availableWrk;
}
void reinit( int _ )
{
++_;
// nothing to do
}
protected:
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 MPI
*/
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<int>& 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<int> workers;
if( last == REST_OF_THE_WORLD )
{
last = Node::comm().size() - 1;
}
for(int i = first; i <= last; ++i)
{
workers.push_back( i );
}
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<int> workers;
for(int i = 1; i < Node::comm().size(); ++i)
{
workers.push_back( i );
}
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<int> workers;
workers.push_back( unique );
init( workers, runs );
}
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<int> & workers, int runs )
{
unsigned int nbWorkers = workers.size();
freeWorkers = 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
// This one liner affects q requests to each worker
for (unsigned int i = 0; i < nbWorkers; attributions[i++] = runs / nbWorkers) ;
// The first line computes r and the one liner affects the remaining
// r requests to workers, in ascending order
unsigned int diff = runs - (runs / nbWorkers) * nbWorkers;
for (unsigned int i = 0; i < diff; ++attributions[i++]);
}
public:
int get( )
{
int assignee = -1;
for( unsigned i = 0; i < busy.size(); ++i )
{
if( !busy[i] && attributions[i] > 0 )
{
busy[i] = true;
--freeWorkers;
assignee = realRank[ i ];
break;
}
}
return assignee;
}
int availableWorkers( )
{
return freeWorkers;
}
std::vector<int> idles()
{
std::vector<int> ret;
for(unsigned int i = 0; i < busy.size(); ++i)
{
if( !busy[i] )
{
ret.push_back( realRank[i] );
}
}
return ret;
}
void confirm( int rank )
{
int i = -1; // i is the real index in table
for( unsigned int j = 0; j < realRank.size(); ++j )
{
if( realRank[j] == rank )
{
i = j;
break;
}
}
--attributions[ i ];
busy[ i ] = false;
++freeWorkers;
}
void reinit( int runs )
{
init( realRank, runs );
}
private:
std::vector<int> attributions;
std::vector<int> realRank;
std::vector<bool> busy;
unsigned int freeWorkers;
};
}
}
# endif // __MPI_ASSIGNMENT_ALGORITHM_H__

71
eo/src/mpi/eoMpiNode.h Normal file
View file

@ -0,0 +1,71 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __MPI_NODE_H__
# define __MPI_NODE_H__
# include <boost/mpi.hpp>
namespace bmpi = boost::mpi;
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 MPI
*/
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;
}
protected:
static bmpi::communicator _comm;
};
}
}
# endif // __MPI_NODE_H__

View file

@ -0,0 +1,387 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EO_PARALLEL_APPLY_H__
# define __EO_PARALLEL_APPLY_H__
# include "eoMpi.h"
# include <eoFunctor.h> // eoUF
# include <vector> // 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<EOT>, 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 MPI
*/
template<class EOT>
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 table The table to apply. If this value is NULL, user will have to call init() before launching the
* job.
*/
ParallelApplyData(
eoUF<EOT&, void> & _proc,
int _masterRank,
int _packetSize,
std::vector<EOT> * table = 0
) :
_table( table ), func( _proc ), index( 0 ), packetSize( _packetSize ), masterRank( _masterRank ), comm( Node::comm() )
{
if ( _packetSize <= 0 )
{
throw std::runtime_error("Packet size should not be negative.");
}
if( table )
{
size = table->size();
}
}
/**
* @brief Reinitializes the data for a new table to evaluate.
*/
void init( std::vector<EOT>& table )
{
index = 0;
size = table.size();
_table = &table;
assignedTasks.clear();
}
std::vector<EOT>& table()
{
return *_table;
}
// All elements are public since functors will often use them.
std::vector<EOT> * _table;
eoUF<EOT&, void> & func;
int index;
int size;
std::map< int /* worker rank */, ParallelApplyAssignment /* last assignment */> assignedTasks;
int packetSize;
std::vector<EOT> tempArray;
int masterRank;
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<EOT> >
{
public:
using SendTaskFunction< ParallelApplyData<EOT> >::_data;
SendTaskParallelApply( SendTaskParallelApply<EOT> * w = 0 ) : SendTaskFunction< ParallelApplyData<EOT> >( w )
{
// empty
}
void operator()(int wrkRank)
{
int futureIndex;
if( _data->index + _data->packetSize < _data->size )
{
futureIndex = _data->index + _data->packetSize;
} else {
futureIndex = _data->size;
}
int sentSize = futureIndex - _data->index ;
_data->comm.send( wrkRank, 1, sentSize );
eo::log << eo::progress << "Evaluating individual " << _data->index << std::endl;
_data->assignedTasks[ wrkRank ].index = _data->index;
_data->assignedTasks[ wrkRank ].size = sentSize;
_data->comm.send( wrkRank, 1, & ( (_data->table())[ _data->index ] ) , sentSize );
_data->index = futureIndex;
}
};
/**
* @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<EOT> >
{
public:
using HandleResponseFunction< ParallelApplyData<EOT> >::_data;
HandleResponseParallelApply( HandleResponseParallelApply<EOT> * w = 0 ) : HandleResponseFunction< ParallelApplyData<EOT> >( w )
{
// empty
}
void operator()(int wrkRank)
{
_data->comm.recv( wrkRank, 1, & (_data->table()[ _data->assignedTasks[wrkRank].index ] ), _data->assignedTasks[wrkRank].size );
}
};
/**
* @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<EOT> >
{
public:
using ProcessTaskFunction< ParallelApplyData<EOT> >::_data;
ProcessTaskParallelApply( ProcessTaskParallelApply<EOT> * w = 0 ) : ProcessTaskFunction< ParallelApplyData<EOT> >( w )
{
// empty
}
void operator()()
{
int recvSize;
_data->comm.recv( _data->masterRank, 1, recvSize );
_data->tempArray.resize( recvSize );
_data->comm.recv( _data->masterRank, 1, & _data->tempArray[0] , recvSize );
timerStat.start("worker_processes");
for( int i = 0; i < recvSize ; ++i )
{
_data->func( _data->tempArray[ i ] );
}
timerStat.stop("worker_processes");
_data->comm.send( _data->masterRank, 1, & _data->tempArray[0], recvSize );
}
};
/**
* @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<EOT> >
{
public:
using IsFinishedFunction< ParallelApplyData<EOT> >::_data;
IsFinishedParallelApply( IsFinishedParallelApply<EOT> * w = 0 ) : IsFinishedFunction< ParallelApplyData<EOT> >( w )
{
// empty
}
bool operator()()
{
return _data->index == _data->size;
}
};
/**
* @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 MPI
*/
template< class EOT >
struct ParallelApplyStore : public JobStore< ParallelApplyData<EOT> >
{
using JobStore< ParallelApplyData<EOT> >::_stf;
using JobStore< ParallelApplyData<EOT> >::_hrf;
using JobStore< ParallelApplyData<EOT> >::_ptf;
using JobStore< ParallelApplyData<EOT> >::_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<EOT&, void> & _proc,
int _masterRank,
int _packetSize = 1,
// JobStore functors
SendTaskParallelApply<EOT> * stpa = 0,
HandleResponseParallelApply<EOT>* hrpa = 0,
ProcessTaskParallelApply<EOT>* ptpa = 0,
IsFinishedParallelApply<EOT>* ifpa = 0
) :
_data( _proc, _masterRank, _packetSize )
{
if( stpa == 0 ) {
stpa = new SendTaskParallelApply<EOT>;
stpa->needDelete( true );
}
if( hrpa == 0 ) {
hrpa = new HandleResponseParallelApply<EOT>;
hrpa->needDelete( true );
}
if( ptpa == 0 ) {
ptpa = new ProcessTaskParallelApply<EOT>;
ptpa->needDelete( true );
}
if( ifpa == 0 ) {
ifpa = new IsFinishedParallelApply<EOT>;
ifpa->needDelete( true );
}
_stf = stpa;
_hrf = hrpa;
_ptf = ptpa;
_iff = ifpa;
}
ParallelApplyData<EOT>* 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<EOT>& _pop )
{
_data.init( _pop );
}
virtual ~ParallelApplyStore() // for inheritance purposes only
{
}
protected:
ParallelApplyData<EOT> _data;
};
/**
* @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 MPI
* @see eoParallelApply.h
*/
template< typename EOT >
class ParallelApply : public MultiJob< ParallelApplyData<EOT> >
{
public:
ParallelApply(
AssignmentAlgorithm & algo,
int _masterRank,
ParallelApplyStore<EOT> & store
) :
MultiJob< ParallelApplyData<EOT> >( algo, _masterRank, store )
{
// empty
}
};
/**
* @example t-mpi-parallelApply.cpp
* @example t-mpi-multipleRoles.cpp
*/
}
}
# endif // __EO_PARALLEL_APPLY_H__

140
eo/src/mpi/eoTerminateJob.h Normal file
View file

@ -0,0 +1,140 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EO_TERMINATE_H__
# define __EO_TERMINATE_H__
# include "eoMpi.h"
namespace eo
{
namespace mpi
{
/**
* @ingroup MPI
* @{
*/
/**
* @brief Send task functor which does nothing.
*/
struct DummySendTaskFunction : public SendTaskFunction<void>
{
void operator()( int _ )
{
++_;
}
};
/**
* @brief Handle response functor which does nothing.
*/
struct DummyHandleResponseFunction : public HandleResponseFunction<void>
{
void operator()( int _ )
{
++_;
}
};
/**
* @brief Process task functor which does nothing.
*/
struct DummyProcessTaskFunction : public ProcessTaskFunction<void>
{
void operator()()
{
// nothing!
}
};
/**
* @brief Is finished functor which returns true everytime.
*/
struct DummyIsFinishedFunction : public IsFinishedFunction<void>
{
bool operator()()
{
return true;
}
};
/**
* @brief Job store containing all dummy functors and containing no data.
*/
struct DummyJobStore : public JobStore<void>
{
using JobStore<void>::_stf;
using JobStore<void>::_hrf;
using JobStore<void>::_ptf;
using JobStore<void>::_iff;
DummyJobStore()
{
_stf = new DummySendTaskFunction;
_stf->needDelete( true );
_hrf = new DummyHandleResponseFunction;
_hrf->needDelete( true );
_ptf = new DummyProcessTaskFunction;
_ptf->needDelete( true );
_iff = new DummyIsFinishedFunction;
_iff->needDelete( true );
}
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<void>
{
/**
* @brief Main EmptyJob ctor
*
* @param algo Assignment (scheduling) algorithm used.
* @param masterRank The rank of the master process.
*/
EmptyJob( AssignmentAlgorithm& algo, int masterRank ) :
OneShotJob<void>( algo, masterRank, *(new DummyJobStore) )
// the job store is deleted on destructor
{
// empty
}
~EmptyJob()
{
std::vector< int > idles = assignmentAlgo.idles();
for(unsigned i = 0, size = idles.size(); i < size; ++i)
{
comm.send( idles[i], Channel::Commands, Message::Kill );
}
delete & this->store;
}
};
/**
* @}
*/
}
}
# endif // __EO_TERMINATE_H__

58
eo/src/serial/Array.cpp Normal file
View file

@ -0,0 +1,58 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# include "Array.h"
namespace eoserial
{
std::ostream& Array::print( std::ostream& out ) const
{
out << "[";
bool first = true;
for (ArrayChildren::const_iterator it = begin(),
end = this->end();
it != end;
++it)
{
if ( first )
{
first = false;
} else {
out << ", ";
}
(*it)->print( out );
}
out << "]\n";
return out;
}
Array::~Array()
{
for (ArrayChildren::iterator it = begin(),
end = this->end();
it != end;
++it)
{
delete *it;
}
}
} // namespace eoserial

170
eo/src/serial/Array.h Normal file
View file

@ -0,0 +1,170 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EOSERIAL_ARRAY_H__
# define __EOSERIAL_ARRAY_H__
# include <vector>
# include "Entity.h"
# include "Serializable.h"
# include "Object.h"
namespace eoserial
{
// Forward declaration for below declarations.
class Array;
/*
* Declarations of functions present in Utils.h
* These are put here to avoid instead of including the file Utils.h, which would
* cause a circular inclusion.
*/
template< class T >
void unpack( const Array & array, unsigned int index, T & value );
void unpackObject( const Array & array, unsigned int index, Persistent & value );
template< class Container, template<class> class UnpackAlgorithm >
void unpackArray( const Array & array, unsigned int index, Container & container );
/**
* @brief Represents a JSON array.
*
* Wrapper for an array, so as to be used as a JSON object.
*
* @ingroup Serialization
*/
class Array : public eoserial::Entity, public std::vector< eoserial::Entity* >
{
protected:
typedef std::vector< eoserial::Entity* > ArrayChildren;
public:
/**
* @brief Adds the serializable object as a JSON object.
* @param obj Object which implemnets JsonSerializable.
*/
void push_back( const eoserial::Printable* obj )
{
ArrayChildren::push_back( obj->pack() );
}
/**
* @brief Proxy for vector::push_back.
*/
void push_back( eoserial::Entity* json )
{
ArrayChildren::push_back( json );
}
/**
* @brief Prints the JSON array into the given stream.
* @param out The stream
*/
virtual std::ostream& print( std::ostream& out ) const;
/**
* @brief Dtor
*/
~Array();
/*
* The following parts allows the user to automatically deserialize an eoserial::Array into a
* standard container, by giving the algorithm which will be used to deserialize contained entities.
*/
/**
* @brief Functor which determines how to retrieve the real value contained in a eoserial::Entity at
* a given place.
*
* It will be applied for each contained variable in the array.
*/
template<class Container>
struct BaseAlgorithm
{
/**
* @brief Main operator.
*
* @param array The eoserial::Array from which we're reading.
* @param i The index of the contained value.
* @param container The standard (STL) container in which we'll push back the read value.
*/
virtual void operator()( const eoserial::Array& array, unsigned int i, Container & container ) const = 0;
};
/**
* @brief BaseAlgorithm for retrieving primitive variables.
*
* This one should be used to retrieve primitive (and types which implement operator>>) variables, for instance
* int, double, std::string, etc...
*/
template<typename C>
struct UnpackAlgorithm : public BaseAlgorithm<C>
{
void operator()( const eoserial::Array& array, unsigned int i, C & container ) const
{
typename C::value_type t;
unpack( array, i, t );
container.push_back( t );
}
};
/**
* @brief BaseAlgorithm for retrieving eoserial::Persistent objects.
*
* This one should be used to retrieve objects which implement eoserial::Persistent.
*/
template<typename C>
struct UnpackObjectAlgorithm : public BaseAlgorithm<C>
{
void operator()( const eoserial::Array& array, unsigned int i, C & container ) const
{
typename C::value_type t;
unpackObject( array, i, t );
container.push_back( t );
}
};
/**
* @brief General algorithm for array deserialization.
*
* Applies the BaseAlgorithm to each contained variable in the eoserial::Array.
*/
template<class Container, template<class T> class UnpackAlgorithm>
inline void deserialize( Container & array )
{
UnpackAlgorithm< Container > algo;
for( unsigned int i = 0, size = this->size();
i < size;
++i)
{
algo( *this, i, array );
}
}
};
} // namespace eoserial
# endif // __EOSERIAL_ARRAY_H__

View file

@ -0,0 +1,35 @@
######################################################################################
### 1) Include the sources
######################################################################################
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
######################################################################################
### 2) Define the eoserial target
######################################################################################
SET(EOSERIAL_LIB_OUTPUT_PATH ${EO_BINARY_DIR}/lib)
SET(LIBRARY_OUTPUT_PATH ${EOSERIAL_LIB_OUTPUT_PATH})
SET(EOSERIAL_SOURCES
Array.cpp
Object.cpp
Parser.cpp
String.cpp
)
ADD_LIBRARY(eoserial STATIC ${EOSERIAL_SOURCES})
INSTALL(TARGETS eoserial ARCHIVE DESTINATION lib COMPONENT libraries)
FILE(GLOB HDRS *.h)
INSTALL(FILES ${HDRS} DESTINATION include/eo/serial COMPONENT headers)
######################################################################################
### 3) Optionnal
######################################################################################
SET(EOSERIAL_VERSION ${GLOBAL_VERSION})
SET_TARGET_PROPERTIES(eoserial PROPERTIES VERSION "${EOSERIAL_VERSION}")
######################################################################################

70
eo/src/serial/Entity.h Normal file
View file

@ -0,0 +1,70 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EOSERIAL_ENTITY_H__
# define __EOSERIAL_ENTITY_H__
# include <iostream> // ostream
/**
* @brief Contains all the necessary entities to serialize eo objects into JSON objects.
*
* Allows serialization from user objects into JSON objects, if they implement the interface
* eoserial::Serializable or eoserial::Persistent. The following user objects can be serialized:
* - primitive types (int, std::string, ...), in particular every type that can be written into a
* std::stringstream.
* - objects which implement eoserial::Serializable.
* - array of serializable things (primitive or serializable objects).
*
* @ingroup Utilities
* @defgroup Serialization Serialization helpers
**/
namespace eoserial
{
/**
* @brief JSON entity
*
* This class represents a JSON entity, which can be JSON objects,
* strings or arrays. It is the base class for the JSON hierarchy.
*
* @ingroup Serialization
*/
class Entity
{
public:
/**
* Virtual dtor (base class).
*/
virtual ~Entity() { /* empty */ }
/**
* @brief Prints the content of a JSON object into a stream.
* @param out The stream in which we're printing.
*/
virtual std::ostream& print( std::ostream& out ) const = 0;
};
} // namespace eoserial
# endif // __ENTITY_H__

60
eo/src/serial/Object.cpp Normal file
View file

@ -0,0 +1,60 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# include "Object.h"
using namespace eoserial;
namespace eoserial
{
std::ostream& Object::print( std::ostream& out ) const
{
out << '{';
bool first = true;
for(JsonValues::const_iterator it = begin(), end = this->end();
it != end;
++it)
{
if ( first )
{
first = false;
} else {
out << ", ";
}
out << '"' << it->first << "\":"; // key
it->second->print( out ); // value
}
out << "}\n";
return out;
}
Object::~Object()
{
for(JsonValues::iterator it = begin(), end = this->end();
it != end;
++it)
{
delete it->second;
}
}
} // namespace eoserial

88
eo/src/serial/Object.h Normal file
View file

@ -0,0 +1,88 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EOSERIAL_OBJECT_H__
# define __EOSERIAL_OBJECT_H__
# include <map>
# include <string>
# include "Entity.h"
# include "Serializable.h"
namespace eoserial
{
/**
* @brief JSON Object
*
* This class represents a JSON object, which is basically a dictionnary
* of keys (strings) and values (JSON entities).
*
* @ingroup Serialization
*/
class Object : public eoserial::Entity, public std::map< std::string, eoserial::Entity* >
{
public:
typedef std::map<std::string, eoserial::Entity*> JsonValues;
/**
* @brief Adds a pair into the JSON object.
* @param key The key associated with the eoserial object
* @param json The JSON object as created with framework.
*/
void add( const std::string& key, eoserial::Entity* json )
{
(*this)[ key ] = json;
}
/**
* @brief Adds a pair into the JSON object.
* @param key The key associated with the eoserial object
* @param obj A JSON-serializable object
*/
void add( const std::string& key, const eoserial::Printable* obj )
{
(*this)[ key ] = obj->pack();
}
/**
* @brief Deserializes a Serializable class instance from this JSON object.
* @param obj The object we want to rebuild.
*/
void deserialize( eoserial::Persistent & obj )
{
obj.unpack( this );
}
/**
* @brief Dtor
*/
~Object();
/**
* @brief Prints the content of a JSON object into a stream.
*/
virtual std::ostream& print( std::ostream& out ) const;
};
} // namespace eoserial
# endif // __EOSERIAL_OBJECT_H__

171
eo/src/serial/Parser.cpp Normal file
View file

@ -0,0 +1,171 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# include <string>
# include "Parser.h"
# include "Array.h"
# include "Object.h"
# include "String.h"
// in debug mode only
// # define DEBUG(x) std::cout << x << std::endl;
# define DEBUG(x)
using namespace eoserial;
namespace eoserial
{
/**
* @brief Parses a string contained between double quotes.
*
* Strings can contain escaped double quotes.
* @param str The string we're parsing.
* @param pos The index of current position in parsed string.
* This index will be updated so as to allow the parser to
* continue.
*/
static std::string parseString(const std::string& str, size_t & pos)
{
// example : "hello"
// example 2 : "\"world\""
// for hello:
// firstQuote == 0, secondQuote == 6
// sub string should be from firstQuote+1 to secondQuote-1
// so its size should be (secondQuote-1 -(firstQuote+1) + 1)
std::string value;
size_t firstQuote = str.find( '"', pos );
size_t secondQuote;
/* instead of just seeking the second quote, we need to ensure
// that there is no escaped quote before this one.
// actually this is harder than that. Using backslashes
// to escape double quotes mean that backslashes have to be
// escaped to.
// example : "text\\" to symbolize : text\
// example : "text\\\" to symbolize : text\"
// In fact, we should find if number of backslashes is odd; in this case,
// the double quotes are escaped and we should find the next one.
*/
int backslashesCount;
do {
++pos;
secondQuote = str.find( '"', pos );
size_t i = secondQuote - 1;
// Find the backslashes
backslashesCount = 0;
while ( str[ i ] == '\\' )
{
--i;
++backslashesCount;
}
pos = secondQuote;
} while( backslashesCount % 2 == 1 );
value = str.substr( firstQuote+1, secondQuote-firstQuote-1 );
pos = secondQuote + 1;
return value;
}
/**
* @brief Moves the given index pos to the next character which is
* neither a coma, a space nor a new line.
*
* @param str The string in which we want to ignores those characters.
* @param pos The index of current position in parsed string.
*/
static void ignoreChars(const std::string& str, size_t & pos)
{
// ignore white spaces and comas
for (char current = str[ pos ];
current == ',' || current == ' ' || current == '\n';
current = str[ ++pos ]);
}
String* Parser::parseJsonString(const std::string & str, size_t & pos)
{
return new String( parseString( str, pos ) );
}
Object* Parser::parse(const std::string & str)
{
size_t initial(0); // we begin at position 0
return static_cast<Object*>( parseRight(str, initial) );
}
Entity* Parser::parseRight(const std::string & str, size_t & pos)
{
Entity* value = 0;
if ( str[ pos ] == '{' )
{
// next one is an object
DEBUG("We read an object.")
Object* obj = new Object;
pos += 1;
while( pos < str.size() && str[ pos ] != '}' )
{
parseLeft( str, pos, obj );
ignoreChars( str, pos );
}
DEBUG("We just finished to read an object ! ")
pos += 1; // we're on the }, go to the next char
value = obj;
}
else if ( str[ pos ] == '"' )
{
// next one is a string
DEBUG("We read a string")
value = parseJsonString( str, pos );
}
else if ( str[ pos ] == '[' )
{
// next one is an array
DEBUG("We read an array")
Array* array = new Array;
pos += 1;
while( pos < str.size() && str[ pos ] != ']' )
{
Entity* child = parseRight( str, pos );
if ( child )
array->push_back( child );
}
DEBUG("We've finished to read our array.")
pos += 1; // we're on the ], go to the next char
value = array;
}
ignoreChars( str, pos );
return value;
}
void Parser::parseLeft(const std::string & str, size_t & pos, Object* eoserial)
{
std::string key = parseString(str, pos);
++pos; // the colon
DEBUG("We've read the key ")
(*eoserial)[ key ] = parseRight( str, pos );
}
} // namespace eoserial

103
eo/src/serial/Parser.h Normal file
View file

@ -0,0 +1,103 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EOSERIAL_PARSER_H__
# define __EOSERIAL_PARSER_H__
# include "Entity.h"
# include "String.h"
# include "Object.h"
/**
* @file Parser.h
*
* This file contains a tiny JSON parser used in DAE. This parser just handles
* a subset of JSON grammar, with the following restrictions :
* - all strings must be surrounded by double quotes.
* - everything which is not an object or an array is considered to be a string
* (even integers, booleans,...).
* - no syntax check is done. We trust the programmer and he has to ensure that
* every JSON string he produces is valid.
*
* @author Benjamin BOUVIER
*/
namespace eoserial
{
/**
* @brief Parser from a JSON source.
*
* This parser does just retrieve values and does NOT check the structure of
* the input. This implies that if the input is not correct, the result is undefined
* and can result to a failure on execution.
*
* @ingroup Serialization
*/
class Parser
{
public:
/**
* @brief Parses the given string and returns the JSON object read.
*/
static eoserial::Object* parse(const std::string & str);
protected:
/**
* @brief Parses the right part of a JSON object as a string.
*
* The right part of an object can be a string (for instance :
* "key":"value"), a JSON array (for instance: "key":["1"]) or
* another JSON object (for instance: "key":{"another_key":"value"}).
*
* The right parts are found after keys (which are parsed by parseLeft)
* and in arrays.
*
* @param str The string we're parsing.
* @param pos The index of the current position in the string.
* @return The JSON object matching the right part.
*/
static eoserial::Entity* parseRight(const std::string & str, size_t & pos);
/**
* @brief Parses the left value of a key-value pair, which is the key.
*
* @param str The string we're parsing.
* @param pos The index of the current position in the string.
* @param json The current JSON object for which we're adding a key-value pair.
*/
static void parseLeft(const std::string & str, size_t & pos, eoserial::Object* json);
/**
* @brief Retrieves a string in a JSON content.
*
* @param str The string we're parsing.
* @param pos The index of the current position of parsing,
* which will be updated.
*/
static eoserial::String* parseJsonString(const std::string & str, size_t & pos);
};
} // namespace eoserial
# endif // __EOSERIAL_PARSER_H__

View file

@ -0,0 +1,66 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EOSERIAL_SERIALIZABLE_H__
# define __EOSERIAL_SERIALIZABLE_H__
namespace eoserial
{
class Object; // to avoid recursive inclusion with JsonObject
/**
* @brief Interface showing that object can be written to a eoserial type
* (currently JSON).
*
* @ingroup Serialization
*/
class Printable
{
public:
/**
* @brief Serializes the object to JSON format.
* @return A JSON object created with new.
*/
virtual eoserial::Object* pack() const = 0;
};
/**
* @brief Interface showing that object can be eoserialized (written and read
* from an input).
*
* Note : Persistent objects should have a default non-arguments constructor.
*
* @ingroup Serialization
*/
class Persistent : public Printable
{
public:
/**
* @brief Loads class fields from a JSON object.
* @param json A JSON object. Programmer doesn't have to delete it, it
* is automatically done.
*/
virtual void unpack(const eoserial::Object* json) = 0;
};
} // namespace eoserial
# endif // __EOSERIAL_SERIALIZABLE_H__

32
eo/src/serial/String.cpp Normal file
View file

@ -0,0 +1,32 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# include "String.h"
namespace eoserial
{
std::ostream& String::print( std::ostream& out ) const
{
out << '"' << *this << '"';
return out;
}
} // namespace eoserial

103
eo/src/serial/String.h Normal file
View file

@ -0,0 +1,103 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EOSERIAL_STRING_H__
# define __EOSERIAL_STRING_H__
# include <string>
# include <sstream>
# include <limits>
# include "Entity.h"
namespace eoserial
{
/**
* @brief JSON String
*
* Wrapper for string, so as to be used as a JSON object.
*
* @ingroup Serialization
*/
class String : public eoserial::Entity, public std::string
{
public:
/**
* @brief Default ctor.
* @param str The string we want to wrap.
*/
String( const std::string& str ) : std::string( str ) {}
/**
* @brief Ctor used only when parsing.
*/
String( ) {}
/**
* @brief Prints out the string.
*/
virtual std::ostream& print( std::ostream& out ) const;
/**
* @brief Deserializes the current String into a given primitive type value.
* @param value The value in which we're writing.
*/
template<class T>
inline void deserialize( T & value );
protected:
// Copy and reaffectation are forbidden
explicit String( const String& _ );
String& operator=( const String& _ );
};
/**
* @brief Casts a eoserial::String into a primitive value, or in a type which at
* least overload operator>>.
*
* @param value A reference to the variable we're writing into.
*
* It's not necessary to specify the variable type, which can be infered by compiler when
* invoking.
*/
template<class T>
inline void String::deserialize( T & value )
{
std::stringstream ss;
ss.precision(std::numeric_limits<double>::digits10 + 1);
ss << *this;
ss >> value;
}
/**
* @brief Specialization for strings, which don't need to be converted through
* a stringstream.
*/
template<>
inline void String::deserialize( std::string & value )
{
value = *this;
}
} // namespace eoserial
# endif // __EOSERIAL_STRING_H__

189
eo/src/serial/Utils.h Normal file
View file

@ -0,0 +1,189 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EOSERIAL_UTILS_H__
# define __EOSERIAL_UTILS_H__
# include "Array.h"
# include "Object.h"
# include "String.h"
namespace eoserial
{
/* ***************************
* DESERIALIZATION FUNCTIONS *
*****************************
These functions are useful for casting eoserial::objects into simple, primitive
variables or into class instance which implement eoserial::Persistent.
The model is always quite the same :
- the first argument is the containing object (which is a eoserial::Entity,
an object or an array)
- the second argument is the key or index,
- the last argument is the value in which we're writing.
*/
template< class T >
inline void unpack( const Object & obj, const std::string & key, T & value )
{
static_cast<String*>( obj.find( key )->second )->deserialize( value );
}
inline void unpackObject( const Object & obj, const std::string & key, Persistent & value )
{
static_cast<Object*>( obj.find( key )->second )->deserialize( value );
}
template< class Container, template<class> class UnpackAlgorithm >
inline void unpackArray( const Object & obj, const std::string & key, Container & array )
{
static_cast<Array*>( obj.find( key )->second )->deserialize< Container, UnpackAlgorithm >( array );
}
template< class T >
inline void unpack( const Array & array, unsigned int index, T & value )
{
static_cast<String*>( array[ index ] )->deserialize( value );
}
inline void unpackObject( const Array & array, unsigned int index, Persistent & value )
{
static_cast<Object*>( array[ index ] )->deserialize( value );
}
template< class Container, template<class> class UnpackAlgorithm >
inline void unpackArray( const Array & array, unsigned int index, Container & container )
{
static_cast<Array*>( array[ index ] )->deserialize< Container, UnpackAlgorithm >( container );
}
/* *****************************
*** SERIALIZATION FUNCTIONS ***
*******************************
These functions are useful for casting classic objects and
eoserial::Persistent objects into eoserial entities which
can be manipulated by the framework.
*/
/**
* @brief Casts a value of a stream-serializable type (i.e, which implements
* operator <<) into a JsonString.
*
* This is used when serializing the objects : all primitives types should be
* converted into strings to get more easily manipulated.
*
* @param value The value we're converting.
* @return JsonString wrapper for the value.
*/
template <typename T>
String* make( const T & value )
{
std::stringstream ss;
ss.precision(std::numeric_limits<double>::digits10 + 1);
ss << value;
return new String( ss.str() );
}
/**
* @brief Specialization for strings : no need to convert as they're still
* usable as strings.
*/
template<>
inline String* make( const std::string & value )
{
return new String( value );
}
/*
* These functions are useful for automatically serializing STL containers into
* eoserial arrays which could be used by the framework.
**/
/**
* @brief Functor which explains how to push the value into the eoserial::Array.
*/
template< class T >
struct PushAlgorithm
{
/**
* @brief Main operator.
*
* @param array The eoserial::array in which we're writing.
* @param value The variable we are writing.
*/
virtual void operator()( Array & array, const T & value ) = 0;
};
/**
* @brief Push algorithm for primitive variables.
*
* This one should be used when inserting primitive (and types which implement
* operator<<) variables.
*/
template< class T >
struct MakeAlgorithm : public PushAlgorithm<T>
{
void operator()( Array & array, const T & value )
{
array.push_back( make( value ) );
}
};
/**
* @brief Push algorithm for eoserial::Persistent variables.
*/
template< class T >
struct SerializablePushAlgorithm : public PushAlgorithm<T>
{
void operator()( Array & array, const T & obj )
{
// obj address is not saved into array.push_back.
array.push_back( &obj );
}
};
/**
* @brief Casts a STL container (vector<int> or list<std::string>, for instance)
* into a eoserial::Array.
*
* @þaram PushAlgorithm The algorithm used for inserting new element in the eoserial::Array.
* This algorithm is directly called, so it is its own charge to invoke push_back on the
* eoserial::Array.
*/
template< class Container, template<class> class PushAlgorithm >
Array* makeArray( const Container & array )
{
Array* returned_array = new Array;
typedef typename Container::const_iterator iterator;
typedef typename Container::value_type Type;
PushAlgorithm< Type > algo;
for (
iterator it = array.begin(), end = array.end();
it != end;
++it)
{
algo( *returned_array, *it );
}
return returned_array;
}
} // namespace eoserial
# endif //__EOSERIAL_UTILS_H__

33
eo/src/serial/eoSerial.h Normal file
View file

@ -0,0 +1,33 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EOSERIAL_HEADERS__
# define __EOSERIAL_HEADERS__
# include "Object.h"
# include "Serializable.h"
# include "Array.h"
# include "Object.h"
# include "String.h"
# include "Parser.h"
# include "Utils.h"
# endif // __EOSERIAL_HEADERS__

View file

@ -0,0 +1,8 @@
{"a":"b",
"obj":
{"obj_a":"obj_}b","subobj_a":
{"subk":"subv"}
},
"c":"d",
"array":["1","2",{"\"array\"_obj\"":"array_ov]"}, ["3"], "4"]
}

View file

@ -21,7 +21,7 @@
Contact: http://eodev.sourceforge.net
Authors:
Caner Candan <caner.candan@thalesgroup.com>
Caner Candan <caner.candan@thalesgroup.com>
*/
@ -34,11 +34,12 @@ Caner Candan <caner.candan@thalesgroup.com>
eoParallel::eoParallel() :
_isEnabled( false, "parallelize-loop", "Enable memory shared parallelization into evaluation's loops", '\0' ),
_isDynamic( false, "parallelize-dynamic", "Enable dynamic memory shared parallelization", '\0' ),
_isDynamic( true, "parallelize-dynamic", "Enable dynamic memory shared parallelization", '\0' ),
_prefix( "results", "parallelize-prefix", "Here's the prefix filename where the results are going to be stored", '\0' ),
_nthreads( 0, "parallelize-nthreads", "Define the number of threads you want to use, nthreads = 0 means you want to use all threads available", '\0' ),
_enableResults( false, "parallelize-enable-results", "Enable the generation of results", '\0' ),
_doMeasure( false, "parallelize-do-measure", "Do some measures during execution", '\0' ),
_packetSize( 1U, "parallelize-packet-size", "Number of elements which should be sent in a single message during a parallel evaluation based on message passing.", '\0'),
_t_start(0)
{
}
@ -92,6 +93,7 @@ void eoParallel::_createParameters( eoParser& parser )
parser.processParam( _nthreads, section );
parser.processParam( _enableResults, section );
parser.processParam( _doMeasure, section );
parser.processParam( _packetSize, section );
}
void make_parallel(eoParser& parser)

View file

@ -20,8 +20,7 @@
Contact: http://eodev.sourceforge.net
Authors:
Caner Candan <caner.candan@thalesgroup.com>
Caner Candan <caner.candan@thalesgroup.com>
*/
/** @defgroup Parallel Parallel
@ -54,6 +53,7 @@ public:
std::string prefix() const;
inline unsigned int nthreads() const { return _nthreads.value(); }
inline unsigned int packetSize() const { return _packetSize.value(); }
inline bool enableResults() const { return _enableResults.value(); }
inline bool doMeasure() const { return _doMeasure.value(); }
@ -68,6 +68,7 @@ private:
eoValueParam<bool> _isDynamic;
eoValueParam<std::string> _prefix;
eoValueParam<unsigned int> _nthreads;
eoValueParam<unsigned int> _packetSize;
eoValueParam<bool> _enableResults;
eoValueParam<bool> _doMeasure;
double _t_start;

309
eo/src/utils/eoTimer.h Normal file
View file

@ -0,0 +1,309 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
# ifndef __EO_TIMER_H__
# define __EO_TIMER_H__
# include <sys/time.h> // time()
# include <sys/resource.h> // rusage()
# include <vector> // std::vector
# include <map> // std::map
# include "utils/eoParallel.h" // eo::parallel
# ifdef WITH_MPI
// For serialization purposes
# include <boost/serialization/access.hpp>
# include <boost/serialization/vector.hpp>
# include <boost/serialization/map.hpp>
# endif
/**
* @brief Timer allowing to measure time between a start point and a stop point.
*
* This timer allows the user to measure user time, system time and wallclock time
* between two points. Basically, user time is time spent in developer code ; system
* time is time spent during the IO wait and system calls ; wallclock is the difference
* of time we could observe by measuring time with a watch.
*
* @ingroup Utilities
*/
class eoTimer
{
public:
/**
* @brief Default ctor. Begins all the timers.
*/
eoTimer()
{
uuremainder = 0;
usremainder = 0;
restart();
}
/**
* @brief Restarts all the timers and launch the measure.
*/
void restart()
{
wc_start = time(NULL);
getrusage( RUSAGE_SELF, &_start );
}
/**
* @brief Measures the user time spent since the last restart().
*
* If the number of elapsed seconds is zero, the spent milliseconds are
* added to a remainder. If the remainder exceeds one second, it is
* added to the number of elapsed seconds.
*
* @return Number of seconds elapsed in user space.
*/
long int usertime()
{
struct rusage _now;
getrusage( RUSAGE_SELF, &_now );
long int result = _now.ru_utime.tv_sec - _start.ru_utime.tv_sec;
long int remainder = _now.ru_utime.tv_usec - _start.ru_utime.tv_usec;
if( remainder >= 0 )
{
uuremainder += remainder;
} else
{
uuremainder += ( 1000000 - remainder );
--result;
}
if( uuremainder >= 1000000 )
{
uuremainder -= 1000000;
++result;
}
return result;
}
/**
* @brief Measures the system time spent since the last restart().
*
* If the number of elapsed seconds is zero, the spent milliseconds are
* added to a remainder. If the remainder exceeds one second, it is
* added to the number of elapsed seconds.
*
* @return Number of seconds elapsed in system (kernel) space.
*/
long int systime()
{
struct rusage _now;
getrusage( RUSAGE_SELF, &_now );
long int result = _now.ru_stime.tv_sec - _start.ru_stime.tv_sec;
long int remainder = _now.ru_stime.tv_usec - _start.ru_stime.tv_usec;
if( remainder >= 0 )
{
usremainder += remainder;
} else
{
usremainder += ( 1000000 - remainder );
--result;
}
if( usremainder >= 1000000 )
{
usremainder -= 1000000;
++result;
}
return result;
}
/**
* @brief Measures the wallclock time spent since the last restart().
*
* @return Number of seconds elapsed, as a double.
*/
double wallclock()
{
return std::difftime( std::time(NULL) , wc_start );
}
protected:
// Structure used to measure user and system time.
struct rusage _start;
// Remainder (in milliseconds) for user time.
long int uuremainder;
// Remainder (in milliseconds) for system time.
long int usremainder;
// Structure used to measure wallclock time.
time_t wc_start;
};
/**
* @brief Registers a group of statistics, each statistic corresponding to user, system and wallclock times distribution.
*
* This class helps the user to measure time in different parts of an application. A name is associated to a statistic,
* on each call to start() and stop() for this name, a new number is added to the statistic, for each of the three
* measured times.
*
* The statistics are only registered if the option "--parallelized-do-measure" is set to true, which can be checked
* thanks to global object eo::parallel.
*
* This shows how the eoTimerStat can be used :
* @code
* eoTimerStat timerStat;
* timerStat.start("first_point");
* for( int i = 0; i < 1000; ++i )
* {
* timerStat.start("single_computation");
* single_computation( i );
* timerStat.stop("single_computation");
* }
* // After this loop, timerStat contains a statistic of key "single_computation" which contains 1000 measures for
* // each type of time.
* timerStat.stop("first_point");
* // After this line, timerStat contains another statistic of key "first_point" which counted the duration of the
* // whole loop.
*
* int singleComputationUsertimeMean = 0;
* for( int i = 0; i < 1000; ++i )
* {
* singleComputationUsertimeMean += timerStat.stats()["single_computation"].utime[i];
* }
* std::cout << "Mean of user time spent in single computation: " << singleComputationUsertimeMean / 1000. << std::endl;
* @endcode
*
* When using MPI, these statistics can be readily be serialized, so as to be sent over a network, for instance.
*
* Implementation details: this eoTimerStat is in fact a map of strings (key) / Stat (value). Stat is an internal
* structure directly defined in the class, which contains three vectors modeling the distributions of the different
* types of elapsed times. Another map of strings (key) / eoTimer (value) allows to effectively retrieve the different
* times. The struct Stat will be exposed to client, which will use its members ; however,
* the client doesn't have anything to do directly with the timer, that's why the two maps are splitted.
*
* @ingroup Utilities
*/
class eoTimerStat
{
public:
/**
* @brief Statistic related to a key (name).
*
* This structure is the value in the map saved in the eoTimerStat. It contains the statistic bound to a key,
* which are the user time distribution, the system time distribution and the wallclock time distribution, as
* std::vector s.
*
* It can readily be serialized with boost when compiling with mpi.
*/
struct Stat
{
std::vector<long int> utime;
std::vector<long int> stime;
std::vector<double> wtime;
#ifdef WITH_MPI
// Gives access to boost serialization
friend class boost::serialization::access;
/**
* Serializes the single statistic in a boost archive (useful for boost::mpi).
* Just serializes the 3 vectors.
*/
template <class Archive>
void serialize( Archive & ar, const unsigned int version )
{
ar & utime & stime & wtime;
(void) version; // avoid compilation warning
}
# endif
};
#ifdef WITH_MPI
// Gives access to boost serialization
friend class boost::serialization::access;
/**
* Serializes the timerStat object in a boost archive (useful for boost::mpi).
* Just serializes the map.
*/
template <class Archive>
void serialize( Archive & ar, const unsigned int version )
{
ar & _stats;
(void) version; // avoid compilation warning
}
# endif
/**
* @brief Starts a new measure for the given key.
*
* This is only performed if parallel.doMeasure() is true, which is equivalent to the fact that
* parser found "--parallel-do-measure=1" in command line args.
*
* @param key The key of the statistic.
*/
void start( const std::string & key )
{
if( eo::parallel.doMeasure() )
{
_timers[ key ].restart();
}
}
/**
* @brief Stops the mesure for the given key and saves the elapsed times.
*
* Must follow a call of start with the same key.
*
* This is only performed if parallel.doMeasure() is true, which is equivalent to the fact that
* parser found "--parallel-do-measure=1" in command line args.
*
* @param key The key of the statistic.
*/
void stop( const std::string& key )
{
if( eo::parallel.doMeasure() )
{
Stat & s = _stats[ key ];
eoTimer & t = _timers[ key ];
s.utime.push_back( t.usertime() );
s.stime.push_back( t.systime() );
s.wtime.push_back( t.wallclock() );
}
}
/**
* @brief Getter for the statistics map.
*/
std::map< std::string, Stat >& stats()
{
return _stats;
}
protected:
// Statistics map: links a key (string) to a statistic.
std::map< std::string, Stat > _stats;
// Timers map: links a key to its timer.
std::map< std::string, eoTimer > _timers;
};
# endif // __TIMER_H__

View file

@ -14,12 +14,23 @@ INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/contrib)
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/contrib/MGE)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
IF(WITH_MPI)
INCLUDE_DIRECTORIES(${BOOST_DIR}/include)
INCLUDE_DIRECTORIES(${MPI_DIR}/include)
ENDIF()
######################################################################################
### 2) Specify where CMake can find the libraries
######################################################################################
LINK_DIRECTORIES(${EO_BINARY_DIR}/lib)
IF(WITH_MPI)
LINK_DIRECTORIES(${BOOST_DIR}/lib)
LINK_DIRECTORIES(${MPI_DIR}/lib)
SET(CMAKE_CXX_COMPILER "${MPI_DIR}/bin/mpicxx")
ENDIF()
######################################################################################
### 3) Define your targets and link the librairies
######################################################################################
@ -65,8 +76,8 @@ SET (TEST_LIST
t-eoExtendedVelocity
t-eoLogger
t-eoIQRStat
t-eoParallel
#t-openmp # does not work anymore since functions used in this test were removed from EO
#t-eoParallel
#t-openmp
#t-eoDualFitness
t-eoParser
)

View file

@ -0,0 +1,55 @@
###############################################################################
##
## CMakeLists file for eo/test/mpi
##
###############################################################################
######################################################################################
### 1) Include the sources
######################################################################################
MESSAGE("EO SOURCE DIR: ${EO_SOURCE_DIR}")
MESSAGE("OMPI: ${MPI_DIR}")
MESSAGE("BOOST: ${BOOST_DIR}")
INCLUDE_DIRECTORIES(${MPI_DIR}/include)
INCLUDE_DIRECTORIES(${BOOST_DIR}/include)
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
######################################################################################
### 2) Specify where CMake can find the libraries
######################################################################################
LINK_DIRECTORIES(${EO_BINARY_DIR}/lib)
LINK_DIRECTORIES(${MPI_DIR}/lib)
LINK_DIRECTORIES(${BOOST_DIR}/lib)
######################################################################################
### 3) Define your targets and link the librairies
######################################################################################
SET (TEST_LIST
t-mpi-parallelApply
t-mpi-wrapper
t-mpi-multipleRoles
t-mpi-eval
)
FOREACH (test ${TEST_LIST})
SET ("T_${test}_SOURCES" "${test}.cpp")
ENDFOREACH (test)
SET(CMAKE_CXX_COMPILER "${MPI_DIR}/bin/mpicxx")
ADD_DEFINITIONS(-DWITH_MPI)
IF(ENABLE_CMAKE_TESTING)
FOREACH (test ${TEST_LIST})
ADD_EXECUTABLE(${test} ${T_${test}_SOURCES})
ADD_TEST(${test} ${test})
TARGET_LINK_LIBRARIES(${test} boost_mpi boost_serialization eoutils eompi eoserial eo)
INSTALL(TARGETS ${test} RUNTIME DESTINATION share/eo/test COMPONENT test)
ENDFOREACH (test)
ENDIF()
######################################################################################

227
eo/test/mpi/t-mpi-eval.cpp Normal file
View file

@ -0,0 +1,227 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
/*
* This file shows an example of parallel evaluation of a population, when using an eoEasyEA algorithm.
* Moreover, we add a basic wrapper on the parallel evaluation, so as to show how to retrieve the best solutions.
*/
//-----------------------------------------------------------------------------
#include <eo>
#include <eoPopEvalFunc.h>
#include <es/make_real.h>
#include "../real_value.h"
#include <mpi/eoMpi.h>
#include <boost/mpi.hpp>
#include <vector>
using namespace std;
//-----------------------------------------------------------------------------
class eoRealSerializable : public eoReal< eoMinimizingFitness >, public eoserial::Persistent
{
public:
eoRealSerializable(unsigned size = 0, double value = 0.0):
eoReal<eoMinimizingFitness>(size, value) {}
eoserial::Object* pack() const
{
eoserial::Object* obj = new eoserial::Object;
obj->add( "array",
eoserial::makeArray< vector<double>, eoserial::MakeAlgorithm >
( *this )
);
return obj;
}
void unpack( const eoserial::Object* obj )
{
eoserial::unpackArray< vector<double>, eoserial::Array::UnpackAlgorithm >
( *obj, "array", *this );
}
// Gives access to boost serialization
friend class boost::serialization::access;
/**
* Serializes the decomposition in a boost archive (useful for boost::mpi)
*/
template <class Archive>
void save( Archive & ar, const unsigned int version ) const
{
std::stringstream ss;
printOn( ss );
std::string asStr = ss.str();
ar & asStr;
(void) version; // avoid compilation warning
}
/**
* Deserializes the decomposition from a boost archive (useful for boost:mpi)
*/
template <class Archive>
void load( Archive & ar, const unsigned int version )
{
std::string asStr;
ar & asStr;
std::stringstream ss;
ss << asStr;
readFrom( ss );
(void) version; // avoid compilation warning
}
// Indicates that boost save and load operations are not the same.
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
typedef eoRealSerializable EOT;
/*
* Wrapper for HandleResponse: shows the best answer, as it is found.
*
* Finding the best solution is an associative operation (as it is based on a "min" function, which is associative too)
* and that's why we can perform it here. Indeed, the min element of 5 elements is the min element of the 3 first
* elements and the min element of the 2 last elements:
* min(1, 2, 3, 4, 5) = min( min(1, 2, 3), min(4, 5) )
*
* This is a reduction. See MapReduce example to have another examples of reduction.
*/
struct CatBestAnswers : public eo::mpi::HandleResponseParallelApply<EOT>
{
CatBestAnswers()
{
best.fitness( 1000000000. );
}
/*
our structure inherits the member _wrapped from HandleResponseFunction,
which is a HandleResponseFunction pointer;
it inherits also the member _d (like Data), which is a pointer to the
ParallelApplyData used in the HandleResponseParallelApply&lt;EOT&gt;. Details
of this data are contained in the file eoParallelApply. We need just to know that
it contains a member assignedTasks which maps a worker rank and the sent slice
to be processed by the worker, and a reference to the processed table via the
call of the data() function.
*/
// if EOT were a template, we would have to do: (thank you C++ :)
// using eo::mpi::HandleResponseParallelApply<EOT>::_wrapped;
// using eo::mpi::HandleResponseParallelApply<EOT>::d;
void operator()(int wrkRank)
{
eo::mpi::ParallelApplyData<EOT> * d = _data;
// Retrieve informations about the slice processed by the worker
int index = d->assignedTasks[wrkRank].index;
int size = d->assignedTasks[wrkRank].size;
// call to the wrapped function HERE
(*_wrapped)( wrkRank );
// Compare fitnesses of evaluated individuals with the best saved
for(int i = index; i < index+size; ++i)
{
if( best.fitness() < d->table()[ i ].fitness() )
{
eo::log << eo::quiet << "Better solution found:" << d->table()[i].fitness() << std::endl;
best = d->table()[ i ];
}
}
}
protected:
EOT best;
};
int main(int ac, char** av)
{
eo::mpi::Node::init( ac, av );
// eo::log << eo::setlevel( eo::debug );
eo::log << eo::setlevel( eo::quiet );
eoParser parser(ac, av);
unsigned int popSize = parser.getORcreateParam((unsigned int)100, "popSize", "Population Size", 'P', "Evolution Engine").value();
unsigned int dimSize = parser.getORcreateParam((unsigned int)10, "dimSize", "Dimension Size", 'd', "Evolution Engine").value();
uint32_t seedParam = parser.getORcreateParam((uint32_t)0, "seed", "Random number seed", 0).value();
if (seedParam == 0) { seedParam = time(0); }
make_parallel(parser);
make_help(parser);
rng.reseed( seedParam );
eoUniformGenerator< double > gen(-5, 5);
eoInitFixedLength< EOT > init( dimSize, gen );
eoEvalFuncPtr< EOT, double, const std::vector< double >& > mainEval( real_value );
eoEvalFuncCounter< EOT > eval( mainEval );
// until this point, everything (but eo::mpi::Node::init) is exactly as in an sequential version.
// We then instanciate the parallel algorithm. The store is directly used by the eoParallelPopLoopEval, which
// internally uses parallel apply.
int rank = eo::mpi::Node::comm().rank();
eo::mpi::DynamicAssignmentAlgorithm assign;
if( rank == eo::mpi::DEFAULT_MASTER )
{
eoPop< EOT > pop( popSize, init );
eo::log << "Size of population : " << popSize << std::endl;
/*
eo::mpi::ParallelApplyStore< EOT > store( eval, eo::mpi::DEFAULT_MASTER );
store.wrapHandleResponse( new CatBestAnswers );
eoParallelPopLoopEval< EOT > popEval( assign, eo::mpi::DEFAULT_MASTER, &store );
*/
eoParallelPopLoopEval< EOT > popEval( assign, eo::mpi::DEFAULT_MASTER, eval, 5 );
eo::log << eo::quiet << "Before first evaluation." << std::endl;
popEval( pop, pop );
eo::log << eo::quiet << "After first evaluation." << std::endl;
pop = eoPop< EOT >( popSize, init );
popEval( pop, pop );
eo::log << eo::quiet << "After second evaluation." << std::endl;
eo::log << eo::quiet << "DONE!" << std::endl;
} else
{
eoPop< EOT > pop; // the population doesn't have to be initialized, as it is not used by workers.
eoParallelPopLoopEval< EOT > popEval( assign, eo::mpi::DEFAULT_MASTER, eval );
popEval( pop, pop );
}
return 0;
}
//-----------------------------------------------------------------------------

View file

@ -0,0 +1,169 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
/*
* This file shows an example of how to make a hierarchy between nodes, when using a parallel apply. In this basic
* test, the master delegates the charge of finding workers to 2 "sub" masters, which then send part of the table to
* their workers.
*
* It's convenient to establish a role map, so as to clearly identify every role:
* - The node 0 is the general master, that delegates the job. It sends the table to the 2 submasters, and waits for the
* results.
* - Nodes 1 and 2 are the worker of the first job: the delegates. They receive the elements of the table and
* retransmit them to the subworkers. They play the roles of worker in the delegating job, and master in the plus one
* job.
* - Following nodes (3 to 6) are workers of the plus one job. They do the real job. Nodes 3 and 5 are attached to
* submaster 1, 4 and 6 to submaster 2.
*
* This test requires exactly 7 hosts. If the size is bigger, an exception will be thrown at the beginning.
**/
# include <mpi/eoMpi.h>
# include <mpi/eoParallelApply.h>
# include <mpi/eoTerminateJob.h>
# include <boost/serialization/vector.hpp>
# include <iostream>
# include <vector>
using namespace std;
using namespace eo::mpi;
// The real job to execute, for the subworkers: add one to each element of a table.
struct SubWork: public eoUF< int&, void >
{
void operator() ( int & x )
{
cout << "Subwork phase." << endl;
++x;
}
};
// Function called by both subworkers and delegates.
// v is the vector to process, rank is the MPI rank of the sub master
void subtask( vector<int>& v, int rank )
{
// Attach workers according to nodes.
// Submaster with rank 1 will have ranks 3 and 5 as subworkers.
// Submaster with rank 2 will have ranks 4 and 6 as subworkers.
vector<int> workers;
workers.push_back( rank + 2 );
workers.push_back( rank + 4 );
DynamicAssignmentAlgorithm algo( workers );
SubWork sw;
// Launch the job!
ParallelApplyStore<int> store( sw, rank );
store.data( v );
ParallelApply<int> job( algo, rank, store );
job.run();
EmptyJob stop( algo, rank );
}
// Functor applied by submasters. Wait for the subworkers responses and then add some random processing (here, multiply
// each result by two).
// Note that this work receives a vector of integers as an entry, while subworkers task's operator receives a simple
// integer.
struct Work: public eoUF< vector<int>&, void >
{
void operator() ( vector<int>& v )
{
cout << "Work phase..." << endl;
subtask( v, Node::comm().rank() );
for( int i = 0; i < v.size(); ++i )
{
v[i] *= 2;
}
}
};
int main(int argc, char** argv)
{
// eo::log << eo::setlevel( eo::debug );
Node::init( argc, argv );
if( Node::comm().size() != 7 ) {
throw std::runtime_error("World size should be 7.");
}
vector<int> v;
v.push_back(1);
v.push_back(3);
v.push_back(3);
v.push_back(7);
v.push_back(42);
// As submasters' operator receives a vector<int> as an input, and ParallelApply takes a vector of
// operator's input as an input, we have to deal with a vector of vector of integers for the master task.
vector< vector<int> > metaV;
// Here, we send twice the same vector. We could also have splitted the first vector into two vectors, one
// containing the beginning and another one containing the end.
metaV.push_back( v );
metaV.push_back( v );
// Assigning roles is done by comparing MPI ranks.
switch( Node::comm().rank() )
{
// Nodes from 0 to 2 are implicated into the delegating task.
case 0:
case 1:
case 2:
{
Work w;
DynamicAssignmentAlgorithm algo( 1, 2 );
ParallelApplyStore< vector<int> > store( w, 0 );
store.data( metaV );
ParallelApply< vector<int> > job( algo, 0, store );
job.run();
if( job.isMaster() )
{
EmptyJob stop( algo, 0 );
v = metaV[0];
cout << "Results : " << endl;
for(int i = 0; i < v.size(); ++i)
{
cout << v[i] << ' ';
}
cout << endl;
}
}
break;
// Other nodes are implicated into the subwork task.
default:
{
// all the other nodes are sub workers
int rank = Node::comm().rank();
if ( rank == 3 or rank == 5 )
{
subtask( v, 1 );
} else {
subtask( v, 2 );
}
}
break;
}
return 0;
}

View file

@ -0,0 +1,214 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
/*
* This file shows an example of use of parallel apply, in the following context: each element of a table is
* incremented... in a parallel fashion. While this operation is very easy to perform even on a single host, it's just
* an example for parallel apply use.
*
* Besides, this is also a test for assignment (scheduling) algorithms, in different cases. The test succeeds if and
* only if the program terminates without any segfault ; otherwise, there could be a deadlock which prevents the end or
* a segfault at any time.
*
* One important thing is to instanciate an EmptyJob after having launched a ParallelApplyJob, so as the workers to be
* aware that the job is done (as it's a MultiJob).
*
* This test needs at least 3 processes to be launched. Under this size, it will directly throw an exception, at the
* beginning;
*/
# include <mpi/eoMpi.h>
# include <mpi/eoParallelApply.h>
# include <mpi/eoTerminateJob.h>
# include <iostream>
# include <vector>
using namespace std;
using namespace eo::mpi;
/*
* The function to be called on each element of the table: just increment the value.
*/
struct plusOne : public eoUF< int&, void >
{
void operator() ( int & x )
{
++x;
}
};
/*
* Internal structure representating a test.
*/
struct Test
{
AssignmentAlgorithm * assign; // used assignment algorithm for this test.
string description; // textual description of the test
int requiredNodesNumber; // number of required nodes. NB : chosen nodes ranks must be sequential
};
int main(int argc, char** argv)
{
// eo::log << eo::setlevel( eo::debug ); // if you like tty full of rainbows, decomment this line and comment the following one.
eo::log << eo::setlevel( eo::quiet );
bool launchOnlyOne = false ; // Set this to true if you wanna launch only the first test.
Node::init( argc, argv );
// Initializes a vector with random values.
srand( time(0) );
vector<int> v;
for( int i = 0; i < 1000; ++i )
{
v.push_back( rand() );
}
// We need to be sure the values are correctly incremented between each test. So as to check this, we save the
// original vector into a variable originalV, and put an offset variable to 0. After each test, the offset is
// incremented and we can compare the returned value of each element to the value of each element in originalV +
// offset. If the two values are different, there has been a problem.
int offset = 0;
vector<int> originalV = v;
// Instanciates the functor to apply on each element
plusOne plusOneInstance;
vector< Test > tests;
const int ALL = Node::comm().size();
if( ALL < 3 ) {
throw std::runtime_error("Needs at least 3 processes to be launched!");
}
// Tests are auto described thanks to member "description"
Test tIntervalStatic;
tIntervalStatic.assign = new StaticAssignmentAlgorithm( 1, REST_OF_THE_WORLD, v.size() );
tIntervalStatic.description = "Correct static assignment with interval."; // workers have ranks from 1 to size - 1
tIntervalStatic.requiredNodesNumber = ALL;
tests.push_back( tIntervalStatic );
if( !launchOnlyOne )
{
Test tWorldStatic;
tWorldStatic.assign = new StaticAssignmentAlgorithm( v.size() );
tWorldStatic.description = "Correct static assignment with whole world as workers.";
tWorldStatic.requiredNodesNumber = ALL;
tests.push_back( tWorldStatic );
Test tStaticOverload;
tStaticOverload.assign = new StaticAssignmentAlgorithm( v.size()+100 );
tStaticOverload.description = "Static assignment with too many runs.";
tStaticOverload.requiredNodesNumber = ALL;
tests.push_back( tStaticOverload );
Test tUniqueStatic;
tUniqueStatic.assign = new StaticAssignmentAlgorithm( 1, v.size() );
tUniqueStatic.description = "Correct static assignment with unique worker.";
tUniqueStatic.requiredNodesNumber = 2;
tests.push_back( tUniqueStatic );
Test tVectorStatic;
vector<int> workers;
workers.push_back( 1 );
workers.push_back( 2 );
tVectorStatic.assign = new StaticAssignmentAlgorithm( workers, v.size() );
tVectorStatic.description = "Correct static assignment with precise workers specified.";
tVectorStatic.requiredNodesNumber = 3;
tests.push_back( tVectorStatic );
Test tIntervalDynamic;
tIntervalDynamic.assign = new DynamicAssignmentAlgorithm( 1, REST_OF_THE_WORLD );
tIntervalDynamic.description = "Dynamic assignment with interval.";
tIntervalDynamic.requiredNodesNumber = ALL;
tests.push_back( tIntervalDynamic );
Test tUniqueDynamic;
tUniqueDynamic.assign = new DynamicAssignmentAlgorithm( 1 );
tUniqueDynamic.description = "Dynamic assignment with unique worker.";
tUniqueDynamic.requiredNodesNumber = 2;
tests.push_back( tUniqueDynamic );
Test tVectorDynamic;
tVectorDynamic.assign = new DynamicAssignmentAlgorithm( workers );
tVectorDynamic.description = "Dynamic assignment with precise workers specified.";
tVectorDynamic.requiredNodesNumber = tVectorStatic.requiredNodesNumber;
tests.push_back( tVectorDynamic );
Test tWorldDynamic;
tWorldDynamic.assign = new DynamicAssignmentAlgorithm;
tWorldDynamic.description = "Dynamic assignment with whole world as workers.";
tWorldDynamic.requiredNodesNumber = ALL;
tests.push_back( tWorldDynamic );
}
for( unsigned int i = 0; i < tests.size(); ++i )
{
// Instanciates a store with the functor, the master rank and size of packet (see ParallelApplyStore doc).
ParallelApplyStore< int > store( plusOneInstance, eo::mpi::DEFAULT_MASTER, 3 );
// Updates the contained data
store.data( v );
// Creates the job with the assignment algorithm, the master rank and the store
ParallelApply< int > job( *(tests[i].assign), eo::mpi::DEFAULT_MASTER, store );
// Only master writes information
if( job.isMaster() )
{
cout << "Test : " << tests[i].description << endl;
}
// Workers whose rank is inferior to required nodes number have to run the test, the other haven't anything to
// do.
if( Node::comm().rank() < tests[i].requiredNodesNumber )
{
job.run();
}
// After the job run, the master checks the result with offset and originalV
if( job.isMaster() )
{
// This job has to be instanciated, not launched, so as to tell the workers they're done with the parallel
// job.
EmptyJob stop( *(tests[i].assign), eo::mpi::DEFAULT_MASTER );
++offset;
for(int i = 0; i < v.size(); ++i)
{
cout << v[i] << ' ';
if( originalV[i] + offset != v[i] )
{
cout << " <-- ERROR at this point." << endl;
exit( EXIT_FAILURE );
}
}
cout << endl;
}
// MPI synchronization (all the processes wait to be here).
Node::comm().barrier();
delete tests[i].assign;
}
return 0;
}

View file

@ -0,0 +1,132 @@
/*
(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 <benjamin.bouvier@gmail.com>
*/
/*
* This file shows an example of how to wrap a handler of a job store. Here, the wrapped handler is the "IsFinished"
* one. The only function that has been added is that the wrapper prints a message on standard output, indicating what
* the wrapped function returns as a result.
*
* This test is performed on a parallel apply job, the same as in parallelApply. The main difference is when
* instanciating the store.
*/
# include <mpi/eoMpi.h>
# include <mpi/eoParallelApply.h>
# include <mpi/eoTerminateJob.h>
# include <iostream>
# include <vector>
using namespace std;
using namespace eo::mpi;
// Job functor.
struct plusOne : public eoUF< int&, void >
{
void operator() ( int & x )
{
++x;
}
};
/*
* Shows the wrapped result of IsFinished, prints a message and returns the wrapped value.
* times is an integer counting how many time the wrapper (hence the wrapped too) has been called.
*/
template< class EOT >
struct ShowWrappedResult : public IsFinishedParallelApply<EOT>
{
using IsFinishedParallelApply<EOT>::_wrapped;
ShowWrappedResult ( IsFinishedParallelApply<EOT> * w = 0 ) : IsFinishedParallelApply<EOT>( w ), times( 0 )
{
// empty
}
bool operator()()
{
bool wrappedValue = _wrapped->operator()(); // (*_wrapped)();
cout << times << ") Wrapped function would say that it is " << ( wrappedValue ? "":"not ") << "finished" << std::endl;
++times;
return wrappedValue;
}
private:
int times;
};
int main(int argc, char** argv)
{
// eo::log << eo::setlevel( eo::debug );
eo::log << eo::setlevel( eo::quiet );
Node::init( argc, argv );
srand( time(0) );
vector<int> v;
for( int i = 0; i < 1000; ++i )
{
v.push_back( rand() );
}
int offset = 0;
vector<int> originalV = v;
plusOne plusOneInstance;
StaticAssignmentAlgorithm assign( v.size() );
ParallelApplyStore< int > store( plusOneInstance, eo::mpi::DEFAULT_MASTER, 1 );
store.data( v );
// This is the only thing which changes: we wrap the IsFinished function.
// According to RAII, we'll delete the invokated wrapper at the end of the main ; the store won't delete it
// automatically.
IsFinishedParallelApply<int>* wrapper = new ShowWrappedResult<int>;
store.wrapIsFinished( wrapper );
ParallelApply<int> job( assign, eo::mpi::DEFAULT_MASTER, store );
// Equivalent to:
// Job< ParallelApplyData<int> > job( assign, 0, store );
job.run();
EmptyJob stop( assign, eo::mpi::DEFAULT_MASTER );
if( job.isMaster() )
{
++offset;
for(int i = 0; i < v.size(); ++i)
{
cout << v[i] << ' ';
if( originalV[i] + offset != v[i] )
{
cout << " <-- ERROR at this point." << endl;
exit( EXIT_FAILURE );
}
}
cout << endl;
}
delete wrapper;
return 0;
}

View file

@ -0,0 +1,405 @@
html {
height: 100%;
}
body.deck-container {
overflow-y: auto;
position: static;
}
.deck-container {
position: relative;
min-height: 100%;
margin: 0 auto;
padding: 0 48px;
font-size: 16px;
line-height: 1.25;
overflow: hidden;
/* Resets and base styles from HTML5 Boilerplate */
/* End HTML5 Boilerplate adaptations */
}
.js .deck-container {
visibility: hidden;
}
.ready .deck-container {
visibility: visible;
}
.touch .deck-container {
-webkit-text-size-adjust: none;
-moz-text-size-adjust: none;
}
.deck-container div, .deck-container span, .deck-container object, .deck-container iframe,
.deck-container h1, .deck-container h2, .deck-container h3, .deck-container h4, .deck-container h5, .deck-container h6, .deck-container p, .deck-container blockquote, .deck-container pre,
.deck-container abbr, .deck-container address, .deck-container cite, .deck-container code, .deck-container del, .deck-container dfn, .deck-container em, .deck-container img, .deck-container ins, .deck-container kbd, .deck-container q, .deck-container samp,
.deck-container small, .deck-container strong, .deck-container sub, .deck-container sup, .deck-container var, .deck-container b, .deck-container i, .deck-container dl, .deck-container dt, .deck-container dd, .deck-container ol, .deck-container ul, .deck-container li,
.deck-container fieldset, .deck-container form, .deck-container label, .deck-container legend,
.deck-container table, .deck-container caption, .deck-container tbody, .deck-container tfoot, .deck-container thead, .deck-container tr, .deck-container th, .deck-container td,
.deck-container article, .deck-container aside, .deck-container canvas, .deck-container details, .deck-container figcaption, .deck-container figure,
.deck-container footer, .deck-container header, .deck-container hgroup, .deck-container menu, .deck-container nav, .deck-container section, .deck-container summary,
.deck-container time, .deck-container mark, .deck-container audio, .deck-container video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
.deck-container article, .deck-container aside, .deck-container details, .deck-container figcaption, .deck-container figure,
.deck-container footer, .deck-container header, .deck-container hgroup, .deck-container menu, .deck-container nav, .deck-container section {
display: block;
}
.deck-container blockquote, .deck-container q {
quotes: none;
}
.deck-container blockquote:before, .deck-container blockquote:after, .deck-container q:before, .deck-container q:after {
content: "";
content: none;
}
.deck-container ins {
background-color: #ff9;
color: #000;
text-decoration: none;
}
.deck-container mark {
background-color: #ff9;
color: #000;
font-style: italic;
font-weight: bold;
}
.deck-container del {
text-decoration: line-through;
}
.deck-container abbr[title], .deck-container dfn[title] {
border-bottom: 1px dotted;
cursor: help;
}
.deck-container table {
border-collapse: collapse;
border-spacing: 0;
}
.deck-container hr {
display: block;
height: 1px;
border: 0;
border-top: 1px solid #ccc;
margin: 1em 0;
padding: 0;
}
.deck-container input, .deck-container select {
vertical-align: middle;
}
.deck-container select, .deck-container input, .deck-container textarea, .deck-container button {
font: 99% sans-serif;
}
.deck-container pre, .deck-container code, .deck-container kbd, .deck-container samp {
font-family: monospace, sans-serif;
}
.deck-container a {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.deck-container a:hover, .deck-container a:active {
outline: none;
}
.deck-container ul, .deck-container ol {
margin-left: 2em;
vertical-align: top;
}
.deck-container ol {
list-style-type: decimal;
}
.deck-container nav ul, .deck-container nav li {
margin: 0;
list-style: none;
list-style-image: none;
}
.deck-container small {
font-size: 85%;
}
.deck-container strong, .deck-container th {
font-weight: bold;
}
.deck-container td {
vertical-align: top;
}
.deck-container sub, .deck-container sup {
font-size: 75%;
line-height: 0;
position: relative;
}
.deck-container sup {
top: -0.5em;
}
.deck-container sub {
bottom: -0.25em;
}
.deck-container textarea {
overflow: auto;
}
.ie6 .deck-container legend, .ie7 .deck-container legend {
margin-left: -7px;
}
.deck-container input[type="radio"] {
vertical-align: text-bottom;
}
.deck-container input[type="checkbox"] {
vertical-align: bottom;
}
.ie7 .deck-container input[type="checkbox"] {
vertical-align: baseline;
}
.ie6 .deck-container input {
vertical-align: text-bottom;
}
.deck-container label, .deck-container input[type="button"], .deck-container input[type="submit"], .deck-container input[type="image"], .deck-container button {
cursor: pointer;
}
.deck-container button, .deck-container input, .deck-container select, .deck-container textarea {
margin: 0;
}
.deck-container input:invalid, .deck-container textarea:invalid {
border-radius: 1px;
-moz-box-shadow: 0px 0px 5px red;
-webkit-box-shadow: 0px 0px 5px red;
box-shadow: 0px 0px 5px red;
}
.deck-container input:invalid .no-boxshadow, .deck-container textarea:invalid .no-boxshadow {
background-color: #f0dddd;
}
.deck-container button {
width: auto;
overflow: visible;
}
.ie7 .deck-container img {
-ms-interpolation-mode: bicubic;
}
.deck-container, .deck-container select, .deck-container input, .deck-container textarea {
color: #444;
}
.deck-container a {
color: #607890;
}
.deck-container a:hover, .deck-container a:focus {
color: #036;
}
.deck-container a:link {
-webkit-tap-highlight-color: #fff;
}
.deck-container.deck-loading {
display: none;
}
.slide {
width: auto;
min-height: 100%;
position: relative;
}
.slide h1 {
font-size: 4.5em;
}
.slide h1, .slide .vcenter {
font-weight: bold;
text-align: center;
padding-top: 1em;
max-height: 100%;
}
.csstransforms .slide h1, .csstransforms .slide .vcenter {
padding: 0 48px;
position: absolute;
left: 0;
right: 0;
top: 50%;
-webkit-transform: translate(0, -50%);
-moz-transform: translate(0, -50%);
-ms-transform: translate(0, -50%);
-o-transform: translate(0, -50%);
transform: translate(0, -50%);
}
.slide .vcenter h1 {
position: relative;
top: auto;
padding: 0;
-webkit-transform: none;
-moz-transform: none;
-ms-transform: none;
-o-transform: none;
transform: none;
}
.slide h2 {
font-size: 2.25em;
font-weight: bold;
padding-top: .5em;
margin: 0 0 .66666em 0;
border-bottom: 3px solid #888;
}
.slide h3 {
font-size: 1.4375em;
font-weight: bold;
margin-bottom: .30435em;
}
.slide h4 {
font-size: 1.25em;
font-weight: bold;
margin-bottom: .25em;
}
.slide h5 {
font-size: 1.125em;
font-weight: bold;
margin-bottom: .2222em;
}
.slide h6 {
font-size: 1em;
font-weight: bold;
}
.slide img, .slide iframe, .slide video {
display: block;
max-width: 100%;
}
.slide video, .slide iframe, .slide img {
display: block;
margin: 0 auto;
}
.slide p, .slide blockquote, .slide iframe, .slide img, .slide ul, .slide ol, .slide pre, .slide video {
margin-bottom: 1em;
}
.slide pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
padding: 1em;
border: 1px solid #888;
}
.slide em {
font-style: italic;
}
.slide li {
padding: .25em 0;
vertical-align: middle;
}
.deck-before, .deck-previous, .deck-next, .deck-after {
position: absolute;
left: -999em;
top: -999em;
}
.deck-current {
z-index: 2;
}
.slide .slide {
visibility: hidden;
position: static;
min-height: 0;
}
.deck-child-current {
position: static;
z-index: 2;
}
.deck-child-current .slide {
visibility: hidden;
}
.deck-child-current .deck-previous, .deck-child-current .deck-before, .deck-child-current .deck-current {
visibility: visible;
}
@media screen and (max-device-width: 480px) {
/* html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */
}
@media print {
* {
background: transparent !important;
color: black !important;
text-shadow: none !important;
filter: none !important;
-ms-filter: none !important;
-webkit-box-reflect: none !important;
-moz-box-reflect: none !important;
-webkit-box-shadow: none !important;
-moz-box-shadow: none !important;
box-shadow: none !important;
}
* :before, * :after {
display: none !important;
}
a, a:visited {
color: #444 !important;
text-decoration: underline;
}
a[href]:after {
content: " (" attr(href) ")";
}
abbr[title]:after {
content: " (" attr(title) ")";
}
.ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after {
content: "";
}
pre, blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group;
}
tr, img {
page-break-inside: avoid;
}
@page {
margin: 0.5cm;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3 {
page-break-after: avoid;
}
.slide {
position: static !important;
visibility: visible !important;
display: block !important;
-webkit-transform: none !important;
-moz-transform: none !important;
-o-transform: none !important;
-ms-transform: none !important;
transform: none !important;
opacity: 1 !important;
}
h1, .vcenter {
-webkit-transform: none !important;
-moz-transform: none !important;
-o-transform: none !important;
-ms-transform: none !important;
transform: none !important;
padding: 0 !important;
position: static !important;
}
.deck-container > .slide {
page-break-after: always;
}
.deck-container {
width: 100% !important;
height: auto !important;
padding: 0 !important;
display: block !important;
}
script {
display: none;
}
}

View file

@ -0,0 +1,41 @@
.deck-container .goto-form {
position: absolute;
z-index: 3;
bottom: 10px;
left: 50%;
height: 1.75em;
margin: 0 0 0 -9.125em;
line-height: 1.75em;
padding: 0.625em;
display: none;
background: #ccc;
overflow: hidden;
}
.borderradius .deck-container .goto-form {
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
}
.deck-container .goto-form label {
font-weight: bold;
}
.deck-container .goto-form label, .deck-container .goto-form input {
display: inline-block;
font-family: inherit;
}
.deck-goto .goto-form {
display: block;
}
#goto-slide {
width: 8.375em;
margin: 0 0.625em;
height: 1.4375em;
}
@media print {
.goto-form, #goto-slide {
display: none !important;
}
}

View file

@ -0,0 +1,13 @@
.deck-container .deck-permalink {
display: none;
position: absolute;
z-index: 4;
bottom: 30px;
right: 0;
width: 48px;
text-align: center;
}
.no-history .deck-container:hover .deck-permalink {
display: block;
}

View file

@ -0,0 +1,47 @@
.deck-menu .slide {
background: #eee;
position: relative;
left: 0;
top: 0;
visibility: visible;
cursor: pointer;
}
.no-csstransforms .deck-menu > .slide {
float: left;
width: 22%;
height: 22%;
min-height: 0;
margin: 1%;
font-size: 0.22em;
overflow: hidden;
padding: 0 0.5%;
}
.csstransforms .deck-menu > .slide {
-webkit-transform: scale(0.22) !important;
-moz-transform: scale(0.22) !important;
-o-transform: scale(0.22) !important;
-ms-transform: scale(0.22) !important;
transform: scale(0.22) !important;
-webkit-transform-origin: 0 0;
-moz-transform-origin: 0 0;
-o-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
height: 100%;
overflow: hidden;
padding: 0 48px;
margin: 12px;
}
.deck-menu iframe, .deck-menu img, .deck-menu video {
max-width: 100%;
}
.deck-menu .deck-current, .no-touch .deck-menu .slide:hover {
background: #ddf;
}
.deck-menu.deck-container:hover .deck-prev-link, .deck-menu.deck-container:hover .deck-next-link {
display: none;
}

View file

@ -0,0 +1,43 @@
.deck-container .deck-prev-link, .deck-container .deck-next-link {
display: none;
position: absolute;
z-index: 3;
top: 50%;
width: 32px;
height: 32px;
margin-top: -16px;
font-size: 20px;
font-weight: bold;
line-height: 32px;
vertical-align: middle;
text-align: center;
text-decoration: none;
color: #fff;
background: #888;
}
.borderradius .deck-container .deck-prev-link, .borderradius .deck-container .deck-next-link {
-webkit-border-radius: 16px;
-moz-border-radius: 16px;
border-radius: 16px;
}
.deck-container .deck-prev-link:hover, .deck-container .deck-prev-link:focus, .deck-container .deck-prev-link:active, .deck-container .deck-prev-link:visited, .deck-container .deck-next-link:hover, .deck-container .deck-next-link:focus, .deck-container .deck-next-link:active, .deck-container .deck-next-link:visited {
color: #fff;
}
.deck-container .deck-prev-link {
left: 8px;
}
.deck-container .deck-next-link {
right: 8px;
}
.deck-container:hover .deck-prev-link, .deck-container:hover .deck-next-link {
display: block;
}
.deck-container:hover .deck-prev-link.deck-nav-disabled, .touch .deck-container:hover .deck-prev-link, .deck-container:hover .deck-next-link.deck-nav-disabled, .touch .deck-container:hover .deck-next-link {
display: none;
}
@media print {
.deck-prev-link, .deck-next-link {
display: none !important;
}
}

View file

@ -0,0 +1,28 @@
/* Remove this line if you are embedding deck.js in a page and
using the scale extension. */
.csstransforms {
overflow: hidden;
}
.csstransforms .deck-container.deck-scale:not(.deck-menu) > .slide {
-webkit-box-sizing: padding-box;
-moz-box-sizing: padding-box;
box-sizing: padding-box;
width: 100%;
padding-bottom: 20px;
}
.csstransforms .deck-container.deck-scale:not(.deck-menu) > .slide > .deck-slide-scaler {
-webkit-transform-origin: 50% 0;
-moz-transform-origin: 50% 0;
-o-transform-origin: 50% 0;
-ms-transform-origin: 50% 0;
transform-origin: 50% 0;
}
.csstransforms .deck-container.deck-menu .deck-slide-scaler {
-webkit-transform: none !important;
-moz-transform: none !important;
-o-transform: none !important;
-ms-transform: none !important;
transform: none !important;
}

View file

@ -0,0 +1,18 @@
.deck-container .deck-status {
position: absolute;
bottom: 10px;
right: 5px;
color: #888;
z-index: 3;
margin: 0;
}
body.deck-container .deck-status {
position: fixed;
}
@media print {
.deck-status {
display: none;
}
}

View file

@ -0,0 +1,9 @@
.changed
{
color: green;
}
.specific
{
color: red;
}

View file

@ -0,0 +1,76 @@
.csstransitions.csstransforms {
overflow-x: hidden;
}
.csstransitions.csstransforms .deck-container > .slide {
-webkit-transition: -webkit-transform 500ms ease-in-out;
-moz-transition: -moz-transform 500ms ease-in-out;
-ms-transition: -ms-transform 500ms ease-in-out;
-o-transition: -o-transform 500ms ease-in-out;
transition: transform 500ms ease-in-out;
}
.csstransitions.csstransforms .deck-container:not(.deck-menu) > .slide {
position: absolute;
top: 0;
left: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
padding: 0 48px;
}
.csstransitions.csstransforms .deck-container:not(.deck-menu) > .slide .slide {
position: relative;
left: 0;
top: 0;
-webkit-transition: -webkit-transform 500ms ease-in-out, opacity 500ms ease-in-out;
-moz-transition: -moz-transform 500ms ease-in-out, opacity 500ms ease-in-out;
-ms-transition: -ms-transform 500ms ease-in-out, opacity 500ms ease-in-out;
-o-transition: -o-transform 500ms ease-in-out, opacity 500ms ease-in-out;
transition: -webkit-transform 500ms ease-in-out, opacity 500ms ease-in-out;
}
.csstransitions.csstransforms .deck-container:not(.deck-menu) > .slide .deck-next, .csstransitions.csstransforms .deck-container:not(.deck-menu) > .slide .deck-after {
visibility: visible;
-webkit-transform: translate3d(200%, 0, 0);
-moz-transform: translate(200%, 0);
-ms-transform: translate(200%, 0);
-o-transform: translate(200%, 0);
transform: translate3d(200%, 0, 0);
}
.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-previous {
-webkit-transform: translate3d(-200%, 0, 0);
-moz-transform: translate(-200%, 0);
-ms-transform: translate(-200%, 0);
-o-transform: translate(-200%, 0);
transform: translate3d(-200%, 0, 0);
}
.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-before {
-webkit-transform: translate3d(-400%, 0, 0);
-moz-transform: translate(-400%, 0);
-ms-transform: translate(-400%, 0);
-o-transform: translate(-400%, 0);
transform: translate3d(-400%, 0, 0);
}
.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-next {
-webkit-transform: translate3d(200%, 0, 0);
-moz-transform: translate(200%, 0);
-ms-transform: translate(200%, 0);
-o-transform: translate(200%, 0);
transform: translate3d(200%, 0, 0);
}
.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-after {
-webkit-transform: translate3d(400%, 0, 0);
-moz-transform: translate(400%, 0);
-ms-transform: translate(400%, 0);
-o-transform: translate(400%, 0);
transform: translate3d(400%, 0, 0);
}
.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-before .slide, .csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-previous .slide {
visibility: visible;
}
.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-child-current {
-webkit-transform: none;
-moz-transform: none;
-ms-transform: none;
-o-transform: none;
transform: none;
}

View file

@ -0,0 +1,67 @@
pre.sh_sourceCode {
background-color: white;
color: black;
font-style: normal;
font-weight: normal;
}
pre.sh_sourceCode .sh_keyword { color: blue; font-weight: bold; } /* language keywords */
pre.sh_sourceCode .sh_type { color: darkgreen; } /* basic types */
pre.sh_sourceCode .sh_usertype { color: teal; } /* user defined types */
pre.sh_sourceCode .sh_string { color: red; font-family: monospace; } /* strings and chars */
pre.sh_sourceCode .sh_regexp { color: orange; font-family: monospace; } /* regular expressions */
pre.sh_sourceCode .sh_specialchar { color: pink; font-family: monospace; } /* e.g., \n, \t, \\ */
pre.sh_sourceCode .sh_comment { color: brown; font-style: italic; } /* comments */
pre.sh_sourceCode .sh_number { color: purple; } /* literal numbers */
pre.sh_sourceCode .sh_preproc { color: darkblue; font-weight: bold; } /* e.g., #include, import */
pre.sh_sourceCode .sh_symbol { color: darkred; } /* e.g., <, >, + */
pre.sh_sourceCode .sh_function { color: black; font-weight: bold; } /* function calls and declarations */
pre.sh_sourceCode .sh_cbracket { color: red; } /* block brackets (e.g., {, }) */
pre.sh_sourceCode .sh_todo { font-weight: bold; background-color: cyan; } /* TODO and FIXME */
/* Predefined variables and functions (for instance glsl) */
pre.sh_sourceCode .sh_predef_var { color: darkblue; }
pre.sh_sourceCode .sh_predef_func { color: darkblue; font-weight: bold; }
/* for OOP */
pre.sh_sourceCode .sh_classname { color: teal; }
/* line numbers (not yet implemented) */
pre.sh_sourceCode .sh_linenum { color: black; font-family: monospace; }
/* Internet related */
pre.sh_sourceCode .sh_url { color: blue; text-decoration: underline; font-family: monospace; }
/* for ChangeLog and Log files */
pre.sh_sourceCode .sh_date { color: blue; font-weight: bold; }
pre.sh_sourceCode .sh_time, pre.sh_sourceCode .sh_file { color: darkblue; font-weight: bold; }
pre.sh_sourceCode .sh_ip, pre.sh_sourceCode .sh_name { color: darkgreen; }
/* for Prolog, Perl... */
pre.sh_sourceCode .sh_variable { color: darkgreen; }
/* for LaTeX */
pre.sh_sourceCode .sh_italics { color: darkgreen; font-style: italic; }
pre.sh_sourceCode .sh_bold { color: darkgreen; font-weight: bold; }
pre.sh_sourceCode .sh_underline { color: darkgreen; text-decoration: underline; }
pre.sh_sourceCode .sh_fixed { color: green; font-family: monospace; }
pre.sh_sourceCode .sh_argument { color: darkgreen; }
pre.sh_sourceCode .sh_optionalargument { color: purple; }
pre.sh_sourceCode .sh_math { color: orange; }
pre.sh_sourceCode .sh_bibtex { color: blue; }
/* for diffs */
pre.sh_sourceCode .sh_oldfile { color: orange; }
pre.sh_sourceCode .sh_newfile { color: darkgreen; }
pre.sh_sourceCode .sh_difflines { color: blue; }
/* for css */
pre.sh_sourceCode .sh_selector { color: purple; }
pre.sh_sourceCode .sh_property { color: blue; }
pre.sh_sourceCode .sh_value { color: darkgreen; font-style: italic; }
/* other */
pre.sh_sourceCode .sh_section { color: black; font-weight: bold; }
pre.sh_sourceCode .sh_paren { color: red; }
pre.sh_sourceCode .sh_attribute { color: darkgreen; }

View file

@ -0,0 +1,91 @@
.deck-container em {
color:rgb(255,115,0);
}
.deck-container s {
color:rgb(180,180,211);
}
.deck-container {
font-family: "Helvetica Neue", sans-serif;
font-size: 1.75em;
color:RGB(50,50,101);
background:white url("./img/thales.jpg") no-repeat fixed bottom left;
}
.deck-container .slide {
}
.deck-container .slide h1 {
color:rgb(50,50,101);
}
.deck-container .slide h2 {
color:rgb(255,115,0);
border-bottom-color:lightgray;
}
.deck-container .slide h3 {
color:RGB(57,138,199);
}
.deck-container .slide pre {
border-color: #ccc;
}
.deck-container .slide blockquote {
font-size: 2em;
font-style: italic;
padding: 1em 2em;
color: #000;
border-left: 5px solid #ccc;
font-family:serif;
}
.deck-container .slide blockquote p {
margin: 0;
}
.deck-container .slide blockquote cite {
font-size: .5em;
font-style: normal;
font-weight: bold;
color: #888;
}
.deck-container .slide ::-moz-selection {
background: #c00;
color: #fff;
}
.deck-container .slide ::selection {
background: #c00;
color: #fff;
}
.deck-container .slide a, .deck-container .slide a:hover, .deck-container .slide a:focus, .deck-container .slide a:active, .deck-container .slide a:visited {
color:RGB(152,191,12);
text-decoration: none;
}
.deck-container .slide a:hover, .deck-container .slide a:focus {
text-decoration: underline;
}
.deck-container > .slide .deck-before, .deck-container > .slide .deck-previous {
opacity: 0.4;
}
.deck-container > .slide .deck-before:not(.deck-child-current) .deck-before, .deck-container > .slide .deck-before:not(.deck-child-current) .deck-previous, .deck-container > .slide .deck-previous:not(.deck-child-current) .deck-before, .deck-container > .slide .deck-previous:not(.deck-child-current) .deck-previous {
opacity: 1;
}
.deck-container > .slide .deck-child-current {
opacity: 1;
}
.deck-container .deck-prev-link, .deck-container .deck-next-link {
background: #ccc;
font-family: serif;
}
.deck-container .deck-prev-link, .deck-container .deck-prev-link:hover, .deck-container .deck-prev-link:focus, .deck-container .deck-prev-link:active, .deck-container .deck-prev-link:visited, .deck-container .deck-next-link, .deck-container .deck-next-link:hover, .deck-container .deck-next-link:focus, .deck-container .deck-next-link:active, .deck-container .deck-next-link:visited {
color: #fff;
}
.deck-container .deck-prev-link:hover, .deck-container .deck-prev-link:focus, .deck-container .deck-next-link:hover, .deck-container .deck-next-link:focus {
background: #c00;
text-decoration: none;
}
.deck-container .deck-status {
font-size: 0.6666em;
}
.deck-container.deck-menu .slide {
background: #eee;
}
.deck-container.deck-menu .deck-current, .no-touch .deck-container.deck-menu .slide:hover {
background: #ddf;
}

View file

@ -0,0 +1,903 @@
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js ie6" lang="en"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>EO::MPI parallelization</title>
<meta name="description" content="A short presentation on EO::MPI parallelization">
<meta name="author" content="Benjamin BOUVIER">
<meta name="viewport" content="width=1024, user-scalable=no">
<!-- Core and extension CSS files -->
<link rel="stylesheet" href="css/deck.core.css">
<link rel="stylesheet" href="css/deck.goto.css">
<link rel="stylesheet" href="css/deck.menu.css">
<link rel="stylesheet" href="css/deck.navigation.css">
<link rel="stylesheet" href="css/deck.status.css">
<link rel="stylesheet" href="css/deck.hash.css">
<link rel="stylesheet" href="css/deck.scale.css">
<!-- Style theme. More available in /themes/style/ or create your own. -->
<!-- <link rel="stylesheet" href="../themes/style/web-2.0.css"> -->
<link rel="stylesheet" href="css/thales.css">
<link rel="stylesheet" href="css/eompi.css">
<!-- highlight js -->
<link rel="stylesheet" href="css/shjs.css">
<!-- Transition theme. More available in /themes/transition/ or create your own. -->
<link rel="stylesheet" href="css/horizontal-slide.css">
<script src="js/modernizr.custom.js"></script>
</head>
<body class="deck-container">
<!-- Begin slides -->
<section class="slide" id="title-slide">
<h1>EO's Parallelization with MPI</h1>
</section>
<section class="slide">
<h2>What is parallelization?</h2>
<ul>
<li><h3>Divide and Conquer paradigm</h3>
<p>A difficult problem can be splitted into simpler sub problems.</p>
<p>Therefore the sub problems should be solved faster.</p>
</li>
<li class="slide"><h3>Parallelize.</h3>
<p>Process serveral tasks together (<em>concurrent programming</em>).</p>
<p>By different ways :
<ul>
<li><em>Shared memory</em> : Multitask, OpenMP, etc.</li>
<li><em>Message Passing Interface</em> : MPI.</li>
<li>Other ways, undetailed here: <em>coroutines</em>, <em>multi-thread / LightWeightProcess</em>,
<em>Field Programmable Gate Array (FPGA)</em>, <em>General Purpose GPU (GPGPU)</em>, etc.</li>
</ul>
</p>
</li>
</ul>
</section>
<section class="slide">
<h2>Shared memory (e.g : OpenMP)</h2>
<ul>
<li class="slide"><strong>Multicore</strong> : more than one core per processor.
<ul>
<li>Processes several instruction streams together, each one manipulating different datas.</li>
<li>Different from <em>superscalar</em> processors, which can process more than one instruction during a
single processor cycle.</li>
<li>A multicore processor can however be superscalar.</li>
<li>For instance : <em>IBM's Cell microprocessor (PlayStation3)</em></li>
</ul>
</li>
<li class="slide"><strong>Symmetric multiprocessing</strong> : More than one processor which communicate through
a specific bus.
<ul>
<li><em>Bus contention</em> is the principal limitating factor.</li>
</ul>
</li>
<li class="slide">The main drawback is explicitly contained in the name.
<ul>
<li>Memory is <em>shared</em></li>
<li>Bus contention?</li>
<li>Low memory?</li>
<li>Use of virtual memory (swap) and page faults?</li>
<li>=> Can slow the speedup compared with the number of processors.</li>
</ul>
</li>
</ul>
</section>
<section class="slide">
<h2>Message Passing Interface (e.g : OpenMPI)</h2>
<p>Memory isn't shared here, manipulated objects are sent on a network: there is communication between the machines
(called <em>hosts</em>)</p>
<ul>
<li><strong>Cluster</strong>
<ul class="slide">
<li>Several machines network connected (for instance, in an Ethernet network).</li>
<li>Can have different specifications, but this can affect load balancing.</li>
<li>Most of the <em>supercomputers</em> are clusters.</li>
<li>For exemple : Beowulf Cluster, Sun Grid Engine...</li>
</ul>
</li>
<li><strong>Massive parallel processing</strong>
<ul class="slide">
<li>This is not machines but processors which are directly connected by network.</li>
<li>Like a cluster, but the networks are specific, so as to wire more processors.</li>
</ul>
</li>
<li><strong>Grid (a.k.a Distributed Computing)</strong>
<ul class="slide">
<li>Networks with potentially high latence and low bandwith, like Internet.</li>
<li>Example of programs : BOINC, Seti@home...</li>
</ul>
</li>
</ul>
</section>
<section class="slide">
<h1>Parallelization myths</h1>
</section>
<section class="slide">
<h2>A myth about speed: the car's enigma</h2>
<ul>
<li class="slide">If a car has to travel 100 Km, but has yet traveled 60 Km in 1 hour (i.e it has an average
speed of 60 Km per hour), what is its maximal average speed on the whole traject
?</li>
<p><img src="http://www.canailleblog.com/photos/blogs/chevaux-trop-rigolo-229540.jpg" /></p>
<li class="slide">Some hints:
<ul>
<li>The driver can use all the hilarious gas (N<sub>2</sub>O, nitrous oxyde, a.k.a <em>nitro</em>) he
needs.</li>
<li>Let's suppose even that the car can travel at speed of light, or teleport (despite general theory of
relativity).</li>
</ul>
</li>
<li class="slide">The solution: <strong>100 Km/h</strong></li>
<li class="slide">The explanation: let's suppose that the car teleports after the first 60 Km. It would have
traveled 60 Km in 1 hour and 40 Km in 0 seconds, which is 100 Km in 1 hour: 100 Km/h.</li>
</ul>
</section>
<section class="slide">
<h2>A myth about speed: "Il dit qu'il voit pas le rapport ?"</h2>
<p><img src="http://r.linter.fr/questionnaire/document/image/250/7241.jpg" /></p>
<ul class="slide">
<li>Let P be the parallelizable proportion of the program (~ the remaining distance to travel), which can be
processed by N machines.</li>
<li>Then 1 - P is the sequential (non parallelizable) proportion of the program (~ the already traveled distance), which
can be processed by 1 machine.</li>
<li>A sequential version would take 1 - P + P = 1 unit (of time) to terminate.</li>
<li>A parallel version would take (1-P) + P / N units to terminate.</li>
<li>The <em>speedup</em> (gain of speed) would then be:
<img src="http://upload.wikimedia.org/wikipedia/en/math/f/4/0/f40f1968282e110c7e65222d2b5d3115.png"
style="height:100px;"/>
which tends to 1-P as N tends to infinity.</li>
<li>The maximal theoritical speedup (~ maximal average speed) would then be 1 / (1-P)</li>
<li><h3>This result is known as Amdahl's law</h3></li>
</ul>
</section>
<section class="slide">
<h2>A myth about data : the cat's enigma</h2>
<ul>
<li class="slide">
<p>If 3 cats catch 3 mices in 3 minutes, how many cats are needed to catch 9 mices in 9
minutes ?
<img src="http://www.creabar.com/photo/Animaux/ggq91fch.jpg" />
</p>
</li>
<li class="slide">The solution: 3 too !</li>
<li class="slide">A better question to ask would be: how many mices can catch 9 cats in 3 minutes ?</li>
</ul>
</section>
<section class="slide">
<h2>A myth about data</h2>
<ul>
<li>The idea is no more to make a processing faster (for a constant amount of data) but to process more data (in
the same time).</li>
<li>In this case, the most hosts we add, the most data we can process at a time.</li>
<li>This doesn't contradict Amdahl's Law, which makes the assumption that the amount of data to process stays
the same.</li>
<li>There is also another way to calculate speedup here:
<img src="http://upload.wikimedia.org/wikipedia/en/math/1/9/f/19f6e664e94fbcaa0f5877d74b6bffcd.png"
style="height: 50px;" />
where P is the number of processes, alpha the non parallelizable part of the program.
</li>
<li><h3>This result is known as the Gustafson's Law</h3></li>
</ul>
</section>
<section class="slide">
<h2>A metric: speedup</h2>
<ul>
<li><strong>Speedup</strong> refers to how much a parallel algorithm is faster than a corresponding sequential algorithm</li>
<li>It's a quantitative mesure, relative to the sequential version :
<img src="http://upload.wikimedia.org/wikipedia/en/math/a/8/0/a8080d5bd23d7ba57a9cf4fedcefadad.png"
style="height:100px;"/>
where T<sub>1</sub> is the time taken by the sequential version and T<sub>p</sub> the time taken by the parallel version with p
processes.
</li>
<li>A speedup equal to one indicates that the version is as performant as the sequential version.</li>
<li>Practically, speedup may not be linear in number of processes (Amdahl's Law)</li>
</ul>
</section>
<section class="slide">
<h1>Parallelization in EO</h1>
</section>
<section class="slide">
<h2>Objectives</h2>
<ul>
<li>Remember, tasks have to be independant from one to another.</li>
<li>Process data faster: what takes time in EO?
<ul class="slide">
<li>Evaluation!</li>
</ul>
</li>
<li>Process more data during the same time: where?
<ul class="slide">
<li>Multi-start!</li>
</ul>
</li>
<li>Other objectives :
<ul>
<li>Readily serialize EO objects.</li>
<li>Be able to easily implement other parallel algorithms.</li>
</ul>
</li>
</ul>
</section>
<section class="slide">
<h2>Evaluation: Long story short</h2>
<pre class="sh_cpp"><code>
int main( int argc, char **argv )
{
eo::mpi::Node::init( argc, argv );
// PUT EO STUFF HERE
// Let's make the assumption that pop is a eoPop&lt;EOT&gt;
// and evalFunc is an evaluation functor
eo::mpi::DynamicAssignmentAlgorithm assign;
eoParallelPopLoopEval&lt;EOT&gt; popEval( assign, eo::mpi::DEFAULT_MASTER, evalFunc );
popEval( pop, pop );
}
</code></pre>
</section>
<section class="slide">
<h2>Serializing EO objects</h2>
<ul>
<li>Serializing is writing a message from a binary source into a message transmissible data.</li>
<li>Several formats can be used
<ul class="slide">
<li>Binary directly - but all the hosts have to be the same!</li>
<li>Text based formats: XML, YAML, JSON,...</li>
</ul>
</li>
<li>So why use text?
<ul class="slide">
<li>It's human readable, more or less easily parsable.</li>
<li>It's independant from the data type representations on the machines (e.g: an int on a 32 bits and on
a 64 bits machines are not the same).</li>
<li>Main drawbacks: it takes more space and it needs a processing for encoding and decoding.</li>
</ul>
</li>
</ul>
</section>
<section class="slide">
<h2>eoserial : principle</h2>
<img src="./img/serialisation.png" style="float:left;margin-right:25px;" />
<ul>
<li>JSON serialization
<ul class="slide">
<li>Lighter than XML.</li>
<li>Easily parsable, the grammar is trivial.</li>
<li>Allows to represent tables, objects and texts: it's sufficient!</li>
</ul>
</li>
<li>What happens in your life when you're serializable?
</li>
<li>Implement interface eoserial::Persistent and your object can be saved and loaded, in JSON format.</li>
<li>No need to serialize the whole object, you choose what you need to save and load.</li>
<li>Everything can be serialized!<ul class="slide">
<li>Atomic types are directly serialized into eoserial::String (thanks to std::stringstream)</li>
<li>Arrays are serializable (into eoserial::Array), if what they contain is too.</li>
<li>Object can be serializable (into eoserial::Object), if what they contain is too.</li>
</ul></li>
</ul>
</section>
<section class="slide">
<h2>eoserial : interface eoserial::Persistent</h2>
<pre class="sh_cpp">
<code>
# include &lt;serial/eoSerial.h&gt;
class MyObject : public eoserial::Persistent {
public:
// A persistent class needs a default empty ctor.
MyObject() {}
int id;
// Implementation of eoserial::Persistent::pack
// What to save when making a serialized object?
eoserial::Object* pack() const
{
eoserial::Object* obj = new eoserial::Object;
// eoserial::make creates a eoserial::String from a basic type
eoserial::String* idAsString = eoserial::make( id );
// the key "saved_id" will be associated to the JSON object idAsString
obj->add( "saved_id", idAsString );
// could have be done with
// (*obj)["saved_id"] = idAsString;
// as obj is a std::map pointer
return obj;
}
// Implementation of eoserial::Persistent::unpack
// What data to retrieve from a JSON object and where to put it?
void unpack(const eoserial::Object* json)
{
// retrieves the value from key "saved_id" in "*json" object and put it into member "id"
eoserial::unpack( *json, "saved_id" , id );
}
};
</code>
</pre>
</section>
<section class="slide">
<h2>eoserial : use it</h2>
<pre class="sh_cpp">
<code>
# include &lt;eoSerial.h&gt;
# include &lt;fstream&gt;
# include &lt;cassert&gt;
int main(void)
{
MyObject instance;
instance.id = 42;
// Writes
eoserial::Object* obj = instance.pack();
std::ofstream ofile("filename");
obj->print( ofile );
ofile.close();
delete obj;
// Reads
std::ifstream ifile("filename");
std::stringstream ss;
while( ifile )
{
std::string s;
ifile &gt;&gt; s;
ss &lt;&lt; s;
}
eoserial::Object* objCopy = eoserial::Parser::parse( ss.str() );
MyObject instanceCopy;
instanceCopy.unpack( objCopy );
assert( instanceCopy.id == instance.id );
return 0;
}
</code>
</pre>
</section>
<section class="slide">
<h2>eoserial : more complex uses</h2>
<pre class="sh_cpp"><code>
struct ComplexObject
{
bool someBooleanValue; // will be serialized into a string
MyObject obj; // Objects can contain other objects too
std::vector&lt;int&gt;; // and tables too!
};
int main(void)
{
ComplexObject co;
// let's imagine we've set values of co.
eoserial::Object* json = new eoserial::Object;
// serialize basic type
(*json)["some_boolean_value"] = eoserial::make( co.someBooleanValue );
// MyObject is Persistent, so eoserial knows how to serialize it
json->add( "my_object", &co.obj );
// Instead of having a "for" loop, let's automatically serialize the content of the array
json->add( "int_array",
eoserial::makeArray&lt; std::vector&lt;int&gt;, eoserial::MakeAlgorithm &gt;( co.array ) );
// Print everything on the standard output
json->print( std::cout );
delete json;
return 0;
}
</code></pre>
</section>
<section class="slide">
<h2>MPI</h2>
<ul>
<li>We know how to serialize our objects. Now, we need to transmit them over the network.</li>
<li><strong>Message Passing Interface</strong> (MPI) is a norm.</li>
<li>OpenMPI implements it, in C, in the SPMD (Single Program Multiple Data) fashion. It is an active community
and the library is very well documented.</li>
<li>Boost::mpi gives it a C++ flavour (and tests each status code returned by MPI calls, throwing up exceptions
instead).</li>
<li>MPI helps by:
<ul class="slide">
<li>Managing the administration of roles: each MPI process has a <em>rank</em> and knows the whole
<em>size</em> of the cluster.</li>
<li>Regrouping outputs of different processes into one single output.</li>
<li>Managing the routing of messages and connections between the processes.</li>
<li>Launch a given number of processes via SSH, or a cluster engine (like SGE).</li>
</ul>
</li>
<li>MPI doesn't deal with:
<ul class="slide">
<li>Debugging: if one of your program segfaults, buy a parallel debugger or... Good luck!</li>
<li>More generally, knowing what happens: even the standard output becomes a shared resource without any
protection!</li>
</ul>
</li>
</ul>
</section>
<section class="slide">
<h1>Design of parallel algorithms</h1>
</section>
<section class="slide">
<h2>Some vocabulary</h2>
<ul>
<li>In the most of cases, we want the results to be retrieved in one place. Besides, communication in MPI is
synchronous (it's a design choice making things are simpler).</li>
<li>One process will have particular responsabilities, like aggregating results: it's the <strong>master</strong>.</li>
<li>Other processes will be used to do the processing (it's the goal, after all?) : they're the
<strong>workers</strong>. Or <strong>slaves</strong>, but it may be <em>patronizing</em> and the master is rarely called
the <em>patron</em>.</li>
<li>As there is one master, the algorithm is said to be <strong>centralized</strong>. Some well-known parallel algorithms
use this paradigm: <em>Google's MapReduce</em>, <em>Apache's Hadoop</em>(free implementation of Google's one :-)),...</li>
<li>A <strong>job</strong> is the parallel algorithm seen in its globality (i.e., as a function).</li>
<li>A job is a set of <strong>tasks</strong>, which are the atomic, decomposed part which can be serialized and
processed by a worker, at a time.</li>
</ul>
</section>
<section class="slide">
<h2>Evaluation (1/2)</h2>
<p>
Let's see how we could implement our parallelized evaluation<br/>
It's feasible as evaluating an individual is independant from evaluating another one.<br/>
<pre><code>
// On master side
function parallel_evaluate( population p )
foreach individual i in p,
send i to a worker
if there is no available worker,
wait for any response (return)
and retry
endif
endforeach
inform all the available workers that they are done (yes, it's a centralized algorithm)
wait for all remaining responses
endfunction
when receiving a response:
replace the evaluated individual in the population
// On worker side
function parallel_evaluate( evaluation function f )
wait for a individual i
apply f on it
send i to the master
endfunction
</code></pre>
</p>
</section>
<section class="slide">
<h2>Evaluation (2/2)</h2>
<p>But a parallelization algorithm is interesting only if the process time is higher than the
communication time. If process time is too short relatively to the communication time, we can do the following:
<pre><code>
// On master side<span class="changed">
function parallel_evaluate( population p, number of elements to send each time packet_size )
index = 0
while index &lt; size
sentSize := how many individuals (&lt;= packet_size) can we send to a worker?
find a worker. If there is no one, wait for any response (return) and retry
send the sentSize to the worker
send the individuals to the worker
index += sentSize
endwhile</span>
inform all the available workers that they're done
wait for all remaining responses
endfunction
when receiving a response:
replace the evaluated individuals in the population
// On worker side
function parallel_evaluate( evaluation function f )
<span class="changed">size := wait for a sentSize as described above
individuals := wait for size individuals
apply f on each of them
send back the individuals</span>
endfunction
</code></pre>
</section>
<section class="slide">
<h2>Multi start</h2>
<p>The idea behing multi-start is to run many times the same algorithm (for instance, eoEasyEA), but with different
seeds: the workers launch the algorithm and send their solutions as they come to the master, which saves the
ultimate best solution.</p>
<pre>
// On master side
variable best_score (initialized at the worst value ever) // score can be fitness, for instance
function parallel_multistart( integer runs )
seeds = table of generated seeds, or fixed seeds, whose size is at least "runs"
for i := 0; i &lt; runs; ++i
find a worker. If there is no one, wait for any response (return) and retry
send to the worker a different seed
endfor
inform all the available workers that they're done
wait for all remaining responses
endfunction
when receiving a response:
received_score := receive score from the worker.
If the received_score &gt; best_score
send worker a message indicating that master is interested by the solution
receive the solution
updates the best_score
else
send worker a message indicating that master isn't interested by the solution
endif
// On worker side
function parallel_multistart( algorithm eoAlgo )
seed := wait for a seed
solution := eoAlgo( seed )
send solution score to master
master_is_interested := wait for the response
if master_is_interested
send solution to master
endif
endfunction
</pre>
</section>
<section class="slide">
<h2>Common parts vs specific parts</h2>
<ul>
<li>These two algorithms have common parts and specific parts.</li>
<li>Identifying them allows to design generic parallel algorithms.</li>
<li>In the following code sample, specific parts are in red. Everything else is hence generic.</li>
</ul>
<pre><code>
// On master side
function parallel_evaluate(<span class="specific">population p, number of elements to send each time packet_size </span>)
<span class="specific">index = 0</span>
while <span class="specific">index &lt; size</span>
find a worker. If there is no one, wait for any response (return) and retry
<span class="specific">sentSize := how many individuals (&lt;= packet_size) can we send to a worker?
send the sentSize to the worker
send the individuals to the worker
index += sentSize</span>
endwhile</span>
inform all the available workers that they're done
wait for all remaining responses
endfunction
when receiving a response:
<span class="specific">replace the evaluated individuals in the population</span>
// On worker side
function parallel_evaluate(<span class="specific"> evaluation function f </span>)
<span class="specific">size := wait for a sentSize as described above
individuals := wait for size individuals
apply f on each of them
send back the individuals</span>
endfunction
</code></pre>
</section>
<section class="slide">
<h2>Common parts</h2>
<ul>
<li>Master runs a loop.</li>
<li>Master has to manage workers (find them, wait for them, etc...)</li>
<li>Workers need to be informed if they have something to do or not (stop condition in master part)</li>
<li>Master needs to wait to get all the responses.</li>
</ul>
</section>
<section class="slide">
<h2>Specific parts</h2>
<ul>
<li>Loop condition in the master's part.</li>
<li>What has to be sent to a worker by master?</li>
<li>What has to be done by a worker when it receives an order?</li>
<li>What has to be done when the master receives a response?</li>
</ul>
</section>
<section class="slide">
<h2>Generic parallel algorithm</h2>
<p>The calls to specific parts are in red.</p>
<pre><code>
// Master side
function parallel_algorithm()
while ! <span class="specific">isFinished()</span>
worker := none
while worker is none
wait for a response and affect worker the origin of the response
<span class="specific">handleResponse( worker )</span>
worker = retrieve worker
endwhile
send worker a work order
<span class="specific">sendTask( worker )</span>
endwhile
foreach available worker
indicate worker it's done (send them a termination order)
endforeach
while all responses haven't be received
worker := none
wait for a response and affect worker the origin of the response
<span class="specific">handleResponse( worker )</span>
send worker a termination order
endwhile
endfunction
// Worker side
function parallel_algorithm()
order := receive order
while order is not termination order
<span class="specific">processTask( )</span>
order = receive order
endwhile
endfunction
</code></pre>
</section>
<section class="slide">
<h2>TLDR;</h2>
<img src="./img/generic_parallel.png" style="height:800px;"/>
</section>
<section class="slide">
<h2>Functors</h2>
<ul>
<li>Using functors allows them to wrap <em>and</em> be wrapped (decorator pattern).</li>
<li>IsFinished : implements <em>bool operator()()</em>, indicating that the job is over.</li>
<li>SendTask : implements <em>void operator()( int worker_rank )</em>, indicating what to send to the
worker.</li>
<li>ProcessTask : implements <em>void operator()()</em>, indicating what the worker has to do when it receives a
task.</li>
<li>HandleResponse : implements <em>void operator()( int worker_rank )</em>, indicating what to do when
receiving a worker response.</li>
<li>Implementing these 4 functors is sufficient for a parallel algorithm!</li>
<li>You can also wrap the existing one to add functionalities.</li>
</ul>
</section>
<section class="slide">
<h2>Stores</h2>
<ul>
<li>These 4 functors can use about the same data.</li>
<li>This data needs to be shared : all the functors are templated on a JobData structure.</li>
<li>A job needs data and functors to be launched.</li>
<li>Several jobs can use the same data and functors.</li>
<li>=> Data and functors are saved into a store, which can be reused between different jobs.</li>
</ul>
</section>
<section class="slide">
<h2>Scheduling tasks between workers</h2>
<ul>
<li>Until here, we don't know how to schedule tasks between workers.</li>
<li>Naive, simple solution: as soon as a worker has finished a task, give it a new task. Workers are put in a
queue, this is the <strong>dynamic assignment</strong> (scheduling).</li>
<li>If the worker's number of call is well-known, initially give to each worker a fixed amount of tasks. When a
worker has finished a task, give it another task only if it the amount of remaining tasks is positive ; else,
wait for another worker. Workers are managed with a fixed table, this is the <strong>static
assignment</strong>.</li>
</ul>
</section>
<section class="slide">
<h2>Let's go back to evaluation in EO</h2>
<ul>
<li>The idea behind applying a functor to each element of a table is very generic. Google, Python and Javascript
call it <strong>map</strong>, we call it <strong>ParallelApply</strong>, according to existing
<strong>apply</strong> function, in apply.h.</li>
<li>There is also a <em>ParallelApplyJob</em>, a <em>ParallelApplyStore</em> which contains a
<em>ParallelApplyData</em>, a <em>IsFinishedParallelApply</em>, etc...</li>
<li>This is what is used when calling parallel evaluation.</li>
</ul>
</section>
<section class="slide">
<h2>Customizing evaluation: reminder</h2>
<pre class="sh_cpp"><code>
int main( int argc, char **argv )
{
eo::mpi::Node::init( argc, argv );
// PUT EO STUFF HERE
// Let's make the assumption that pop is a eoPop&lt;EOT&gt;
// and evalFunc is an evaluation functor
eo::mpi::DynamicAssignmentAlgorithm assign;
eoParallelPopLoopEval&lt;EOT&gt; popEval( assign, eo::mpi::DEFAULT_MASTER, evalFunc );
// The store is hidden behind this call, but it can be given at eoParallelPopLoopEval constructor!
popEval( pop, pop );
}
</code></pre>
</section>
<section class="slide">
<h2>Customizing evaluation: the idea</h2>
<ul>
<li>We would like to retrieve best individuals, as soon as they're processed, and print their fitness in the
standard output, for instance.</li>
<li>We can wrap one of the 4 functors.</li>
<li>Master side or worker side?
<ul class="slide">
<li>Master side: we want to retrieve the <em>global</em> best individual, not the best individual in
population slices.</li>
<li>We have 3 choices: IsFinished, HandleResponse, SendTask.</li>
<li>So which one?
<ul class="slide">
<li>The functor HandleResponse should be reimplemented: in a sequential version, it would be done just
after the evaluation of an individual. The HandleResponse is the nearest functor called after having
received the result.</li>
</ul>
</li>
</ul>
</li>
<li>How to do it?
<ol class="slide">
<li>Retrieve which slice has been processed by the worker.</li>
<li>Call the embedded HandleResponse.</li>
<li>Compare the fitnesses of individuals in the slice to the global best individual.</li>
</ol>
</li>
</ul>
</section>
<section class="slide">
<h2>Customizing evaluation: implementation!</h2>
<pre class="sh_cpp"><code>
// Our objective is to minimize fitness, for instance
struct CatBestAnswers : public eo::mpi::HandleResponseParallelApply&lt;EOT&gt;
{
CatBestAnswers()
{
best.fitness( 1000000000. );
}
void operator()(int wrkRank)
{
// Retrieve informations about the slice processed by the worker
int index = _data->assignedTasks[wrkRank].index;
int size = _data->assignedTasks[wrkRank].size;
// call to the wrapped function HERE
(*_wrapped)( wrkRank );
// Compare fitnesses of evaluated individuals with the best saved
for(int i = index; i &lt; index+size; ++i)
{
if( best.fitness() &lt; _data->table()[ i ].fitness() )
{
eo::log &lt;&lt; eo::quiet &lt;&lt; "Better solution found:" &lt;&lt; _data->table()[i].fitness() &lt;&lt; std::endl;
best = _data->table()[ i ];
}
}
}
protected:
EOT best;
};
</code></pre>
</section>
<section class="slide">
<h2>Using customized handler</h2>
<pre class="sh_cpp"><code>
int main( int argc, char **argv )
{
eo::mpi::Node::init( argc, argv );
// PUT EO STUFF HERE
// Let's make the assumption that pop is a eoPop&lt;EOT&gt;
// and evalFunc is an evaluation functor
eo::mpi::DynamicAssignmentAlgorithm assign;
// What was used before
// eoParallelPopLoopEval&lt;EOT&gt; popEval( assign, eo::mpi::DEFAULT_MASTER, evalFunc );
// What's new
eo::mpi::ParallelApplyStore&lt; EOT &gt; store( evalFunc, eo::mpi::DEFAULT_MASTER );
CatBestAnswer catBestAnswers;
store.wrapHandleResponse( &catBestAnswers );
eoParallelPopLoopEval&lt; EOT &gt; popEval( assign, eo::mpi::DEFAULT_MASTER, &store );
// What doesn't change
popEval( pop, pop );
}
</code></pre>
</section>
<section class="slide">
<h1>Thank you for your attention</h1>
</section>
<section class="slide">
<h2>Remarks</h2>
<ul>
<li>This presentation is made of HTML5, CSS3, JavaScript, thanks to frameworks
<a href="http://imakewebthings.com/deck.js/">Deck.js</a> (slides) and <a href="http://shjs.sourceforge.net/">SHJS</a> (syntax
highlighting).
<li>If you have any complaint to make, please refer to <a href="mailto:johann@dreo.fr">Johann Dreo</a>.</li>
<li>If you have any question or compliment, please refer to <a href="mailto:benjamin.bouvier@gmail.com">me</a>
(Benjamin Bouvier).</li>
</ul>
</section>
<!-- deck.navigation snippet -->
<a href="#" class="deck-prev-link" title="Précédent">&#8592;</a>
<a href="#" class="deck-next-link" title="Suivant">&#8594;</a>
<!-- deck.status snippet -->
<p class="deck-status">
<span class="deck-status-current"></span>
/
<span class="deck-status-total"></span>
</p>
<!-- deck.goto snippet -->
<form action="." method="get" class="goto-form">
<label for="goto-slide">Go to slide:</label>
<input type="text" name="slidenum" id="goto-slide" list="goto-datalist">
<datalist id="goto-datalist"></datalist>
<input type="submit" value="Go">
</form>
<!-- deck.hash snippet -->
<a href="." title="Permalink to this slide" class="deck-permalink">#</a>
<!-- Grab CDN jQuery, with a protocol relative URL; fall back to local if offline -->
<!-- <script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js"></script> -->
<script>window.jQuery || document.write('<script src="js/jquery-1.7.min.js"><\/script>')</script>
<!-- Deck Core and extensions -->
<script src="js/deck.core.js"></script>
<script src="js/deck.hash.js"></script>
<script src="js/deck.menu.js"></script>
<script src="js/deck.goto.js"></script>
<script src="js/deck.status.js"></script>
<script src="js/deck.navigation.js"></script>
<script src="js/deck.scale.js"></script>
<!-- Initialize the deck -->
<script>
$(function() {
$.deck('.slide');
});
</script>
<!-- Initialize the highlighter -->
<script src="js/shjs.js"></script>
<script src="js/shjs-cpp.js"></script>
<script>
sh_highlightDocument();
</script>
</body>
</html>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,498 @@
/*!
Deck JS - deck.core
Copyright (c) 2011 Caleb Troughton
Dual licensed under the MIT license and GPL license.
https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt
*/
/*
The deck.core module provides all the basic functionality for creating and
moving through a deck. It does so by applying classes to indicate the state of
the deck and its slides, allowing CSS to take care of the visual representation
of each state. It also provides methods for navigating the deck and inspecting
its state, as well as basic key bindings for going to the next and previous
slides. More functionality is provided by wholly separate extension modules
that use the API provided by core.
*/
(function($, deck, document, undefined) {
var slides, // Array of all the uh, slides...
current, // Array index of the current slide
$container, // Keeping this cached
events = {
/*
This event fires whenever the current slide changes, whether by way of
next, prev, or go. The callback function is passed two parameters, from
and to, equal to the indices of the old slide and the new slide
respectively. If preventDefault is called on the event within this handler
the slide change does not occur.
$(document).bind('deck.change', function(event, from, to) {
alert('Moving from slide ' + from + ' to ' + to);
});
*/
change: 'deck.change',
/*
This event fires at the beginning of deck initialization, after the options
are set but before the slides array is created. This event makes a good hook
for preprocessing extensions looking to modify the deck.
*/
beforeInitialize: 'deck.beforeInit',
/*
This event fires at the end of deck initialization. Extensions should
implement any code that relies on user extensible options (key bindings,
element selectors, classes) within a handler for this event. Native
events associated with Deck JS should be scoped under a .deck event
namespace, as with the example below:
var $d = $(document);
$.deck.defaults.keys.myExtensionKeycode = 70; // 'h'
$d.bind('deck.init', function() {
$d.bind('keydown.deck', function(event) {
if (event.which === $.deck.getOptions().keys.myExtensionKeycode) {
// Rock out
}
});
});
*/
initialize: 'deck.init'
},
options = {},
$d = $(document),
/*
Internal function. Updates slide and container classes based on which
slide is the current slide.
*/
updateStates = function() {
var oc = options.classes,
osc = options.selectors.container,
old = $container.data('onSlide'),
$all = $();
// Container state
$container.removeClass(oc.onPrefix + old)
.addClass(oc.onPrefix + current)
.data('onSlide', current);
// Remove and re-add child-current classes for nesting
$('.' + oc.current).parentsUntil(osc).removeClass(oc.childCurrent);
slides[current].parentsUntil(osc).addClass(oc.childCurrent);
// Remove previous states
$.each(slides, function(i, el) {
$all = $all.add(el);
});
$all.removeClass([
oc.before,
oc.previous,
oc.current,
oc.next,
oc.after
].join(" "));
// Add new states back in
slides[current].addClass(oc.current);
if (current > 0) {
slides[current-1].addClass(oc.previous);
}
if (current + 1 < slides.length) {
slides[current+1].addClass(oc.next);
}
if (current > 1) {
$.each(slides.slice(0, current - 1), function(i, el) {
el.addClass(oc.before);
});
}
if (current + 2 < slides.length) {
$.each(slides.slice(current+2), function(i, el) {
el.addClass(oc.after);
});
}
},
/* Methods exposed in the jQuery.deck namespace */
methods = {
/*
jQuery.deck(selector, options)
selector: string | jQuery | array
options: object, optional
Initializes the deck, using each element matched by selector as a slide.
May also be passed an array of string selectors or jQuery objects, in
which case each selector in the array is considered a slide. The second
parameter is an optional options object which will extend the default
values.
$.deck('.slide');
or
$.deck([
'#first-slide',
'#second-slide',
'#etc'
]);
*/
init: function(elements, opts) {
var startTouch,
tolerance,
esp = function(e) {
e.stopPropagation();
};
options = $.extend(true, {}, $[deck].defaults, opts);
slides = [];
current = 0;
$container = $(options.selectors.container);
tolerance = options.touch.swipeTolerance;
// Pre init event for preprocessing hooks
$d.trigger(events.beforeInitialize);
// Hide the deck while states are being applied to kill transitions
$container.addClass(options.classes.loading);
// Fill slides array depending on parameter type
if ($.isArray(elements)) {
$.each(elements, function(i, e) {
slides.push($(e));
});
}
else {
$(elements).each(function(i, e) {
slides.push($(e));
});
}
/* Remove any previous bindings, and rebind key events */
$d.unbind('keydown.deck').bind('keydown.deck', function(e) {
if (e.which === options.keys.next || $.inArray(e.which, options.keys.next) > -1) {
methods.next();
e.preventDefault();
}
else if (e.which === options.keys.previous || $.inArray(e.which, options.keys.previous) > -1) {
methods.prev();
e.preventDefault();
}
});
/* Bind touch events for swiping between slides on touch devices */
$container.unbind('touchstart.deck').bind('touchstart.deck', function(e) {
if (!startTouch) {
startTouch = $.extend({}, e.originalEvent.targetTouches[0]);
}
})
.unbind('touchmove.deck').bind('touchmove.deck', function(e) {
$.each(e.originalEvent.changedTouches, function(i, t) {
if (startTouch && t.identifier === startTouch.identifier) {
if (t.screenX - startTouch.screenX > tolerance || t.screenY - startTouch.screenY > tolerance) {
$[deck]('prev');
startTouch = undefined;
}
else if (t.screenX - startTouch.screenX < -1 * tolerance || t.screenY - startTouch.screenY < -1 * tolerance) {
$[deck]('next');
startTouch = undefined;
}
return false;
}
});
e.preventDefault();
})
.unbind('touchend.deck').bind('touchend.deck', function(t) {
$.each(t.originalEvent.changedTouches, function(i, t) {
if (startTouch && t.identifier === startTouch.identifier) {
startTouch = undefined;
}
});
})
.scrollLeft(0).scrollTop(0)
/* Stop propagation of key events within editable elements of slides */
.undelegate('input, textarea, select, button, meter, progress, [contentEditable]', 'keydown', esp)
.delegate('input, textarea, select, button, meter, progress, [contentEditable]', 'keydown', esp);
/*
Kick iframe videos, which dont like to redraw w/ transforms.
Remove this if Webkit ever fixes it.
*/
$.each(slides, function(i, $el) {
$el.unbind('webkitTransitionEnd.deck').bind('webkitTransitionEnd.deck',
function(event) {
if ($el.hasClass($[deck]('getOptions').classes.current)) {
var embeds = $(this).find('iframe').css('opacity', 0);
window.setTimeout(function() {
embeds.css('opacity', 1);
}, 100);
}
});
});
if (slides.length) {
updateStates();
}
// Show deck again now that slides are in place
$container.removeClass(options.classes.loading);
$d.trigger(events.initialize);
},
/*
jQuery.deck('go', index)
index: integer | string
Moves to the slide at the specified index if index is a number. Index is
0-based, so $.deck('go', 0); will move to the first slide. If index is a
string this will move to the slide with the specified id. If index is out
of bounds or doesn't match a slide id the call is ignored.
*/
go: function(index) {
var e = $.Event(events.change),
ndx;
/* Number index, easy. */
if (typeof index === 'number' && index >= 0 && index < slides.length) {
ndx = index;
}
/* Id string index, search for it and set integer index */
else if (typeof index === 'string') {
$.each(slides, function(i, $slide) {
if ($slide.attr('id') === index) {
ndx = i;
return false;
}
});
};
/* Out of bounds, id doesn't exist, illegal input, eject */
if (typeof ndx === 'undefined') return;
$d.trigger(e, [current, ndx]);
if (e.isDefaultPrevented()) {
/* Trigger the event again and undo the damage done by extensions. */
$d.trigger(events.change, [ndx, current]);
}
else {
current = ndx;
updateStates();
}
},
/*
jQuery.deck('next')
Moves to the next slide. If the last slide is already active, the call
is ignored.
*/
next: function() {
methods.go(current+1);
},
/*
jQuery.deck('prev')
Moves to the previous slide. If the first slide is already active, the
call is ignored.
*/
prev: function() {
methods.go(current-1);
},
/*
jQuery.deck('getSlide', index)
index: integer, optional
Returns a jQuery object containing the slide at index. If index is not
specified, the current slide is returned.
*/
getSlide: function(index) {
var i = typeof index !== 'undefined' ? index : current;
if (typeof i != 'number' || i < 0 || i >= slides.length) return null;
return slides[i];
},
/*
jQuery.deck('getSlides')
Returns all slides as an array of jQuery objects.
*/
getSlides: function() {
return slides;
},
/*
jQuery.deck('getContainer')
Returns a jQuery object containing the deck container as defined by the
container option.
*/
getContainer: function() {
return $container;
},
/*
jQuery.deck('getOptions')
Returns the options object for the deck, including any overrides that
were defined at initialization.
*/
getOptions: function() {
return options;
},
/*
jQuery.deck('extend', name, method)
name: string
method: function
Adds method to the deck namespace with the key of name. This doesnt
give access to any private member data public methods must still be
used within method but lets extension authors piggyback on the deck
namespace rather than pollute jQuery.
$.deck('extend', 'alert', function(msg) {
alert(msg);
});
// Alerts 'boom'
$.deck('alert', 'boom');
*/
extend: function(name, method) {
methods[name] = method;
}
};
/* jQuery extension */
$[deck] = function(method, arg) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
}
else {
return methods.init(method, arg);
}
};
/*
The default settings object for a deck. All deck extensions should extend
this object to add defaults for any of their options.
options.classes.after
This class is added to all slides that appear after the 'next' slide.
options.classes.before
This class is added to all slides that appear before the 'previous'
slide.
options.classes.childCurrent
This class is added to all elements in the DOM tree between the
'current' slide and the deck container. For standard slides, this is
mostly seen and used for nested slides.
options.classes.current
This class is added to the current slide.
options.classes.loading
This class is applied to the deck container during loading phases and is
primarily used as a way to short circuit transitions between states
where such transitions are distracting or unwanted. For example, this
class is applied during deck initialization and then removed to prevent
all the slides from appearing stacked and transitioning into place
on load.
options.classes.next
This class is added to the slide immediately following the 'current'
slide.
options.classes.onPrefix
This prefix, concatenated with the current slide index, is added to the
deck container as you change slides.
options.classes.previous
This class is added to the slide immediately preceding the 'current'
slide.
options.selectors.container
Elements matched by this CSS selector will be considered the deck
container. The deck container is used to scope certain states of the
deck, as with the onPrefix option, or with extensions such as deck.goto
and deck.menu.
options.keys.next
The numeric keycode used to go to the next slide.
options.keys.previous
The numeric keycode used to go to the previous slide.
options.touch.swipeTolerance
The number of pixels the users finger must travel to produce a swipe
gesture.
*/
$[deck].defaults = {
classes: {
after: 'deck-after',
before: 'deck-before',
childCurrent: 'deck-child-current',
current: 'deck-current',
loading: 'deck-loading',
next: 'deck-next',
onPrefix: 'on-slide-',
previous: 'deck-previous'
},
selectors: {
container: '.deck-container'
},
keys: {
// enter, space, page down, right arrow, down arrow,
next: [13, 32, 34, 39, 40],
// backspace, page up, left arrow, up arrow
previous: [8, 33, 37, 38]
},
touch: {
swipeTolerance: 60
}
};
$d.ready(function() {
$('html').addClass('ready');
});
/*
FF + Transforms + Flash video don't get along...
Firefox will reload and start playing certain videos after a
transform. Blanking the src when a previously shown slide goes out
of view prevents this.
*/
$d.bind('deck.change', function(e, from, to) {
var oldFrames = $[deck]('getSlide', from).find('iframe'),
newFrames = $[deck]('getSlide', to).find('iframe');
oldFrames.each(function() {
var $this = $(this),
curSrc = $this.attr('src');
if(curSrc) {
$this.data('deck-src', curSrc).attr('src', '');
}
});
newFrames.each(function() {
var $this = $(this),
originalSrc = $this.data('deck-src');
if (originalSrc) {
$this.attr('src', originalSrc);
}
});
});
})(jQuery, 'deck', document);

View file

@ -0,0 +1,170 @@
/*!
Deck JS - deck.goto
Copyright (c) 2011 Caleb Troughton
Dual licensed under the MIT license and GPL license.
https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt
*/
/*
This module adds the necessary methods and key bindings to show and hide a form
for jumping to any slide number/id in the deck (and processes that form
accordingly). The form-showing state is indicated by the presence of a class on
the deck container.
*/
(function($, deck, undefined) {
var $d = $(document);
/*
Extends defaults/options.
options.classes.goto
This class is added to the deck container when showing the Go To Slide
form.
options.selectors.gotoDatalist
The element that matches this selector is the datalist element that will
be populated with options for each of the slide ids. In browsers that
support the datalist element, this provides a drop list of slide ids to
aid the user in selecting a slide.
options.selectors.gotoForm
The element that matches this selector is the form that is submitted
when a user hits enter after typing a slide number/id in the gotoInput
element.
options.selectors.gotoInput
The element that matches this selector is the text input field for
entering a slide number/id in the Go To Slide form.
options.keys.goto
The numeric keycode used to show the Go To Slide form.
options.countNested
If false, only top level slides will be counted when entering a
slide number.
*/
$.extend(true, $[deck].defaults, {
classes: {
goto: 'deck-goto'
},
selectors: {
gotoDatalist: '#goto-datalist',
gotoForm: '.goto-form',
gotoInput: '#goto-slide'
},
keys: {
goto: 71 // g
},
countNested: true
});
/*
jQuery.deck('showGoTo')
Shows the Go To Slide form by adding the class specified by the goto class
option to the deck container.
*/
$[deck]('extend', 'showGoTo', function() {
$[deck]('getContainer').addClass($[deck]('getOptions').classes.goto);
$($[deck]('getOptions').selectors.gotoInput).focus();
});
/*
jQuery.deck('hideGoTo')
Hides the Go To Slide form by removing the class specified by the goto class
option from the deck container.
*/
$[deck]('extend', 'hideGoTo', function() {
$($[deck]('getOptions').selectors.gotoInput).blur();
$[deck]('getContainer').removeClass($[deck]('getOptions').classes.goto);
});
/*
jQuery.deck('toggleGoTo')
Toggles between showing and hiding the Go To Slide form.
*/
$[deck]('extend', 'toggleGoTo', function() {
$[deck]($[deck]('getContainer').hasClass($[deck]('getOptions').classes.goto) ? 'hideGoTo' : 'showGoTo');
});
$d.bind('deck.init', function() {
var opts = $[deck]('getOptions'),
$datalist = $(opts.selectors.gotoDatalist),
slideTest = $.map([
opts.classes.before,
opts.classes.previous,
opts.classes.current,
opts.classes.next,
opts.classes.after
], function(el, i) {
return '.' + el;
}).join(', '),
rootCounter = 1;
// Bind key events
$d.unbind('keydown.deckgoto').bind('keydown.deckgoto', function(e) {
var key = $[deck]('getOptions').keys.goto;
if (e.which === key || $.inArray(e.which, key) > -1) {
e.preventDefault();
$[deck]('toggleGoTo');
}
});
/* Populate datalist and work out countNested*/
$.each($[deck]('getSlides'), function(i, $slide) {
var id = $slide.attr('id'),
$parentSlides = $slide.parentsUntil(opts.selectors.container, slideTest);
if (id) {
$datalist.append('<option value="' + id + '">');
}
if ($parentSlides.length) {
$slide.removeData('rootIndex');
}
else if (!opts.countNested) {
$slide.data('rootIndex', rootCounter);
++rootCounter;
}
});
// Process form submittal, go to the slide entered
$(opts.selectors.gotoForm)
.unbind('submit.deckgoto')
.bind('submit.deckgoto', function(e) {
var $field = $($[deck]('getOptions').selectors.gotoInput),
ndx = parseInt($field.val(), 10);
if (!$[deck]('getOptions').countNested) {
if (ndx >= rootCounter) return false;
$.each($[deck]('getSlides'), function(i, $slide) {
if ($slide.data('rootIndex') === ndx) {
ndx = i + 1;
return false;
}
});
}
$[deck]('go', isNaN(ndx) ? $field.val() : ndx - 1);
$[deck]('hideGoTo');
$field.val('');
e.preventDefault();
});
// Dont let keys in the input trigger deck actions
$(opts.selectors.gotoInput)
.unbind('keydown.deckgoto')
.bind('keydown.deckgoto', function(e) {
e.stopPropagation();
});
});
})(jQuery, 'deck');

View file

@ -0,0 +1,141 @@
/*!
Deck JS - deck.hash
Copyright (c) 2011 Caleb Troughton
Dual licensed under the MIT license and GPL license.
https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt
*/
/*
This module adds deep linking to individual slides, enables internal links
to slides within decks, and updates the address bar with the hash as the user
moves through the deck. A permalink anchor is also updated. Standard themes
hide this link in browsers that support the History API, and show it for
those that do not. Slides that do not have an id are assigned one according to
the hashPrefix option. In addition to the on-slide container state class
kept by core, this module adds an on-slide state class that uses the id of each
slide.
*/
(function ($, deck, window, undefined) {
var $d = $(document),
$window = $(window),
/* Collection of internal fragment links in the deck */
$internals,
/*
Internal only function. Given a string, extracts the id from the hash,
matches it to the appropriate slide, and navigates there.
*/
goByHash = function(str) {
var id = str.substr(str.indexOf("#") + 1),
slides = $[deck]('getSlides');
$.each(slides, function(i, $el) {
if ($el.attr('id') === id) {
$[deck]('go', i);
return false;
}
});
// If we don't set these to 0 the container scrolls due to hashchange
$[deck]('getContainer').scrollLeft(0).scrollTop(0);
};
/*
Extends defaults/options.
options.selectors.hashLink
The element matching this selector has its href attribute updated to
the hash of the current slide as the user navigates through the deck.
options.hashPrefix
Every slide that does not have an id is assigned one at initialization.
Assigned ids take the form of hashPrefix + slideIndex, e.g., slide-0,
slide-12, etc.
options.preventFragmentScroll
When deep linking to a hash of a nested slide, this scrolls the deck
container to the top, undoing the natural browser behavior of scrolling
to the document fragment on load.
*/
$.extend(true, $[deck].defaults, {
selectors: {
hashLink: '.deck-permalink'
},
hashPrefix: 'slide-',
preventFragmentScroll: true
});
$d.bind('deck.init', function() {
var opts = $[deck]('getOptions');
$internals = $(),
slides = $[deck]('getSlides');
$.each(slides, function(i, $el) {
var hash;
/* Hand out ids to the unfortunate slides born without them */
if (!$el.attr('id') || $el.data('deckAssignedId') === $el.attr('id')) {
$el.attr('id', opts.hashPrefix + i);
$el.data('deckAssignedId', opts.hashPrefix + i);
}
hash ='#' + $el.attr('id');
/* Deep link to slides on init */
if (hash === window.location.hash) {
$[deck]('go', i);
}
/* Add internal links to this slide */
$internals = $internals.add('a[href="' + hash + '"]');
});
if (!Modernizr.hashchange) {
/* Set up internal links using click for the poor browsers
without a hashchange event. */
$internals.unbind('click.deckhash').bind('click.deckhash', function(e) {
goByHash($(this).attr('href'));
});
}
/* Set up first id container state class */
if (slides.length) {
$[deck]('getContainer').addClass(opts.classes.onPrefix + $[deck]('getSlide').attr('id'));
};
})
/* Update permalink, address bar, and state class on a slide change */
.bind('deck.change', function(e, from, to) {
var hash = '#' + $[deck]('getSlide', to).attr('id'),
opts = $[deck]('getOptions'),
osp = opts.classes.onPrefix,
$c = $[deck]('getContainer');
$c.removeClass(osp + $[deck]('getSlide', from).attr('id'));
$c.addClass(osp + $[deck]('getSlide', to).attr('id'));
$(opts.selectors.hashLink).attr('href', hash);
if (Modernizr.history) {
window.history.replaceState({}, "", hash);
}
});
/* Deals with internal links in modern browsers */
$window.bind('hashchange.deckhash', function(e) {
if (e.originalEvent && e.originalEvent.newURL) {
goByHash(e.originalEvent.newURL);
}
else {
goByHash(window.location.hash);
}
})
/* Prevent scrolling on deep links */
.bind('load', function() {
if ($[deck]('getOptions').preventFragmentScroll) {
$[deck]('getContainer').scrollLeft(0).scrollTop(0);
}
});
})(jQuery, 'deck', this);

View file

@ -0,0 +1,187 @@
/*!
Deck JS - deck.menu
Copyright (c) 2011 Caleb Troughton
Dual licensed under the MIT license and GPL license.
https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt
*/
/*
This module adds the methods and key binding to show and hide a menu of all
slides in the deck. The deck menu state is indicated by the presence of a class
on the deck container.
*/
(function($, deck, undefined) {
var $d = $(document),
rootSlides; // Array of top level slides
/*
Extends defaults/options.
options.classes.menu
This class is added to the deck container when showing the slide menu.
options.keys.menu
The numeric keycode used to toggle between showing and hiding the slide
menu.
options.touch.doubletapWindow
Two consecutive touch events within this number of milliseconds will
be considered a double tap, and will toggle the menu on touch devices.
*/
$.extend(true, $[deck].defaults, {
classes: {
menu: 'deck-menu'
},
keys: {
menu: 77 // m
},
touch: {
doubletapWindow: 400
}
});
/*
jQuery.deck('showMenu')
Shows the slide menu by adding the class specified by the menu class option
to the deck container.
*/
$[deck]('extend', 'showMenu', function() {
var $c = $[deck]('getContainer'),
opts = $[deck]('getOptions');
if ($c.hasClass(opts.classes.menu)) return;
// Hide through loading class to short-circuit transitions (perf)
$c.addClass([opts.classes.loading, opts.classes.menu].join(' '));
/* Forced to do this in JS until CSS learns second-grade math. Save old
style value for restoration when menu is hidden. */
if (Modernizr.csstransforms) {
$.each(rootSlides, function(i, $slide) {
$slide.data('oldStyle', $slide.attr('style'));
$slide.css({
'position': 'absolute',
'left': ((i % 4) * 25) + '%',
'top': (Math.floor(i / 4) * 25) + '%'
});
});
}
// Need to ensure the loading class renders first, then remove
window.setTimeout(function() {
$c.removeClass(opts.classes.loading)
.scrollTop($[deck]('getSlide').offset().top);
}, 0);
});
/*
jQuery.deck('hideMenu')
Hides the slide menu by removing the class specified by the menu class
option from the deck container.
*/
$[deck]('extend', 'hideMenu', function() {
var $c = $[deck]('getContainer'),
opts = $[deck]('getOptions');
if (!$c.hasClass(opts.classes.menu)) return;
$c.removeClass(opts.classes.menu);
$c.addClass(opts.classes.loading);
/* Restore old style value */
if (Modernizr.csstransforms) {
$.each(rootSlides, function(i, $slide) {
var oldStyle = $slide.data('oldStyle');
$slide.attr('style', oldStyle ? oldStyle : '');
});
}
window.setTimeout(function() {
$c.removeClass(opts.classes.loading).scrollTop(0);
}, 0);
});
/*
jQuery.deck('toggleMenu')
Toggles between showing and hiding the slide menu.
*/
$[deck]('extend', 'toggleMenu', function() {
$[deck]('getContainer').hasClass($[deck]('getOptions').classes.menu) ?
$[deck]('hideMenu') : $[deck]('showMenu');
});
$d.bind('deck.init', function() {
var opts = $[deck]('getOptions'),
touchEndTime = 0,
currentSlide,
slideTest = $.map([
opts.classes.before,
opts.classes.previous,
opts.classes.current,
opts.classes.next,
opts.classes.after
], function(el, i) {
return '.' + el;
}).join(', ');
// Build top level slides array
rootSlides = [];
$.each($[deck]('getSlides'), function(i, $el) {
if (!$el.parentsUntil(opts.selectors.container, slideTest).length) {
rootSlides.push($el);
}
});
// Bind key events
$d.unbind('keydown.deckmenu').bind('keydown.deckmenu', function(e) {
if (e.which === opts.keys.menu || $.inArray(e.which, opts.keys.menu) > -1) {
$[deck]('toggleMenu');
e.preventDefault();
}
});
// Double tap to toggle slide menu for touch devices
$[deck]('getContainer').unbind('touchstart.deckmenu').bind('touchstart.deckmenu', function(e) {
currentSlide = $[deck]('getSlide');
})
.unbind('touchend.deckmenu').bind('touchend.deckmenu', function(e) {
var now = Date.now();
// Ignore this touch event if it caused a nav change (swipe)
if (currentSlide !== $[deck]('getSlide')) return;
if (now - touchEndTime < opts.touch.doubletapWindow) {
$[deck]('toggleMenu');
e.preventDefault();
}
touchEndTime = now;
});
// Selecting slides from the menu
$.each($[deck]('getSlides'), function(i, $s) {
$s.unbind('click.deckmenu').bind('click.deckmenu', function(e) {
if (!$[deck]('getContainer').hasClass(opts.classes.menu)) return;
$[deck]('go', i);
$[deck]('hideMenu');
e.stopPropagation();
e.preventDefault();
});
});
})
.bind('deck.change', function(e, from, to) {
var container = $[deck]('getContainer');
if (container.hasClass($[deck]('getOptions').classes.menu)) {
container.scrollTop($[deck]('getSlide', to).offset().top);
}
});
})(jQuery, 'deck');

View file

@ -0,0 +1,91 @@
/*!
Deck JS - deck.navigation
Copyright (c) 2011 Caleb Troughton
Dual licensed under the MIT license and GPL license.
https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt
*/
/*
This module adds clickable previous and next links to the deck.
*/
(function($, deck, undefined) {
var $d = $(document),
/* Updates link hrefs, and disabled states if last/first slide */
updateButtons = function(e, from, to) {
var opts = $[deck]('getOptions'),
last = $[deck]('getSlides').length - 1,
prevSlide = $[deck]('getSlide', to - 1),
nextSlide = $[deck]('getSlide', to + 1),
prevId = prevSlide ? prevSlide.attr('id') : undefined;
nextId = nextSlide ? nextSlide.attr('id') : undefined;
$(opts.selectors.previousLink)
.toggleClass(opts.classes.navDisabled, !to)
.attr('href', '#' + (prevId ? prevId : ''));
$(opts.selectors.nextLink)
.toggleClass(opts.classes.navDisabled, to === last)
.attr('href', '#' + (nextId ? nextId : ''));
};
/*
Extends defaults/options.
options.classes.navDisabled
This class is added to a navigation link when that action is disabled.
It is added to the previous link when on the first slide, and to the
next link when on the last slide.
options.selectors.nextLink
The elements that match this selector will move the deck to the next
slide when clicked.
options.selectors.previousLink
The elements that match this selector will move to deck to the previous
slide when clicked.
*/
$.extend(true, $[deck].defaults, {
classes: {
navDisabled: 'deck-nav-disabled'
},
selectors: {
nextLink: '.deck-next-link',
previousLink: '.deck-prev-link'
}
});
$d.bind('deck.init', function() {
var opts = $[deck]('getOptions'),
slides = $[deck]('getSlides'),
$current = $[deck]('getSlide'),
ndx;
// Setup prev/next link events
$(opts.selectors.previousLink)
.unbind('click.decknavigation')
.bind('click.decknavigation', function(e) {
$[deck]('prev');
e.preventDefault();
});
$(opts.selectors.nextLink)
.unbind('click.decknavigation')
.bind('click.decknavigation', function(e) {
$[deck]('next');
e.preventDefault();
});
// Find where we started in the deck and set initial states
$.each(slides, function(i, $slide) {
if ($slide === $current) {
ndx = i;
return false;
}
});
updateButtons(null, ndx, ndx);
})
.bind('deck.change', updateButtons);
})(jQuery, 'deck');

View file

@ -0,0 +1,170 @@
/*!
Deck JS - deck.scale
Copyright (c) 2011-2012 Caleb Troughton
Dual licensed under the MIT license and GPL license.
https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt
*/
/*
This module adds automatic scaling to the deck. Slides are scaled down
using CSS transforms to fit within the deck container. If the container is
big enough to hold the slides without scaling, no scaling occurs. The user
can disable and enable scaling with a keyboard shortcut.
Note: CSS transforms may make Flash videos render incorrectly. Presenters
that need to use video may want to disable scaling to play them. HTML5 video
works fine.
*/
(function($, deck, window, undefined) {
var $d = $(document),
$w = $(window),
baseHeight, // Value to scale against
timer, // Timeout id for debouncing
rootSlides,
/*
Internal function to do all the dirty work of scaling the slides.
*/
scaleDeck = function() {
var opts = $[deck]('getOptions'),
obh = opts.baseHeight,
$container = $[deck]('getContainer'),
baseHeight = obh ? obh : $container.height();
// Scale each slide down if necessary (but don't scale up)
$.each(rootSlides, function(i, $slide) {
var slideHeight = $slide.innerHeight(),
$scaler = $slide.find('.' + opts.classes.scaleSlideWrapper),
scale = $container.hasClass(opts.classes.scale) ?
baseHeight / slideHeight :
1;
$.each('Webkit Moz O ms Khtml'.split(' '), function(i, prefix) {
if (scale === 1) {
$scaler.css(prefix + 'Transform', '');
}
else {
$scaler.css(prefix + 'Transform', 'scale(' + scale + ')');
}
});
});
}
/*
Extends defaults/options.
options.classes.scale
This class is added to the deck container when scaling is enabled.
It is enabled by default when the module is included.
options.classes.scaleSlideWrapper
Scaling is done using a wrapper around the contents of each slide. This
class is applied to that wrapper.
options.keys.scale
The numeric keycode used to toggle enabling and disabling scaling.
options.baseHeight
When baseHeight is falsy, as it is by default, the deck is scaled in
proportion to the height of the deck container. You may instead specify
a height as a number of px, and slides will be scaled against this
height regardless of the container size.
options.scaleDebounce
Scaling on the browser resize event is debounced. This number is the
threshold in milliseconds. You can learn more about debouncing here:
http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
*/
$.extend(true, $[deck].defaults, {
classes: {
scale: 'deck-scale',
scaleSlideWrapper: 'deck-slide-scaler'
},
keys: {
scale: 83 // s
},
baseHeight: null,
scaleDebounce: 200
});
/*
jQuery.deck('disableScale')
Disables scaling and removes the scale class from the deck container.
*/
$[deck]('extend', 'disableScale', function() {
$[deck]('getContainer').removeClass($[deck]('getOptions').classes.scale);
scaleDeck();
});
/*
jQuery.deck('enableScale')
Enables scaling and adds the scale class to the deck container.
*/
$[deck]('extend', 'enableScale', function() {
$[deck]('getContainer').addClass($[deck]('getOptions').classes.scale);
scaleDeck();
});
/*
jQuery.deck('toggleScale')
Toggles between enabling and disabling scaling.
*/
$[deck]('extend', 'toggleScale', function() {
var $c = $[deck]('getContainer');
$[deck]($c.hasClass($[deck]('getOptions').classes.scale) ?
'disableScale' : 'enableScale');
});
$d.bind('deck.init', function() {
var opts = $[deck]('getOptions'),
slideTest = $.map([
opts.classes.before,
opts.classes.previous,
opts.classes.current,
opts.classes.next,
opts.classes.after
], function(el, i) {
return '.' + el;
}).join(', ');
// Build top level slides array
rootSlides = [];
$.each($[deck]('getSlides'), function(i, $el) {
if (!$el.parentsUntil(opts.selectors.container, slideTest).length) {
rootSlides.push($el);
}
});
// Use a wrapper on each slide to handle content scaling
$.each(rootSlides, function(i, $slide) {
$slide.children().wrapAll('<div class="' + opts.classes.scaleSlideWrapper + '"/>');
});
// Debounce the resize scaling
$w.unbind('resize.deckscale').bind('resize.deckscale', function() {
window.clearTimeout(timer);
timer = window.setTimeout(scaleDeck, opts.scaleDebounce);
})
// Scale once on load, in case images or something change layout
.unbind('load.deckscale').bind('load.deckscale', scaleDeck);
// Bind key events
$d.unbind('keydown.deckscale').bind('keydown.deckscale', function(e) {
if (e.which === opts.keys.scale || $.inArray(e.which, opts.keys.scale) > -1) {
$[deck]('toggleScale');
e.preventDefault();
}
});
// Enable scale on init
$[deck]('enableScale');
});
})(jQuery, 'deck', this);

View file

@ -0,0 +1,95 @@
/*!
Deck JS - deck.status
Copyright (c) 2011 Caleb Troughton
Dual licensed under the MIT license and GPL license.
https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt
*/
/*
This module adds a (current)/(total) style status indicator to the deck.
*/
(function($, deck, undefined) {
var $d = $(document),
updateCurrent = function(e, from, to) {
var opts = $[deck]('getOptions');
$(opts.selectors.statusCurrent).text(opts.countNested ?
to + 1 :
$[deck]('getSlide', to).data('rootSlide')
);
};
/*
Extends defaults/options.
options.selectors.statusCurrent
The element matching this selector displays the current slide number.
options.selectors.statusTotal
The element matching this selector displays the total number of slides.
options.countNested
If false, only top level slides will be counted in the current and
total numbers.
*/
$.extend(true, $[deck].defaults, {
selectors: {
statusCurrent: '.deck-status-current',
statusTotal: '.deck-status-total'
},
countNested: true
});
$d.bind('deck.init', function() {
var opts = $[deck]('getOptions'),
slides = $[deck]('getSlides'),
$current = $[deck]('getSlide'),
ndx;
// Set total slides once
if (opts.countNested) {
$(opts.selectors.statusTotal).text(slides.length);
}
else {
/* Determine root slides by checking each slide's ancestor tree for
any of the slide classes. */
var rootIndex = 1,
slideTest = $.map([
opts.classes.before,
opts.classes.previous,
opts.classes.current,
opts.classes.next,
opts.classes.after
], function(el, i) {
return '.' + el;
}).join(', ');
/* Store the 'real' root slide number for use during slide changes. */
$.each(slides, function(i, $el) {
var $parentSlides = $el.parentsUntil(opts.selectors.container, slideTest);
$el.data('rootSlide', $parentSlides.length ?
$parentSlides.last().data('rootSlide') :
rootIndex++
);
});
$(opts.selectors.statusTotal).text(rootIndex - 1);
}
// Find where we started in the deck and set initial state
$.each(slides, function(i, $el) {
if ($el === $current) {
ndx = i;
return false;
}
});
updateCurrent(null, ndx, ndx);
})
/* Update current slide number with each change event */
.bind('deck.change', updateCurrent);
})(jQuery, 'deck');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
if(!this.sh_languages){this.sh_languages={}}sh_languages.cpp=[[[/(\b(?:class|struct|typename))([ \t]+)([A-Za-z0-9_]+)/g,["sh_keyword","sh_normal","sh_classname"],-1],[/\b(?:class|const_cast|delete|dynamic_cast|explicit|false|friend|inline|mutable|namespace|new|operator|private|protected|public|reinterpret_cast|static_cast|template|this|throw|true|try|typeid|typename|using|virtual)\b/g,"sh_keyword",-1],[/\/\/\//g,"sh_comment",1],[/\/\//g,"sh_comment",7],[/\/\*\*/g,"sh_comment",8],[/\/\*/g,"sh_comment",9],[/(\bstruct)([ \t]+)([A-Za-z0-9_]+)/g,["sh_keyword","sh_normal","sh_classname"],-1],[/^[ \t]*#(?:[ \t]*include)/g,"sh_preproc",10,1],[/^[ \t]*#(?:[ \t]*[A-Za-z0-9_]*)/g,"sh_preproc",-1],[/\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g,"sh_number",-1],[/"/g,"sh_string",13],[/'/g,"sh_string",14],[/\b(?:__asm|__cdecl|__declspec|__export|__far16|__fastcall|__fortran|__import|__pascal|__rtti|__stdcall|_asm|_cdecl|__except|_export|_far16|_fastcall|__finally|_fortran|_import|_pascal|_stdcall|__thread|__try|asm|auto|break|case|catch|cdecl|const|continue|default|do|else|enum|extern|for|goto|if|pascal|register|return|sizeof|static|struct|switch|typedef|union|volatile|while)\b/g,"sh_keyword",-1],[/\b(?:bool|char|double|float|int|long|short|signed|unsigned|void|wchar_t)\b/g,"sh_type",-1],[/~|!|%|\^|\*|\(|\)|-|\+|=|\[|\]|\\|:|;|,|\.|\/|\?|&|<|>|\|/g,"sh_symbol",-1],[/\{|\}/g,"sh_cbracket",-1],[/(?:[A-Za-z]|_)[A-Za-z0-9_]*(?=[ \t]*\()/g,"sh_function",-1],[/([A-Za-z](?:[^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]|[_])*)((?:<.*>)?)(\s+(?=[*&]*[A-Za-z][^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]*\s*[`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\[\]]+))/g,["sh_usertype","sh_usertype","sh_normal"],-1]],[[/$/g,null,-2],[/(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g,"sh_url",-1],[/<\?xml/g,"sh_preproc",2,1],[/<!DOCTYPE/g,"sh_preproc",4,1],[/<!--/g,"sh_comment",5],[/<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)(?:\/)?>/g,"sh_keyword",-1],[/<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g,"sh_keyword",6,1],[/&(?:[A-Za-z0-9]+);/g,"sh_preproc",-1],[/<(?:\/)?[A-Za-z][A-Za-z0-9]*(?:\/)?>/g,"sh_keyword",-1],[/<(?:\/)?[A-Za-z][A-Za-z0-9]*/g,"sh_keyword",6,1],[/@[A-Za-z]+/g,"sh_type",-1],[/(?:TODO|FIXME|BUG)(?:[:]?)/g,"sh_todo",-1]],[[/\?>/g,"sh_preproc",-2],[/([^=" \t>]+)([ \t]*)(=?)/g,["sh_type","sh_normal","sh_symbol"],-1],[/"/g,"sh_string",3]],[[/\\(?:\\|")/g,null,-1],[/"/g,"sh_string",-2]],[[/>/g,"sh_preproc",-2],[/([^=" \t>]+)([ \t]*)(=?)/g,["sh_type","sh_normal","sh_symbol"],-1],[/"/g,"sh_string",3]],[[/-->/g,"sh_comment",-2],[/<!--/g,"sh_comment",5]],[[/(?:\/)?>/g,"sh_keyword",-2],[/([^=" \t>]+)([ \t]*)(=?)/g,["sh_type","sh_normal","sh_symbol"],-1],[/"/g,"sh_string",3]],[[/$/g,null,-2]],[[/\*\//g,"sh_comment",-2],[/(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g,"sh_url",-1],[/<\?xml/g,"sh_preproc",2,1],[/<!DOCTYPE/g,"sh_preproc",4,1],[/<!--/g,"sh_comment",5],[/<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)(?:\/)?>/g,"sh_keyword",-1],[/<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g,"sh_keyword",6,1],[/&(?:[A-Za-z0-9]+);/g,"sh_preproc",-1],[/<(?:\/)?[A-Za-z][A-Za-z0-9]*(?:\/)?>/g,"sh_keyword",-1],[/<(?:\/)?[A-Za-z][A-Za-z0-9]*/g,"sh_keyword",6,1],[/@[A-Za-z]+/g,"sh_type",-1],[/(?:TODO|FIXME|BUG)(?:[:]?)/g,"sh_todo",-1]],[[/\*\//g,"sh_comment",-2],[/(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g,"sh_url",-1],[/(?:TODO|FIXME|BUG)(?:[:]?)/g,"sh_todo",-1]],[[/$/g,null,-2],[/</g,"sh_string",11],[/"/g,"sh_string",12],[/\/\/\//g,"sh_comment",1],[/\/\//g,"sh_comment",7],[/\/\*\*/g,"sh_comment",8],[/\/\*/g,"sh_comment",9]],[[/$/g,null,-2],[/>/g,"sh_string",-2]],[[/$/g,null,-2],[/\\(?:\\|")/g,null,-1],[/"/g,"sh_string",-2]],[[/"/g,"sh_string",-2],[/\\./g,"sh_specialchar",-1]],[[/'/g,"sh_string",-2],[/\\./g,"sh_specialchar",-1]]];

File diff suppressed because one or more lines are too long