diff --git a/eo/src/serial/Parser.cpp b/eo/src/serial/Parser.cpp index 258174c8..57b52a0d 100644 --- a/eo/src/serial/Parser.cpp +++ b/eo/src/serial/Parser.cpp @@ -118,12 +118,14 @@ Entity* Parser::parseRight(const std::string & str, size_t & pos) { Entity* value = 0; + ignoreChars( str, pos ); if ( str[ pos ] == '{' ) { // next one is an object DEBUG("We read an object.") Object* obj = new Object; pos += 1; + ignoreChars( str, pos ); while( pos < str.size() && str[ pos ] != '}' ) { parseLeft( str, pos, obj ); @@ -145,11 +147,13 @@ Entity* Parser::parseRight(const std::string & str, size_t & pos) DEBUG("We read an array") Array* array = new Array; pos += 1; + ignoreChars( str, pos ); while( pos < str.size() && str[ pos ] != ']' ) { Entity* child = parseRight( str, pos ); if ( child ) array->push_back( child ); + ignoreChars( str, pos ); } DEBUG("We've finished to read our array.") pos += 1; // we're on the ], go to the next char diff --git a/eo/src/utils/eoRNG.h b/eo/src/utils/eoRNG.h index 78223c35..ce648cab 100644 --- a/eo/src/utils/eoRNG.h +++ b/eo/src/utils/eoRNG.h @@ -245,6 +245,18 @@ public : double normal(double mean, double stdev) { return mean + normal(stdev); } + /** + * @brief Forgets the last cached value of normal(), so as to be able to perform some repeatable calls to normal(). + * + * As normal() stores a cached value for performance purposes, sequences of pseudo random numbers can't be repeated + * when reseeding, since the cached value can be yield before a number is generated. To avoid that, this method + * allows one to clean the cache and force to regenerate a new pseudo random number. + */ + void clearCache() + { + cached = false; + } + /** Random numbers using a negative exponential distribution @param mean Mean value of distribution diff --git a/eo/src/utils/eoTimer.h b/eo/src/utils/eoTimer.h index 4ff451b4..9f9371dd 100644 --- a/eo/src/utils/eoTimer.h +++ b/eo/src/utils/eoTimer.h @@ -22,7 +22,7 @@ Authors: # ifndef __EO_TIMER_H__ # define __EO_TIMER_H__ -# include // time() +# include // gettimeofday() # include // rusage() # include // std::vector @@ -61,7 +61,7 @@ class eoTimer */ void restart() { - wc_start = time(NULL); + gettimeofday( &wc_start, NULL ); getrusage( RUSAGE_SELF, &_start ); } @@ -138,7 +138,9 @@ class eoTimer */ double wallclock() { - return std::difftime( std::time(NULL) , wc_start ); + struct timeval wc_end; + gettimeofday( &wc_end, NULL ); + return ( wc_end.tv_sec - wc_start.tv_sec ) + ( wc_end.tv_usec - wc_start.tv_usec ) / 1000000.; } protected: @@ -149,7 +151,7 @@ class eoTimer // Remainder (in milliseconds) for system time. long int usremainder; // Structure used to measure wallclock time. - time_t wc_start; + struct timeval wc_start; }; /** @@ -203,6 +205,14 @@ class eoTimerStat { public: + /** + * @brief Initializes a timer stat object. + */ + eoTimerStat() : _forceDoMeasure(false) + { + // empty + } + /** * @brief Statistic related to a key (name). * @@ -272,6 +282,14 @@ class eoTimerStat } # endif + /** + * @brief Forces the measures to be retrieved. + */ + void forceDoMeasure() + { + _forceDoMeasure = true; + } + /** * @brief Starts a new measure for the given key. * @@ -282,7 +300,7 @@ class eoTimerStat */ void start( const std::string & key ) { - if( eo::parallel.doMeasure() ) + if( eo::parallel.doMeasure() or _forceDoMeasure ) { _timers[ key ].restart(); } @@ -300,7 +318,7 @@ class eoTimerStat */ void stop( const std::string& key ) { - if( eo::parallel.doMeasure() ) + if( eo::parallel.doMeasure() or _forceDoMeasure ) { Stat & s = _stats[ key ]; eoTimer & t = _timers[ key ]; @@ -318,11 +336,21 @@ class eoTimerStat return _stats; } + /** + * @brief Empties the statistics map. + */ + void clear() + { + _stats.clear(); + } + 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; + // boolean to force the retrieval of statistics + bool _forceDoMeasure; }; # endif // __TIMER_H__ diff --git a/eo/test/mpi/DISTRIB_XP_README.md b/eo/test/mpi/DISTRIB_XP_README.md new file mode 100644 index 00000000..22b56b25 --- /dev/null +++ b/eo/test/mpi/DISTRIB_XP_README.md @@ -0,0 +1,21 @@ +README +------ + +To launch a set of experiments with t-mpi-distrib-exp: + +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. + diff --git a/eo/test/mpi/experiments.json b/eo/test/mpi/experiments.json new file mode 100644 index 00000000..c8dfde46 --- /dev/null +++ b/eo/test/mpi/experiments.json @@ -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":""} + {"size":"10", "packet_size":"1", "seed":"1337", "distribution":{"name":"normal", "mean":"100", "stddev":"20"}, "worker_print_waiting_time":"1", "filename":"exp2.result.txt"} + ] +} diff --git a/eo/test/mpi/gen-xp.py b/eo/test/mpi/gen-xp.py new file mode 100644 index 00000000..274e0243 --- /dev/null +++ b/eo/test/mpi/gen-xp.py @@ -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 +""" +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() + diff --git a/eo/test/mpi/t-mpi-distrib-exp.cpp b/eo/test/mpi/t-mpi-distrib-exp.cpp index 4fad9d44..7d081b56 100644 --- a/eo/test/mpi/t-mpi-distrib-exp.cpp +++ b/eo/test/mpi/t-mpi-distrib-exp.cpp @@ -1,29 +1,80 @@ +/* + 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 + */ + +/** + * @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 // usleep # include +# include # include +# include # include # include # include -# include "../test/mpi/t-mpi-common.h" +# include "t-mpi-common.h" using namespace eo::mpi; +// Serializable int typedef SerializableBase 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 ) { - std::cout << "Sleeping for " << milliseconds << "ms..." << std::endl; + if( _print ) + std::cout << "Sleeping for " << milliseconds << "ms..." << std::endl; // usleep takes an input in microseconds usleep( milliseconds * 1000 ); } -} wait; -class Distribution : public std::vector< type > + private: + bool _print; +}; + +/** + * @brief Represents a distribution of processing times. + */ +class Distribution : public std::vector< type >, public eoserial::Persistent { public: @@ -34,7 +85,9 @@ class Distribution : public std::vector< type > { for( unsigned i = 0; i < size; ++i ) { - push_back( next_element() ); + int next = next_element(); + if( next < 0 ) next = 0; + push_back( next ); } } @@ -42,13 +95,15 @@ class Distribution : public std::vector< type > * @brief Returns the next element of the distribution to put in the * vector. * - * @returns Number of milliseconds to wait + * @returns Number of milliseconds to wait. Can be negative ; in this case, + * the number will be truncated to 0ms. */ virtual int next_element() = 0; - // Idea for function name: enlarge_your_parser /** * @brief Creates params and retrieves values from parser + * + * Parser's params should take milliseconds as inputs. */ virtual void make_parser( eoParser & parser ) = 0; @@ -56,21 +111,39 @@ class Distribution : public std::vector< type > * @brief Returns true if this distribution has been activated by the * command line. * - * Serves to main program to check if at least one distribution has been + * 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() : _rng(0) + UniformDistribution() { // empty } @@ -78,57 +151,405 @@ class UniformDistribution : public Distribution 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", '\0', "Uniform").value(); - _max = parser.createParam( 1.0, "uniform-max", "Maximum for 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( 1000. * _rng.uniform( _min, _max ) ); + 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: - eoRng _rng; - 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( obj->find("distribution")->second ); + std::string distribName = *static_cast( 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 ); + + 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(); + 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 ); - // TODO 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 - Distribution & distrib = uniformDistribution; - // Make parser of distribution here - distrib.make_parser( parser ); + // 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(); + 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(); - make_parallel( parser ); - make_help( parser ); + 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(); - ParallelApplyStore< type> store( wait, DEFAULT_MASTER, packet_size ); - - // Fill distribution - distrib.fill( size ); - store.data( distrib ); - - DynamicAssignmentAlgorithm scheduling; - ParallelApply< type > job( scheduling, DEFAULT_MASTER, store ); - - job.run(); - - if( job.isMaster() ) + if( !useExperimentFile ) { - EmptyJob( scheduling, DEFAULT_MASTER ); // to terminate parallel apply + std::vector 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;