* New tree configuration of the project:
.../
... + -- EO
| |
| |
+-- src ----- + -- EDO
| |
| |
+-- test + -- MO
| |
| |
+-- tutorial + -- MOEO
| |
| |
+-- doc + -- SMP
| |
| |
... + -- EOMPI
|
|
+ -- EOSERIAL
Question for current maintainers: ./README: new release?
Also:
* Moving out eompi & eoserial modules (issue #2).
* Correction of the errors when executing "make doc" command.
* Adding a solution for the conflicting headers problem (see the two CMake Cache
Values: PROJECT_TAG & PROJECT_HRS_INSTALL_SUBPATH) (issue #1)
* Header inclusions:
** src: changing absolute paths into relative paths ('#include <...>' -> '#include "..."')
** test, tutorial: changing relative paths into absolute paths ('#include "..."' -> '#include <...>')
* Moving out some scripts from EDO -> to the root
* Add a new script for compilation and installation (see build_gcc_linux_install)
* Compilation with uBLAS library or EDO module: now ok
* Minor modifications on README & INSTALL files
* Comment eompi failed tests with no end
*** TODO: CPack (debian (DEB) & RedHat (RPM) packages) (issues #6 & #7) ***
This commit is contained in:
parent
515bd5943d
commit
490e837f7a
2359 changed files with 7688 additions and 16329 deletions
53
test/eompi/CMakeLists.txt
Executable file
53
test/eompi/CMakeLists.txt
Executable file
|
|
@ -0,0 +1,53 @@
|
|||
###############################################################################
|
||||
##
|
||||
## CMakeLists file for eo/test/mpi
|
||||
##
|
||||
###############################################################################
|
||||
|
||||
######################################################################################
|
||||
### 1) Include the sources
|
||||
######################################################################################
|
||||
|
||||
#include_directories(${EOMPI_SRC_DIR}/src)
|
||||
#include_directories(${EOSERIAL_SRC_DIR}/src)
|
||||
#include_directories(${EO_SRC_DIR}/src)
|
||||
#include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
######################################################################################
|
||||
### 2) Specify where CMake can find the libraries
|
||||
######################################################################################
|
||||
|
||||
link_directories(${EO_BIN_DIR}/lib)
|
||||
link_directories(${EOMPI_BIN_DIR}/lib)
|
||||
link_directories(${EOSERIAL_BIN_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
|
||||
#t-mpi-multistart
|
||||
t-mpi-distrib-exp
|
||||
)
|
||||
|
||||
foreach (test ${TEST_LIST})
|
||||
set ("T_${test}_SOURCES" "${test}.cpp")
|
||||
endforeach (test)
|
||||
|
||||
set(CMAKE_CXX_COMPILER "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} eoutils eompi eoserial eo)
|
||||
install(TARGETS ${test} RUNTIME DESTINATION share/eo/test COMPONENT test)
|
||||
endforeach (test)
|
||||
endif()
|
||||
|
||||
######################################################################################
|
||||
25
test/eompi/DISTRIB_XP_README.md
Executable file
25
test/eompi/DISTRIB_XP_README.md
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
README
|
||||
------
|
||||
|
||||
To launch a set of experiments with t-mpi-distrib-exp:
|
||||
|
||||
0) Compile it:
|
||||
|
||||
mpic++ -o distrib-exp t-mpi-distrib-exp.cpp -I../../src/ -I../../src/mpi/ -DWITH_MPI -L ../../../build/eo/lib/ -leoutils -leo -leompi -leoserial
|
||||
|
||||
1) Generate the experiments, thanks to the script gen-xp.py
|
||||
This script will guide you and ask you for all experiments. The prefix is used in the results filenames.
|
||||
You may want to modify the name of the experiments file (default value: "experiments.json") or
|
||||
the pattern of the results files. However, you have to ensure that the pattern is an one-to-one
|
||||
function of the parameters, otherwise some results could be lost.
|
||||
|
||||
2) Launch the t-mpi-distrib-exp program with mpirun:
|
||||
For 4 cores (= 1 master + 3 workers)
|
||||
mpirun -np 4 ./t-mpi-distrib-exp --use-experiment-file=1 --experiment-file=/home/eodev/eo/test/mpi/experiments.json
|
||||
|
||||
For 16 cores (= 1 master + 15 workers)
|
||||
mpirun -np 5 ./t-mpi-distrib-exp --use-experiment-file=1 --experiment-file=/home/eodev/eo/test/mpi/experiments.json
|
||||
|
||||
3) The program will generate the results of the experiments, as txt files. There is one result file for each run of each
|
||||
experiment.
|
||||
|
||||
6
test/eompi/experiments.json
Executable file
6
test/eompi/experiments.json
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"experiments":[
|
||||
{"size":"10", "packet_size":"1", "seed":"1337", "distribution":{"name":"normal", "mean":"500", "stddev":"100"}, "worker_print_waiting_time":"1", "filename":"exp1.result.txt"},
|
||||
{"size":"10", "packet_size":"1", "seed":"1337", "distribution":{"name":"normal", "mean":"100", "stddev":"20"}, "worker_print_waiting_time":"1", "filename":"exp2.result.txt"}
|
||||
]
|
||||
}
|
||||
152
test/eompi/gen-xp.py
Executable file
152
test/eompi/gen-xp.py
Executable file
|
|
@ -0,0 +1,152 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
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; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
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
|
||||
|
||||
Authors:
|
||||
Benjamin Bouvier <benjamin.bouvier@gmail.com>
|
||||
"""
|
||||
import json
|
||||
|
||||
# Where will be saved the experiments?
|
||||
EXPERIMENTS_FILENAME = "experiments.json"
|
||||
# What will be the pattern for experiments filenames?
|
||||
FILENAME_PATTERN = "%(prefix)s_%(distrib_name)s_%(size)s_%(packet_size)s_%(run)s.txt"
|
||||
|
||||
def input_number_at_least( min ):
|
||||
n = min - 1
|
||||
while n < min:
|
||||
try:
|
||||
n = int(raw_input("Enter a number greater or equal to %s: "% min))
|
||||
except Exception:
|
||||
print "Please enter an integer."
|
||||
return n
|
||||
|
||||
def input_number_between( min, max ):
|
||||
n = min - 1
|
||||
while n < min or n > max:
|
||||
try:
|
||||
n = int(raw_input("Enter a number between %s and %s: " % (min,max)))
|
||||
except Exception:
|
||||
print "Please enter a number."
|
||||
return n
|
||||
|
||||
def choose_continue():
|
||||
print """Do you want to continue?
|
||||
0. No
|
||||
1. Yes"""
|
||||
return bool( input_number_between(0,1) )
|
||||
|
||||
def choose_distribution_uniform():
|
||||
distribution = {}
|
||||
distribution["name"] = "uniform"
|
||||
print "Enter the minimum value (in milliseconds): "
|
||||
min = input_number_at_least( 0 )
|
||||
distribution["min"] = str(min)
|
||||
print "Enter the maximum value (in milliseconds): "
|
||||
distribution["max"] = str(input_number_at_least( min ))
|
||||
return distribution
|
||||
|
||||
def choose_distribution_normal():
|
||||
distribution = {}
|
||||
distribution["name"] = "normal"
|
||||
print "Enter the mean (in milliseconds): "
|
||||
distribution["mean"] = str(input_number_at_least( 0 ))
|
||||
print "Enter the standard deviation (in milliseconds): "
|
||||
distribution["stddev"] = str(input_number_at_least( 0 ))
|
||||
return distribution
|
||||
|
||||
def choose_distribution_power():
|
||||
distribution = {}
|
||||
distribution["name"] = "exponential"
|
||||
print "Enter the mean (in milliseconds): "
|
||||
distribution["mean"] = str(input_number_at_least( 0 ))
|
||||
return distribution
|
||||
|
||||
def choose_distribution():
|
||||
print """Choose your distribution:
|
||||
0. Uniform
|
||||
1. Normal
|
||||
2. Exponential"""
|
||||
choice = input_number_between( 0, 2 )
|
||||
choose_distrib_functions = [ choose_distribution_uniform, choose_distribution_normal, choose_distribution_power ]
|
||||
return choose_distrib_functions[ choice ]()
|
||||
|
||||
def choose_packet_size():
|
||||
print "Enter the size of a packet (group of elements):"
|
||||
return str(input_number_at_least( 0 ))
|
||||
|
||||
def choose_size():
|
||||
print "Enter the total size (size of population):"
|
||||
return str(input_number_at_least( 0 ))
|
||||
|
||||
def choose_worker_print():
|
||||
print """Should the workers print the time they sleep on stdout?
|
||||
0. No
|
||||
1. Yes"""
|
||||
return str(input_number_between( 0, 1 ))
|
||||
|
||||
def choose_nb_runs():
|
||||
print """How many runs should be launched for this configuration? Seeds will be automatically affected to the number
|
||||
of run+1 (for instance, the first run has a seed of 1, the second has a seed of 2, etc.)."""
|
||||
return input_number_at_least( 1 )
|
||||
|
||||
def choose_prefix():
|
||||
print """What is the name of the experiment? It will be used as the prefix of file names."""
|
||||
return raw_input("Enter the prefix name: ")
|
||||
|
||||
def main():
|
||||
|
||||
prefix = choose_prefix()
|
||||
exps = []
|
||||
|
||||
while True:
|
||||
exp = {}
|
||||
exp["distribution"] = choose_distribution()
|
||||
|
||||
exp["size"] = choose_size()
|
||||
exp["packet_size"] = choose_packet_size()
|
||||
exp["worker_print_waiting_time"] = choose_worker_print()
|
||||
runs = choose_nb_runs()
|
||||
for i in range( runs ):
|
||||
exp["seed"] = str(i+1)
|
||||
|
||||
filename_map = exp.copy()
|
||||
filename_map["run"] = exp["seed"]
|
||||
filename_map["distrib_name"] = exp["distribution"]["name"]
|
||||
filename_map["prefix"] = prefix
|
||||
filename = FILENAME_PATTERN % filename_map
|
||||
|
||||
exp["filename"] = filename
|
||||
copy = exp.copy()
|
||||
exps.append( copy )
|
||||
|
||||
if not choose_continue():
|
||||
break
|
||||
|
||||
# Write the experiments in a file
|
||||
f = file( EXPERIMENTS_FILENAME , 'wb')
|
||||
f.write("""{"experiments":[""")
|
||||
i = 0
|
||||
for exp in exps:
|
||||
if i > 0:
|
||||
f.write(",\n")
|
||||
i += 1
|
||||
f.write( json.dumps(exp) )
|
||||
f.write("]}")
|
||||
f.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
51
test/eompi/t-mpi-common.h
Executable file
51
test/eompi/t-mpi-common.h
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
# ifndef __T_MPI_COMMON_H__
|
||||
# define __T_MPI_COMMON_H__
|
||||
|
||||
#include <paradiseo/eoserial.h>
|
||||
|
||||
/**
|
||||
* @file t-mpi-common.h
|
||||
*
|
||||
* This file shows an example of serialization of a primitive type, so as to be used in a parallel algorithm.
|
||||
* It is fully compatible with the basic type, by implementing conversion operator and constructor based on type.
|
||||
* It can contain any simple type which can be written in a std::ostream.
|
||||
*/
|
||||
|
||||
template< class T >
|
||||
struct SerializableBase : public eoserial::Persistent
|
||||
{
|
||||
public:
|
||||
|
||||
operator T&()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
SerializableBase() : _value()
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
SerializableBase( T base ) : _value( base )
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
void unpack( const eoserial::Object* obj )
|
||||
{
|
||||
eoserial::unpack( *obj, "value", _value );
|
||||
}
|
||||
|
||||
eoserial::Object* pack(void) const
|
||||
{
|
||||
eoserial::Object* obj = new eoserial::Object;
|
||||
obj->add("value", eoserial::make( _value ) );
|
||||
return obj;
|
||||
}
|
||||
|
||||
private:
|
||||
T _value;
|
||||
};
|
||||
|
||||
|
||||
# endif // __T_MPI_COMMON_H__
|
||||
556
test/eompi/t-mpi-distrib-exp.cpp
Executable file
556
test/eompi/t-mpi-distrib-exp.cpp
Executable file
|
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
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; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
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
|
||||
|
||||
* Authors:
|
||||
* Benjamin Bouvier <benjamin.bouvier@gmail.com>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file t-mpi-distrib-exp.cpp
|
||||
* @brief File for parallel experimentations.
|
||||
*
|
||||
* When using parallel evaluation, the individuals to evaluate are sent by packets (group),
|
||||
* so as to avoid that communication time be more important than worker's execution time.
|
||||
* However, the ideal size of packet depends on the problem and the time needed to carry out
|
||||
* the atomic operation on each individual. This experiment tries to find a relation between
|
||||
* the total number of elements to process (size), the execution time and the size of packet.
|
||||
* This could lead to an heuristic allowing to optimize the size of packet according to the
|
||||
* processing times.
|
||||
*/
|
||||
# include <unistd.h> // usleep
|
||||
|
||||
# include <iostream>
|
||||
# include <iomanip>
|
||||
# include <string>
|
||||
# include <sstream>
|
||||
# include <vector>
|
||||
|
||||
# include <paradiseo/eo.h>
|
||||
|
||||
# include <paradiseo/eompi.h>
|
||||
# include "t-mpi-common.h"
|
||||
|
||||
using namespace eo::mpi;
|
||||
|
||||
// Serializable int
|
||||
typedef SerializableBase<int> type;
|
||||
|
||||
/*
|
||||
* The task is the following: the worker receives a number of milliseconds to wait, which
|
||||
* simulates the process of one individual. This way, the sequences of processing times are
|
||||
* generated only by the master and are more easily reproductible.
|
||||
*/
|
||||
struct Wait : public eoUF< type &, void >
|
||||
{
|
||||
Wait( bool print ) : _print( print )
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
void operator()( type & milliseconds )
|
||||
{
|
||||
if( _print )
|
||||
std::cout << "Sleeping for " << milliseconds << "ms..." << std::endl;
|
||||
// usleep takes an input in microseconds
|
||||
usleep( milliseconds * 1000 );
|
||||
}
|
||||
|
||||
private:
|
||||
bool _print;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a distribution of processing times.
|
||||
*/
|
||||
class Distribution : public std::vector< type >, public eoserial::Persistent
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Really fills the vector with the distribution values.
|
||||
*/
|
||||
void fill( unsigned size )
|
||||
{
|
||||
for( unsigned i = 0; i < size; ++i )
|
||||
{
|
||||
int next = next_element();
|
||||
if( next < 0 ) next = 0;
|
||||
push_back( next );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the next element of the distribution to put in the
|
||||
* vector.
|
||||
*
|
||||
* @returns Number of milliseconds to wait. Can be negative ; in this case,
|
||||
* the number will be truncated to 0ms.
|
||||
*/
|
||||
virtual int next_element() = 0;
|
||||
|
||||
/**
|
||||
* @brief Creates params and retrieves values from parser
|
||||
*
|
||||
* Parser's params should take milliseconds as inputs.
|
||||
*/
|
||||
virtual void make_parser( eoParser & parser ) = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if this distribution has been activated by the
|
||||
* command line.
|
||||
*
|
||||
* Used by the main program so as to check if at least one distribution has been
|
||||
* activated.
|
||||
*/
|
||||
bool isActive() { return _active; }
|
||||
|
||||
/**
|
||||
* @brief Prints the name and the parameters of the distribution
|
||||
*/
|
||||
virtual std::string toString() const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
bool _active;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Uniform distribution.
|
||||
*
|
||||
* This is an uniform distribution, defined by a minimum value and a maximum value.
|
||||
* In the uniform distribution, every number from min to max has the same probability
|
||||
* to appear.
|
||||
*
|
||||
* The 3 parameters activable from a parser are the following:
|
||||
* - uniform=1 : if we want to use the uniform distribution
|
||||
* - uniform-min=x : use x milliseconds as the minimum value of waiting time.
|
||||
* - uniform-max=y : use y milliseconds as the maximum value of waiting time.
|
||||
* Ensure that x < y, or the results are unpredictable.
|
||||
*/
|
||||
class UniformDistribution : public Distribution
|
||||
{
|
||||
public:
|
||||
|
||||
UniformDistribution()
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
void make_parser( eoParser & parser )
|
||||
{
|
||||
_active = parser.createParam( false, "uniform", "Uniform distribution", '\0', "Uniform").value();
|
||||
_min = parser.createParam( 0.0, "uniform-min", "Minimum for uniform distribution, in ms.", '\0', "Uniform").value();
|
||||
_max = parser.createParam( 1.0, "uniform-max", "Maximum for uniform distribution, in ms.", '\0', "Uniform").value();
|
||||
}
|
||||
|
||||
int next_element()
|
||||
{
|
||||
return std::floor( eo::rng.uniform( _min, _max ) );
|
||||
}
|
||||
|
||||
eoserial::Object* pack( void ) const
|
||||
{
|
||||
eoserial::Object* obj = new eoserial::Object;
|
||||
obj->add( "name", eoserial::make( "uniform" ) );
|
||||
obj->add( "min", eoserial::make( _min ) );
|
||||
obj->add( "max", eoserial::make( _max ) );
|
||||
return obj;
|
||||
}
|
||||
|
||||
void unpack( const eoserial::Object* obj )
|
||||
{
|
||||
eoserial::unpack( *obj, "min", _min );
|
||||
eoserial::unpack( *obj, "max", _max );
|
||||
}
|
||||
|
||||
std::string toString() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "uniform" << '\n'
|
||||
<< "min: " << _min << '\n'
|
||||
<< "max: " << _max << '\n';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
double _min;
|
||||
double _max;
|
||||
|
||||
} uniformDistribution;
|
||||
|
||||
/**
|
||||
* @brief Normal (gaussian) distribution of times.
|
||||
*
|
||||
* A normal distribution is defined by a mean and a standard deviation.
|
||||
* The 3 parameters activable from the parser are the following:
|
||||
* - normal=1: activates the gaussian distribution.
|
||||
* - normal-mean=50: use 50ms as the mean of the distribution.
|
||||
* - normal-stddev=10: use 10ms as the standard deviation of the distribution.
|
||||
*/
|
||||
class NormalDistribution : public Distribution
|
||||
{
|
||||
public:
|
||||
|
||||
NormalDistribution()
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
void make_parser( eoParser & parser )
|
||||
{
|
||||
_active = parser.createParam( false, "normal", "Normal distribution", '\0', "Normal").value();
|
||||
_mean = parser.createParam( 0.0, "normal-mean", "Mean for the normal distribution (0 by default), in ms.", '\0', "Normal").value();
|
||||
_stddev = parser.createParam( 1.0, "normal-stddev", "Standard deviation for the normal distribution (1ms by default), 0 isn't acceptable.", '\0', "Normal").value();
|
||||
}
|
||||
|
||||
int next_element()
|
||||
{
|
||||
return std::floor( eo::rng.normal( _mean, _stddev ) );
|
||||
}
|
||||
|
||||
eoserial::Object* pack( void ) const
|
||||
{
|
||||
eoserial::Object* obj = new eoserial::Object;
|
||||
obj->add( "name", eoserial::make( "normal" ) );
|
||||
obj->add( "mean", eoserial::make( _mean ) );
|
||||
obj->add( "stddev", eoserial::make( _stddev ) );
|
||||
return obj;
|
||||
}
|
||||
|
||||
void unpack( const eoserial::Object* obj )
|
||||
{
|
||||
eoserial::unpack( *obj, "mean", _mean );
|
||||
eoserial::unpack( *obj, "stddev", _stddev );
|
||||
}
|
||||
|
||||
std::string toString() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "normal" << '\n'
|
||||
<< "mean: " << _mean << '\n'
|
||||
<< "stddev: " << _stddev << '\n';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
double _mean;
|
||||
double _stddev;
|
||||
} normalDistribution;
|
||||
|
||||
/**
|
||||
* @brief Exponential distribution.
|
||||
*
|
||||
* This distribution belongs to the category of the decreasing power laws and are affected by long trails
|
||||
* phenomenons.
|
||||
* An exponential distribution is only defined by its mean.
|
||||
*
|
||||
* The 2 parameters activable from the parser are the following:
|
||||
* - exponential=1: to activate the exponential distribution.
|
||||
* - exponential-mean=50: indicates that the mean must be 50ms.
|
||||
*/
|
||||
class ExponentialDistribution : public Distribution
|
||||
{
|
||||
public:
|
||||
|
||||
ExponentialDistribution()
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
void make_parser( eoParser & parser )
|
||||
{
|
||||
_active = parser.createParam( false, "exponential", "Exponential distribution", '\0', "Exponential").value();
|
||||
_mean = parser.createParam( 0.0, "exponential-mean", "Mean for the exponential distribution (0 by default), in ms.", '\0', "Exponential").value();
|
||||
}
|
||||
|
||||
int next_element()
|
||||
{
|
||||
return std::floor( eo::rng.negexp( _mean ) );
|
||||
}
|
||||
|
||||
eoserial::Object* pack( void ) const
|
||||
{
|
||||
eoserial::Object* obj = new eoserial::Object;
|
||||
obj->add( "name", eoserial::make( "exponential" ) );
|
||||
obj->add( "mean", eoserial::make( _mean ) );
|
||||
return obj;
|
||||
}
|
||||
|
||||
void unpack( const eoserial::Object* obj )
|
||||
{
|
||||
eoserial::unpack( *obj, "mean", _mean );
|
||||
}
|
||||
|
||||
std::string toString() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "exponential" << '\n'
|
||||
<< "mean: " << _mean << '\n';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
double _mean;
|
||||
|
||||
} exponentialDistribution;
|
||||
|
||||
/**
|
||||
* @brief Serializable experiment.
|
||||
*
|
||||
* Allows an experiment to be saved and loaded via a file, using eoserial.
|
||||
*
|
||||
* Construct the experiment with the good parameters from the command line or load experiments from a file. Then call run() to launch the parallel job.
|
||||
*
|
||||
* If a filename is given to the constructor (or during the loading), the results of the experiments (time series) will
|
||||
* be redirected to the file with the given file name. Otherwise (filename == ""), the output will just be shown on the
|
||||
* standard output.
|
||||
*/
|
||||
class Experiment : public eoserial::Persistent
|
||||
{
|
||||
public:
|
||||
|
||||
Experiment() : _distribution(0), _worker_print_waiting_time( false ), _fileName("")
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
Experiment( Distribution* distrib, unsigned size, unsigned packet_size, bool print_waiting_time, unsigned seed, const std::string& fileName = "" ) :
|
||||
_distribution( distrib ),
|
||||
_size( size ),
|
||||
_packet_size( packet_size ),
|
||||
_worker_print_waiting_time( print_waiting_time ),
|
||||
_seed( seed ),
|
||||
_fileName( fileName )
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
eoserial::Object* pack( void ) const
|
||||
{
|
||||
eoserial::Object* obj = new eoserial::Object;
|
||||
obj->add( "size", eoserial::make( _size ) );
|
||||
obj->add( "packet_size", eoserial::make( _packet_size ) );
|
||||
obj->add( "worker_print_waiting_time", eoserial::make( _worker_print_waiting_time ) );
|
||||
obj->add( "seed", eoserial::make( _seed ) );
|
||||
if( _distribution )
|
||||
{
|
||||
obj->add( "distribution", _distribution );
|
||||
}
|
||||
obj->add( "filename", eoserial::make( _fileName ) );
|
||||
return obj;
|
||||
}
|
||||
|
||||
void unpack( const eoserial::Object* obj )
|
||||
{
|
||||
eoserial::unpack( *obj, "size", _size );
|
||||
eoserial::unpack( *obj, "packet_size", _packet_size );
|
||||
eoserial::unpack( *obj, "worker_print_waiting_time", _worker_print_waiting_time );
|
||||
eoserial::unpack( *obj, "seed", _seed );
|
||||
eoserial::unpack( *obj, "filename", _fileName );
|
||||
|
||||
eoserial::Object* distribObject = static_cast<eoserial::Object*>( obj->find("distribution")->second );
|
||||
std::string distribName = *static_cast<eoserial::String*>( distribObject->find("name")->second );
|
||||
|
||||
// TODO find a better design...
|
||||
if( distribName == "normal" ) {
|
||||
_distribution = & normalDistribution;
|
||||
} else if( distribName == "uniform" ) {
|
||||
_distribution = & uniformDistribution;
|
||||
} else if( distribName == "exponential" ) {
|
||||
_distribution = & exponentialDistribution;
|
||||
} else {
|
||||
throw std::runtime_error("When unpacking experience, no distribution found.");
|
||||
}
|
||||
|
||||
eoserial::unpackObject( *obj, "distribution", *_distribution );
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
mpi::communicator& comm = eo::mpi::Node::comm();
|
||||
// reinits every objects
|
||||
eo::rng.reseed( _seed );
|
||||
eo::rng.clearCache(); // trick for repeatable sequences of normal numbers, cf eo::rng
|
||||
_distribution->clear();
|
||||
_distribution->fill( _size );
|
||||
|
||||
eo::mpi::timerStat.start("run");
|
||||
Wait wait( _worker_print_waiting_time );
|
||||
ParallelApplyStore< type > store( wait, DEFAULT_MASTER, _packet_size );
|
||||
store.data( *_distribution );
|
||||
DynamicAssignmentAlgorithm scheduling;
|
||||
ParallelApply< type > job( scheduling, DEFAULT_MASTER, store );
|
||||
|
||||
job.run();
|
||||
eo::mpi::timerStat.stop("run");
|
||||
if( job.isMaster() )
|
||||
{
|
||||
EmptyJob( scheduling, DEFAULT_MASTER ); // to terminate parallel apply
|
||||
// Receive statistics
|
||||
typedef std::map< std::string, eoTimerStat::Stat > typeStats;
|
||||
|
||||
std::ostream* pout;
|
||||
std::ofstream file;
|
||||
bool fileSaveActivated = false;
|
||||
if( _fileName == "" ) {
|
||||
pout = & std::cout;
|
||||
} else {
|
||||
pout = & file;
|
||||
file.open( _fileName.c_str() );
|
||||
fileSaveActivated = true;
|
||||
}
|
||||
std::ostream& out = *pout;
|
||||
|
||||
// Reminder of the parameters
|
||||
out << "size: " << _size << '\n'
|
||||
<< "packet_size: " << _packet_size << '\n'
|
||||
<< "distribution: " << _distribution->toString()
|
||||
<< "seed: " << _seed << '\n' << std::endl;
|
||||
|
||||
// Results
|
||||
out << std::fixed << std::setprecision( 5 );
|
||||
for( int i = 1, s = comm.size(); i < s; ++i )
|
||||
{
|
||||
eoTimerStat timerStat;
|
||||
comm.recv( i, eo::mpi::Channel::Commands, timerStat );
|
||||
typeStats stats = timerStat.stats();
|
||||
for( typeStats::iterator it = stats.begin(),
|
||||
end = stats.end();
|
||||
it != end;
|
||||
++it )
|
||||
{
|
||||
out << i << " " << it->first << std::endl;
|
||||
for( int j = 0, t = it->second.wtime.size(); j < t; ++j )
|
||||
{
|
||||
out << it->second.wtime[j] << " ";
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
if( fileSaveActivated ) {
|
||||
file.close();
|
||||
}
|
||||
} else
|
||||
{
|
||||
// Send statistics
|
||||
comm.send( DEFAULT_MASTER, eo::mpi::Channel::Commands, eo::mpi::timerStat );
|
||||
}
|
||||
timerStat.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Distribution* _distribution;
|
||||
unsigned _size;
|
||||
unsigned _packet_size;
|
||||
bool _worker_print_waiting_time;
|
||||
unsigned _seed;
|
||||
std::string _fileName;
|
||||
};
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
Node::init( argc, argv );
|
||||
eoParser parser( argc, argv );
|
||||
|
||||
// forces the statistics to be retrieved
|
||||
eo::mpi::timerStat.forceDoMeasure();
|
||||
|
||||
// General parameters for the experimentation
|
||||
unsigned size = parser.createParam( 10U, "size", "Number of elements to distribute.", 's', "Distribution").value();
|
||||
unsigned packet_size = parser.createParam( 1U, "packet-size", "Number of elements to distribute at each time for a single worker.", 'p', "Parallelization").value();
|
||||
bool worker_print_waiting_time = parser.createParam( false, "print-waiting-time", "Do the workers need to print the time they wait?", '\0', "Parallelization").value();
|
||||
unsigned seed = parser.createParam( 0U, "seed", "Seed of random generator", '\0', "General").value();
|
||||
std::string fileName = parser.createParam( std::string(""), "filename", "File name to which redirect the results (for a single experiment)", '\0', "General").value();
|
||||
|
||||
bool useExperimentFile = parser.createParam( false, "use-experiment-file", "Put to true if you want to launch experiments from a file formatted in JSON (see experiment-file).", '\0', "General").value();
|
||||
std::string experimentFile = parser.createParam( std::string("experiments.json"), "experiment-file", "File name of experiments to provide, in format JSON.", '\0', "General").value();
|
||||
|
||||
if( !useExperimentFile )
|
||||
{
|
||||
std::vector<Distribution*> distribs;
|
||||
distribs.push_back( &uniformDistribution );
|
||||
distribs.push_back( &normalDistribution );
|
||||
distribs.push_back( &exponentialDistribution );
|
||||
|
||||
// for each available distribution, check if activated.
|
||||
// If no distribution is activated, show an error message
|
||||
// If two distributions or more are activated, show an error message
|
||||
// Otherwise, use the activated distribution as distrib
|
||||
bool isChosenDistrib = false;
|
||||
Distribution* pdistrib = 0;
|
||||
for( int i = 0, s = distribs.size(); i < s; ++i )
|
||||
{
|
||||
distribs[i]->make_parser( parser );
|
||||
if( distribs[i]->isActive() )
|
||||
{
|
||||
if( isChosenDistrib )
|
||||
{
|
||||
throw std::runtime_error("Only one distribution can be chosen during a launch!");
|
||||
} else
|
||||
{
|
||||
isChosenDistrib = true;
|
||||
pdistrib = distribs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
make_parallel( parser );
|
||||
make_help( parser );
|
||||
|
||||
if( !isChosenDistrib )
|
||||
{
|
||||
throw std::runtime_error("No distribution chosen. One distribution should be chosen.");
|
||||
}
|
||||
|
||||
Experiment e( pdistrib, size, packet_size, worker_print_waiting_time, seed, fileName );
|
||||
e.run();
|
||||
}
|
||||
else // use experiments file
|
||||
{
|
||||
// read content of file
|
||||
std::ifstream file( experimentFile.c_str() );
|
||||
std::string fileContent;
|
||||
while( file )
|
||||
{
|
||||
char temp[4096];
|
||||
file.getline( temp, 4096, '\n' );
|
||||
fileContent += temp;
|
||||
fileContent += '\n';
|
||||
}
|
||||
file.close();
|
||||
|
||||
// transform content into array of experiments
|
||||
eoserial::Object* wrapper = eoserial::Parser::parse( fileContent );
|
||||
eoserial::Array& experiments = *static_cast< eoserial::Array* >( wrapper->find("experiments")->second );
|
||||
|
||||
for( unsigned i = 0, s = experiments.size(); i < s; ++i )
|
||||
{
|
||||
std::cout << "Launching experiment " << (i+1) << "..." << std::endl;
|
||||
eoserial::Object* expObj = static_cast< eoserial::Object* >( experiments[i] );
|
||||
Experiment exp;
|
||||
exp.unpack( expObj );
|
||||
exp.run();
|
||||
}
|
||||
delete wrapper;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
206
test/eompi/t-mpi-eval.cpp
Executable file
206
test/eompi/t-mpi-eval.cpp
Executable file
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
(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 <paradiseo/eo.h>
|
||||
#include <paradiseo/eo/eoPopEvalFunc.h>
|
||||
|
||||
#include <paradiseo/eo/es/make_real.h>
|
||||
#include "../eo/real_value.h"
|
||||
|
||||
#include <paradiseo/eompi.h>
|
||||
|
||||
#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 )
|
||||
);
|
||||
|
||||
bool invalidFitness = invalid();
|
||||
obj->add("invalid", eoserial::make( invalidFitness ) );
|
||||
if( !invalidFitness )
|
||||
{
|
||||
double fitnessVal = fitness();
|
||||
obj->add("fitness", eoserial::make( fitnessVal ) );
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
void unpack( const eoserial::Object* obj )
|
||||
{
|
||||
this->clear();
|
||||
eoserial::unpackArray< vector<double>, eoserial::Array::UnpackAlgorithm >
|
||||
( *obj, "array", *this );
|
||||
|
||||
bool invalidFitness;
|
||||
eoserial::unpack( *obj, "invalid", invalidFitness );
|
||||
if( invalidFitness ) {
|
||||
invalidate();
|
||||
} else {
|
||||
double fitnessVal;
|
||||
eoserial::unpack<double>( *obj, "fitness", fitnessVal );
|
||||
fitness( fitnessVal );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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<EOT>. 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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
196
test/eompi/t-mpi-multipleRoles.cpp
Executable file
196
test/eompi/t-mpi-multipleRoles.cpp
Executable file
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
(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 <paradiseo/eompi.h>
|
||||
# include <paradiseo/eompi/eoParallelApply.h>
|
||||
# include <paradiseo/eompi/eoTerminateJob.h>
|
||||
|
||||
# include "t-mpi-common.h"
|
||||
|
||||
# include <iostream>
|
||||
|
||||
# include <vector>
|
||||
using namespace std;
|
||||
|
||||
using namespace eo::mpi;
|
||||
|
||||
/*
|
||||
* This class allows the user to easily serialize a vector of elements which implement eoserial::Persistent too.
|
||||
*
|
||||
* T is the type of the contained element, which must implement eoserial::Persistent too.
|
||||
*
|
||||
* Here, it contains SerializableBase<int>, which is a serializable integer that can be used as an integer.
|
||||
*/
|
||||
template< class T >
|
||||
struct SerializableVector : public std::vector<T>, public eoserial::Persistent
|
||||
{
|
||||
public:
|
||||
|
||||
void unpack( const eoserial::Object* obj )
|
||||
{
|
||||
this->clear();
|
||||
eoserial::Array* vector = static_cast<eoserial::Array*>( obj->find("vector")->second );
|
||||
vector->deserialize< std::vector<T>, eoserial::Array::UnpackObjectAlgorithm >( *this );
|
||||
}
|
||||
|
||||
eoserial::Object* pack( void ) const
|
||||
{
|
||||
eoserial::Object* obj = new eoserial::Object;
|
||||
obj->add("vector", eoserial::makeArray< std::vector<T>, eoserial::SerializablePushAlgorithm >( *this ) );
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
// The real job to execute, for the subworkers: add one to each element of a table.
|
||||
struct SubWork: public eoUF< SerializableBase<int>&, void >
|
||||
{
|
||||
void operator() ( SerializableBase<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< SerializableBase<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< SerializableBase<int> > store( sw, rank );
|
||||
store.data( v );
|
||||
ParallelApply< SerializableBase<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< SerializableVector< SerializableBase<int> >&, void >
|
||||
{
|
||||
void operator() ( SerializableVector< SerializableBase<int> >& v )
|
||||
{
|
||||
cout << "Work phase..." << endl;
|
||||
subtask( v, Node::comm().rank() );
|
||||
for( unsigned 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.");
|
||||
}
|
||||
|
||||
SerializableVector< SerializableBase<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< SerializableVector< SerializableBase<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< SerializableVector< SerializableBase<int> > > store( w, 0 );
|
||||
store.data( metaV );
|
||||
ParallelApply< SerializableVector< SerializableBase<int> > > job( algo, 0, store );
|
||||
job.run();
|
||||
if( job.isMaster() )
|
||||
{
|
||||
EmptyJob stop( algo, 0 );
|
||||
v = metaV[0];
|
||||
cout << "Results : " << endl;
|
||||
for(unsigned 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;
|
||||
}
|
||||
169
test/eompi/t-mpi-multistart.cpp
Executable file
169
test/eompi/t-mpi-multistart.cpp
Executable file
|
|
@ -0,0 +1,169 @@
|
|||
# include <paradiseo/eompi/eoMultiStart.h>
|
||||
using namespace eo::mpi;
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <paradiseo/eo.h>
|
||||
#include <paradiseo/eo/es.h>
|
||||
|
||||
/*
|
||||
* This file is based on the tutorial lesson 1. We'll consider that you know all the EO
|
||||
* related parts of the algorithm and we'll focus our attention on parallelization.
|
||||
*
|
||||
* This file shows an example of multistart applied to a eoSGA (simple genetic
|
||||
* algorithm). As individuals need to be serialized, we implement a class inheriting
|
||||
* from eoReal (which is the base individual), so as to manipulate individuals as they
|
||||
* were eoReal AND serialize them.
|
||||
*
|
||||
* The main function shows how to launch a multistart job, with default functors. If you
|
||||
* don't know which functors to use, these ones should fit the most of your purposes.
|
||||
*/
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
* eoReal is a vector of double: we just have to serializes the value and the fitness.
|
||||
*/
|
||||
class SerializableEOReal: public eoReal<double>, public eoserial::Persistent
|
||||
{
|
||||
public:
|
||||
|
||||
SerializableEOReal(unsigned size = 0, double value = 0.0) :
|
||||
eoReal<double>(size, value)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
void unpack( const eoserial::Object* obj )
|
||||
{
|
||||
this->clear();
|
||||
eoserial::unpackArray
|
||||
< std::vector<double>, eoserial::Array::UnpackAlgorithm >
|
||||
( *obj, "vector", *this );
|
||||
|
||||
bool invalidFitness;
|
||||
eoserial::unpack( *obj, "invalid_fitness", invalidFitness );
|
||||
if( invalidFitness )
|
||||
{
|
||||
this->invalidate();
|
||||
} else
|
||||
{
|
||||
double f;
|
||||
eoserial::unpack( *obj, "fitness", f );
|
||||
this->fitness( f );
|
||||
}
|
||||
}
|
||||
|
||||
eoserial::Object* pack( void ) const
|
||||
{
|
||||
eoserial::Object* obj = new eoserial::Object;
|
||||
obj->add( "vector", eoserial::makeArray< std::vector<double>, eoserial::MakeAlgorithm >( *this ) );
|
||||
|
||||
bool invalidFitness = this->invalid();
|
||||
obj->add( "invalid_fitness", eoserial::make( invalidFitness ) );
|
||||
if( !invalidFitness )
|
||||
{
|
||||
obj->add( "fitness", eoserial::make( this->fitness() ) );
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
// REPRESENTATION
|
||||
//-----------------------------------------------------------------------------
|
||||
// define your individuals
|
||||
typedef SerializableEOReal Indi;
|
||||
|
||||
// EVAL
|
||||
//-----------------------------------------------------------------------------
|
||||
// a simple fitness function that computes the euclidian norm of a real vector
|
||||
// @param _indi A real-valued individual
|
||||
|
||||
double real_value(const Indi & _indi)
|
||||
{
|
||||
double sum = 0;
|
||||
for (unsigned i = 0; i < _indi.size(); i++)
|
||||
sum += _indi[i]*_indi[i];
|
||||
return (-sum); // maximizing only
|
||||
}
|
||||
|
||||
/************************** PARALLELIZATION JOB *******************************/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Node::init( argc, argv );
|
||||
|
||||
// PARAMETRES
|
||||
// all parameters are hard-coded!
|
||||
const unsigned int SEED = 133742; // seed for random number generator
|
||||
const unsigned int VEC_SIZE = 8; // Number of object variables in genotypes
|
||||
const unsigned int POP_SIZE = 100; // Size of population
|
||||
const unsigned int T_SIZE = 3; // size for tournament selection
|
||||
const unsigned int MAX_GEN = 100; // Maximum number of generation before STOP
|
||||
const float CROSS_RATE = 0.8; // Crossover rate
|
||||
const double EPSILON = 0.01; // range for real uniform mutation
|
||||
const float MUT_RATE = 0.5; // mutation rate
|
||||
|
||||
eoEvalFuncPtr<Indi> eval( real_value );
|
||||
eoPop<Indi> pop;
|
||||
eoUniformGenerator< double > generator;
|
||||
eoInitFixedLength< Indi > init( VEC_SIZE, generator );
|
||||
pop = eoPop<Indi>( POP_SIZE, init );
|
||||
|
||||
eoDetTournamentSelect<Indi> select(T_SIZE);
|
||||
eoSegmentCrossover<Indi> xover;
|
||||
eoUniformMutation<Indi> mutation(EPSILON);
|
||||
|
||||
eoGenContinue<Indi> continuator(MAX_GEN);
|
||||
/* Does work too with a steady fit continuator. */
|
||||
// eoSteadyFitContinue< Indi > continuator( 10, 50 );
|
||||
|
||||
eoSGA<Indi> gga(select, xover, CROSS_RATE, mutation, MUT_RATE,
|
||||
eval, continuator);
|
||||
|
||||
/* How to assign tasks, which are starts? */
|
||||
DynamicAssignmentAlgorithm assignmentAlgo;
|
||||
/* Before a worker starts its algorithm, how does it reinits the population?
|
||||
* There are a few default usable functors, defined in eoMultiStart.h.
|
||||
*
|
||||
* This one (ReuseSamePopEA) doesn't modify the population after a start, so
|
||||
* the same population is reevaluated on each multistart: the solution tend
|
||||
* to get better and better.
|
||||
*/
|
||||
ReuseSamePopEA< Indi > resetAlgo( continuator, pop, eval );
|
||||
/**
|
||||
* How to send seeds to the workers, at the beginning of the parallel job?
|
||||
* This functors indicates that seeds should be random values.
|
||||
*/
|
||||
GetRandomSeeds< Indi > getSeeds( SEED );
|
||||
|
||||
// Builds the store
|
||||
MultiStartStore< Indi > store(
|
||||
gga,
|
||||
DEFAULT_MASTER,
|
||||
resetAlgo,
|
||||
getSeeds);
|
||||
|
||||
// Creates the multistart job and runs it.
|
||||
// The last argument indicates that we want to launch 5 runs.
|
||||
MultiStart< Indi > msjob( assignmentAlgo, DEFAULT_MASTER, store, 5 );
|
||||
msjob.run();
|
||||
|
||||
if( msjob.isMaster() )
|
||||
{
|
||||
msjob.best_individuals().sort();
|
||||
std::cout << "Global best individual has fitness " << msjob.best_individuals().best_element().fitness() << std::endl;
|
||||
}
|
||||
|
||||
MultiStart< Indi > msjob10( assignmentAlgo, DEFAULT_MASTER, store, 10 );
|
||||
msjob10.run();
|
||||
|
||||
if( msjob10.isMaster() )
|
||||
{
|
||||
msjob10.best_individuals().sort();
|
||||
std::cout << "Global best individual has fitness " << msjob10.best_individuals().best_element().fitness() << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
220
test/eompi/t-mpi-parallelApply.cpp
Executable file
220
test/eompi/t-mpi-parallelApply.cpp
Executable file
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
(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.
|
||||
*
|
||||
* The table of integers has to be serialized before it's sent. The wrapper object SerializableBase allows to serialize
|
||||
* any type and manipulate it like this type: SerializableBase<int> can be exactly be used as an integer.
|
||||
*
|
||||
* 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 <paradiseo/eompi.h>
|
||||
# include <paradiseo/eompi/eoParallelApply.h>
|
||||
# include <paradiseo/eompi/eoTerminateJob.h>
|
||||
|
||||
# include "t-mpi-common.h"
|
||||
|
||||
# include <iostream>
|
||||
# include <cstdlib>
|
||||
|
||||
# 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< SerializableBase<int>&, void >
|
||||
{
|
||||
void operator() ( SerializableBase<int> & x )
|
||||
{
|
||||
++x; // implicit conversion of SerializableBase<int> in the integer it contains
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* 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< SerializableBase<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< SerializableBase<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< SerializableBase<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< SerializableBase<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(unsigned 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;
|
||||
}
|
||||
|
||||
133
test/eompi/t-mpi-wrapper.cpp
Executable file
133
test/eompi/t-mpi-wrapper.cpp
Executable file
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
(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 <paradiseo/eompi.h>
|
||||
# include <paradiseo/eompi/eoParallelApply.h>
|
||||
# include <paradiseo/eompi/eoTerminateJob.h>
|
||||
|
||||
# include "t-mpi-common.h"
|
||||
|
||||
# include <iostream>
|
||||
# include <cstdlib>
|
||||
|
||||
# include <vector>
|
||||
using namespace std;
|
||||
|
||||
using namespace eo::mpi;
|
||||
|
||||
// Job functor.
|
||||
struct plusOne : public eoUF< SerializableBase<int>&, void >
|
||||
{
|
||||
void operator() ( SerializableBase<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< SerializableBase<int> > v;
|
||||
for( int i = 0; i < 1000; ++i )
|
||||
{
|
||||
v.push_back( rand() );
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
vector< SerializableBase<int> > originalV = v;
|
||||
|
||||
plusOne plusOneInstance;
|
||||
|
||||
StaticAssignmentAlgorithm assign( v.size() );
|
||||
|
||||
ParallelApplyStore< SerializableBase<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.
|
||||
ShowWrappedResult< SerializableBase<int> > wrapper;
|
||||
store.wrapIsFinished( &wrapper );
|
||||
|
||||
ParallelApply< SerializableBase<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(unsigned 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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
104
test/eompi/template-job.cpp
Executable file
104
test/eompi/template-job.cpp
Executable file
|
|
@ -0,0 +1,104 @@
|
|||
# include <paradiseo/eompi.h>
|
||||
|
||||
using namespace eo::mpi;
|
||||
|
||||
/*
|
||||
* This file is a template for a new eo::mpi::Job. You have everything that should be necessary to implement a new
|
||||
* parallelized algorithm.
|
||||
*
|
||||
* Replace __TEMPLATE__ by the name of your algorithm (for instance: MultiStart, ParallelApply, etc.).
|
||||
*/
|
||||
|
||||
template< class EOT >
|
||||
struct __TEMPLATE__Data
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
template< class EOT >
|
||||
class SendTask__TEMPLATE__ : public SendTaskFunction< __TEMPLATE__Data< EOT > >
|
||||
{
|
||||
public:
|
||||
|
||||
using SendTaskFunction< __TEMPLATE__Data< EOT > >::_data;
|
||||
|
||||
void operator()( int wrkRank )
|
||||
{
|
||||
// TODO implement me
|
||||
}
|
||||
};
|
||||
|
||||
template< class EOT >
|
||||
class HandleResponse__TEMPLATE__ : public HandleResponseFunction< __TEMPLATE__Data< EOT > >
|
||||
{
|
||||
public:
|
||||
|
||||
using HandleResponseFunction< __TEMPLATE__Data< EOT > >::_data;
|
||||
|
||||
void operator()( int wrkRank )
|
||||
{
|
||||
// TODO implement me
|
||||
}
|
||||
};
|
||||
|
||||
template< class EOT >
|
||||
class ProcessTask__TEMPLATE__ : public ProcessTaskFunction< __TEMPLATE__Data< EOT > >
|
||||
{
|
||||
public:
|
||||
using ProcessTaskFunction< __TEMPLATE__Data<EOT> >::_data;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
// TODO implement me
|
||||
}
|
||||
};
|
||||
|
||||
template< class EOT >
|
||||
class IsFinished__TEMPLATE__ : public IsFinishedFunction< __TEMPLATE__Data< EOT > >
|
||||
{
|
||||
public:
|
||||
|
||||
using IsFinishedFunction< __TEMPLATE__Data< EOT > >::_data;
|
||||
|
||||
bool operator()()
|
||||
{
|
||||
// TODO implement me
|
||||
}
|
||||
};
|
||||
|
||||
template< class EOT >
|
||||
class __TEMPLATE__Store : public JobStore< __TEMPLATE__Data< EOT > >
|
||||
{
|
||||
public:
|
||||
|
||||
__TEMPLATE__Data<EOT>* data()
|
||||
{
|
||||
// TODO implement me
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template< class EOT >
|
||||
class __TEMPLATE__ : public MultiJob< __TEMPLATE__Data< EOT > >
|
||||
{
|
||||
public:
|
||||
|
||||
__TEMPLATE__( AssignmentAlgorithm & algo,
|
||||
int masterRank,
|
||||
__TEMPLATE__Store< EOT > & store ) :
|
||||
MultiJob< __TEMPLATE__Data< EOT > >( algo, masterRank, store )
|
||||
{
|
||||
// TODO implement me
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Node::init( argc, argv );
|
||||
|
||||
DynamicAssignmentAlgorithm assignmentAlgo;
|
||||
__TEMPLATE__Store<int> store;
|
||||
__TEMPLATE__<int> job( assignmentAlgo, DEFAULT_MASTER, store );
|
||||
}
|
||||
*/
|
||||
Loading…
Add table
Add a link
Reference in a new issue