From 0bd33fe6921edba91d6fd98e3521653695e12e29 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 15 Jun 2012 10:56:59 +0200 Subject: [PATCH 01/76] Serialization of EO user objects. --- eo/src/CMakeLists.txt | 1 + eo/src/eo | 3 + eo/src/serial/Array.cpp | 38 ++++++++ eo/src/serial/Array.h | 149 +++++++++++++++++++++++++++++++ eo/src/serial/CMakeLists.txt | 35 ++++++++ eo/src/serial/Entity.h | 34 +++++++ eo/src/serial/Object.cpp | 40 +++++++++ eo/src/serial/Object.h | 67 ++++++++++++++ eo/src/serial/Parser.cpp | 153 ++++++++++++++++++++++++++++++++ eo/src/serial/Parser.h | 78 ++++++++++++++++ eo/src/serial/Serializable.h | 44 +++++++++ eo/src/serial/String.cpp | 11 +++ eo/src/serial/String.h | 79 +++++++++++++++++ eo/src/serial/Utils.h | 167 +++++++++++++++++++++++++++++++++++ eo/src/serial/eoSerial.h | 12 +++ eo/src/serial/json_example | 8 ++ 16 files changed, 919 insertions(+) create mode 100644 eo/src/serial/Array.cpp create mode 100644 eo/src/serial/Array.h create mode 100644 eo/src/serial/CMakeLists.txt create mode 100644 eo/src/serial/Entity.h create mode 100644 eo/src/serial/Object.cpp create mode 100644 eo/src/serial/Object.h create mode 100644 eo/src/serial/Parser.cpp create mode 100644 eo/src/serial/Parser.h create mode 100644 eo/src/serial/Serializable.h create mode 100644 eo/src/serial/String.cpp create mode 100644 eo/src/serial/String.h create mode 100644 eo/src/serial/Utils.h create mode 100644 eo/src/serial/eoSerial.h create mode 100644 eo/src/serial/json_example diff --git a/eo/src/CMakeLists.txt b/eo/src/CMakeLists.txt index c03bc509a..b6a8c2308 100644 --- a/eo/src/CMakeLists.txt +++ b/eo/src/CMakeLists.txt @@ -43,6 +43,7 @@ ADD_SUBDIRECTORY(ga) ADD_SUBDIRECTORY(gp) ADD_SUBDIRECTORY(other) ADD_SUBDIRECTORY(utils) +ADD_SUBDIRECTORY(serial) IF(ENABLE_PYEO) ADD_SUBDIRECTORY(pyeo) diff --git a/eo/src/eo b/eo/src/eo index 8cebd23a0..23df6ee5f 100644 --- a/eo/src/eo +++ b/eo/src/eo @@ -141,6 +141,9 @@ #include // includes eoRealBounds.h #include // no eoIntVectorBounds +// Serialization stuff +#include + // aliens #include #include diff --git a/eo/src/serial/Array.cpp b/eo/src/serial/Array.cpp new file mode 100644 index 000000000..f70b00e83 --- /dev/null +++ b/eo/src/serial/Array.cpp @@ -0,0 +1,38 @@ +# include "Array.h" + +namespace eoserial +{ + +std::ostream& Array::print( std::ostream& out ) const +{ + out << "["; + bool first = true; + for (ArrayChildren::const_iterator it = begin(), + end = this->end(); + it != end; + ++it) + { + if ( first ) + { + first = false; + } else { + out << ", "; + } + (*it)->print( out ); + } + out << "]\n"; + return out; +} + +Array::~Array() +{ + for (ArrayChildren::iterator it = begin(), + end = this->end(); + it != end; + ++it) + { + delete *it; + } +} + +} // namespace eoserial diff --git a/eo/src/serial/Array.h b/eo/src/serial/Array.h new file mode 100644 index 000000000..69231980c --- /dev/null +++ b/eo/src/serial/Array.h @@ -0,0 +1,149 @@ +# ifndef __EOSERIAL_ARRAY_H__ +# define __EOSERIAL_ARRAY_H__ + +# include +# include + +# include "Entity.h" +# include "Serializable.h" + +# include "Object.h" +# include "String.h" + +namespace eoserial +{ + +// Forward declaration for below declarations. +class Array; + +/* + * Declarations of functions present in Utils.h + * These are put here to avoid instead of including the file Utils.h, which would + * cause a circular inclusion. + */ +template< class T > +void unpack( const Array & array, unsigned int index, T & value ); + +void unpackObject( const Array & array, unsigned int index, Persistent & value ); + +template< class Container, template class UnpackAlgorithm > +void unpackArray( const Array & array, unsigned int index, Container & container ); + +/** + * @brief Represents a JSON array. + * + * Wrapper for an array, so as to be used as a JSON object. + */ +class Array : public eoserial::Entity, public std::vector< eoserial::Entity* > +{ +protected: + typedef std::vector< eoserial::Entity* > ArrayChildren; + +public: + /** + * @brief Adds the serializable object as a JSON object. + * @param obj Object which implemnets JsonSerializable. + */ + void push_back( const eoserial::Printable* obj ) + { + ArrayChildren::push_back( obj->pack() ); + } + + /** + * @brief Proxy for vector::push_back. + */ + void push_back( eoserial::Entity* json ) + { + ArrayChildren::push_back( json ); + } + + /** + * @brief Prints the JSON array into the given stream. + * @param out The stream + */ + virtual std::ostream& print( std::ostream& out ) const; + + /** + * @brief Dtor + */ + ~Array(); + + /* + * The following parts allows the user to automatically deserialize an eoserial::Array into a + * standard container, by giving the algorithm which will be used to deserialize contained entities. + */ + + /** + * @brief Functor which determines how to retrieve the real value contained in a eoserial::Entity at + * a given place. + * + * It will be applied for each contained variable in the array. + */ + template + struct BaseAlgorithm + { + /** + * @brief Main operator. + * + * @param array The eoserial::Array from which we're reading. + * @param i The index of the contained value. + * @param container The standard (STL) container in which we'll push back the read value. + */ + virtual void operator()( const eoserial::Array& array, unsigned int i, Container & container ) const = 0; + }; + + /** + * @brief BaseAlgorithm for retrieving primitive variables. + * + * This one should be used to retrieve primitive (and types which implement operator>>) variables, for instance + * int, double, std::string, etc... + */ + template + struct UnpackAlgorithm : public BaseAlgorithm + { + void operator()( const eoserial::Array& array, unsigned int i, C & container ) const + { + typename C::value_type t; + unpack( array, i, t ); + container.push_back( t ); + } + }; + + /** + * @brief BaseAlgorithm for retrieving eoserial::Persistent objects. + * + * This one should be used to retrieve objects which implement eoserial::Persistent. + */ + template + struct UnpackObjectAlgorithm : public BaseAlgorithm + { + void operator()( const eoserial::Array& array, unsigned int i, C & container ) const + { + typename C::value_type t; + unpackObject( array, i, t ); + container.push_back( t ); + } + }; + + /** + * @brief General algorithm for array deserialization. + * + * Applies the BaseAlgorithm to each contained variable in the eoserial::Array. + */ + template class UnpackAlgorithm> + inline void deserialize( Container & array ) + { + UnpackAlgorithm< Container > algo; + for( unsigned int i = 0, size = this->size(); + i < size; + ++i) + { + algo( *this, i, array ); + } + } +}; + +} // namespace eoserial + +# endif // __EOSERIAL_ARRAY_H__ + diff --git a/eo/src/serial/CMakeLists.txt b/eo/src/serial/CMakeLists.txt new file mode 100644 index 000000000..5358b30a4 --- /dev/null +++ b/eo/src/serial/CMakeLists.txt @@ -0,0 +1,35 @@ +###################################################################################### +### 1) Include the sources +###################################################################################### + +INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +###################################################################################### +### 2) Define the eoserial target +###################################################################################### + +SET(EOSERIAL_LIB_OUTPUT_PATH ${EO_BINARY_DIR}/lib) +SET(LIBRARY_OUTPUT_PATH ${EOSERIAL_LIB_OUTPUT_PATH}) + +SET(EOSERIAL_SOURCES + Array.cpp + Object.cpp + Parser.cpp + String.cpp + ) + +ADD_LIBRARY(eoserial STATIC ${EOSERIAL_SOURCES}) +INSTALL(TARGETS eoserial ARCHIVE DESTINATION lib COMPONENT libraries) + +FILE(GLOB HDRS *.h) +INSTALL(FILES ${HDRS} DESTINATION include/eo/serial COMPONENT headers) + +###################################################################################### +### 3) Optionnal +###################################################################################### + +SET(EOSERIAL_VERSION ${GLOBAL_VERSION}) +SET_TARGET_PROPERTIES(eoserial PROPERTIES VERSION "${EOSERIAL_VERSION}") + +###################################################################################### diff --git a/eo/src/serial/Entity.h b/eo/src/serial/Entity.h new file mode 100644 index 000000000..df10002df --- /dev/null +++ b/eo/src/serial/Entity.h @@ -0,0 +1,34 @@ +# ifndef __EOSERIAL_ENTITY_H__ +# define __EOSERIAL_ENTITY_H__ + +# include +# include + +namespace eoserial +{ + +/** + * @brief JSON entity + * + * This class represents a JSON entity, which can be JSON objects, + * strings or arrays. It is the base class for the JSON hierarchy. + */ +class Entity +{ +public: + + /** + * Virtual dtor (base class). + */ + virtual ~Entity() { /* empty */ } + + /** + * @brief Prints the content of a JSON object into a stream. + * @param out The stream in which we're printing. + */ + virtual std::ostream& print( std::ostream& out ) const = 0; +}; + +} // namespace eoserial + +# endif // __ENTITY_H__ diff --git a/eo/src/serial/Object.cpp b/eo/src/serial/Object.cpp new file mode 100644 index 000000000..3e557a126 --- /dev/null +++ b/eo/src/serial/Object.cpp @@ -0,0 +1,40 @@ +# include "Object.h" + +using namespace eoserial; + +namespace eoserial +{ + +std::ostream& Object::print( std::ostream& out ) const +{ + out << '{'; + bool first = true; + for(JsonValues::const_iterator it = begin(), end = this->end(); + it != end; + ++it) + { + if ( first ) + { + first = false; + } else { + out << ", "; + } + + out << '"' << it->first << "\":"; // key + it->second->print( out ); // value + } + out << "}\n"; + return out; +} + +Object::~Object() +{ + for(JsonValues::iterator it = begin(), end = this->end(); + it != end; + ++it) + { + delete it->second; + } +} + +} // namespace eoserial diff --git a/eo/src/serial/Object.h b/eo/src/serial/Object.h new file mode 100644 index 000000000..367692527 --- /dev/null +++ b/eo/src/serial/Object.h @@ -0,0 +1,67 @@ +# ifndef __EOSERIAL_OBJECT_H__ +# define __EOSERIAL_OBJECT_H__ + +# include +# include +# include + +# include "Entity.h" +# include "Serializable.h" + +namespace eoserial +{ + +/** + * @brief JSON Object + * + * This class represents a JSON object, which is basically a dictionnary + * of keys (strings) and values (JSON entities). + */ +class Object : public eoserial::Entity, public std::map< std::string, eoserial::Entity* > +{ +public: + typedef std::map JsonValues; + + /** + * @brief Adds a pair into the JSON object. + * @param key The key associated with the eoserial object + * @param eoserial The JSON object as created with framework. + */ + void add( const std::string& key, eoserial::Entity* json ) + { + (*this)[ key ] = json; + } + + /** + * @brief Adds a pair into the JSON object. + * @param key The key associated with the eoserial object + * @param obj A JSON-serializable object + */ + void add( const std::string& key, const eoserial::Printable* obj ) + { + (*this)[ key ] = obj->pack(); + } + + /** + * @brief Deserializes a Serializable class instance from this JSON object. + * @param obj The object we want to rebuild. + */ + void deserialize( eoserial::Persistent & obj ) + { + obj.unpack( this ); + } + + /** + * @brief Dtor + */ + ~Object(); + + /** + * @brief Prints the content of a JSON object into a stream. + */ + virtual std::ostream& print( std::ostream& out ) const; +}; + +} // namespace eoserial +# endif // __EOSERIAL_OBJECT_H__ + diff --git a/eo/src/serial/Parser.cpp b/eo/src/serial/Parser.cpp new file mode 100644 index 000000000..c7822d292 --- /dev/null +++ b/eo/src/serial/Parser.cpp @@ -0,0 +1,153 @@ +# include +# include +# include +# include + +# include "Parser.h" + +# include "Array.h" +# include "Object.h" +# include "String.h" + +// in debug mode only +// # define DEBUG(x) std::cout << x << std::endl; +# define DEBUG(x) + +using namespace eoserial; + +namespace eoserial +{ + +/** + * @brief Parses a string contained between double quotes. + * + * Strings can contain escaped double quotes. + * @param str The string we're parsing. + * @param pos The index of current position in parsed string. + * This index will be updated so as to allow the parser to + * continue. + */ +static std::string parseString(const std::string& str, size_t & pos) +{ + // example : "hello" + // example 2 : "\"world\"" + // for hello: + // firstQuote == 0, secondQuote == 6 + // sub string should be from firstQuote+1 to secondQuote-1 + // so its size should be (secondQuote-1 -(firstQuote+1) + 1) + std::string value; + size_t firstQuote = str.find( '"', pos ); + size_t secondQuote; + + /* instead of just seeking the second quote, we need to ensure + // that there is no escaped quote before this one. + // actually this is harder than that. Using backslashes + // to escape double quotes mean that backslashes have to be + // escaped to. + // example : "text\\" to symbolize : text\ + // example : "text\\\" to symbolize : text\" + // In fact, we should find if number of backslashes is odd; in this case, + // the double quotes are escaped and we should find the next one. + */ + int backslashesCount; + do { + ++pos; + secondQuote = str.find( '"', pos ); + size_t i = secondQuote - 1; + + // Find the backslashes + backslashesCount = 0; + while ( str[ i ] == '\\' ) + { + --i; + ++backslashesCount; + } + pos = secondQuote; + } while( backslashesCount % 2 == 1 ); + + value = str.substr( firstQuote+1, secondQuote-firstQuote-1 ); + pos = secondQuote + 1; + return value; +} + +/** + * @brief Moves the given index pos to the next character which is + * neither a coma, a space nor a new line. + * + * @param str The string in which we want to ignores those characters. + * @param pos The index of current position in parsed string. + */ +static void ignoreChars(const std::string& str, size_t & pos) +{ + // ignore white spaces and comas + for (char current = str[ pos ]; + current == ',' || current == ' ' || current == '\n'; + current = str[ ++pos ]); +} + +String* Parser::parseJsonString(const std::string & str, size_t & pos) +{ + return new String( parseString( str, pos ) ); +} + +Object* Parser::parse(const std::string & str) +{ + size_t initial(0); // we begin at position 0 + return static_cast( parseRight(str, initial) ); +} + +Entity* Parser::parseRight(const std::string & str, size_t & pos) +{ + Entity* value = 0; + + if ( str[ pos ] == '{' ) + { + // next one is an object + DEBUG("We read an object.") + Object* obj = new Object; + pos += 1; + while( pos < str.size() && str[ pos ] != '}' ) + { + parseLeft( str, pos, obj ); + ignoreChars( str, pos ); + } + DEBUG("We just finished to read an object ! ") + pos += 1; // we're on the }, go to the next char + value = obj; + } + else if ( str[ pos ] == '"' ) + { + // next one is a string + DEBUG("We read a string") + value = parseJsonString( str, pos ); + } + else if ( str[ pos ] == '[' ) + { + // next one is an array + DEBUG("We read an array") + Array* array = new Array; + pos += 1; + while( pos < str.size() && str[ pos ] != ']' ) + { + Entity* child = parseRight( str, pos ); + if ( child ) + array->push_back( child ); + } + DEBUG("We've finished to read our array.") + pos += 1; // we're on the ], go to the next char + value = array; + } + ignoreChars( str, pos ); + return value; +} + +void Parser::parseLeft(const std::string & str, size_t & pos, Object* eoserial) +{ + std::string key = parseString(str, pos); + ++pos; // the colon + DEBUG("We've read the key ") + (*eoserial)[ key ] = parseRight( str, pos ); +} + +} // namespace eoserial + diff --git a/eo/src/serial/Parser.h b/eo/src/serial/Parser.h new file mode 100644 index 000000000..f0a94ee21 --- /dev/null +++ b/eo/src/serial/Parser.h @@ -0,0 +1,78 @@ +# ifndef __EOSERIAL_PARSER_H__ +# define __EOSERIAL_PARSER_H__ + +# include "Entity.h" +# include "String.h" +# include "Object.h" + +/** + * This file contains a tiny JSON parser used in DAE. This parser just handles + * a subset of JSON grammar, with the following restrictions : + * - all strings must be surrounded by double quotes. + * - everything which is not an object or an array is considered to be a string + * (even integers, booleans,...). + * - no syntax check is done. We trust the programmer and he has to ensure that + * every JSON string he produces is valid. + * + * @author Benjamin BOUVIER + */ + +namespace eoserial +{ + +/** + * @brief Parser from a JSON source. + * + * This parser does just retrieve values and does NOT check the structure of + * the input. This implies that if the input is not correct, the result is undefined + * and can result to a failure on execution. + */ +class Parser +{ + public: + + /** + * @brief Parses the given string and returns the JSON object read. + */ + static eoserial::Object* parse(const std::string & str); + + protected: + + /** + * @brief Parses the right part of a JSON object as a string. + * + * The right part of an object can be a string (for instance : + * "key":"value"), a JSON array (for instance: "key":["1"]) or + * another JSON object (for instance: "key":{"another_key":"value"}). + * + * The right parts are found after keys (which are parsed by parseLeft) + * and in arrays. + * + * @param str The string we're parsing. + * @param pos The index of the current position in the string. + * @return The JSON object matching the right part. + */ + static eoserial::Entity* parseRight(const std::string & str, size_t & pos); + + /** + * @brief Parses the left value of a key-value pair, which is the key. + * + * @param str The string we're parsing. + * @param pos The index of the current position in the string. + * @param eoserial The current JSON object for which we're adding a key-value pair. + */ + static void parseLeft(const std::string & str, size_t & pos, eoserial::Object* json); + + /** + * @brief Retrieves a string in a JSON content. + * + * @param str The string we're parsing. + * @param pos The index of the current position of parsing, + * which will be updated. + */ + static eoserial::String* parseJsonString(const std::string & str, size_t & pos); +}; + +} // namespace eoserial + +# endif // __EOSERIAL_PARSER_H__ diff --git a/eo/src/serial/Serializable.h b/eo/src/serial/Serializable.h new file mode 100644 index 000000000..482a918a8 --- /dev/null +++ b/eo/src/serial/Serializable.h @@ -0,0 +1,44 @@ +# ifndef __EOSERIAL_SERIALIZABLE_H__ +# define __EOSERIAL_SERIALIZABLE_H__ + +# include + +namespace eoserial +{ + +class Object; // to avoid recursive inclusion with JsonObject + +/** + * @brief Interface showing that object can be written to a eoserial type + * (currently JSON). + */ +class Printable +{ +public: + /** + * @brief Serializes the object to JSON format. + * @return A JSON object created with new. + */ + virtual eoserial::Object* pack() const = 0; +}; + +/** + * @brief Interface showing that object can be eoserialized (written and read + * from an input). + * + * Note : Persistent objects should have a default non-arguments constructor. + */ +class Persistent : public Printable +{ + public: + /** + * @brief Loads class fields from a JSON object. + * @param json A JSON object. Programmer doesn't have to delete it, it + * is automatically done. + */ + virtual void unpack(const eoserial::Object* json) = 0; +}; + +} // namespace eoserial + +# endif // __EOSERIAL_SERIALIZABLE_H__ diff --git a/eo/src/serial/String.cpp b/eo/src/serial/String.cpp new file mode 100644 index 000000000..deba05a06 --- /dev/null +++ b/eo/src/serial/String.cpp @@ -0,0 +1,11 @@ +# include "String.h" + +namespace eoserial +{ + std::ostream& String::print( std::ostream& out ) const + { + out << '"' << *this << '"'; + return out; + } +} // namespace eoserial + diff --git a/eo/src/serial/String.h b/eo/src/serial/String.h new file mode 100644 index 000000000..43a2cffa8 --- /dev/null +++ b/eo/src/serial/String.h @@ -0,0 +1,79 @@ +# ifndef __EOSERIAL_STRING_H__ +# define __EOSERIAL_STRING_H__ + +# include +# include + +# include "Entity.h" + +namespace eoserial +{ + +/** + * @brief JSON String + * + * Wrapper for string, so as to be used as a JSON object. + */ +class String : public eoserial::Entity, public std::string +{ + public: + + /** + * @brief Default ctor. + * @param str The string we want to wrap. + */ + String( const std::string& str ) : std::string( str ) {} + + /** + * @brief Ctor used only on parsing. + */ + String( ) {} + + /** + * @brief Prints out the string. + */ + virtual std::ostream& print( std::ostream& out ) const; + + /** + * @brief Deserializes the current String into a given primitive type value. + * @param value The value in which we're writing. + */ + template + inline void deserialize( T & value ); + + protected: + // Copy and reaffectation are forbidden + explicit String( const String& _ ); + String& operator=( const String& _ ); +}; + +/** + * @brief Casts a eoserial::String into a primitive value, or in a type which at + * least overload operator>>. + * + * @param value A reference to the variable we're writing into. + * + * It's not necessary to specify the variable type, which can be infered by compiler when + * invoking. + */ +template +inline void String::deserialize( T & value ) +{ + std::stringstream ss; + ss << *this; + ss >> value; +} + +/** + * @brief Specialization for strings, which don't need to be converted through + * a stringstream. + */ +template<> +inline void String::deserialize( std::string & value ) +{ + value = *this; +} + +} // namespace eoserial + +# endif // __EOSERIAL_STRING_H__ diff --git a/eo/src/serial/Utils.h b/eo/src/serial/Utils.h new file mode 100644 index 000000000..70fd2f75a --- /dev/null +++ b/eo/src/serial/Utils.h @@ -0,0 +1,167 @@ +# ifndef __EOSERIAL_UTILS_H__ +# define __EOSERIAL_UTILS_H__ + +# include "Array.h" +# include "Object.h" +# include "String.h" + +namespace eoserial +{ + /***************************** + * DESERIALIZATION FUNCTIONS * + ***************************** + These functions are useful for casting eoserial::objects into simple, primitive + variables or into class instance which implement eoserial::Persistent. + + The model is always quite the same : + - the first argument is the containing object (which is a eoserial::Entity, + an object or an array) + - the second argument is the key or index, + - the last argument is the value in which we're writing. + */ + + template< class T > + inline void unpack( const Object & obj, const std::string & key, T & value ) + { + static_cast( obj.find( key )->second )->deserialize( value ); + } + + inline void unpackObject( const Object & obj, const std::string & key, Persistent & value ) + { + static_cast( obj.find( key )->second )->deserialize( value ); + } + + template< class Container, template class UnpackAlgorithm > + inline void unpackArray( const Object & obj, const std::string & key, Container & array ) + { + static_cast( obj.find( key )->second )->deserialize< Container, UnpackAlgorithm >( array ); + } + + template< class T > + inline void unpack( const Array & array, unsigned int index, T & value ) + { + static_cast( array[ index ] )->deserialize( value ); + } + + inline void unpackObject( const Array & array, unsigned int index, Persistent & value ) + { + static_cast( array[ index ] )->deserialize( value ); + } + + template< class Container, template class UnpackAlgorithm > + inline void unpackArray( const Array & array, unsigned int index, Container & container ) + { + static_cast( array[ index ] )->deserialize< Container, UnpackAlgorithm >( container ); + } + + /******************************* + *** SERIALIZATION FUNCTIONS *** + ******************************* + These functions are useful for casting classic objects and + eoserial::Persistent objects into eoserial entities which + can be manipulated by the framework. + */ + + /** + * @brief Casts a value of a stream-serializable type (i.e, which implements + * operator <<) into a JsonString. + * + * This is used when serializing the objects : all primitives types should be + * converted into strings to get more easily manipulated. + * + * @param value The value we're converting. + * @return JsonString wrapper for the value. + */ + template + String* make( const T & value ) + { + std::stringstream ss; + ss << value; + return new String( ss.str() ); + } + + /** + * @brief Specialization for strings : no need to convert as they're still + * usable as strings. + */ + template<> + inline String* make( const std::string & value ) + { + return new String( value ); + } + + /* + * These functions are useful for automatically serializing STL containers into + * eoserial arrays which could be used by the framework. + **/ + + /** + * @brief Functor which explains how to push the value into the eoserial::Array. + */ + template< class T > + struct PushAlgorithm + { + /** + * @brief Main operator. + * + * @param array The eoserial::array in which we're writing. + * @param value The variable we are writing. + */ + virtual void operator()( Array & array, const T & value ) = 0; + }; + + /** + * @brief Push algorithm for primitive variables. + * + * This one should be used when inserting primitive (and types which implement + * operator<<) variables. + */ + template< class T > + struct MakeAlgorithm : public PushAlgorithm + { + void operator()( Array & array, const T & value ) + { + array.push_back( make( value ) ); + } + }; + + /** + * @brief Push algorithm for eoserial::Persistent variables. + */ + template< class T > + struct SerializablePushAlgorithm : public PushAlgorithm + { + void operator()( Array & array, const T & obj ) + { + // obj address is not saved into array.push_back. + array.push_back( &obj ); + } + }; + + /** + * @brief Casts a STL container (vector or list, for instance) + * into a eoserial::Array. + * + * @þaram PushAlgorithm The algorithm used for inserting new element in the eoserial::Array. + * This algorithm is directly called, so it is its own charge to invoke push_back on the + * eoserial::Array. + */ + template< class Container, template class PushAlgorithm > + Array* makeArray( const Container & array ) + { + Array* returned_array = new Array; + typedef typename Container::const_iterator iterator; + typedef typename Container::value_type Type; + PushAlgorithm< Type > algo; + for ( + iterator it = array.begin(), end = array.end(); + it != end; + ++it) + { + algo( *returned_array, *it ); + } + return returned_array; + } +} // namespace eoserial + +# endif //__EOSERIAL_UTILS_H__ diff --git a/eo/src/serial/eoSerial.h b/eo/src/serial/eoSerial.h new file mode 100644 index 000000000..a10f6c014 --- /dev/null +++ b/eo/src/serial/eoSerial.h @@ -0,0 +1,12 @@ +# ifndef __EOSERIAL_HEADERS__ +# define __EOSERIAL_HEADERS__ + +# include "Object.h" +# include "Serializable.h" +# include "Array.h" +# include "Object.h" +# include "String.h" +# include "Parser.h" +# include "Utils.h" + +# endif // __EOSERIAL_HEADERS__ diff --git a/eo/src/serial/json_example b/eo/src/serial/json_example new file mode 100644 index 000000000..7ecb3edda --- /dev/null +++ b/eo/src/serial/json_example @@ -0,0 +1,8 @@ +{"a":"b", +"obj": + {"obj_a":"obj_}b","subobj_a": + {"subk":"subv"} + }, +"c":"d", +"array":["1","2",{"\"array\"_obj\"":"array_ov]"}, ["3"], "4"] +} From abc18a653df6812102fa8ffaf84dfb4bc3c50fbc Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 15 Jun 2012 11:01:28 +0200 Subject: [PATCH 02/76] Added a gitignore for compiled files --- eo/.gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 eo/.gitignore diff --git a/eo/.gitignore b/eo/.gitignore new file mode 100644 index 000000000..977910719 --- /dev/null +++ b/eo/.gitignore @@ -0,0 +1,3 @@ +*.swp +debug/ +release/ From 2851cad471681833eb0d8ca392fe3e2fa5b9d4be Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 15 Jun 2012 17:07:29 +0200 Subject: [PATCH 03/76] Configure eo-conf.cmake to enable or disable MPI use. --- eo/eo-conf.cmake | 7 +++++++ eo/src/CMakeLists.txt | 29 ++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/eo/eo-conf.cmake b/eo/eo-conf.cmake index e8f285cbd..2c115cfef 100644 --- a/eo/eo-conf.cmake +++ b/eo/eo-conf.cmake @@ -5,3 +5,10 @@ SET(PROJECT_VERSION_MINOR 3) SET(PROJECT_VERSION_PATCH 0) SET(PROJECT_VERSION_MISC "-edge") +# If you plan to use MPI, precise here where are the static libraries from +# openmpi and boost::mpi. + +SET(WITH_MPI FALSE CACHE BOOL "Use mpi ?" FORCE) +SET(MPI_DIR "put root directory of openmpi here" CACHE PATH "OpenMPI directory" FORCE) +SET(BOOST_DIR "put root directory of boost here" CACHE PATH "Boost directory" FORCE) + diff --git a/eo/src/CMakeLists.txt b/eo/src/CMakeLists.txt index b6a8c2308..f674da4ec 100644 --- a/eo/src/CMakeLists.txt +++ b/eo/src/CMakeLists.txt @@ -5,7 +5,30 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) ###################################################################################### -### 2) Define the eo target +### 2) Optionnal: add MPI and Boost MPI dependencies. +###################################################################################### + +IF(WITH_MPI) + MESSAGE("[EO] Compilation with MPI and BoostMPI.") + + SET(CMAKE_CXX_COMPILER "${MPI_DIR}/bin/mpicxx") + + # headers location + INCLUDE_DIRECTORIES(${MPI_DIR}/include) + INCLUDE_DIRECTORIES(${BOOST_DIR}/include) + + # lib location + LINK_DIRECTORIES(${MPI_DIR}/lib) + LINK_DIRECTORIES(${BOOST_DIR}/lib) + + # for conditional compilation in code + ADD_DEFINITIONS(-DWITH_MPI) + + LINK_LIBRARIES(boost_mpi boost_serialization) +ENDIF() + +###################################################################################### +### 3) Define the eo target ###################################################################################### SET(EO_LIB_OUTPUT_PATH ${EO_BINARY_DIR}/lib) @@ -27,14 +50,14 @@ FILE(GLOB HDRS *.h eo) INSTALL(FILES ${HDRS} DESTINATION include/eo COMPONENT headers) ###################################################################################### -### 3) Optionnal: define your target(s)'s version: no effect for windows +### 4) Optionnal: define your target(s)'s version: no effect for windows ###################################################################################### SET(EO_VERSION ${GLOBAL_VERSION}) SET_TARGET_PROPERTIES(eo PROPERTIES VERSION "${EO_VERSION}") ###################################################################################### -### 4) Where must cmake go now ? +### 5) Where must cmake go now ? ###################################################################################### ADD_SUBDIRECTORY(do) From bc7f662b59375e7a1a5bca1edd9efabd9e3a035f Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 18 Jun 2012 14:18:22 +0200 Subject: [PATCH 04/76] Added rules to ignore CMake generated directories and Makefile --- eo/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/eo/.gitignore b/eo/.gitignore index 977910719..2bfc9fc77 100644 --- a/eo/.gitignore +++ b/eo/.gitignore @@ -1,3 +1,5 @@ *.swp debug/ release/ +*CMakeFiles* +*Makefile From 04f972475cf2dffbb6c77b40efb5e60be0fb5f5d Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 18 Jun 2012 14:20:06 +0200 Subject: [PATCH 05/76] First version of generic MPI parallelization --- eo/src/eoParallelApply.h | 49 ++++++ eo/src/eompi.h | 289 +++++++++++++++++++++++++++++++ eo/test/t-eoMpiParallelApply.cpp | 59 +++++++ 3 files changed, 397 insertions(+) create mode 100644 eo/src/eoParallelApply.h create mode 100644 eo/src/eompi.h create mode 100644 eo/test/t-eoMpiParallelApply.cpp diff --git a/eo/src/eoParallelApply.h b/eo/src/eoParallelApply.h new file mode 100644 index 000000000..6a7c6eaad --- /dev/null +++ b/eo/src/eoParallelApply.h @@ -0,0 +1,49 @@ +# ifndef __EO_PARALLEL_APPLY_H__ +# define __EO_PARALLEL_APPLY_H__ + +# include "eompi.h" + +# include +# include + +template< typename EOT > +class ParallelApply : public MpiJob< EOT > +{ + public: + + ParallelApply( eoUF & _proc, std::vector& _pop ) : + MpiJob( _pop ), + func( _proc ) + { + // empty + } + + virtual void sendTask( int wrkRank, int index ) + { + MpiJob::comm.send( wrkRank, 1, MpiJob::data[ index ] ); + } + + virtual void handleResponse( int wrkRank, int index ) + { + MpiJob::comm.recv( wrkRank, 1, MpiJob::data[ index ] ); + } + + virtual void processTask( ) + { + EOT ind; + cout << "Receiving individual." << endl; + MpiJob::comm.recv( 0, 1, ind ); + cout << "Applying function." << endl; + func( ind ); + cout << "Sending result." << endl; + MpiJob::comm.send( 0, 1, ind ); + cout << "Leaving processTask" << endl; + } + + protected: + eoUF& func; +}; + +# endif // __EO_PARALLEL_APPLY_H__ + + diff --git a/eo/src/eompi.h b/eo/src/eompi.h new file mode 100644 index 000000000..82d1a585b --- /dev/null +++ b/eo/src/eompi.h @@ -0,0 +1,289 @@ +# ifndef __EO_MPI_H__ +# define __EO_MPI_H__ + +# include +# include + +# include +namespace mpi = boost::mpi; + +# include +using namespace std; +// TODO TODOB comment! + +namespace EoMpi +{ + namespace Channel + { + const int Commands = 0; + } + + namespace Message + { + const int Continue = 0; + const int Finish = 1; + } +} + +class MpiNode; + +class MpiNodeStore +{ + public: + + static void instance( MpiNode* _instance ) + { + singleton = _instance; + } + + static MpiNode* instance() + { + return singleton; + } + + protected: + + static MpiNode* singleton; +}; + +MpiNode* MpiNodeStore::singleton; + +class MpiNode +{ +protected: + mpi::environment& env; + mpi::communicator& _comm; + + int rank; + int size; + + int argc; + char** argv; + +public: + MpiNode( mpi::environment& _env, mpi::communicator& __comm ) : + env(_env), + _comm(__comm), + rank(__comm.rank()), + size(__comm.size()) + { + // empty + } + + virtual ~MpiNode() + { + // empty + } + + mpi::communicator& comm() + { + return _comm; + } +}; + +struct AssignmentAlgorithm +{ + virtual int get( ) = 0; + virtual void size( int s ) = 0; + virtual int size( ) = 0; + virtual void confirm( int wrkRank ) = 0; +}; + +struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm +{ + public: + virtual int get( ) + { + int assignee = -1; + if (! availableWrk.empty() ) + { + assignee = availableWrk.back(); + availableWrk.pop_back(); + } + return assignee; + } +; + void size( int s ) + { + for( int i = 1; i < s ; ++i ) + { + availableWrk.push_back( i ); + } + } + + int size() + { + return availableWrk.size(); + } + + void confirm( int rank ) + { + availableWrk.push_back( rank ); + } + + protected: + std::vector< int > availableWrk; +}; + +template< typename EOT > +class MpiJob +{ + public: + + MpiJob( std::vector< EOT > & _data ) : + data( _data ), + comm( MpiNodeStore::instance()->comm() ) + { + // empty + } + + // master + virtual void sendTask( int wrkRank, int index ) = 0; + virtual void handleResponse( int wrkRank, int index ) = 0; + // worker + virtual void processTask( ) = 0; + + void master( AssignmentAlgorithm & assignmentAlgorithm ) + { + for( int i = 0, size = data.size(); + i < size; + ++i) + { + cout << "Beginning loop for i = " << i << endl; + int assignee = assignmentAlgorithm.get( ); + cout << "Assignee : " << assignee << endl; + while( assignee <= 0 ) + { + cout << "Waitin' for node..." << endl; + mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); + int wrkRank = status.source(); + cout << "Node " << wrkRank << " just terminated." << endl; + handleResponse( wrkRank, assignedTasks[ wrkRank ] ); + assignmentAlgorithm.confirm( wrkRank ); + assignee = assignmentAlgorithm.get( ); + } + cout << "Assignee found : " << assignee << endl; + assignedTasks[ assignee ] = i; + comm.send( assignee, EoMpi::Channel::Commands, EoMpi::Message::Continue ); + sendTask( assignee, i ); + } + + // frees all the idle workers + int idle; + vector idles; + while ( ( idle = assignmentAlgorithm.get( ) ) > 0 ) + { + comm.send( idle, EoMpi::Channel::Commands, EoMpi::Message::Finish ); + idles.push_back( idle ); + } + for (int i = 0; i < idles.size(); ++i) + { + assignmentAlgorithm.confirm( idles[i] ); + } + + // wait for all responses + int wrkNb = comm.size() - 1; + while( assignmentAlgorithm.size() != wrkNb ) + { + mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); + int wrkRank = status.source(); + handleResponse( wrkRank, assignedTasks[ wrkRank ] ); + comm.send( wrkRank, EoMpi::Channel::Commands, EoMpi::Message::Finish ); + assignmentAlgorithm.confirm( wrkRank ); + } + } + + void worker( ) + { + int order; + while( true ) + { + comm.recv( 0, EoMpi::Channel::Commands, order ); + if ( order == EoMpi::Message::Finish ) + { + return; + } else + { + processTask( ); + } + } + } + +protected: + + std::vector & data; + std::map< int /* worker rank */, int /* index in vector */> assignedTasks; + + mpi::communicator& comm; +}; + +class MasterNode : public MpiNode +{ +public: + MasterNode( int _argc, char** _argv, + mpi::environment& _env, + mpi::communicator& _comm + ) : + MpiNode(_env, _comm ) + { + // empty + } + + void setAssignmentAlgorithm( AssignmentAlgorithm* assignmentAlgo ) + { + _assignmentAlgo = assignmentAlgo; + _assignmentAlgo->size( _comm.size() ); + } + + template< typename EOT > + void run( MpiJob< EOT > & job ) + { + job.master( *_assignmentAlgo ); + } + +protected: + AssignmentAlgorithm* _assignmentAlgo; +}; + +class WorkerNode : public MpiNode +{ + public: + + WorkerNode( + int _argc, char** _argv, + mpi::environment& _env, + mpi::communicator& _comm ) : + MpiNode( _env, _comm ) + { + // empty + } + + template< typename EOT > + void run( MpiJob & job ) + { + job.worker( ); + } +}; + +class MpiSingletonFactory +{ + public: + + static void init( int argc, char** argv ) + { + MpiNode* singleton; + //mpi::environment* env = new mpi::environment ( argc, argv ); + //mpi::communicator* world = new mpi::communicator; // TODO clean + static mpi::environment env( argc, argv ); + static mpi::communicator world; + if ( world.rank() == 0 ) + { + singleton = new MasterNode( argc, argv, env, world ); + } else + { + singleton = new WorkerNode( argc, argv, env, world ); + } + MpiNodeStore::instance( singleton ); + } +}; +# endif // __EO_MPI_H__ diff --git a/eo/test/t-eoMpiParallelApply.cpp b/eo/test/t-eoMpiParallelApply.cpp new file mode 100644 index 000000000..77583420e --- /dev/null +++ b/eo/test/t-eoMpiParallelApply.cpp @@ -0,0 +1,59 @@ +# include +# include +# include + +# include +using namespace std; + +struct plusOne : public eoUF< int&, void > +{ + void operator() ( int & x ) + { + ++x; + } +}; + +int main(int argc, char** argv) +{ + DynamicAssignmentAlgorithm algo; + cout << "Appel à init... " << endl; + MpiSingletonFactory::init( argc, argv ); + + cout << "Création des données... " << endl; + vector v; + + v.push_back(1); + v.push_back(3); + v.push_back(3); + v.push_back(7); + v.push_back(42); + + plusOne plusOneInstance; + + cout << "Création du job..." << endl; + ParallelApply job( plusOneInstance, v ); + + cout << "Création de l'instance..." << endl; + MpiNode* instance = MpiNodeStore::instance(); + if( dynamic_cast( instance ) != 0 ) + { + cout << "[Master] Algorithme d'assignation" << endl; + static_cast( instance )->setAssignmentAlgorithm( &algo ); + cout << "[Master] Lancement." << endl; + static_cast( instance )->run( job ); + + for (int i = 0; i < v.size(); ++i ) + { + cout << v[i] << endl; + } + } else if ( dynamic_cast( instance ) != 0 ) + { + cout << "[Worker] Lancement." << endl; + static_cast( instance )->run( job ); + } else + { + cout << "Nothing to be done;" << endl; + } + + return 0; +} From bd959cb3efa62e76b38aa3da84e4d8d107226e09 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 18 Jun 2012 15:22:47 +0200 Subject: [PATCH 06/76] First parallel evaluation with MPI. Look at t-eoMpiParallel.cpp --- eo/src/apply.h | 52 +++++++++++------ eo/test/t-eoMpiParallel.cpp | 111 ++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 16 deletions(-) create mode 100644 eo/test/t-eoMpiParallel.cpp diff --git a/eo/src/apply.h b/eo/src/apply.h index f685f8d3b..39a365057 100644 --- a/eo/src/apply.h +++ b/eo/src/apply.h @@ -33,6 +33,11 @@ #include #include +# ifdef WITH_MPI +# include +# include +# endif // WITH_MPI + /** Applies a unary function to a std::vector of things. @@ -48,33 +53,48 @@ void apply(eoUF& _proc, std::vector& _pop) double t1 = 0; if ( eo::parallel.enableResults() ) - { - t1 = omp_get_wtime(); - } + { + t1 = omp_get_wtime(); + } if (!eo::parallel.isDynamic()) - { + { #pragma omp parallel for if(eo::parallel.isEnabled()) //default(none) shared(_proc, _pop, size) - for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); } - } + for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); } + } else - { + { #pragma omp parallel for schedule(dynamic) if(eo::parallel.isEnabled()) - //doesnot work with gcc 4.1.2 - //default(none) shared(_proc, _pop, size) - for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); } - } + //doesnot work with gcc 4.1.2 + //default(none) shared(_proc, _pop, size) + for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); } + } if ( eo::parallel.enableResults() ) - { - double t2 = omp_get_wtime(); - eoLogger log; - log << eo::file(eo::parallel.prefix()) << t2 - t1 << ' '; - } + { + double t2 = omp_get_wtime(); + eoLogger log; + log << eo::file(eo::parallel.prefix()) << t2 - t1 << ' '; + } #else // _OPENMP +#ifdef WITH_MPI + ParallelApply job( _proc, _pop ); + MasterNode* master = dynamic_cast( MpiNodeStore::instance() ); + if ( master ) + { + DynamicAssignmentAlgorithm algo; + master->setAssignmentAlgorithm( &algo ); + master->run( job ); + } else + { + WorkerNode* wrk = dynamic_cast( MpiNodeStore::instance() ); + wrk->run( job ); + } +#else // !_MPI for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); } +#endif #endif // !_OPENMP } diff --git a/eo/test/t-eoMpiParallel.cpp b/eo/test/t-eoMpiParallel.cpp new file mode 100644 index 000000000..5529b8e2c --- /dev/null +++ b/eo/test/t-eoMpiParallel.cpp @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// t-eoMpiParallel.cpp +//----------------------------------------------------------------------------- + +#include +#include +//#include +#include "real_value.h" + +#include + +//----------------------------------------------------------------------------- + +class eoRealSerializable : public eoReal< eoMinimizingFitness >, public eoserial::Persistent +{ + public: + + eoRealSerializable(unsigned size = 0, double value = 0.0): + eoReal(size, value) {} + + eoserial::Object* pack() const + { + eoserial::Object* obj = new eoserial::Object; + obj->add( "array", + eoserial::makeArray< vector, eoserial::MakeAlgorithm > + ( *this ) + ); + return obj; + } + + void unpack( const eoserial::Object* obj ) + { + eoserial::unpackArray< vector, eoserial::Array::UnpackAlgorithm > + ( *obj, "array", *this ); + } + + // Gives access to boost serialization + friend class boost::serialization::access; + + /** + * Serializes the decomposition in a boost archive (useful for boost::mpi) + */ + template + void save( Archive & ar, const unsigned int version ) const + { + std::stringstream ss; + printOn( ss ); + std::string asStr = ss.str(); + ar & asStr; + + (void) version; // avoid compilation warning + } + + /** + * Deserializes the decomposition from a boost archive (useful for boost:mpi) + */ + template + void load( Archive & ar, const unsigned int version ) + { + std::string asStr; + ar & asStr; + std::stringstream ss; + ss << asStr; + readFrom( ss ); + + (void) version; // avoid compilation warning + } + + // Indicates that boost save and load operations are not the same. + BOOST_SERIALIZATION_SPLIT_MEMBER() + +}; + +typedef eoRealSerializable EOT; + +int main(int ac, char** av) +{ + MpiSingletonFactory::init( ac, av ); + + 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 ); + + eoPop< EOT > pop( popSize, init ); + + eo::log << "Size of population : " << popSize << std::endl; + + eoPopLoopEval< EOT > popEval( eval ); + popEval( pop, pop ); + + eo::log << eo::quiet << "DONE!" << std::endl; + + return 0; +} + +//----------------------------------------------------------------------------- From fed65c4f082bc508820a3f432c573d16684af78c Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 18 Jun 2012 17:02:41 +0200 Subject: [PATCH 07/76] Added eoParallelPopEvalFunc and allow compilation of full library. --- eo/src/CMakeLists.txt | 1 + eo/src/apply.h | 15 ++++++++++----- eo/src/eoPopEvalFunc.h | 20 ++++++++++++++++++++ eo/src/eompi.cpp | 4 ++++ eo/src/eompi.h | 2 -- 5 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 eo/src/eompi.cpp diff --git a/eo/src/CMakeLists.txt b/eo/src/CMakeLists.txt index f674da4ec..3d46be7ad 100644 --- a/eo/src/CMakeLists.txt +++ b/eo/src/CMakeLists.txt @@ -41,6 +41,7 @@ SET(EO_SOURCES eoCtrlCContinue.cpp eoScalarFitnessAssembled.cpp eoSIGContinue.cpp + eompi.cpp ) ADD_LIBRARY(eo STATIC ${EO_SOURCES}) diff --git a/eo/src/apply.h b/eo/src/apply.h index 39a365057..c01ac5740 100644 --- a/eo/src/apply.h +++ b/eo/src/apply.h @@ -79,8 +79,17 @@ void apply(eoUF& _proc, std::vector& _pop) #else // _OPENMP + for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); } + +#endif // !_OPENMP +} + #ifdef WITH_MPI +template +void parallelApply(eoUF& _proc, std::vector& _pop) +{ ParallelApply job( _proc, _pop ); + MasterNode* master = dynamic_cast( MpiNodeStore::instance() ); if ( master ) { @@ -92,12 +101,8 @@ void apply(eoUF& _proc, std::vector& _pop) WorkerNode* wrk = dynamic_cast( MpiNodeStore::instance() ); wrk->run( job ); } -#else // !_MPI - for (size_t i = 0; i < size; ++i) { _proc(_pop[i]); } -#endif - -#endif // !_OPENMP } +#endif /** This is a variant of apply which is called in parallel diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index aba74d9ab..cbd38b21c 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -77,6 +77,26 @@ private: eoEvalFunc & eval; }; +#ifdef WITH_MPI +// TODO TODOB commenter +template +class eoParallelPopLoopEval : public eoPopEvalFunc { +public: + /** Ctor: set value of embedded eoEvalFunc */ + eoParallelPopLoopEval(eoEvalFunc & _eval) : eval(_eval) {} + + /** Do the job: simple loop over the offspring */ + void operator()(eoPop & _parents, eoPop & _offspring) + { + (void)_parents; + parallelApply(eval, _offspring); + } + +private: + eoEvalFunc & eval; +}; +#endif + ///////////////////////////////////////////////////////////// // eoTimeVaryingLoopEval ///////////////////////////////////////////////////////////// diff --git a/eo/src/eompi.cpp b/eo/src/eompi.cpp new file mode 100644 index 000000000..eff6bc39d --- /dev/null +++ b/eo/src/eompi.cpp @@ -0,0 +1,4 @@ +# include "eompi.h" + +MpiNode* MpiNodeStore::singleton; + diff --git a/eo/src/eompi.h b/eo/src/eompi.h index 82d1a585b..9abcbb0f2 100644 --- a/eo/src/eompi.h +++ b/eo/src/eompi.h @@ -46,8 +46,6 @@ class MpiNodeStore static MpiNode* singleton; }; -MpiNode* MpiNodeStore::singleton; - class MpiNode { protected: From 6ac423bf22c4e68014edcd6cabc1af3218321e46 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 20 Jun 2012 11:46:18 +0200 Subject: [PATCH 08/76] Adding eoEasyEA constructor allowing to precise which eoPopEvalFunc should be used --- eo/src/eoEasyEA.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/eo/src/eoEasyEA.h b/eo/src/eoEasyEA.h index 2c7c5474c..4d74932f7 100644 --- a/eo/src/eoEasyEA.h +++ b/eo/src/eoEasyEA.h @@ -102,6 +102,33 @@ template class eoEasyEA: public eoAlgo offspring.reserve(_offspringSize); // This line avoids an incremental resize of offsprings. } + /** + * @brief Ctor allowing to specify which pop eval function we're going to use. + * + * Ctor taking a breed and merge, an overload of ctor to define an offspring size, and + * the pop eval function used. This allows to precise if we would like to use the + * parallel evaluation, for instance. + */ + eoEasyEA( + eoContinue& _continuator, + eoEvalFunc& _eval, + eoPopEvalFunc& _pop_eval, + eoBreed& _breed, + eoReplacement& _replace, + unsigned _offspringSize + ) : continuator(_continuator), + eval (_eval), + loopEval(_eval), + popEval(_pop_eval), + selectTransform(dummySelect, dummyTransform), + breed(_breed), + mergeReduce(dummyMerge, dummyReduce), + replace(_replace), + isFirstCall(true) + { + offspring.reserve(_offspringSize); // This line avoids an incremental resize of offsprings. + } + /* eoEasyEA(eoContinue & _continuator, eoPopEvalFunc & _pop_eval, @@ -219,6 +246,8 @@ template class eoEasyEA: public eoAlgo /// Apply a few generation of evolution to the population. virtual void operator()(eoPop& _pop) { + + eo::log << "[EasyEA] Call to operator()" << std::endl; if (isFirstCall) { size_t total_capacity = _pop.capacity() + offspring.capacity(); @@ -227,22 +256,33 @@ template class eoEasyEA: public eoAlgo isFirstCall = false; } + // TODO TODOB delete all log traces + std::cout << "[EasyEA] After is first call." << std::endl; + eoPop empty_pop; + std::cout << "[EasyEA] After empty_pop." << std::endl; popEval(empty_pop, _pop); // A first eval of pop. + std::cout << "[EasyEA] After pop_eval." << std::endl; do { try { + std::cout << "[EasyEA] Beginning try." << std::endl; unsigned pSize = _pop.size(); + std::cout << "[EasyEA] psize determinated." << std::endl; offspring.clear(); // new offspring + std::cout << "[EasyEA] offspring cleared." << std::endl; breed(_pop, offspring); + std::cout << "[EasyEA] After breed, evaluating pop." << std::endl; popEval(_pop, offspring); // eval of parents + offspring if necessary + std::cout << "[EasyEA] After evaluation, replacing pop." << std::endl; replace(_pop, offspring); // after replace, the new pop. is in _pop + std::cout << "[EasyEA] After replacing, continuator." << std::endl; if (pSize > _pop.size()) throw std::runtime_error("Population shrinking!"); From fd2facb14e06440d53034766f162086831fa17f1 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 21 Jun 2012 15:03:41 +0200 Subject: [PATCH 09/76] Moved mpi related files into mpi/ and compiling it as an extern library. --- eo/src/CMakeLists.txt | 5 +++-- eo/src/apply.h | 4 ++-- eo/src/mpi/CMakeLists.txt | 32 ++++++++++++++++++++++++++++++ eo/src/{ => mpi}/eoParallelApply.h | 4 ---- eo/src/{ => mpi}/eompi.cpp | 0 eo/src/{ => mpi}/eompi.h | 0 eo/test/t-eoMpiParallelApply.cpp | 4 ++-- 7 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 eo/src/mpi/CMakeLists.txt rename eo/src/{ => mpi}/eoParallelApply.h (83%) rename eo/src/{ => mpi}/eompi.cpp (100%) rename eo/src/{ => mpi}/eompi.h (100%) diff --git a/eo/src/CMakeLists.txt b/eo/src/CMakeLists.txt index 3d46be7ad..caeb673ad 100644 --- a/eo/src/CMakeLists.txt +++ b/eo/src/CMakeLists.txt @@ -5,7 +5,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) ###################################################################################### -### 2) Optionnal: add MPI and Boost MPI dependencies. +### 2) Optional: add MPI and Boost MPI dependencies. ###################################################################################### IF(WITH_MPI) @@ -25,6 +25,8 @@ IF(WITH_MPI) ADD_DEFINITIONS(-DWITH_MPI) LINK_LIBRARIES(boost_mpi boost_serialization) + + ADD_SUBDIRECTORY(mpi) ENDIF() ###################################################################################### @@ -41,7 +43,6 @@ SET(EO_SOURCES eoCtrlCContinue.cpp eoScalarFitnessAssembled.cpp eoSIGContinue.cpp - eompi.cpp ) ADD_LIBRARY(eo STATIC ${EO_SOURCES}) diff --git a/eo/src/apply.h b/eo/src/apply.h index c01ac5740..d2006fe04 100644 --- a/eo/src/apply.h +++ b/eo/src/apply.h @@ -34,8 +34,8 @@ #include # ifdef WITH_MPI -# include -# include +# include +# include # endif // WITH_MPI /** diff --git a/eo/src/mpi/CMakeLists.txt b/eo/src/mpi/CMakeLists.txt new file mode 100644 index 000000000..c770acd13 --- /dev/null +++ b/eo/src/mpi/CMakeLists.txt @@ -0,0 +1,32 @@ +###################################################################################### +### 1) Include the sources +###################################################################################### + +INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +###################################################################################### +### 2) Define the eompi target +###################################################################################### + +SET(EOMPI_LIB_OUTPUT_PATH ${EO_BINARY_DIR}/lib) +SET(LIBRARY_OUTPUT_PATH ${EOMPI_LIB_OUTPUT_PATH}) + +SET(EOMPI_SOURCES + eompi.cpp + ) + +ADD_LIBRARY(eompi STATIC ${EOMPI_SOURCES}) +INSTALL(TARGETS eompi ARCHIVE DESTINATION lib COMPONENT libraries) + +FILE(GLOB HDRS *.h) +INSTALL(FILES ${HDRS} DESTINATION include/eo/mpi COMPONENT headers) + +###################################################################################### +### 3) Optionnal +###################################################################################### + +SET(EOMPI_VERSION ${GLOBAL_VERSION}) +SET_TARGET_PROPERTIES(eompi PROPERTIES VERSION "${EOMPI_VERSION}") + +###################################################################################### diff --git a/eo/src/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h similarity index 83% rename from eo/src/eoParallelApply.h rename to eo/src/mpi/eoParallelApply.h index 6a7c6eaad..528d0cb29 100644 --- a/eo/src/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -31,13 +31,9 @@ class ParallelApply : public MpiJob< EOT > virtual void processTask( ) { EOT ind; - cout << "Receiving individual." << endl; MpiJob::comm.recv( 0, 1, ind ); - cout << "Applying function." << endl; func( ind ); - cout << "Sending result." << endl; MpiJob::comm.send( 0, 1, ind ); - cout << "Leaving processTask" << endl; } protected: diff --git a/eo/src/eompi.cpp b/eo/src/mpi/eompi.cpp similarity index 100% rename from eo/src/eompi.cpp rename to eo/src/mpi/eompi.cpp diff --git a/eo/src/eompi.h b/eo/src/mpi/eompi.h similarity index 100% rename from eo/src/eompi.h rename to eo/src/mpi/eompi.h diff --git a/eo/test/t-eoMpiParallelApply.cpp b/eo/test/t-eoMpiParallelApply.cpp index 77583420e..c20cc94fb 100644 --- a/eo/test/t-eoMpiParallelApply.cpp +++ b/eo/test/t-eoMpiParallelApply.cpp @@ -1,5 +1,5 @@ -# include -# include +# include +# include # include # include From d0a00a5216a1028c71d4c1f3b4e71d9733177823 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 21 Jun 2012 15:05:32 +0200 Subject: [PATCH 10/76] Don't force user to compile with OpenMP. --- eo/CMakeLists.txt | 10 ++++++---- eo/eo-conf.cmake | 7 +++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/eo/CMakeLists.txt b/eo/CMakeLists.txt index c10ac3c5e..78cab0a56 100644 --- a/eo/CMakeLists.txt +++ b/eo/CMakeLists.txt @@ -44,10 +44,12 @@ ENABLE_LANGUAGE(C) ### 2) Include required modules / configuration files ##################################################################################### -FIND_PACKAGE(OpenMP) -IF(OPENMP_FOUND) - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") +IF(WITH_OMP) + FIND_PACKAGE(OpenMP) + IF(OPENMP_FOUND) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + ENDIF() ENDIF() INCLUDE(CMakeBackwardCompatibilityCXX) diff --git a/eo/eo-conf.cmake b/eo/eo-conf.cmake index 2c115cfef..1f5ee8150 100644 --- a/eo/eo-conf.cmake +++ b/eo/eo-conf.cmake @@ -5,10 +5,13 @@ SET(PROJECT_VERSION_MINOR 3) SET(PROJECT_VERSION_PATCH 0) SET(PROJECT_VERSION_MISC "-edge") +# If you plan to use OpenMP, put the following boolean to true : +SET(WITH_OMP FALSE CACHE BOOL "Use OpenMP ?" FORCE) + # If you plan to use MPI, precise here where are the static libraries from # openmpi and boost::mpi. SET(WITH_MPI FALSE CACHE BOOL "Use mpi ?" FORCE) -SET(MPI_DIR "put root directory of openmpi here" CACHE PATH "OpenMPI directory" FORCE) -SET(BOOST_DIR "put root directory of boost here" CACHE PATH "Boost directory" FORCE) +SET(MPI_DIR "/mpi/directory" CACHE PATH "OpenMPI directory" FORCE) +SET(BOOST_DIR "/boost/directory" CACHE PATH "Boost directory" FORCE) From 122d0debf098b9c36cc8f4f70bcbd555153a660c Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 21 Jun 2012 16:10:51 +0200 Subject: [PATCH 11/76] eoMpi API is simpler and allows to have multiple roles for a given node. --- eo/src/mpi/assignmentAlgorithm.h | 49 ++++++++ eo/src/mpi/eoParallelApply.h | 4 +- eo/src/mpi/eompi.cpp | 3 +- eo/src/mpi/eompi.h | 196 +++++++------------------------ eo/test/t-eoMpiParallelApply.cpp | 19 ++- 5 files changed, 112 insertions(+), 159 deletions(-) create mode 100644 eo/src/mpi/assignmentAlgorithm.h diff --git a/eo/src/mpi/assignmentAlgorithm.h b/eo/src/mpi/assignmentAlgorithm.h new file mode 100644 index 000000000..bd635c23f --- /dev/null +++ b/eo/src/mpi/assignmentAlgorithm.h @@ -0,0 +1,49 @@ +# ifndef __ASSIGNMENT_ALGORITHM_H__ +# define __ASSIGNMENT_ALGORITHM_H__ + +struct AssignmentAlgorithm +{ + virtual int get( ) = 0; + virtual int size( ) = 0; + virtual void confirm( int wrkRank ) = 0; +}; + +struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm +{ + public: + DynamicAssignmentAlgorithm( int offset, int size ) + { + for( int i = 0; offset + i < size; ++i) + { + availableWrk.push_back( offset + i ); + } + } + + virtual int get( ) + { + int assignee = -1; + if (! availableWrk.empty() ) + { + assignee = availableWrk.back(); + availableWrk.pop_back(); + } + return assignee; + } + + int size() + { + return availableWrk.size(); + } + + void confirm( int rank ) + { + availableWrk.push_back( rank ); + } + + protected: + std::vector< int > availableWrk; +}; + + + +# endif // __ASSIGNMENT_ALGORITHM_H__ diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 528d0cb29..80958e411 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -11,8 +11,8 @@ class ParallelApply : public MpiJob< EOT > { public: - ParallelApply( eoUF & _proc, std::vector& _pop ) : - MpiJob( _pop ), + ParallelApply( eoUF & _proc, std::vector& _pop, AssignmentAlgorithm & algo ) : + MpiJob( _pop, algo ), func( _proc ) { // empty diff --git a/eo/src/mpi/eompi.cpp b/eo/src/mpi/eompi.cpp index eff6bc39d..6429a31c5 100644 --- a/eo/src/mpi/eompi.cpp +++ b/eo/src/mpi/eompi.cpp @@ -1,4 +1,5 @@ # include "eompi.h" -MpiNode* MpiNodeStore::singleton; +// MpiNode* MpiNodeStore::singleton; +mpi::communicator MpiNode::_comm; diff --git a/eo/src/mpi/eompi.h b/eo/src/mpi/eompi.h index 9abcbb0f2..3b04441d0 100644 --- a/eo/src/mpi/eompi.h +++ b/eo/src/mpi/eompi.h @@ -7,6 +7,8 @@ # include namespace mpi = boost::mpi; +# include "assignmentAlgorithm.h" + # include using namespace std; // TODO TODOB comment! @@ -25,102 +27,22 @@ namespace EoMpi } } -class MpiNode; - -class MpiNodeStore +class MpiNode { public: - static void instance( MpiNode* _instance ) + static void init( int argc, char** argv ) { - singleton = _instance; + static mpi::environment env( argc, argv ); } - static MpiNode* instance() - { - return singleton; - } - - protected: - - static MpiNode* singleton; -}; - -class MpiNode -{ -protected: - mpi::environment& env; - mpi::communicator& _comm; - - int rank; - int size; - - int argc; - char** argv; - -public: - MpiNode( mpi::environment& _env, mpi::communicator& __comm ) : - env(_env), - _comm(__comm), - rank(__comm.rank()), - size(__comm.size()) - { - // empty - } - - virtual ~MpiNode() - { - // empty - } - - mpi::communicator& comm() + static mpi::communicator& comm() { return _comm; } -}; - -struct AssignmentAlgorithm -{ - virtual int get( ) = 0; - virtual void size( int s ) = 0; - virtual int size( ) = 0; - virtual void confirm( int wrkRank ) = 0; -}; - -struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm -{ - public: - virtual int get( ) - { - int assignee = -1; - if (! availableWrk.empty() ) - { - assignee = availableWrk.back(); - availableWrk.pop_back(); - } - return assignee; - } -; - void size( int s ) - { - for( int i = 1; i < s ; ++i ) - { - availableWrk.push_back( i ); - } - } - - int size() - { - return availableWrk.size(); - } - - void confirm( int rank ) - { - availableWrk.push_back( rank ); - } protected: - std::vector< int > availableWrk; + static mpi::communicator _comm; }; template< typename EOT > @@ -128,9 +50,10 @@ class MpiJob { public: - MpiJob( std::vector< EOT > & _data ) : + MpiJob( std::vector< EOT > & _data, AssignmentAlgorithm& algo ) : data( _data ), - comm( MpiNodeStore::instance()->comm() ) + comm( MpiNode::comm() ), + assignmentAlgo( algo ) { // empty } @@ -141,14 +64,14 @@ class MpiJob // worker virtual void processTask( ) = 0; - void master( AssignmentAlgorithm & assignmentAlgorithm ) + void master( ) { for( int i = 0, size = data.size(); i < size; ++i) { cout << "Beginning loop for i = " << i << endl; - int assignee = assignmentAlgorithm.get( ); + int assignee = assignmentAlgo.get( ); cout << "Assignee : " << assignee << endl; while( assignee <= 0 ) { @@ -157,8 +80,8 @@ class MpiJob int wrkRank = status.source(); cout << "Node " << wrkRank << " just terminated." << endl; handleResponse( wrkRank, assignedTasks[ wrkRank ] ); - assignmentAlgorithm.confirm( wrkRank ); - assignee = assignmentAlgorithm.get( ); + assignmentAlgo.confirm( wrkRank ); + assignee = assignmentAlgo.get( ); } cout << "Assignee found : " << assignee << endl; assignedTasks[ assignee ] = i; @@ -169,25 +92,25 @@ class MpiJob // frees all the idle workers int idle; vector idles; - while ( ( idle = assignmentAlgorithm.get( ) ) > 0 ) + while ( ( idle = assignmentAlgo.get( ) ) > 0 ) { comm.send( idle, EoMpi::Channel::Commands, EoMpi::Message::Finish ); idles.push_back( idle ); } for (int i = 0; i < idles.size(); ++i) { - assignmentAlgorithm.confirm( idles[i] ); + assignmentAlgo.confirm( idles[i] ); } // wait for all responses int wrkNb = comm.size() - 1; - while( assignmentAlgorithm.size() != wrkNb ) + while( assignmentAlgo.size() != wrkNb ) { mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); int wrkRank = status.source(); handleResponse( wrkRank, assignedTasks[ wrkRank ] ); comm.send( wrkRank, EoMpi::Channel::Commands, EoMpi::Message::Finish ); - assignmentAlgorithm.confirm( wrkRank ); + assignmentAlgo.confirm( wrkRank ); } } @@ -211,77 +134,44 @@ protected: std::vector & data; std::map< int /* worker rank */, int /* index in vector */> assignedTasks; - + AssignmentAlgorithm& assignmentAlgo; mpi::communicator& comm; }; -class MasterNode : public MpiNode -{ -public: - MasterNode( int _argc, char** _argv, - mpi::environment& _env, - mpi::communicator& _comm - ) : - MpiNode(_env, _comm ) - { - // empty - } - - void setAssignmentAlgorithm( AssignmentAlgorithm* assignmentAlgo ) - { - _assignmentAlgo = assignmentAlgo; - _assignmentAlgo->size( _comm.size() ); - } - - template< typename EOT > - void run( MpiJob< EOT > & job ) - { - job.master( *_assignmentAlgo ); - } - -protected: - AssignmentAlgorithm* _assignmentAlgo; -}; - -class WorkerNode : public MpiNode +template< class EOT > +class Role { public: - - WorkerNode( - int _argc, char** _argv, - mpi::environment& _env, - mpi::communicator& _comm ) : - MpiNode( _env, _comm ) + Role( MpiJob & job, bool master ) : + _job( job ), + _master( master ) { // empty } - template< typename EOT > - void run( MpiJob & job ) + bool master() { - job.worker( ); + return _master; } -}; -class MpiSingletonFactory -{ - public: - - static void init( int argc, char** argv ) - { - MpiNode* singleton; - //mpi::environment* env = new mpi::environment ( argc, argv ); - //mpi::communicator* world = new mpi::communicator; // TODO clean - static mpi::environment env( argc, argv ); - static mpi::communicator world; - if ( world.rank() == 0 ) + virtual void run( ) { - singleton = new MasterNode( argc, argv, env, world ); - } else - { - singleton = new WorkerNode( argc, argv, env, world ); + if( _master ) + { + _job.master( ); + } else + { + _job.worker( ); + } } - MpiNodeStore::instance( singleton ); - } + + virtual ~Role() + { + // empty + } + + protected: + bool _master; + MpiJob & _job; }; # endif // __EO_MPI_H__ diff --git a/eo/test/t-eoMpiParallelApply.cpp b/eo/test/t-eoMpiParallelApply.cpp index c20cc94fb..ab559815c 100644 --- a/eo/test/t-eoMpiParallelApply.cpp +++ b/eo/test/t-eoMpiParallelApply.cpp @@ -1,5 +1,6 @@ # include # include + # include # include @@ -15,9 +16,9 @@ struct plusOne : public eoUF< int&, void > int main(int argc, char** argv) { - DynamicAssignmentAlgorithm algo; cout << "Appel à init... " << endl; - MpiSingletonFactory::init( argc, argv ); + MpiNode::init( argc, argv ); + DynamicAssignmentAlgorithm algo( 1, MpiNode::comm().size() ); cout << "Création des données... " << endl; vector v; @@ -31,8 +32,19 @@ int main(int argc, char** argv) plusOne plusOneInstance; cout << "Création du job..." << endl; - ParallelApply job( plusOneInstance, v ); + ParallelApply job( plusOneInstance, v, algo ); + Role node( job, MpiNode::comm().rank() == 0 ); + node.run(); + if( node.master() ) + { + for(int i = 0; i < v.size(); ++i) + { + cout << v[i] << ' '; + } + cout << endl; + } + /* cout << "Création de l'instance..." << endl; MpiNode* instance = MpiNodeStore::instance(); if( dynamic_cast( instance ) != 0 ) @@ -54,6 +66,7 @@ int main(int argc, char** argv) { cout << "Nothing to be done;" << endl; } + */ return 0; } From 9275fbedad695cf716d4b1c49f77e45de65eac87 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 21 Jun 2012 17:20:24 +0200 Subject: [PATCH 12/76] Making mpi tests dir --- eo/test/{t-eoMpiParallelApply.cpp => mpi/parallelApply.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename eo/test/{t-eoMpiParallelApply.cpp => mpi/parallelApply.cpp} (100%) diff --git a/eo/test/t-eoMpiParallelApply.cpp b/eo/test/mpi/parallelApply.cpp similarity index 100% rename from eo/test/t-eoMpiParallelApply.cpp rename to eo/test/mpi/parallelApply.cpp From 2aa312e43d168f6d25cde3fe3ea1a4135d4ff55a Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 21 Jun 2012 17:21:13 +0200 Subject: [PATCH 13/76] Allowing more than one master. --- eo/src/mpi/assignmentAlgorithm.h | 2 +- eo/src/mpi/eoParallelApply.h | 15 ++++++----- eo/src/mpi/eompi.h | 46 +++++++++++++++++++++----------- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/eo/src/mpi/assignmentAlgorithm.h b/eo/src/mpi/assignmentAlgorithm.h index bd635c23f..89b97c062 100644 --- a/eo/src/mpi/assignmentAlgorithm.h +++ b/eo/src/mpi/assignmentAlgorithm.h @@ -13,7 +13,7 @@ struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm public: DynamicAssignmentAlgorithm( int offset, int size ) { - for( int i = 0; offset + i < size; ++i) + for( int i = 0; offset + i <= size; ++i) { availableWrk.push_back( offset + i ); } diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 80958e411..990f8fadb 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -10,9 +10,12 @@ template< typename EOT > class ParallelApply : public MpiJob< EOT > { public: + using MpiJob::comm; + using MpiJob::data; + using MpiJob::_masterRank; - ParallelApply( eoUF & _proc, std::vector& _pop, AssignmentAlgorithm & algo ) : - MpiJob( _pop, algo ), + ParallelApply( eoUF & _proc, std::vector& _pop, AssignmentAlgorithm & algo, int _masterRank ) : + MpiJob( _pop, algo, _masterRank ), func( _proc ) { // empty @@ -20,20 +23,20 @@ class ParallelApply : public MpiJob< EOT > virtual void sendTask( int wrkRank, int index ) { - MpiJob::comm.send( wrkRank, 1, MpiJob::data[ index ] ); + comm.send( wrkRank, 1, data[ index ] ); } virtual void handleResponse( int wrkRank, int index ) { - MpiJob::comm.recv( wrkRank, 1, MpiJob::data[ index ] ); + comm.recv( wrkRank, 1, data[ index ] ); } virtual void processTask( ) { EOT ind; - MpiJob::comm.recv( 0, 1, ind ); + comm.recv( _masterRank, 1, ind ); func( ind ); - MpiJob::comm.send( 0, 1, ind ); + comm.send( _masterRank, 1, ind ); } protected: diff --git a/eo/src/mpi/eompi.h b/eo/src/mpi/eompi.h index 3b04441d0..df041db5c 100644 --- a/eo/src/mpi/eompi.h +++ b/eo/src/mpi/eompi.h @@ -50,10 +50,11 @@ class MpiJob { public: - MpiJob( std::vector< EOT > & _data, AssignmentAlgorithm& algo ) : + MpiJob( std::vector< EOT > & _data, AssignmentAlgorithm& algo, int masterRank ) : data( _data ), comm( MpiNode::comm() ), - assignmentAlgo( algo ) + assignmentAlgo( algo ), + _masterRank( masterRank ) { // empty } @@ -66,29 +67,33 @@ class MpiJob void master( ) { + int totalWorkers = assignmentAlgo.size(); + cout << "[M] Have " << totalWorkers << " workers." << endl; + for( int i = 0, size = data.size(); i < size; ++i) { - cout << "Beginning loop for i = " << i << endl; + cout << "[M] Beginning loop for i = " << i << endl; int assignee = assignmentAlgo.get( ); - cout << "Assignee : " << assignee << endl; + cout << "[M] Assignee : " << assignee << endl; while( assignee <= 0 ) { - cout << "Waitin' for node..." << endl; + cout << "[M] Waitin' for node..." << endl; mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); int wrkRank = status.source(); - cout << "Node " << wrkRank << " just terminated." << endl; + cout << "[M] Node " << wrkRank << " just terminated." << endl; handleResponse( wrkRank, assignedTasks[ wrkRank ] ); assignmentAlgo.confirm( wrkRank ); assignee = assignmentAlgo.get( ); } - cout << "Assignee found : " << assignee << endl; + cout << "[M] Assignee found : " << assignee << endl; assignedTasks[ assignee ] = i; comm.send( assignee, EoMpi::Channel::Commands, EoMpi::Message::Continue ); sendTask( assignee, i ); } + cout << "[M] Frees all the idle." << endl; // frees all the idle workers int idle; vector idles; @@ -102,9 +107,9 @@ class MpiJob assignmentAlgo.confirm( idles[i] ); } + cout << "[M] Waits for all responses." << endl; // wait for all responses - int wrkNb = comm.size() - 1; - while( assignmentAlgo.size() != wrkNb ) + while( assignmentAlgo.size() != totalWorkers ) { mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); int wrkRank = status.source(); @@ -112,6 +117,8 @@ class MpiJob comm.send( wrkRank, EoMpi::Channel::Commands, EoMpi::Message::Finish ); assignmentAlgo.confirm( wrkRank ); } + + cout << "[M] Leaving master task." << endl; } void worker( ) @@ -119,34 +126,41 @@ class MpiJob int order; while( true ) { - comm.recv( 0, EoMpi::Channel::Commands, order ); + cout << "[W] Waiting for an order..." << std::endl; + comm.recv( _masterRank, EoMpi::Channel::Commands, order ); if ( order == EoMpi::Message::Finish ) { return; } else { + cout << "[W] Processing task..." << std::endl; processTask( ); } } } + int masterRank() + { + return _masterRank; + } + protected: std::vector & data; std::map< int /* worker rank */, int /* index in vector */> assignedTasks; AssignmentAlgorithm& assignmentAlgo; mpi::communicator& comm; + int _masterRank; }; template< class EOT > class Role { public: - Role( MpiJob & job, bool master ) : - _job( job ), - _master( master ) + Role( MpiJob & job ) : + _job( job ) { - // empty + _master = job.masterRank() == MpiNode::comm().rank(); } bool master() @@ -156,7 +170,7 @@ class Role virtual void run( ) { - if( _master ) + if( MpiNode::comm().rank() == _job.masterRank() ) { _job.master( ); } else @@ -171,7 +185,7 @@ class Role } protected: - bool _master; MpiJob & _job; + bool _master; }; # endif // __EO_MPI_H__ From 92bd4eec1b1d1d5731d88481c004289020702b53 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 21 Jun 2012 17:22:28 +0200 Subject: [PATCH 14/76] Adding tests for multiple roles and updating parallel apply. --- eo/test/mpi/multipleRoles.cpp | 91 +++++++++++++++++++++++++++++++++++ eo/test/mpi/parallelApply.cpp | 6 +-- 2 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 eo/test/mpi/multipleRoles.cpp diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/multipleRoles.cpp new file mode 100644 index 000000000..bd91245b2 --- /dev/null +++ b/eo/test/mpi/multipleRoles.cpp @@ -0,0 +1,91 @@ +# include +# include + +# include + +# include + +# include +using namespace std; + +// Role map +// 0 : general master +// 1 : worker of general job, master of subjob +// 2 and more : workers of subjob + +struct plusOne : public eoUF< int&, void > +{ + void operator() ( int & x ) + { + cout << "Subjob is being applied." << endl; + ++x; + } +}; + +void subtask( vector& v ) +{ + DynamicAssignmentAlgorithm algo( 2, MpiNode::comm().size()-1 ); + plusOne plusOneInstance; + ParallelApply job( plusOneInstance, v, algo, 1 ); + Role node( job ); + node.run(); +} + +struct transmit : public eoUF< vector&, void > +{ + void operator() ( vector& v ) + { + cout << "Into the master subjob..." << endl; + subtask( v ); + } +}; + +int main(int argc, char** argv) +{ + MpiNode::init( argc, argv ); + vector v; + + v.push_back(1); + v.push_back(3); + v.push_back(3); + v.push_back(7); + v.push_back(42); + + transmit transmitInstance; + + vector< vector > metaV; + metaV.push_back( v ); + + switch( MpiNode::comm().rank() ) + { + case 0: + case 1: + { + // only one node is assigned to subjob mastering + DynamicAssignmentAlgorithm algo( 1, 1 ); + ParallelApply< vector > job( transmitInstance, metaV, algo, 0 ); + Role< vector > node( job ); + node.run(); + if( node.master() ) + { + v = metaV[0]; + cout << "Results : " << endl; + for(int i = 0; i < v.size(); ++i) + { + cout << v[i] << ' '; + } + cout << endl; + } + } + break; + + default: + { + // all the other nodes are sub workers + subtask( v ); + } + break; + } + + return 0; +} diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index ab559815c..82686bf74 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -18,7 +18,7 @@ int main(int argc, char** argv) { cout << "Appel à init... " << endl; MpiNode::init( argc, argv ); - DynamicAssignmentAlgorithm algo( 1, MpiNode::comm().size() ); + DynamicAssignmentAlgorithm algo( 1, MpiNode::comm().size()-1 ); cout << "Création des données... " << endl; vector v; @@ -32,8 +32,8 @@ int main(int argc, char** argv) plusOne plusOneInstance; cout << "Création du job..." << endl; - ParallelApply job( plusOneInstance, v, algo ); - Role node( job, MpiNode::comm().rank() == 0 ); + ParallelApply job( plusOneInstance, v, algo, 0 ); + Role node( job ); node.run(); if( node.master() ) From 3c8e902155017f66366f3f58874521ab0ff9919b Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 21 Jun 2012 17:23:25 +0200 Subject: [PATCH 15/76] Removing useless comments in parallelApply. --- eo/test/mpi/parallelApply.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 82686bf74..605f39112 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -44,29 +44,6 @@ int main(int argc, char** argv) } cout << endl; } - /* - cout << "Création de l'instance..." << endl; - MpiNode* instance = MpiNodeStore::instance(); - if( dynamic_cast( instance ) != 0 ) - { - cout << "[Master] Algorithme d'assignation" << endl; - static_cast( instance )->setAssignmentAlgorithm( &algo ); - cout << "[Master] Lancement." << endl; - static_cast( instance )->run( job ); - - for (int i = 0; i < v.size(); ++i ) - { - cout << v[i] << endl; - } - } else if ( dynamic_cast( instance ) != 0 ) - { - cout << "[Worker] Lancement." << endl; - static_cast( instance )->run( job ); - } else - { - cout << "Nothing to be done;" << endl; - } - */ return 0; } From 33e062d1665972eb13a94abb5c442b53ed209b52 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 21 Jun 2012 17:31:07 +0200 Subject: [PATCH 16/76] Simplified master condition. --- eo/src/mpi/eompi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eo/src/mpi/eompi.h b/eo/src/mpi/eompi.h index df041db5c..cbb66a464 100644 --- a/eo/src/mpi/eompi.h +++ b/eo/src/mpi/eompi.h @@ -170,7 +170,7 @@ class Role virtual void run( ) { - if( MpiNode::comm().rank() == _job.masterRank() ) + if( _master ) { _job.master( ); } else From 28ab2004ea59b1a3683c5d4bde28d4ff761862d9 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 21 Jun 2012 18:26:56 +0200 Subject: [PATCH 17/76] MpiJob now just deal with loop logic, not with handled data. Handled data is now handled by the MpiJob subclasses. Tests updated. --- eo/src/mpi/eoParallelApply.h | 49 ++++++++++++++++++++++++------ eo/src/mpi/eompi.h | 57 ++++++++++++++++++++++++++--------- eo/test/mpi/multipleRoles.cpp | 4 +-- eo/test/mpi/parallelApply.cpp | 2 +- 4 files changed, 84 insertions(+), 28 deletions(-) diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 990f8fadb..86e2b04a5 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -7,28 +7,53 @@ # include template< typename EOT > -class ParallelApply : public MpiJob< EOT > +struct ParallelApplyContinuator : public BaseContinuator +{ + ParallelApplyContinuator( int index, int size ) + { + _index = index; + _size = size; + } + + void index( int i ) { _index = i; } + + bool operator()() + { + return _index < _size; + } + +private: + int _index; + int _size; +}; + +template< typename EOT > +class ParallelApply : public MpiJob { public: - using MpiJob::comm; - using MpiJob::data; - using MpiJob::_masterRank; ParallelApply( eoUF & _proc, std::vector& _pop, AssignmentAlgorithm & algo, int _masterRank ) : - MpiJob( _pop, algo, _masterRank ), - func( _proc ) + MpiJob( algo, + new ParallelApplyContinuator( 0, _pop.size() ), + _masterRank ), + func( _proc ), + index( 0 ), + data( _pop ) { - // empty + pa_continuator = static_cast*>( _continuator ); } - virtual void sendTask( int wrkRank, int index ) + virtual void sendTask( int wrkRank ) { + assignedTasks[ wrkRank ] = index; comm.send( wrkRank, 1, data[ index ] ); + ++index; + pa_continuator->index( index ); } - virtual void handleResponse( int wrkRank, int index ) + virtual void handleResponse( int wrkRank ) { - comm.recv( wrkRank, 1, data[ index ] ); + comm.recv( wrkRank, 1, data[ assignedTasks[ wrkRank ] ] ); } virtual void processTask( ) @@ -40,7 +65,11 @@ class ParallelApply : public MpiJob< EOT > } protected: + vector & data; eoUF& func; + int index; + ParallelApplyContinuator * pa_continuator; + std::map< int /* worker rank */, int /* index in vector */> assignedTasks; }; # endif // __EO_PARALLEL_APPLY_H__ diff --git a/eo/src/mpi/eompi.h b/eo/src/mpi/eompi.h index cbb66a464..8bc25d313 100644 --- a/eo/src/mpi/eompi.h +++ b/eo/src/mpi/eompi.h @@ -45,23 +45,33 @@ class MpiNode static mpi::communicator _comm; }; -template< typename EOT > +struct BaseContinuator +{ + virtual bool operator()() = 0; +}; + +// template< typename EOT > class MpiJob { public: - MpiJob( std::vector< EOT > & _data, AssignmentAlgorithm& algo, int masterRank ) : - data( _data ), - comm( MpiNode::comm() ), + MpiJob( AssignmentAlgorithm& algo, BaseContinuator* c, int masterRank ) : assignmentAlgo( algo ), - _masterRank( masterRank ) + comm( MpiNode::comm() ), + _masterRank( masterRank ), + _continuator( c ) { // empty } + ~MpiJob() + { + delete _continuator; + } + // master - virtual void sendTask( int wrkRank, int index ) = 0; - virtual void handleResponse( int wrkRank, int index ) = 0; + virtual void sendTask( int wrkRank ) = 0; + virtual void handleResponse( int wrkRank ) = 0; // worker virtual void processTask( ) = 0; @@ -70,6 +80,25 @@ class MpiJob int totalWorkers = assignmentAlgo.size(); cout << "[M] Have " << totalWorkers << " workers." << endl; + while( (*_continuator)() ) + { + int assignee = assignmentAlgo.get( ); + cout << "[M] Assignee : " << assignee << endl; + while( assignee <= 0 ) + { + cout << "[M] Waitin' for node..." << endl; + mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); + int wrkRank = status.source(); + cout << "[M] Node " << wrkRank << " just terminated." << endl; + handleResponse( wrkRank ); + assignmentAlgo.confirm( wrkRank ); + assignee = assignmentAlgo.get( ); + } + comm.send( assignee, EoMpi::Channel::Commands, EoMpi::Message::Continue ); + sendTask( assignee ); + } + + /* for( int i = 0, size = data.size(); i < size; ++i) @@ -92,6 +121,7 @@ class MpiJob comm.send( assignee, EoMpi::Channel::Commands, EoMpi::Message::Continue ); sendTask( assignee, i ); } + */ cout << "[M] Frees all the idle." << endl; // frees all the idle workers @@ -102,7 +132,7 @@ class MpiJob comm.send( idle, EoMpi::Channel::Commands, EoMpi::Message::Finish ); idles.push_back( idle ); } - for (int i = 0; i < idles.size(); ++i) + for (unsigned int i = 0; i < idles.size(); ++i) { assignmentAlgo.confirm( idles[i] ); } @@ -113,7 +143,7 @@ class MpiJob { mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); int wrkRank = status.source(); - handleResponse( wrkRank, assignedTasks[ wrkRank ] ); + handleResponse( wrkRank ); comm.send( wrkRank, EoMpi::Channel::Commands, EoMpi::Message::Finish ); assignmentAlgo.confirm( wrkRank ); } @@ -145,19 +175,16 @@ class MpiJob } protected: - - std::vector & data; - std::map< int /* worker rank */, int /* index in vector */> assignedTasks; AssignmentAlgorithm& assignmentAlgo; + BaseContinuator* _continuator; mpi::communicator& comm; int _masterRank; }; -template< class EOT > class Role { public: - Role( MpiJob & job ) : + Role( MpiJob & job ) : _job( job ) { _master = job.masterRank() == MpiNode::comm().rank(); @@ -185,7 +212,7 @@ class Role } protected: - MpiJob & _job; + MpiJob & _job; bool _master; }; # endif // __EO_MPI_H__ diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/multipleRoles.cpp index bd91245b2..c300fdbb1 100644 --- a/eo/test/mpi/multipleRoles.cpp +++ b/eo/test/mpi/multipleRoles.cpp @@ -27,7 +27,7 @@ void subtask( vector& v ) DynamicAssignmentAlgorithm algo( 2, MpiNode::comm().size()-1 ); plusOne plusOneInstance; ParallelApply job( plusOneInstance, v, algo, 1 ); - Role node( job ); + Role node( job ); node.run(); } @@ -64,7 +64,7 @@ int main(int argc, char** argv) // only one node is assigned to subjob mastering DynamicAssignmentAlgorithm algo( 1, 1 ); ParallelApply< vector > job( transmitInstance, metaV, algo, 0 ); - Role< vector > node( job ); + Role node( job ); node.run(); if( node.master() ) { diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 605f39112..dd11f7b3a 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -33,7 +33,7 @@ int main(int argc, char** argv) cout << "Création du job..." << endl; ParallelApply job( plusOneInstance, v, algo, 0 ); - Role node( job ); + Role node( job ); node.run(); if( node.master() ) From fb871382e09377870bf7d387a3bf13c116f34327 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 22 Jun 2012 12:09:19 +0200 Subject: [PATCH 18/76] Removed useless continuator functor, replaced with a virtual method into MpiJob. --- eo/src/mpi/eoParallelApply.h | 36 ++++++++--------------------- eo/src/mpi/eompi.h | 45 ++++-------------------------------- 2 files changed, 13 insertions(+), 68 deletions(-) diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 86e2b04a5..cf964521a 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -6,41 +6,19 @@ # include # include -template< typename EOT > -struct ParallelApplyContinuator : public BaseContinuator -{ - ParallelApplyContinuator( int index, int size ) - { - _index = index; - _size = size; - } - - void index( int i ) { _index = i; } - - bool operator()() - { - return _index < _size; - } - -private: - int _index; - int _size; -}; - template< typename EOT > class ParallelApply : public MpiJob { public: ParallelApply( eoUF & _proc, std::vector& _pop, AssignmentAlgorithm & algo, int _masterRank ) : - MpiJob( algo, - new ParallelApplyContinuator( 0, _pop.size() ), - _masterRank ), + MpiJob( algo, _masterRank ), func( _proc ), index( 0 ), + size( _pop.size() ), data( _pop ) { - pa_continuator = static_cast*>( _continuator ); + // empty } virtual void sendTask( int wrkRank ) @@ -48,7 +26,6 @@ class ParallelApply : public MpiJob assignedTasks[ wrkRank ] = index; comm.send( wrkRank, 1, data[ index ] ); ++index; - pa_continuator->index( index ); } virtual void handleResponse( int wrkRank ) @@ -64,11 +41,16 @@ class ParallelApply : public MpiJob comm.send( _masterRank, 1, ind ); } + bool isFinished() + { + return index = size; + } + protected: vector & data; eoUF& func; int index; - ParallelApplyContinuator * pa_continuator; + int size; std::map< int /* worker rank */, int /* index in vector */> assignedTasks; }; diff --git a/eo/src/mpi/eompi.h b/eo/src/mpi/eompi.h index 8bc25d313..f043c162b 100644 --- a/eo/src/mpi/eompi.h +++ b/eo/src/mpi/eompi.h @@ -45,31 +45,20 @@ class MpiNode static mpi::communicator _comm; }; -struct BaseContinuator -{ - virtual bool operator()() = 0; -}; - -// template< typename EOT > class MpiJob { public: - MpiJob( AssignmentAlgorithm& algo, BaseContinuator* c, int masterRank ) : + MpiJob( AssignmentAlgorithm& algo, int masterRank ) : assignmentAlgo( algo ), comm( MpiNode::comm() ), - _masterRank( masterRank ), - _continuator( c ) + _masterRank( masterRank ) { // empty } - ~MpiJob() - { - delete _continuator; - } - // master + virtual bool isFinished() = 0; virtual void sendTask( int wrkRank ) = 0; virtual void handleResponse( int wrkRank ) = 0; // worker @@ -80,7 +69,7 @@ class MpiJob int totalWorkers = assignmentAlgo.size(); cout << "[M] Have " << totalWorkers << " workers." << endl; - while( (*_continuator)() ) + while( ! isFinished() ) { int assignee = assignmentAlgo.get( ); cout << "[M] Assignee : " << assignee << endl; @@ -98,31 +87,6 @@ class MpiJob sendTask( assignee ); } - /* - for( int i = 0, size = data.size(); - i < size; - ++i) - { - cout << "[M] Beginning loop for i = " << i << endl; - int assignee = assignmentAlgo.get( ); - cout << "[M] Assignee : " << assignee << endl; - while( assignee <= 0 ) - { - cout << "[M] Waitin' for node..." << endl; - mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); - int wrkRank = status.source(); - cout << "[M] Node " << wrkRank << " just terminated." << endl; - handleResponse( wrkRank, assignedTasks[ wrkRank ] ); - assignmentAlgo.confirm( wrkRank ); - assignee = assignmentAlgo.get( ); - } - cout << "[M] Assignee found : " << assignee << endl; - assignedTasks[ assignee ] = i; - comm.send( assignee, EoMpi::Channel::Commands, EoMpi::Message::Continue ); - sendTask( assignee, i ); - } - */ - cout << "[M] Frees all the idle." << endl; // frees all the idle workers int idle; @@ -176,7 +140,6 @@ class MpiJob protected: AssignmentAlgorithm& assignmentAlgo; - BaseContinuator* _continuator; mpi::communicator& comm; int _masterRank; }; From da9eb9ce7b417b737722f07fdb9dbd8cbcc9e338 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 22 Jun 2012 14:24:23 +0200 Subject: [PATCH 19/76] Merged MpiJob and Role, using eoLogger instead of cout. --- eo/src/mpi/eoParallelApply.h | 8 ++-- eo/src/mpi/eompi.h | 85 ++++++++++++----------------------- eo/test/mpi/parallelApply.cpp | 10 ++--- 3 files changed, 38 insertions(+), 65 deletions(-) diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index cf964521a..0c4f1e6e4 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -36,18 +36,18 @@ class ParallelApply : public MpiJob virtual void processTask( ) { EOT ind; - comm.recv( _masterRank, 1, ind ); + comm.recv( masterRank, 1, ind ); func( ind ); - comm.send( _masterRank, 1, ind ); + comm.send( masterRank, 1, ind ); } bool isFinished() { - return index = size; + return index == size; } protected: - vector & data; + std::vector & data; eoUF& func; int index; int size; diff --git a/eo/src/mpi/eompi.h b/eo/src/mpi/eompi.h index f043c162b..58ddf1311 100644 --- a/eo/src/mpi/eompi.h +++ b/eo/src/mpi/eompi.h @@ -7,10 +7,9 @@ # include namespace mpi = boost::mpi; -# include "assignmentAlgorithm.h" +# include -# include -using namespace std; +# include "assignmentAlgorithm.h" // TODO TODOB comment! namespace EoMpi @@ -49,12 +48,12 @@ class MpiJob { public: - MpiJob( AssignmentAlgorithm& algo, int masterRank ) : - assignmentAlgo( algo ), + MpiJob( AssignmentAlgorithm& _algo, int _masterRank ) : + assignmentAlgo( _algo ), comm( MpiNode::comm() ), - _masterRank( masterRank ) + masterRank( _masterRank ) { - // empty + _isMaster = MpiNode::comm().rank() == _masterRank; } // master @@ -67,18 +66,19 @@ class MpiJob void master( ) { int totalWorkers = assignmentAlgo.size(); - cout << "[M] Have " << totalWorkers << " workers." << endl; + eo::log << eo::debug; + eo::log << "[M] Have " << totalWorkers << " workers." << std::endl; while( ! isFinished() ) { int assignee = assignmentAlgo.get( ); - cout << "[M] Assignee : " << assignee << endl; + eo::log << "[M] Assignee : " << assignee << std::endl; while( assignee <= 0 ) { - cout << "[M] Waitin' for node..." << endl; + eo::log << "[M] Waitin' for node..." << std::endl; mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); int wrkRank = status.source(); - cout << "[M] Node " << wrkRank << " just terminated." << endl; + eo::log << "[M] Node " << wrkRank << " just terminated." << std::endl; handleResponse( wrkRank ); assignmentAlgo.confirm( wrkRank ); assignee = assignmentAlgo.get( ); @@ -87,10 +87,10 @@ class MpiJob sendTask( assignee ); } - cout << "[M] Frees all the idle." << endl; + eo::log << "[M] Frees all the idle." << std::endl; // frees all the idle workers int idle; - vector idles; + std::vector idles; while ( ( idle = assignmentAlgo.get( ) ) > 0 ) { comm.send( idle, EoMpi::Channel::Commands, EoMpi::Message::Finish ); @@ -101,7 +101,7 @@ class MpiJob assignmentAlgo.confirm( idles[i] ); } - cout << "[M] Waits for all responses." << endl; + eo::log << "[M] Waits for all responses." << std::endl; // wait for all responses while( assignmentAlgo.size() != totalWorkers ) { @@ -112,70 +112,43 @@ class MpiJob assignmentAlgo.confirm( wrkRank ); } - cout << "[M] Leaving master task." << endl; + eo::log << "[M] Leaving master task." << std::endl; } void worker( ) { int order; + eo::log << eo::debug; while( true ) { - cout << "[W] Waiting for an order..." << std::endl; - comm.recv( _masterRank, EoMpi::Channel::Commands, order ); + eo::log << "[W] Waiting for an order..." << std::endl; + comm.recv( masterRank, EoMpi::Channel::Commands, order ); if ( order == EoMpi::Message::Finish ) { return; } else { - cout << "[W] Processing task..." << std::endl; + eo::log << "[W] Processing task..." << std::endl; processTask( ); } } } - int masterRank() + void run( ) { - return _masterRank; + ( _isMaster ) ? master( ) : worker( ); + } + + bool isMaster( ) + { + return _isMaster; } protected: AssignmentAlgorithm& assignmentAlgo; mpi::communicator& comm; - int _masterRank; -}; - -class Role -{ - public: - Role( MpiJob & job ) : - _job( job ) - { - _master = job.masterRank() == MpiNode::comm().rank(); - } - - bool master() - { - return _master; - } - - virtual void run( ) - { - if( _master ) - { - _job.master( ); - } else - { - _job.worker( ); - } - } - - virtual ~Role() - { - // empty - } - - protected: - MpiJob & _job; - bool _master; + int masterRank; + bool _isMaster; }; # endif // __EO_MPI_H__ + diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index dd11f7b3a..0fa65f28d 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -16,9 +16,10 @@ struct plusOne : public eoUF< int&, void > int main(int argc, char** argv) { + eo::log << eo::setlevel( eo::debug ); cout << "Appel à init... " << endl; MpiNode::init( argc, argv ); - DynamicAssignmentAlgorithm algo( 1, MpiNode::comm().size()-1 ); + DynamicAssignmentAlgorithm assign( 1, MpiNode::comm().size()-1 ); cout << "Création des données... " << endl; vector v; @@ -32,11 +33,10 @@ int main(int argc, char** argv) plusOne plusOneInstance; cout << "Création du job..." << endl; - ParallelApply job( plusOneInstance, v, algo, 0 ); - Role node( job ); - node.run(); + ParallelApply job( plusOneInstance, v, assign, 0 ); + job.run(); - if( node.master() ) + if( job.isMaster() ) { for(int i = 0; i < v.size(); ++i) { From cf5317f6144eb046dad4f50d6730e1545956832e Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 22 Jun 2012 16:13:08 +0200 Subject: [PATCH 20/76] Static assignement algorithm --- eo/src/mpi/assignmentAlgorithm.h | 102 +++++++++++++++++++++++++++++-- eo/src/mpi/eompi.h | 23 +++---- 2 files changed, 106 insertions(+), 19 deletions(-) diff --git a/eo/src/mpi/assignmentAlgorithm.h b/eo/src/mpi/assignmentAlgorithm.h index 89b97c062..06d3ec493 100644 --- a/eo/src/mpi/assignmentAlgorithm.h +++ b/eo/src/mpi/assignmentAlgorithm.h @@ -1,21 +1,24 @@ # ifndef __ASSIGNMENT_ALGORITHM_H__ # define __ASSIGNMENT_ALGORITHM_H__ +# include + struct AssignmentAlgorithm { virtual int get( ) = 0; - virtual int size( ) = 0; + virtual int availableWorkers( ) = 0; virtual void confirm( int wrkRank ) = 0; + virtual std::vector idles( ) = 0; }; struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm { public: - DynamicAssignmentAlgorithm( int offset, int size ) + DynamicAssignmentAlgorithm( int first, int last ) { - for( int i = 0; offset + i <= size; ++i) + for( int i = first; i <= last; ++i) { - availableWrk.push_back( offset + i ); + availableWrk.push_back( i ); } } @@ -30,7 +33,7 @@ struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm return assignee; } - int size() + int availableWorkers() { return availableWrk.size(); } @@ -40,10 +43,99 @@ struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm availableWrk.push_back( rank ); } + std::vector idles( ) + { + return availableWrk; + } + protected: std::vector< int > availableWrk; }; +struct StaticAssignmentAlgorithm : public AssignmentAlgorithm +{ + public: + StaticAssignmentAlgorithm( int first, int last, int runs ) + { + unsigned int nbWorkers = last - first + 1; + freeWorkers = nbWorkers; + offset = first; + attributions.reserve( nbWorkers ); + busy.resize( nbWorkers, false ); + // Let be the euclidean division of runs by nbWorkers : + // runs == q * nbWorkers + r, 0 <= r < nbWorkers + // This one liner affects q requests to each worker + for (unsigned int i = 0; i < nbWorkers; attributions[i++] = runs / nbWorkers) ; + // The first line computes r and the one liner affects the remaining + // r requests to workers, in ascending order + unsigned int diff = runs - (runs / nbWorkers) * nbWorkers; + for (unsigned int i = 0; i < diff; ++attributions[i++]); + } + + int get( ) + { + int assignee = -1; + for( unsigned i = 0; i < busy.size(); ++i ) + { + if( !busy[i] && attributions[i] > 0 ) + { + busy[i] = true; + --freeWorkers; + assignee = realRank( i ); + break; + } + } + return assignee; + } + + int availableWorkers( ) + { + return freeWorkers; + } + + std::vector idles() + { + std::vector ret; + for(unsigned int i = 0; i < busy.size(); ++i) + { + if( !busy[i] ) + { + eo::log << "Idle : " << realRank(i) << + " / attributions : " << attributions[i] << std::endl; + ret.push_back( realRank(i) ); + } + } + afterIdle = true; + return ret; + } + + void confirm( int rank ) + { + int i = attributionsIndex( rank ); + --attributions[ i ]; + busy[ i ] = false; + ++freeWorkers; + } + + private: + int attributionsIndex( int rank ) + { + return rank - offset; + } + + int realRank( int index ) + { + return index + offset; + } + + std::vector attributions; + std::vector busy; + + bool afterIdle; + int runs; + int offset; + unsigned int freeWorkers; +}; # endif // __ASSIGNMENT_ALGORITHM_H__ diff --git a/eo/src/mpi/eompi.h b/eo/src/mpi/eompi.h index 58ddf1311..44cdbb1a5 100644 --- a/eo/src/mpi/eompi.h +++ b/eo/src/mpi/eompi.h @@ -65,14 +65,13 @@ class MpiJob void master( ) { - int totalWorkers = assignmentAlgo.size(); + int totalWorkers = assignmentAlgo.availableWorkers(); eo::log << eo::debug; eo::log << "[M] Have " << totalWorkers << " workers." << std::endl; while( ! isFinished() ) { int assignee = assignmentAlgo.get( ); - eo::log << "[M] Assignee : " << assignee << std::endl; while( assignee <= 0 ) { eo::log << "[M] Waitin' for node..." << std::endl; @@ -83,27 +82,22 @@ class MpiJob assignmentAlgo.confirm( wrkRank ); assignee = assignmentAlgo.get( ); } + eo::log << "[M] Assignee : " << assignee << std::endl; comm.send( assignee, EoMpi::Channel::Commands, EoMpi::Message::Continue ); sendTask( assignee ); } eo::log << "[M] Frees all the idle." << std::endl; // frees all the idle workers - int idle; - std::vector idles; - while ( ( idle = assignmentAlgo.get( ) ) > 0 ) + std::vector idles = assignmentAlgo.idles(); + for(unsigned int i = 0; i < idles.size(); ++i) { - comm.send( idle, EoMpi::Channel::Commands, EoMpi::Message::Finish ); - idles.push_back( idle ); - } - for (unsigned int i = 0; i < idles.size(); ++i) - { - assignmentAlgo.confirm( idles[i] ); + comm.send( idles[i], EoMpi::Channel::Commands, EoMpi::Message::Finish ); } eo::log << "[M] Waits for all responses." << std::endl; // wait for all responses - while( assignmentAlgo.size() != totalWorkers ) + while( assignmentAlgo.availableWorkers() != totalWorkers ) { mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); int wrkRank = status.source(); @@ -121,14 +115,15 @@ class MpiJob eo::log << eo::debug; while( true ) { - eo::log << "[W] Waiting for an order..." << std::endl; + eo::log << "[W" << comm.rank() << "] Waiting for an order..." << std::endl; comm.recv( masterRank, EoMpi::Channel::Commands, order ); if ( order == EoMpi::Message::Finish ) { + eo::log << "[W" << comm.rank() << "] Leaving worker task." << std::endl; return; } else { - eo::log << "[W] Processing task..." << std::endl; + eo::log << "[W" << comm.rank() << "] Processing task..." << std::endl; processTask( ); } } From aec5236eb1069b7091e6b684113a3452f1425008 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 22 Jun 2012 16:14:27 +0200 Subject: [PATCH 21/76] Updated tests: multipleRoles compiles again and parallelApply tries all kinds of assignments algorithms. --- eo/test/mpi/multipleRoles.cpp | 10 +++--- eo/test/mpi/parallelApply.cpp | 58 ++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/multipleRoles.cpp index c300fdbb1..a45dec420 100644 --- a/eo/test/mpi/multipleRoles.cpp +++ b/eo/test/mpi/multipleRoles.cpp @@ -27,8 +27,7 @@ void subtask( vector& v ) DynamicAssignmentAlgorithm algo( 2, MpiNode::comm().size()-1 ); plusOne plusOneInstance; ParallelApply job( plusOneInstance, v, algo, 1 ); - Role node( job ); - node.run(); + job.run(); } struct transmit : public eoUF< vector&, void > @@ -37,7 +36,7 @@ struct transmit : public eoUF< vector&, void > { cout << "Into the master subjob..." << endl; subtask( v ); - } + } }; int main(int argc, char** argv) @@ -64,9 +63,8 @@ int main(int argc, char** argv) // only one node is assigned to subjob mastering DynamicAssignmentAlgorithm algo( 1, 1 ); ParallelApply< vector > job( transmitInstance, metaV, algo, 0 ); - Role node( job ); - node.run(); - if( node.master() ) + job.run(); + if( job.isMaster() ) { v = metaV[0]; cout << "Results : " << endl; diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 0fa65f28d..278ffd063 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -14,16 +14,18 @@ struct plusOne : public eoUF< int&, void > } }; +struct Test +{ + AssignmentAlgorithm * assign; + string description; +}; + int main(int argc, char** argv) { - eo::log << eo::setlevel( eo::debug ); - cout << "Appel à init... " << endl; + // eo::log << eo::setlevel( eo::debug ); MpiNode::init( argc, argv ); - DynamicAssignmentAlgorithm assign( 1, MpiNode::comm().size()-1 ); - cout << "Création des données... " << endl; vector v; - v.push_back(1); v.push_back(3); v.push_back(3); @@ -32,18 +34,44 @@ int main(int argc, char** argv) plusOne plusOneInstance; - cout << "Création du job..." << endl; - ParallelApply job( plusOneInstance, v, assign, 0 ); - job.run(); + vector< Test > tests; - if( job.isMaster() ) + Test tStatic; + tStatic.assign = new StaticAssignmentAlgorithm( 1, MpiNode::comm().size()-1, v.size() ); + tStatic.description = "Correct static assignment."; + tests.push_back( tStatic ); + + Test tStaticOverload; + tStaticOverload.assign = new StaticAssignmentAlgorithm( 1, MpiNode::comm().size()-1, v.size()+100 ); + tStaticOverload.description = "Static assignment with too many runs."; + tests.push_back( tStaticOverload ); + + Test tDynamic; + tDynamic.assign = new DynamicAssignmentAlgorithm( 1, MpiNode::comm().size()-1 ); + tDynamic.description = "Dynamic assignment."; + tests.push_back( tDynamic ); + + for( unsigned int i = 0; i < tests.size(); ++i ) { - for(int i = 0; i < v.size(); ++i) - { - cout << v[i] << ' '; - } - cout << endl; - } + ParallelApply job( plusOneInstance, v, *(tests[i].assign), 0 ); + if( job.isMaster() ) + { + cout << "Test : " << tests[i].description << endl; + } + + job.run(); + + if( job.isMaster() ) + { + for(int i = 0; i < v.size(); ++i) + { + cout << v[i] << ' '; + } + cout << endl; + } + + delete tests[i].assign; + } return 0; } From 5bfcf4cd2c727b583dca36375b5866a130fc9b1d Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 22 Jun 2012 17:28:31 +0200 Subject: [PATCH 22/76] Moving MpiNode into MpiNode.h --- eo/src/mpi/MpiNode.h | 25 +++++++++++++++++++++++++ eo/src/mpi/eompi.h | 24 +----------------------- 2 files changed, 26 insertions(+), 23 deletions(-) create mode 100644 eo/src/mpi/MpiNode.h diff --git a/eo/src/mpi/MpiNode.h b/eo/src/mpi/MpiNode.h new file mode 100644 index 000000000..10804ce01 --- /dev/null +++ b/eo/src/mpi/MpiNode.h @@ -0,0 +1,25 @@ +# ifndef __MPI_NODE_H__ +# define __MPI_NODE_H__ + +# include +namespace mpi = boost::mpi; + +class MpiNode +{ + public: + + static void init( int argc, char** argv ) + { + static mpi::environment env( argc, argv ); + } + + static mpi::communicator& comm() + { + return _comm; + } + + protected: + static mpi::communicator _comm; +}; + +# endif // __MPI_NODE_H__ diff --git a/eo/src/mpi/eompi.h b/eo/src/mpi/eompi.h index 44cdbb1a5..3b51e7a3a 100644 --- a/eo/src/mpi/eompi.h +++ b/eo/src/mpi/eompi.h @@ -3,12 +3,9 @@ # include # include - -# include -namespace mpi = boost::mpi; - # include +# include "MpiNode.h" # include "assignmentAlgorithm.h" // TODO TODOB comment! @@ -25,25 +22,6 @@ namespace EoMpi const int Finish = 1; } } - -class MpiNode -{ - public: - - static void init( int argc, char** argv ) - { - static mpi::environment env( argc, argv ); - } - - static mpi::communicator& comm() - { - return _comm; - } - - protected: - static mpi::communicator _comm; -}; - class MpiJob { public: From f3cb5eec20229df974865bcccd0dd004d613961b Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 22 Jun 2012 17:31:46 +0200 Subject: [PATCH 23/76] More constructors for assignment algorithms: by interval, by unique worker, by vector of ranks, or whole world. --- eo/src/mpi/assignmentAlgorithm.h | 93 ++++++++++++++++++++++++-------- eo/test/mpi/multipleRoles.cpp | 2 +- eo/test/mpi/parallelApply.cpp | 80 +++++++++++++++++++++++---- 3 files changed, 142 insertions(+), 33 deletions(-) diff --git a/eo/src/mpi/assignmentAlgorithm.h b/eo/src/mpi/assignmentAlgorithm.h index 06d3ec493..58fc09c0e 100644 --- a/eo/src/mpi/assignmentAlgorithm.h +++ b/eo/src/mpi/assignmentAlgorithm.h @@ -2,7 +2,7 @@ # define __ASSIGNMENT_ALGORITHM_H__ # include - +# include "MpiNode.h" struct AssignmentAlgorithm { virtual int get( ) = 0; @@ -14,6 +14,25 @@ struct AssignmentAlgorithm struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm { public: + + DynamicAssignmentAlgorithm( ) + { + for(int i = 1; i < MpiNode::comm().size(); ++i) + { + availableWrk.push_back( i ); + } + } + + DynamicAssignmentAlgorithm( int unique ) + { + availableWrk.push_back( unique ); + } + + DynamicAssignmentAlgorithm( const std::vector & workers ) + { + availableWrk = workers; + } + DynamicAssignmentAlgorithm( int first, int last ) { for( int i = first; i <= last; ++i) @@ -55,11 +74,43 @@ struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm struct StaticAssignmentAlgorithm : public AssignmentAlgorithm { public: + StaticAssignmentAlgorithm( std::vector& workers, int runs ) + { + init( workers, runs ); + } + StaticAssignmentAlgorithm( int first, int last, int runs ) { - unsigned int nbWorkers = last - first + 1; + std::vector workers; + for(int i = first; i <= last; ++i) + { + workers.push_back( i ); + } + init( workers, runs ); + } + + StaticAssignmentAlgorithm( int runs ) + { + std::vector workers; + for(int i = 1; i < MpiNode::comm().size(); ++i) + { + workers.push_back( i ); + } + init( workers, runs ); + } + + StaticAssignmentAlgorithm( int unique, int runs ) + { + std::vector workers; + workers.push_back( unique ); + init( workers, runs ); + } + +private: + void init( std::vector & workers, int runs ) + { + unsigned int nbWorkers = workers.size(); freeWorkers = nbWorkers; - offset = first; attributions.reserve( nbWorkers ); busy.resize( nbWorkers, false ); @@ -71,8 +122,11 @@ struct StaticAssignmentAlgorithm : public AssignmentAlgorithm // r requests to workers, in ascending order unsigned int diff = runs - (runs / nbWorkers) * nbWorkers; for (unsigned int i = 0; i < diff; ++attributions[i++]); + + realRank = workers; } +public: int get( ) { int assignee = -1; @@ -82,7 +136,7 @@ struct StaticAssignmentAlgorithm : public AssignmentAlgorithm { busy[i] = true; --freeWorkers; - assignee = realRank( i ); + assignee = realRank[ i ]; break; } } @@ -101,40 +155,35 @@ struct StaticAssignmentAlgorithm : public AssignmentAlgorithm { if( !busy[i] ) { - eo::log << "Idle : " << realRank(i) << + eo::log << "Idle : " << realRank[i] << " / attributions : " << attributions[i] << std::endl; - ret.push_back( realRank(i) ); + ret.push_back( realRank[i] ); } } - afterIdle = true; return ret; } void confirm( int rank ) { - int i = attributionsIndex( rank ); + int i = -1; + for( int j = 0; j < realRank.size(); ++j ) + { + if( realRank[j] == rank ) + { + i = j; + break; + } + } + --attributions[ i ]; busy[ i ] = false; ++freeWorkers; } private: - int attributionsIndex( int rank ) - { - return rank - offset; - } - - int realRank( int index ) - { - return index + offset; - } - std::vector attributions; + std::vector realRank; std::vector busy; - - bool afterIdle; - int runs; - int offset; unsigned int freeWorkers; }; diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/multipleRoles.cpp index a45dec420..a374def50 100644 --- a/eo/test/mpi/multipleRoles.cpp +++ b/eo/test/mpi/multipleRoles.cpp @@ -61,7 +61,7 @@ int main(int argc, char** argv) case 1: { // only one node is assigned to subjob mastering - DynamicAssignmentAlgorithm algo( 1, 1 ); + DynamicAssignmentAlgorithm algo( 1 ); ParallelApply< vector > job( transmitInstance, metaV, algo, 0 ); job.run(); if( job.isMaster() ) diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 278ffd063..3fd77dc1e 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -18,8 +18,10 @@ struct Test { AssignmentAlgorithm * assign; string description; + int requiredNodesNumber; // nb : chosen nodes ranks must be sequential }; +// These tests require at least 3 processes to be launched. int main(int argc, char** argv) { // eo::log << eo::setlevel( eo::debug ); @@ -32,24 +34,71 @@ int main(int argc, char** argv) v.push_back(7); v.push_back(42); + int offset = 0; + vector originalV = v; + plusOne plusOneInstance; vector< Test > tests; + + const int ALL = MpiNode::comm().size(); - Test tStatic; - tStatic.assign = new StaticAssignmentAlgorithm( 1, MpiNode::comm().size()-1, v.size() ); - tStatic.description = "Correct static assignment."; - tests.push_back( tStatic ); + Test tIntervalStatic; + tIntervalStatic.assign = new StaticAssignmentAlgorithm( 1, MpiNode::comm().size()-1, v.size() ); + tIntervalStatic.description = "Correct static assignment with interval."; + tIntervalStatic.requiredNodesNumber = ALL; + tests.push_back( tIntervalStatic ); + + 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( 1, MpiNode::comm().size()-1, v.size()+100 ); + tStaticOverload.assign = new StaticAssignmentAlgorithm( v.size()+100 ); tStaticOverload.description = "Static assignment with too many runs."; + tStaticOverload.requiredNodesNumber = ALL; tests.push_back( tStaticOverload ); - Test tDynamic; - tDynamic.assign = new DynamicAssignmentAlgorithm( 1, MpiNode::comm().size()-1 ); - tDynamic.description = "Dynamic assignment."; - tests.push_back( tDynamic ); + 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 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 StaticAssignmentAlgorithm( 1, MpiNode::comm().size()-1, v.size() ); + tIntervalDynamic.description = "Correct static assignment with interval."; + tIntervalDynamic.requiredNodesNumber = ALL; + tests.push_back( tIntervalDynamic ); + + Test tUniqueDynamic; + tUniqueDynamic.assign = new StaticAssignmentAlgorithm( 1, v.size() ); + tUniqueDynamic.description = "Correct static assignment with unique worker."; + tUniqueDynamic.requiredNodesNumber = 2; + tests.push_back( tUniqueDynamic ); + + Test tVectorDynamic; + tVectorDynamic.assign = new StaticAssignmentAlgorithm( workers, v.size() ); + tVectorDynamic.description = "Correct static assignment with precise workers specified."; + tVectorDynamic.requiredNodesNumber = tVectorStatic.requiredNodesNumber; + tests.push_back( tVectorDynamic ); + + Test tWorldDynamic; + tWorldDynamic.assign = new StaticAssignmentAlgorithm( v.size() ); + tWorldDynamic.description = "Correct static assignment with whole world as workers."; + tWorldDynamic.requiredNodesNumber = ALL; + tests.push_back( tWorldDynamic ); for( unsigned int i = 0; i < tests.size(); ++i ) { @@ -60,17 +109,28 @@ int main(int argc, char** argv) cout << "Test : " << tests[i].description << endl; } - job.run(); + if( MpiNode::comm().rank() < tests[i].requiredNodesNumber ) + { + job.run(); + } if( job.isMaster() ) { + ++offset; for(int i = 0; i < v.size(); ++i) { cout << v[i] << ' '; + if( originalV[i] + offset != v[i] ) + { + cout << " <-- ERROR at this point." << endl; + exit( EXIT_FAILURE ); + } } cout << endl; } + MpiNode::comm().barrier(); + delete tests[i].assign; } return 0; From ac13550faa7511e874c57acd83963620b130cef6 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 22 Jun 2012 17:40:00 +0200 Subject: [PATCH 24/76] REST_OF_THE_WORLD constant in assignmentAlgorithm. --- eo/src/mpi/assignmentAlgorithm.h | 17 +++++++++++++++++ eo/test/mpi/multipleRoles.cpp | 2 +- eo/test/mpi/parallelApply.cpp | 12 ++++++------ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/eo/src/mpi/assignmentAlgorithm.h b/eo/src/mpi/assignmentAlgorithm.h index 58fc09c0e..a27e8289a 100644 --- a/eo/src/mpi/assignmentAlgorithm.h +++ b/eo/src/mpi/assignmentAlgorithm.h @@ -3,6 +3,12 @@ # include # include "MpiNode.h" + +namespace eo +{ + const int REST_OF_THE_WORLD = -1; +} + struct AssignmentAlgorithm { virtual int get( ) = 0; @@ -35,6 +41,11 @@ struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm DynamicAssignmentAlgorithm( int first, int last ) { + if( last == eo::REST_OF_THE_WORLD ) + { + last = MpiNode::comm().size() - 1; + } + for( int i = first; i <= last; ++i) { availableWrk.push_back( i ); @@ -82,6 +93,12 @@ struct StaticAssignmentAlgorithm : public AssignmentAlgorithm StaticAssignmentAlgorithm( int first, int last, int runs ) { std::vector workers; + + if( last == eo::REST_OF_THE_WORLD ) + { + last = MpiNode::comm().size() - 1; + } + for(int i = first; i <= last; ++i) { workers.push_back( i ); diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/multipleRoles.cpp index a374def50..6fa930d1d 100644 --- a/eo/test/mpi/multipleRoles.cpp +++ b/eo/test/mpi/multipleRoles.cpp @@ -24,7 +24,7 @@ struct plusOne : public eoUF< int&, void > void subtask( vector& v ) { - DynamicAssignmentAlgorithm algo( 2, MpiNode::comm().size()-1 ); + DynamicAssignmentAlgorithm algo( 2, eo::REST_OF_THE_WORLD ); plusOne plusOneInstance; ParallelApply job( plusOneInstance, v, algo, 1 ); job.run(); diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 3fd77dc1e..5a6b64f2c 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -44,7 +44,7 @@ int main(int argc, char** argv) const int ALL = MpiNode::comm().size(); Test tIntervalStatic; - tIntervalStatic.assign = new StaticAssignmentAlgorithm( 1, MpiNode::comm().size()-1, v.size() ); + tIntervalStatic.assign = new StaticAssignmentAlgorithm( 1, eo::REST_OF_THE_WORLD, v.size() ); tIntervalStatic.description = "Correct static assignment with interval."; tIntervalStatic.requiredNodesNumber = ALL; tests.push_back( tIntervalStatic ); @@ -77,26 +77,26 @@ int main(int argc, char** argv) tests.push_back( tVectorStatic ); Test tIntervalDynamic; - tIntervalDynamic.assign = new StaticAssignmentAlgorithm( 1, MpiNode::comm().size()-1, v.size() ); - tIntervalDynamic.description = "Correct static assignment with interval."; + tIntervalDynamic.assign = new StaticAssignmentAlgorithm( 1, eo::REST_OF_THE_WORLD, v.size() ); + tIntervalDynamic.description = "Dynamic assignment with interval."; tIntervalDynamic.requiredNodesNumber = ALL; tests.push_back( tIntervalDynamic ); Test tUniqueDynamic; tUniqueDynamic.assign = new StaticAssignmentAlgorithm( 1, v.size() ); - tUniqueDynamic.description = "Correct static assignment with unique worker."; + tUniqueDynamic.description = "Dynamic assignment with unique worker."; tUniqueDynamic.requiredNodesNumber = 2; tests.push_back( tUniqueDynamic ); Test tVectorDynamic; tVectorDynamic.assign = new StaticAssignmentAlgorithm( workers, v.size() ); - tVectorDynamic.description = "Correct static assignment with precise workers specified."; + tVectorDynamic.description = "Dynamic assignment with precise workers specified."; tVectorDynamic.requiredNodesNumber = tVectorStatic.requiredNodesNumber; tests.push_back( tVectorDynamic ); Test tWorldDynamic; tWorldDynamic.assign = new StaticAssignmentAlgorithm( v.size() ); - tWorldDynamic.description = "Correct static assignment with whole world as workers."; + tWorldDynamic.description = "Dynamic assignment with whole world as workers."; tWorldDynamic.requiredNodesNumber = ALL; tests.push_back( tWorldDynamic ); From 5bf03dec2b090abb25fbeb78faad469570e5c2f3 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 22 Jun 2012 17:56:08 +0200 Subject: [PATCH 25/76] Multiple roles demo has now 2 sub masters. Needs to be launched with at least 7 nodes. --- eo/test/mpi/multipleRoles.cpp | 44 +++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/multipleRoles.cpp index 6fa930d1d..d855c9989 100644 --- a/eo/test/mpi/multipleRoles.cpp +++ b/eo/test/mpi/multipleRoles.cpp @@ -13,34 +13,42 @@ using namespace std; // 1 : worker of general job, master of subjob // 2 and more : workers of subjob -struct plusOne : public eoUF< int&, void > +struct SubWork: public eoUF< int&, void > { void operator() ( int & x ) { - cout << "Subjob is being applied." << endl; + cout << "Subwork phase." << endl; ++x; } }; -void subtask( vector& v ) +void subtask( vector& v, int rank ) { - DynamicAssignmentAlgorithm algo( 2, eo::REST_OF_THE_WORLD ); - plusOne plusOneInstance; - ParallelApply job( plusOneInstance, v, algo, 1 ); + vector workers; + workers.push_back( rank + 2 ); + workers.push_back( rank + 4 ); + DynamicAssignmentAlgorithm algo( workers ); + SubWork sw; + ParallelApply job( sw, v, algo, rank ); job.run(); } -struct transmit : public eoUF< vector&, void > +struct Work: public eoUF< vector&, void > { void operator() ( vector& v ) { - cout << "Into the master subjob..." << endl; - subtask( v ); + cout << "Work phase..." << endl; + subtask( v, MpiNode::comm().rank() ); + for( int i = 0; i < v.size(); ++i ) + { + v[i] *= 2; + } } }; int main(int argc, char** argv) { + // eo::log << eo::setlevel( eo::debug ); MpiNode::init( argc, argv ); vector v; @@ -50,19 +58,19 @@ int main(int argc, char** argv) v.push_back(7); v.push_back(42); - transmit transmitInstance; - vector< vector > metaV; metaV.push_back( v ); + metaV.push_back( v ); switch( MpiNode::comm().rank() ) { case 0: case 1: + case 2: { - // only one node is assigned to subjob mastering - DynamicAssignmentAlgorithm algo( 1 ); - ParallelApply< vector > job( transmitInstance, metaV, algo, 0 ); + Work w; + DynamicAssignmentAlgorithm algo( 1, 2 ); + ParallelApply< vector > job( w, metaV, algo, 0 ); job.run(); if( job.isMaster() ) { @@ -80,7 +88,13 @@ int main(int argc, char** argv) default: { // all the other nodes are sub workers - subtask( v ); + int rank = MpiNode::comm().rank(); + if ( rank == 3 or rank == 5 ) + { + subtask( v, 1 ); + } else { + subtask( v, 2 ); + } } break; } From 66c7b1f12d49cf123b1c60cbc0f977c387a44d7a Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 25 Jun 2012 11:44:14 +0200 Subject: [PATCH 26/76] Using real dynamic assignments for tests. --- eo/test/mpi/parallelApply.cpp | 94 ++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 5a6b64f2c..d95771210 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -24,7 +24,8 @@ struct Test // These tests require at least 3 processes to be launched. int main(int argc, char** argv) { - // eo::log << eo::setlevel( eo::debug ); + eo::log << eo::setlevel( eo::debug ); + bool launchOnlyOne = true; MpiNode::init( argc, argv ); vector v; @@ -49,60 +50,63 @@ int main(int argc, char** argv) tIntervalStatic.requiredNodesNumber = ALL; tests.push_back( tIntervalStatic ); - 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 ); + 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 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 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 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 tVectorStatic; + vector 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 StaticAssignmentAlgorithm( 1, eo::REST_OF_THE_WORLD, v.size() ); - tIntervalDynamic.description = "Dynamic assignment with interval."; - tIntervalDynamic.requiredNodesNumber = ALL; - tests.push_back( tIntervalDynamic ); + Test tIntervalDynamic; + tIntervalDynamic.assign = new DynamicAssignmentAlgorithm( 1, eo::REST_OF_THE_WORLD ); + tIntervalDynamic.description = "Dynamic assignment with interval."; + tIntervalDynamic.requiredNodesNumber = ALL; + tests.push_back( tIntervalDynamic ); - Test tUniqueDynamic; - tUniqueDynamic.assign = new StaticAssignmentAlgorithm( 1, v.size() ); - tUniqueDynamic.description = "Dynamic assignment with unique worker."; - tUniqueDynamic.requiredNodesNumber = 2; - tests.push_back( tUniqueDynamic ); + 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 StaticAssignmentAlgorithm( workers, v.size() ); - tVectorDynamic.description = "Dynamic assignment with precise workers specified."; - tVectorDynamic.requiredNodesNumber = tVectorStatic.requiredNodesNumber; - tests.push_back( tVectorDynamic ); + 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 StaticAssignmentAlgorithm( v.size() ); - tWorldDynamic.description = "Dynamic assignment with whole world as workers."; - tWorldDynamic.requiredNodesNumber = ALL; - tests.push_back( tWorldDynamic ); + 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 ) { - ParallelApply job( plusOneInstance, v, *(tests[i].assign), 0 ); + ParallelApply job( plusOneInstance, v, *(tests[i].assign), 0, 3 ); if( job.isMaster() ) { From f4d8b43f7d15e923acdc59eb7e64590757e2a421 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 25 Jun 2012 13:41:48 +0200 Subject: [PATCH 27/76] ParallelApply can now handle many data at a time. --- eo/src/mpi/eoParallelApply.h | 64 ++++++++++++++++++++++++++++------- eo/test/mpi/multipleRoles.cpp | 4 +-- eo/test/mpi/parallelApply.cpp | 5 +-- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 0c4f1e6e4..e401fda69 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -6,39 +6,76 @@ # include # include + template< typename EOT > class ParallelApply : public MpiJob { + private: + struct ParallelApplyAssignment + { + int index; + int size; + }; public: - ParallelApply( eoUF & _proc, std::vector& _pop, AssignmentAlgorithm & algo, int _masterRank ) : + ParallelApply( + eoUF & _proc, + std::vector& _pop, + AssignmentAlgorithm & algo, + int _masterRank, + int _packetSize = 1 + ) : MpiJob( algo, _masterRank ), func( _proc ), index( 0 ), size( _pop.size() ), - data( _pop ) + data( _pop ), + packetSize( _packetSize ) { - // empty + tempArray = new EOT[ packetSize ]; + } + + ~ParallelApply() + { + delete [] tempArray; } virtual void sendTask( int wrkRank ) { - assignedTasks[ wrkRank ] = index; - comm.send( wrkRank, 1, data[ index ] ); - ++index; + int futureIndex; + + if( index + packetSize < size ) + { + futureIndex = index + packetSize; + } else { + futureIndex = size; + } + + int sentSize = futureIndex - index ; + comm.send( wrkRank, 1, sentSize ); + + assignedTasks[ wrkRank ].index = index; + assignedTasks[ wrkRank ].size = sentSize; + + comm.send( wrkRank, 1, &data[ index ] , sentSize ); + index = futureIndex; } virtual void handleResponse( int wrkRank ) { - comm.recv( wrkRank, 1, data[ assignedTasks[ wrkRank ] ] ); + comm.recv( wrkRank, 1, &data[ assignedTasks[wrkRank].index ], assignedTasks[wrkRank].size ); } virtual void processTask( ) { - EOT ind; - comm.recv( masterRank, 1, ind ); - func( ind ); - comm.send( masterRank, 1, ind ); + int recvSize; + comm.recv( masterRank, 1, recvSize ); + comm.recv( masterRank, 1, tempArray, recvSize ); + for( int i = 0; i < recvSize ; ++i ) + { + func( tempArray[ i ] ); + } + comm.send( masterRank, 1, tempArray, recvSize ); } bool isFinished() @@ -51,7 +88,10 @@ class ParallelApply : public MpiJob eoUF& func; int index; int size; - std::map< int /* worker rank */, int /* index in vector */> assignedTasks; + std::map< int /* worker rank */, ParallelApplyAssignment /* min indexes in vector */> assignedTasks; + + int packetSize; + EOT* tempArray; }; # endif // __EO_PARALLEL_APPLY_H__ diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/multipleRoles.cpp index d855c9989..ab8020732 100644 --- a/eo/test/mpi/multipleRoles.cpp +++ b/eo/test/mpi/multipleRoles.cpp @@ -10,8 +10,8 @@ using namespace std; // Role map // 0 : general master -// 1 : worker of general job, master of subjob -// 2 and more : workers of subjob +// 1, 2 : worker of general job, master of subjob +// 3 to 7 : workers of subjob struct SubWork: public eoUF< int&, void > { diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index d95771210..69b01e485 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -24,8 +24,9 @@ struct Test // These tests require at least 3 processes to be launched. int main(int argc, char** argv) { - eo::log << eo::setlevel( eo::debug ); - bool launchOnlyOne = true; + // eo::log << eo::setlevel( eo::debug ); + bool launchOnlyOne = false; // Set this to true if you wanna launch only the first test. + MpiNode::init( argc, argv ); vector v; From 2cc112c33ba46c6f2976337532087143ece5c3a7 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 25 Jun 2012 13:50:22 +0200 Subject: [PATCH 28/76] Throwing exception when packet size is negative. --- eo/src/mpi/eoParallelApply.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index e401fda69..064396daa 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -6,7 +6,6 @@ # include # include - template< typename EOT > class ParallelApply : public MpiJob { @@ -32,6 +31,10 @@ class ParallelApply : public MpiJob data( _pop ), packetSize( _packetSize ) { + if ( _packetSize <= 0 ) + { + throw std::runtime_error("Packet size should not be negative."); + } tempArray = new EOT[ packetSize ]; } From d9c7ef0300903898b09c1b8f873dc6bba1c532dc Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 25 Jun 2012 13:51:49 +0200 Subject: [PATCH 29/76] Parallel apply test is done on a big table. --- eo/test/mpi/parallelApply.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 69b01e485..143e73500 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -30,12 +30,11 @@ int main(int argc, char** argv) MpiNode::init( argc, argv ); vector v; - v.push_back(1); - v.push_back(3); - v.push_back(3); - v.push_back(7); - v.push_back(42); - + for( int i = 0; i < 1000; ++i ) + { + v.push_back( rand() ); + } + int offset = 0; vector originalV = v; @@ -140,3 +139,4 @@ int main(int argc, char** argv) } return 0; } + From b9a2246f82c00f91d8af5d2ff0d0f80c09b1eca6 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 25 Jun 2012 13:53:34 +0200 Subject: [PATCH 30/76] Generating time based seed for random generator --- eo/test/mpi/parallelApply.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 143e73500..46130b80a 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -29,6 +29,7 @@ int main(int argc, char** argv) MpiNode::init( argc, argv ); + srand( time(0) ); vector v; for( int i = 0; i < 1000; ++i ) { From b291e56e03c60dd04d16808fbbf513cd4c9916af Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 25 Jun 2012 14:11:44 +0200 Subject: [PATCH 31/76] Putting everything in namespace eo::mpi --- eo/src/mpi/CMakeLists.txt | 2 +- eo/src/mpi/MpiNode.h | 25 ---- eo/src/mpi/assignmentAlgorithm.h | 207 -------------------------- eo/src/mpi/eoMpi.cpp | 11 ++ eo/src/mpi/eoMpi.h | 132 ++++++++++++++++ eo/src/mpi/eoMpiAssignmentAlgorithm.h | 207 ++++++++++++++++++++++++++ eo/src/mpi/eoMpiNode.h | 31 ++++ eo/src/mpi/eoParallelApply.h | 183 ++++++++++++----------- eo/src/mpi/eompi.cpp | 5 - eo/src/mpi/eompi.h | 127 ---------------- eo/test/mpi/multipleRoles.cpp | 12 +- eo/test/mpi/parallelApply.cpp | 16 +- 12 files changed, 492 insertions(+), 466 deletions(-) delete mode 100644 eo/src/mpi/MpiNode.h delete mode 100644 eo/src/mpi/assignmentAlgorithm.h create mode 100644 eo/src/mpi/eoMpi.cpp create mode 100644 eo/src/mpi/eoMpi.h create mode 100644 eo/src/mpi/eoMpiAssignmentAlgorithm.h create mode 100644 eo/src/mpi/eoMpiNode.h delete mode 100644 eo/src/mpi/eompi.cpp delete mode 100644 eo/src/mpi/eompi.h diff --git a/eo/src/mpi/CMakeLists.txt b/eo/src/mpi/CMakeLists.txt index c770acd13..0e22e47e0 100644 --- a/eo/src/mpi/CMakeLists.txt +++ b/eo/src/mpi/CMakeLists.txt @@ -13,7 +13,7 @@ SET(EOMPI_LIB_OUTPUT_PATH ${EO_BINARY_DIR}/lib) SET(LIBRARY_OUTPUT_PATH ${EOMPI_LIB_OUTPUT_PATH}) SET(EOMPI_SOURCES - eompi.cpp + eoMpi.cpp ) ADD_LIBRARY(eompi STATIC ${EOMPI_SOURCES}) diff --git a/eo/src/mpi/MpiNode.h b/eo/src/mpi/MpiNode.h deleted file mode 100644 index 10804ce01..000000000 --- a/eo/src/mpi/MpiNode.h +++ /dev/null @@ -1,25 +0,0 @@ -# ifndef __MPI_NODE_H__ -# define __MPI_NODE_H__ - -# include -namespace mpi = boost::mpi; - -class MpiNode -{ - public: - - static void init( int argc, char** argv ) - { - static mpi::environment env( argc, argv ); - } - - static mpi::communicator& comm() - { - return _comm; - } - - protected: - static mpi::communicator _comm; -}; - -# endif // __MPI_NODE_H__ diff --git a/eo/src/mpi/assignmentAlgorithm.h b/eo/src/mpi/assignmentAlgorithm.h deleted file mode 100644 index a27e8289a..000000000 --- a/eo/src/mpi/assignmentAlgorithm.h +++ /dev/null @@ -1,207 +0,0 @@ -# ifndef __ASSIGNMENT_ALGORITHM_H__ -# define __ASSIGNMENT_ALGORITHM_H__ - -# include -# include "MpiNode.h" - -namespace eo -{ - const int REST_OF_THE_WORLD = -1; -} - -struct AssignmentAlgorithm -{ - virtual int get( ) = 0; - virtual int availableWorkers( ) = 0; - virtual void confirm( int wrkRank ) = 0; - virtual std::vector idles( ) = 0; -}; - -struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm -{ - public: - - DynamicAssignmentAlgorithm( ) - { - for(int i = 1; i < MpiNode::comm().size(); ++i) - { - availableWrk.push_back( i ); - } - } - - DynamicAssignmentAlgorithm( int unique ) - { - availableWrk.push_back( unique ); - } - - DynamicAssignmentAlgorithm( const std::vector & workers ) - { - availableWrk = workers; - } - - DynamicAssignmentAlgorithm( int first, int last ) - { - if( last == eo::REST_OF_THE_WORLD ) - { - last = MpiNode::comm().size() - 1; - } - - for( int i = first; i <= last; ++i) - { - availableWrk.push_back( i ); - } - } - - virtual int get( ) - { - int assignee = -1; - if (! availableWrk.empty() ) - { - assignee = availableWrk.back(); - availableWrk.pop_back(); - } - return assignee; - } - - int availableWorkers() - { - return availableWrk.size(); - } - - void confirm( int rank ) - { - availableWrk.push_back( rank ); - } - - std::vector idles( ) - { - return availableWrk; - } - - protected: - std::vector< int > availableWrk; -}; - -struct StaticAssignmentAlgorithm : public AssignmentAlgorithm -{ - public: - StaticAssignmentAlgorithm( std::vector& workers, int runs ) - { - init( workers, runs ); - } - - StaticAssignmentAlgorithm( int first, int last, int runs ) - { - std::vector workers; - - if( last == eo::REST_OF_THE_WORLD ) - { - last = MpiNode::comm().size() - 1; - } - - for(int i = first; i <= last; ++i) - { - workers.push_back( i ); - } - init( workers, runs ); - } - - StaticAssignmentAlgorithm( int runs ) - { - std::vector workers; - for(int i = 1; i < MpiNode::comm().size(); ++i) - { - workers.push_back( i ); - } - init( workers, runs ); - } - - StaticAssignmentAlgorithm( int unique, int runs ) - { - std::vector workers; - workers.push_back( unique ); - init( workers, runs ); - } - -private: - void init( std::vector & workers, int runs ) - { - unsigned int nbWorkers = workers.size(); - freeWorkers = nbWorkers; - attributions.reserve( nbWorkers ); - busy.resize( nbWorkers, false ); - - // Let be the euclidean division of runs by nbWorkers : - // runs == q * nbWorkers + r, 0 <= r < nbWorkers - // This one liner affects q requests to each worker - for (unsigned int i = 0; i < nbWorkers; attributions[i++] = runs / nbWorkers) ; - // The first line computes r and the one liner affects the remaining - // r requests to workers, in ascending order - unsigned int diff = runs - (runs / nbWorkers) * nbWorkers; - for (unsigned int i = 0; i < diff; ++attributions[i++]); - - realRank = workers; - } - -public: - int get( ) - { - int assignee = -1; - for( unsigned i = 0; i < busy.size(); ++i ) - { - if( !busy[i] && attributions[i] > 0 ) - { - busy[i] = true; - --freeWorkers; - assignee = realRank[ i ]; - break; - } - } - return assignee; - } - - int availableWorkers( ) - { - return freeWorkers; - } - - std::vector idles() - { - std::vector ret; - for(unsigned int i = 0; i < busy.size(); ++i) - { - if( !busy[i] ) - { - eo::log << "Idle : " << realRank[i] << - " / attributions : " << attributions[i] << std::endl; - ret.push_back( realRank[i] ); - } - } - return ret; - } - - void confirm( int rank ) - { - int i = -1; - for( int j = 0; j < realRank.size(); ++j ) - { - if( realRank[j] == rank ) - { - i = j; - break; - } - } - - --attributions[ i ]; - busy[ i ] = false; - ++freeWorkers; - } - - private: - std::vector attributions; - std::vector realRank; - std::vector busy; - unsigned int freeWorkers; -}; - -# endif // __ASSIGNMENT_ALGORITHM_H__ diff --git a/eo/src/mpi/eoMpi.cpp b/eo/src/mpi/eoMpi.cpp new file mode 100644 index 000000000..f6e3b3c52 --- /dev/null +++ b/eo/src/mpi/eoMpi.cpp @@ -0,0 +1,11 @@ +# include "eoMpi.h" + +// MpiNode* MpiNodeStore::singleton; +namespace eo +{ + namespace mpi + { + bmpi::communicator Node::_comm; + } +} + diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h new file mode 100644 index 000000000..86ad55ab3 --- /dev/null +++ b/eo/src/mpi/eoMpi.h @@ -0,0 +1,132 @@ +# ifndef __EO_MPI_H__ +# define __EO_MPI_H__ + +# include +# include +# include + +# include "eoMpiNode.h" +# include "eoMpiAssignmentAlgorithm.h" +// TODO TODOB comment! + +namespace eo +{ + namespace mpi + { + namespace Channel + { + const int Commands = 0; + } + + namespace Message + { + const int Continue = 0; + const int Finish = 1; + } + + class Job + { + public: + + Job( AssignmentAlgorithm& _algo, int _masterRank ) : + assignmentAlgo( _algo ), + comm( Node::comm() ), + masterRank( _masterRank ) + { + _isMaster = Node::comm().rank() == _masterRank; + } + + // master + virtual bool isFinished() = 0; + virtual void sendTask( int wrkRank ) = 0; + virtual void handleResponse( int wrkRank ) = 0; + // worker + virtual void processTask( ) = 0; + + void master( ) + { + int totalWorkers = assignmentAlgo.availableWorkers(); + eo::log << eo::debug; + eo::log << "[M" << comm.rank() << "] Have " << totalWorkers << " workers." << std::endl; + + while( ! isFinished() ) + { + int assignee = assignmentAlgo.get( ); + while( assignee <= 0 ) + { + eo::log << "[M" << comm.rank() << "] Waitin' for node..." << std::endl; + bmpi::status status = comm.probe( bmpi::any_source, bmpi::any_tag ); + int wrkRank = status.source(); + eo::log << "[M" << comm.rank() << "] Node " << wrkRank << " just terminated." << std::endl; + handleResponse( wrkRank ); + assignmentAlgo.confirm( wrkRank ); + assignee = assignmentAlgo.get( ); + } + eo::log << "[M" << comm.rank() << "] Assignee : " << assignee << std::endl; + comm.send( assignee, Channel::Commands, Message::Continue ); + sendTask( assignee ); + } + + eo::log << "[M" << comm.rank() << "] Frees all the idle." << std::endl; + // frees all the idle workers + std::vector idles = assignmentAlgo.idles(); + for(unsigned int i = 0; i < idles.size(); ++i) + { + comm.send( idles[i], Channel::Commands, Message::Finish ); + } + + eo::log << "[M" << comm.rank() << "] Waits for all responses." << std::endl; + // wait for all responses + while( assignmentAlgo.availableWorkers() != totalWorkers ) + { + bmpi::status status = comm.probe( bmpi::any_source, bmpi::any_tag ); + int wrkRank = status.source(); + handleResponse( wrkRank ); + comm.send( wrkRank, Channel::Commands, Message::Finish ); + assignmentAlgo.confirm( wrkRank ); + } + + eo::log << "[M" << comm.rank() << "] Leaving master task." << std::endl; + } + + void worker( ) + { + int order; + eo::log << eo::debug; + while( true ) + { + eo::log << "[W" << comm.rank() << "] Waiting for an order..." << std::endl; + comm.recv( masterRank, Channel::Commands, order ); + if ( order == Message::Finish ) + { + eo::log << "[W" << comm.rank() << "] Leaving worker task." << std::endl; + return; + } else + { + eo::log << "[W" << comm.rank() << "] Processing task..." << std::endl; + processTask( ); + } + } + } + + void run( ) + { + ( _isMaster ) ? master( ) : worker( ); + } + + bool isMaster( ) + { + return _isMaster; + } + + protected: + AssignmentAlgorithm& assignmentAlgo; + bmpi::communicator& comm; + int masterRank; + bool _isMaster; + }; + } +} + +# endif // __EO_MPI_H__ + diff --git a/eo/src/mpi/eoMpiAssignmentAlgorithm.h b/eo/src/mpi/eoMpiAssignmentAlgorithm.h new file mode 100644 index 000000000..aa162d951 --- /dev/null +++ b/eo/src/mpi/eoMpiAssignmentAlgorithm.h @@ -0,0 +1,207 @@ +# ifndef __MPI_ASSIGNMENT_ALGORITHM_H__ +# define __MPI_ASSIGNMENT_ALGORITHM_H__ + +# include +# include "eoMpiNode.h" + +namespace eo +{ + namespace mpi + { + const int REST_OF_THE_WORLD = -1; + + struct AssignmentAlgorithm + { + virtual int get( ) = 0; + virtual int availableWorkers( ) = 0; + virtual void confirm( int wrkRank ) = 0; + virtual std::vector idles( ) = 0; + }; + + struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm + { + public: + + DynamicAssignmentAlgorithm( ) + { + for(int i = 1; i < Node::comm().size(); ++i) + { + availableWrk.push_back( i ); + } + } + + DynamicAssignmentAlgorithm( int unique ) + { + availableWrk.push_back( unique ); + } + + DynamicAssignmentAlgorithm( const std::vector & workers ) + { + availableWrk = workers; + } + + DynamicAssignmentAlgorithm( int first, int last ) + { + if( last == REST_OF_THE_WORLD ) + { + last = Node::comm().size() - 1; + } + + for( int i = first; i <= last; ++i) + { + availableWrk.push_back( i ); + } + } + + virtual int get( ) + { + int assignee = -1; + if (! availableWrk.empty() ) + { + assignee = availableWrk.back(); + availableWrk.pop_back(); + } + return assignee; + } + + int availableWorkers() + { + return availableWrk.size(); + } + + void confirm( int rank ) + { + availableWrk.push_back( rank ); + } + + std::vector idles( ) + { + return availableWrk; + } + + protected: + std::vector< int > availableWrk; + }; + + struct StaticAssignmentAlgorithm : public AssignmentAlgorithm + { + public: + StaticAssignmentAlgorithm( std::vector& workers, int runs ) + { + init( workers, runs ); + } + + StaticAssignmentAlgorithm( int first, int last, int runs ) + { + std::vector workers; + + if( last == REST_OF_THE_WORLD ) + { + last = Node::comm().size() - 1; + } + + for(int i = first; i <= last; ++i) + { + workers.push_back( i ); + } + init( workers, runs ); + } + + StaticAssignmentAlgorithm( int runs ) + { + std::vector workers; + for(int i = 1; i < Node::comm().size(); ++i) + { + workers.push_back( i ); + } + init( workers, runs ); + } + + StaticAssignmentAlgorithm( int unique, int runs ) + { + std::vector workers; + workers.push_back( unique ); + init( workers, runs ); + } + + private: + void init( std::vector & workers, int runs ) + { + unsigned int nbWorkers = workers.size(); + freeWorkers = nbWorkers; + attributions.reserve( nbWorkers ); + busy.resize( nbWorkers, false ); + + // Let be the euclidean division of runs by nbWorkers : + // runs == q * nbWorkers + r, 0 <= r < nbWorkers + // This one liner affects q requests to each worker + for (unsigned int i = 0; i < nbWorkers; attributions[i++] = runs / nbWorkers) ; + // The first line computes r and the one liner affects the remaining + // r requests to workers, in ascending order + unsigned int diff = runs - (runs / nbWorkers) * nbWorkers; + for (unsigned int i = 0; i < diff; ++attributions[i++]); + + realRank = workers; + } + + public: + int get( ) + { + int assignee = -1; + for( unsigned i = 0; i < busy.size(); ++i ) + { + if( !busy[i] && attributions[i] > 0 ) + { + busy[i] = true; + --freeWorkers; + assignee = realRank[ i ]; + break; + } + } + return assignee; + } + + int availableWorkers( ) + { + return freeWorkers; + } + + std::vector idles() + { + std::vector ret; + for(unsigned int i = 0; i < busy.size(); ++i) + { + if( !busy[i] ) + { + ret.push_back( realRank[i] ); + } + } + return ret; + } + + void confirm( int rank ) + { + int i = -1; + for( unsigned int j = 0; j < realRank.size(); ++j ) + { + if( realRank[j] == rank ) + { + i = j; + break; + } + } + + --attributions[ i ]; + busy[ i ] = false; + ++freeWorkers; + } + + private: + std::vector attributions; + std::vector realRank; + std::vector busy; + unsigned int freeWorkers; + }; + } +} +# endif // __MPI_ASSIGNMENT_ALGORITHM_H__ diff --git a/eo/src/mpi/eoMpiNode.h b/eo/src/mpi/eoMpiNode.h new file mode 100644 index 000000000..9f1ea7b53 --- /dev/null +++ b/eo/src/mpi/eoMpiNode.h @@ -0,0 +1,31 @@ +# ifndef __MPI_NODE_H__ +# define __MPI_NODE_H__ + +# include +namespace bmpi = boost::mpi; + +namespace eo +{ + namespace mpi + { + class Node + { + public: + + static void init( int argc, char** argv ) + { + static bmpi::environment env( argc, argv ); + } + + static bmpi::communicator& comm() + { + return _comm; + } + + protected: + static bmpi::communicator _comm; + }; + } +} +# endif // __MPI_NODE_H__ + diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 064396daa..1e9d4c1de 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -1,102 +1,107 @@ # ifndef __EO_PARALLEL_APPLY_H__ # define __EO_PARALLEL_APPLY_H__ -# include "eompi.h" +# include "eoMpi.h" # include # include -template< typename EOT > -class ParallelApply : public MpiJob +namespace eo { - private: - struct ParallelApplyAssignment + namespace mpi + { + template< typename EOT > + class ParallelApply : public Job { - int index; - int size; + private: + struct ParallelApplyAssignment + { + int index; + int size; + }; + public: + + ParallelApply( + eoUF & _proc, + std::vector& _pop, + AssignmentAlgorithm & algo, + int _masterRank, + int _packetSize = 1 + ) : + Job( algo, _masterRank ), + func( _proc ), + index( 0 ), + size( _pop.size() ), + data( _pop ), + packetSize( _packetSize ) + { + if ( _packetSize <= 0 ) + { + throw std::runtime_error("Packet size should not be negative."); + } + tempArray = new EOT[ packetSize ]; + } + + ~ParallelApply() + { + delete [] tempArray; + } + + virtual void sendTask( int wrkRank ) + { + int futureIndex; + + if( index + packetSize < size ) + { + futureIndex = index + packetSize; + } else { + futureIndex = size; + } + + int sentSize = futureIndex - index ; + comm.send( wrkRank, 1, sentSize ); + + assignedTasks[ wrkRank ].index = index; + assignedTasks[ wrkRank ].size = sentSize; + + comm.send( wrkRank, 1, &data[ index ] , sentSize ); + index = futureIndex; + } + + virtual void handleResponse( int wrkRank ) + { + comm.recv( wrkRank, 1, &data[ assignedTasks[wrkRank].index ], assignedTasks[wrkRank].size ); + } + + virtual void processTask( ) + { + int recvSize; + comm.recv( masterRank, 1, recvSize ); + comm.recv( masterRank, 1, tempArray, recvSize ); + for( int i = 0; i < recvSize ; ++i ) + { + func( tempArray[ i ] ); + } + comm.send( masterRank, 1, tempArray, recvSize ); + } + + bool isFinished() + { + return index == size; + } + + protected: + std::vector & data; + eoUF& func; + int index; + int size; + std::map< int /* worker rank */, ParallelApplyAssignment /* min indexes in vector */> assignedTasks; + + int packetSize; + EOT* tempArray; }; - public: - - ParallelApply( - eoUF & _proc, - std::vector& _pop, - AssignmentAlgorithm & algo, - int _masterRank, - int _packetSize = 1 - ) : - MpiJob( algo, _masterRank ), - func( _proc ), - index( 0 ), - size( _pop.size() ), - data( _pop ), - packetSize( _packetSize ) - { - if ( _packetSize <= 0 ) - { - throw std::runtime_error("Packet size should not be negative."); - } - tempArray = new EOT[ packetSize ]; - } - - ~ParallelApply() - { - delete [] tempArray; - } - - virtual void sendTask( int wrkRank ) - { - int futureIndex; - - if( index + packetSize < size ) - { - futureIndex = index + packetSize; - } else { - futureIndex = size; - } - - int sentSize = futureIndex - index ; - comm.send( wrkRank, 1, sentSize ); - - assignedTasks[ wrkRank ].index = index; - assignedTasks[ wrkRank ].size = sentSize; - - comm.send( wrkRank, 1, &data[ index ] , sentSize ); - index = futureIndex; - } - - virtual void handleResponse( int wrkRank ) - { - comm.recv( wrkRank, 1, &data[ assignedTasks[wrkRank].index ], assignedTasks[wrkRank].size ); - } - - virtual void processTask( ) - { - int recvSize; - comm.recv( masterRank, 1, recvSize ); - comm.recv( masterRank, 1, tempArray, recvSize ); - for( int i = 0; i < recvSize ; ++i ) - { - func( tempArray[ i ] ); - } - comm.send( masterRank, 1, tempArray, recvSize ); - } - - bool isFinished() - { - return index == size; - } - - protected: - std::vector & data; - eoUF& func; - int index; - int size; - std::map< int /* worker rank */, ParallelApplyAssignment /* min indexes in vector */> assignedTasks; - - int packetSize; - EOT* tempArray; -}; - + } +} # endif // __EO_PARALLEL_APPLY_H__ diff --git a/eo/src/mpi/eompi.cpp b/eo/src/mpi/eompi.cpp deleted file mode 100644 index 6429a31c5..000000000 --- a/eo/src/mpi/eompi.cpp +++ /dev/null @@ -1,5 +0,0 @@ -# include "eompi.h" - -// MpiNode* MpiNodeStore::singleton; -mpi::communicator MpiNode::_comm; - diff --git a/eo/src/mpi/eompi.h b/eo/src/mpi/eompi.h deleted file mode 100644 index 3b51e7a3a..000000000 --- a/eo/src/mpi/eompi.h +++ /dev/null @@ -1,127 +0,0 @@ -# ifndef __EO_MPI_H__ -# define __EO_MPI_H__ - -# include -# include -# include - -# include "MpiNode.h" -# include "assignmentAlgorithm.h" -// TODO TODOB comment! - -namespace EoMpi -{ - namespace Channel - { - const int Commands = 0; - } - - namespace Message - { - const int Continue = 0; - const int Finish = 1; - } -} -class MpiJob -{ - public: - - MpiJob( AssignmentAlgorithm& _algo, int _masterRank ) : - assignmentAlgo( _algo ), - comm( MpiNode::comm() ), - masterRank( _masterRank ) - { - _isMaster = MpiNode::comm().rank() == _masterRank; - } - - // master - virtual bool isFinished() = 0; - virtual void sendTask( int wrkRank ) = 0; - virtual void handleResponse( int wrkRank ) = 0; - // worker - virtual void processTask( ) = 0; - - void master( ) - { - int totalWorkers = assignmentAlgo.availableWorkers(); - eo::log << eo::debug; - eo::log << "[M] Have " << totalWorkers << " workers." << std::endl; - - while( ! isFinished() ) - { - int assignee = assignmentAlgo.get( ); - while( assignee <= 0 ) - { - eo::log << "[M] Waitin' for node..." << std::endl; - mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); - int wrkRank = status.source(); - eo::log << "[M] Node " << wrkRank << " just terminated." << std::endl; - handleResponse( wrkRank ); - assignmentAlgo.confirm( wrkRank ); - assignee = assignmentAlgo.get( ); - } - eo::log << "[M] Assignee : " << assignee << std::endl; - comm.send( assignee, EoMpi::Channel::Commands, EoMpi::Message::Continue ); - sendTask( assignee ); - } - - eo::log << "[M] Frees all the idle." << std::endl; - // frees all the idle workers - std::vector idles = assignmentAlgo.idles(); - for(unsigned int i = 0; i < idles.size(); ++i) - { - comm.send( idles[i], EoMpi::Channel::Commands, EoMpi::Message::Finish ); - } - - eo::log << "[M] Waits for all responses." << std::endl; - // wait for all responses - while( assignmentAlgo.availableWorkers() != totalWorkers ) - { - mpi::status status = comm.probe( mpi::any_source, mpi::any_tag ); - int wrkRank = status.source(); - handleResponse( wrkRank ); - comm.send( wrkRank, EoMpi::Channel::Commands, EoMpi::Message::Finish ); - assignmentAlgo.confirm( wrkRank ); - } - - eo::log << "[M] Leaving master task." << std::endl; - } - - void worker( ) - { - int order; - eo::log << eo::debug; - while( true ) - { - eo::log << "[W" << comm.rank() << "] Waiting for an order..." << std::endl; - comm.recv( masterRank, EoMpi::Channel::Commands, order ); - if ( order == EoMpi::Message::Finish ) - { - eo::log << "[W" << comm.rank() << "] Leaving worker task." << std::endl; - return; - } else - { - eo::log << "[W" << comm.rank() << "] Processing task..." << std::endl; - processTask( ); - } - } - } - - void run( ) - { - ( _isMaster ) ? master( ) : worker( ); - } - - bool isMaster( ) - { - return _isMaster; - } - -protected: - AssignmentAlgorithm& assignmentAlgo; - mpi::communicator& comm; - int masterRank; - bool _isMaster; -}; -# endif // __EO_MPI_H__ - diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/multipleRoles.cpp index ab8020732..5b829c890 100644 --- a/eo/test/mpi/multipleRoles.cpp +++ b/eo/test/mpi/multipleRoles.cpp @@ -1,4 +1,4 @@ -# include +# include # include # include @@ -8,6 +8,8 @@ # include using namespace std; +using namespace eo::mpi; + // Role map // 0 : general master // 1, 2 : worker of general job, master of subjob @@ -38,7 +40,7 @@ struct Work: public eoUF< vector&, void > void operator() ( vector& v ) { cout << "Work phase..." << endl; - subtask( v, MpiNode::comm().rank() ); + subtask( v, Node::comm().rank() ); for( int i = 0; i < v.size(); ++i ) { v[i] *= 2; @@ -49,7 +51,7 @@ struct Work: public eoUF< vector&, void > int main(int argc, char** argv) { // eo::log << eo::setlevel( eo::debug ); - MpiNode::init( argc, argv ); + Node::init( argc, argv ); vector v; v.push_back(1); @@ -62,7 +64,7 @@ int main(int argc, char** argv) metaV.push_back( v ); metaV.push_back( v ); - switch( MpiNode::comm().rank() ) + switch( Node::comm().rank() ) { case 0: case 1: @@ -88,7 +90,7 @@ int main(int argc, char** argv) default: { // all the other nodes are sub workers - int rank = MpiNode::comm().rank(); + int rank = Node::comm().rank(); if ( rank == 3 or rank == 5 ) { subtask( v, 1 ); diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 46130b80a..82f4e0e70 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -1,4 +1,4 @@ -# include +# include # include # include @@ -6,6 +6,8 @@ # include using namespace std; +using namespace eo::mpi; + struct plusOne : public eoUF< int&, void > { void operator() ( int & x ) @@ -27,7 +29,7 @@ int main(int argc, char** argv) // eo::log << eo::setlevel( eo::debug ); bool launchOnlyOne = false; // Set this to true if you wanna launch only the first test. - MpiNode::init( argc, argv ); + Node::init( argc, argv ); srand( time(0) ); vector v; @@ -43,10 +45,10 @@ int main(int argc, char** argv) vector< Test > tests; - const int ALL = MpiNode::comm().size(); + const int ALL = Node::comm().size(); Test tIntervalStatic; - tIntervalStatic.assign = new StaticAssignmentAlgorithm( 1, eo::REST_OF_THE_WORLD, v.size() ); + tIntervalStatic.assign = new StaticAssignmentAlgorithm( 1, REST_OF_THE_WORLD, v.size() ); tIntervalStatic.description = "Correct static assignment with interval."; tIntervalStatic.requiredNodesNumber = ALL; tests.push_back( tIntervalStatic ); @@ -81,7 +83,7 @@ int main(int argc, char** argv) tests.push_back( tVectorStatic ); Test tIntervalDynamic; - tIntervalDynamic.assign = new DynamicAssignmentAlgorithm( 1, eo::REST_OF_THE_WORLD ); + tIntervalDynamic.assign = new DynamicAssignmentAlgorithm( 1, REST_OF_THE_WORLD ); tIntervalDynamic.description = "Dynamic assignment with interval."; tIntervalDynamic.requiredNodesNumber = ALL; tests.push_back( tIntervalDynamic ); @@ -114,7 +116,7 @@ int main(int argc, char** argv) cout << "Test : " << tests[i].description << endl; } - if( MpiNode::comm().rank() < tests[i].requiredNodesNumber ) + if( Node::comm().rank() < tests[i].requiredNodesNumber ) { job.run(); } @@ -134,7 +136,7 @@ int main(int argc, char** argv) cout << endl; } - MpiNode::comm().barrier(); + Node::comm().barrier(); delete tests[i].assign; } From de2df9de810a71b918ca57ad8335a85a951ed1db Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 25 Jun 2012 14:18:04 +0200 Subject: [PATCH 32/76] Preprocessor conditions for debug print --- eo/src/mpi/eoMpi.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 86ad55ab3..b12233c50 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -46,28 +46,38 @@ namespace eo void master( ) { int totalWorkers = assignmentAlgo.availableWorkers(); +# ifndef NDEBUG eo::log << eo::debug; eo::log << "[M" << comm.rank() << "] Have " << totalWorkers << " workers." << std::endl; +# endif while( ! isFinished() ) { int assignee = assignmentAlgo.get( ); while( assignee <= 0 ) { +# ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Waitin' for node..." << std::endl; +# endif bmpi::status status = comm.probe( bmpi::any_source, bmpi::any_tag ); int wrkRank = status.source(); +# ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Node " << wrkRank << " just terminated." << std::endl; +# endif handleResponse( wrkRank ); assignmentAlgo.confirm( wrkRank ); assignee = assignmentAlgo.get( ); } +# ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Assignee : " << assignee << std::endl; +# endif comm.send( assignee, Channel::Commands, Message::Continue ); sendTask( assignee ); } +# ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Frees all the idle." << std::endl; +# endif // frees all the idle workers std::vector idles = assignmentAlgo.idles(); for(unsigned int i = 0; i < idles.size(); ++i) @@ -75,7 +85,9 @@ namespace eo comm.send( idles[i], Channel::Commands, Message::Finish ); } +# ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Waits for all responses." << std::endl; +# endif // wait for all responses while( assignmentAlgo.availableWorkers() != totalWorkers ) { @@ -86,24 +98,34 @@ namespace eo assignmentAlgo.confirm( wrkRank ); } +# ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Leaving master task." << std::endl; +# endif } void worker( ) { int order; +# ifndef NDEBUG eo::log << eo::debug; +# endif while( true ) { +# ifndef NDEBUG eo::log << "[W" << comm.rank() << "] Waiting for an order..." << std::endl; +# endif comm.recv( masterRank, Channel::Commands, order ); if ( order == Message::Finish ) { +# ifndef NDEBUG eo::log << "[W" << comm.rank() << "] Leaving worker task." << std::endl; +# endif return; } else { +# ifndef NDEBUG eo::log << "[W" << comm.rank() << "] Processing task..." << std::endl; +# endif processTask( ); } } From fc68c3b81ef59944a8f2fd94db083f2cab0ca543 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 25 Jun 2012 14:55:15 +0200 Subject: [PATCH 33/76] eoPopEvalFunc updated for parallel evaluation. --- eo/src/apply.h | 24 +++++++------------ eo/src/eoPopEvalFunc.h | 20 ++++++++++++++-- eo/test/{t-eoMpiParallel.cpp => mpi/eval.cpp} | 18 ++++++++++---- 3 files changed, 41 insertions(+), 21 deletions(-) rename eo/test/{t-eoMpiParallel.cpp => mpi/eval.cpp} (90%) diff --git a/eo/src/apply.h b/eo/src/apply.h index d2006fe04..c3c0365c3 100644 --- a/eo/src/apply.h +++ b/eo/src/apply.h @@ -34,7 +34,7 @@ #include # ifdef WITH_MPI -# include +# include # include # endif // WITH_MPI @@ -86,21 +86,15 @@ void apply(eoUF& _proc, std::vector& _pop) #ifdef WITH_MPI template -void parallelApply(eoUF& _proc, std::vector& _pop) +void parallelApply( + eoUF& _proc, + std::vector& _pop, + eo::mpi::AssignmentAlgorithm& _algo, + int _masterRank, + int _packetSize) { - ParallelApply job( _proc, _pop ); - - MasterNode* master = dynamic_cast( MpiNodeStore::instance() ); - if ( master ) - { - DynamicAssignmentAlgorithm algo; - master->setAssignmentAlgorithm( &algo ); - master->run( job ); - } else - { - WorkerNode* wrk = dynamic_cast( MpiNodeStore::instance() ); - wrk->run( job ); - } + eo::mpi::ParallelApply job( _proc, _pop, _algo, _masterRank, _packetSize ); + job.run(); } #endif diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index cbd38b21c..8885ef11d 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -83,17 +83,33 @@ template class eoParallelPopLoopEval : public eoPopEvalFunc { public: /** Ctor: set value of embedded eoEvalFunc */ - eoParallelPopLoopEval(eoEvalFunc & _eval) : eval(_eval) {} + eoParallelPopLoopEval( + eoEvalFunc & _eval, + eo::mpi::AssignmentAlgorithm& _assignAlgo, + int _masterRank, + int _packetSize = 1 + ) : + eval(_eval), + assignAlgo( _assignAlgo ), + masterRank( _masterRank ), + packetSize( _packetSize ) + { + // empty + } /** Do the job: simple loop over the offspring */ void operator()(eoPop & _parents, eoPop & _offspring) { (void)_parents; - parallelApply(eval, _offspring); + parallelApply(eval, _offspring, assignAlgo, masterRank, packetSize); } private: eoEvalFunc & eval; + + eo::mpi::AssignmentAlgorithm & assignAlgo; + int masterRank; + int packetSize; }; #endif diff --git a/eo/test/t-eoMpiParallel.cpp b/eo/test/mpi/eval.cpp similarity index 90% rename from eo/test/t-eoMpiParallel.cpp rename to eo/test/mpi/eval.cpp index 5529b8e2c..7e15ac6f3 100644 --- a/eo/test/t-eoMpiParallel.cpp +++ b/eo/test/mpi/eval.cpp @@ -3,12 +3,19 @@ //----------------------------------------------------------------------------- #include +#include + #include -//#include -#include "real_value.h" +// #include +#include "../real_value.h" + +#include #include +#include +using namespace std; + //----------------------------------------------------------------------------- class eoRealSerializable : public eoReal< eoMinimizingFitness >, public eoserial::Persistent @@ -75,7 +82,7 @@ typedef eoRealSerializable EOT; int main(int ac, char** av) { - MpiSingletonFactory::init( ac, av ); + eo::mpi::Node::init( ac, av ); eoParser parser(ac, av); @@ -100,7 +107,10 @@ int main(int ac, char** av) eo::log << "Size of population : " << popSize << std::endl; - eoPopLoopEval< EOT > popEval( eval ); + eo::log << eo::setlevel( eo::debug ); + + eo::mpi::DynamicAssignmentAlgorithm assign; + eoParallelPopLoopEval< EOT > popEval( eval, assign, 0, 3 ); popEval( pop, pop ); eo::log << eo::quiet << "DONE!" << std::endl; From 7b399aa1ddfe72566c89bb5276f29705c6c2b9b7 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 26 Jun 2012 17:53:32 +0200 Subject: [PATCH 34/76] Putting time conditions into eo::mpi::Job and MultiParallelApply, which doesn't prefigure about number of workers evaluations. --- eo/src/apply.h | 7 ++--- eo/src/eoPopEvalFunc.h | 9 ++++--- eo/src/mpi/eoMpi.h | 34 ++++++++++++++++++++--- eo/src/mpi/eoMultiParallelApply.h | 45 +++++++++++++++++++++++++++++++ eo/src/mpi/eoParallelApply.h | 12 ++++++--- eo/src/mpi/eoTerminateJob.h | 42 +++++++++++++++++++++++++++++ 6 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 eo/src/mpi/eoMultiParallelApply.h create mode 100644 eo/src/mpi/eoTerminateJob.h diff --git a/eo/src/apply.h b/eo/src/apply.h index c3c0365c3..7e59c21b8 100644 --- a/eo/src/apply.h +++ b/eo/src/apply.h @@ -35,7 +35,7 @@ # ifdef WITH_MPI # include -# include +# include # endif // WITH_MPI /** @@ -91,9 +91,10 @@ void parallelApply( std::vector& _pop, eo::mpi::AssignmentAlgorithm& _algo, int _masterRank, - int _packetSize) + int _packetSize, + int _maxTime) { - eo::mpi::ParallelApply job( _proc, _pop, _algo, _masterRank, _packetSize ); + eo::mpi::MultiParallelApply job( _proc, _pop, _algo, _masterRank, _packetSize, _maxTime ); job.run(); } #endif diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index 8885ef11d..77473eb15 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -87,12 +87,14 @@ public: eoEvalFunc & _eval, eo::mpi::AssignmentAlgorithm& _assignAlgo, int _masterRank, - int _packetSize = 1 + int _packetSize = 1, + int _maxTime = 0 ) : eval(_eval), assignAlgo( _assignAlgo ), masterRank( _masterRank ), - packetSize( _packetSize ) + packetSize( _packetSize ), + maxTime( _maxTime ) { // empty } @@ -101,7 +103,7 @@ public: void operator()(eoPop & _parents, eoPop & _offspring) { (void)_parents; - parallelApply(eval, _offspring, assignAlgo, masterRank, packetSize); + parallelApply(eval, _offspring, assignAlgo, masterRank, packetSize, maxTime); } private: @@ -110,6 +112,7 @@ private: eo::mpi::AssignmentAlgorithm & assignAlgo; int masterRank; int packetSize; + int maxTime; }; #endif diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index b12233c50..480bfbcbc 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -3,10 +3,15 @@ # include # include +# include +# include + # include +# include # include "eoMpiNode.h" # include "eoMpiAssignmentAlgorithm.h" + // TODO TODOB comment! namespace eo @@ -28,10 +33,11 @@ namespace eo { public: - Job( AssignmentAlgorithm& _algo, int _masterRank ) : + Job( AssignmentAlgorithm& _algo, int _masterRank, long maxTime = 0 ) : assignmentAlgo( _algo ), comm( Node::comm() ), - masterRank( _masterRank ) + masterRank( _masterRank ), + _maxTime( maxTime ) { _isMaster = Node::comm().rank() == _masterRank; } @@ -43,6 +49,8 @@ namespace eo // worker virtual void processTask( ) = 0; + protected: + void master( ) { int totalWorkers = assignmentAlgo.availableWorkers(); @@ -50,9 +58,18 @@ namespace eo eo::log << eo::debug; eo::log << "[M" << comm.rank() << "] Have " << totalWorkers << " workers." << std::endl; # endif - + bool timeStopped = false; while( ! isFinished() ) { + // Time restrictions + getrusage( RUSAGE_SELF , &_usage ); + _current = _usage.ru_utime.tv_sec + _usage.ru_stime.tv_sec; + if( _maxTime > 0 && _current > _maxTime ) + { + timeStopped = true; + break; + } + int assignee = assignmentAlgo.get( ); while( assignee <= 0 ) { @@ -71,6 +88,7 @@ namespace eo # ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Assignee : " << assignee << std::endl; # endif + comm.send( assignee, Channel::Commands, Message::Continue ); sendTask( assignee ); } @@ -101,6 +119,10 @@ namespace eo # ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Leaving master task." << std::endl; # endif + if( timeStopped ) + { + throw eoMaxTimeException( _current ); + } } void worker( ) @@ -131,6 +153,8 @@ namespace eo } } + public: + void run( ) { ( _isMaster ) ? master( ) : worker( ); @@ -146,6 +170,10 @@ namespace eo bmpi::communicator& comm; int masterRank; bool _isMaster; + + struct rusage _usage; + long _current; + const long _maxTime; }; } } diff --git a/eo/src/mpi/eoMultiParallelApply.h b/eo/src/mpi/eoMultiParallelApply.h new file mode 100644 index 000000000..f3df801d0 --- /dev/null +++ b/eo/src/mpi/eoMultiParallelApply.h @@ -0,0 +1,45 @@ + +# ifndef __EO_MULTI_PARALLEL_APPLY_H__ +# define __EO_MULTI_PARALLEL_APPLY_H__ + +# include "eoParallelApply.h" + +namespace eo +{ + namespace mpi + { + template< typename EOT > + class MultiParallelApply : public ParallelApply + { + public: + + // using ParallelApply::comm; + using ParallelApply::masterRank; + + MultiParallelApply( + eoUF & _proc, + std::vector& _pop, + AssignmentAlgorithm & algo, + int _masterRank, + int _packetSize = 1, + long _maxTime = 0 + ) : + ParallelApply( _proc, _pop, algo, _masterRank, _packetSize, _maxTime ) + { + // empty + } + + virtual void processTask( ) + { + int order = Message::Continue; + while( order != Message::Finish ) + { + ParallelApply::processTask( ); + ParallelApply::comm.recv( masterRank, Channel::Commands, order ); + } + } + }; + } +} +# endif // __EO_PARALLEL_APPLY_H__ + diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 1e9d4c1de..af0666cc6 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -26,9 +26,10 @@ namespace eo std::vector& _pop, AssignmentAlgorithm & algo, int _masterRank, - int _packetSize = 1 + int _packetSize = 1, + long _maxTime = 0 ) : - Job( algo, _masterRank ), + Job( algo, _masterRank, _maxTime ), func( _proc ), index( 0 ), size( _pop.size() ), @@ -42,7 +43,7 @@ namespace eo tempArray = new EOT[ packetSize ]; } - ~ParallelApply() + virtual ~ParallelApply() { delete [] tempArray; } @@ -59,8 +60,11 @@ namespace eo } int sentSize = futureIndex - index ; + comm.send( wrkRank, 1, sentSize ); + eo::log << eo::progress << "Evaluating individual " << index << std::endl; + assignedTasks[ wrkRank ].index = index; assignedTasks[ wrkRank ].size = sentSize; @@ -85,7 +89,7 @@ namespace eo comm.send( masterRank, 1, tempArray, recvSize ); } - bool isFinished() + virtual bool isFinished() { return index == size; } diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h new file mode 100644 index 000000000..4cb182255 --- /dev/null +++ b/eo/src/mpi/eoTerminateJob.h @@ -0,0 +1,42 @@ +# ifndef __EO_TERMINATE_H__ +# define __EO_TERMINATE_H__ + +# include "eoMpi.h" + +namespace eo +{ + namespace mpi + { + class TerminateJob : public Job + { + public: + TerminateJob( AssignmentAlgorithm& algo, int _ ) + : Job( algo, _ ) + { + // empty + } + + void sendTask( int wrkRank ) + { + // empty + } + + void handleResponse( int wrkRank ) + { + // empty + } + + void processTask( ) + { + // empty + } + + bool isFinished() + { + return true; + } + }; + } +} + +# endif // __EO_TERMINATE_H__ From 2861fc98de4f4fd6d2bbd508f62ac96594fa5d8e Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 27 Jun 2012 15:09:12 +0200 Subject: [PATCH 35/76] Mesuring time with eoTimer. --- eo/src/mpi/eoMpi.cpp | 1 + eo/src/mpi/eoMpi.h | 13 ++++ eo/src/mpi/eoParallelApply.h | 2 + eo/src/utils/eoTimer.h | 145 +++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 eo/src/utils/eoTimer.h diff --git a/eo/src/mpi/eoMpi.cpp b/eo/src/mpi/eoMpi.cpp index f6e3b3c52..9ffc84bda 100644 --- a/eo/src/mpi/eoMpi.cpp +++ b/eo/src/mpi/eoMpi.cpp @@ -6,6 +6,7 @@ namespace eo namespace mpi { bmpi::communicator Node::_comm; + eoTimerStat timerStat; } } diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 480bfbcbc..a516b442a 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -7,6 +7,7 @@ # include # include +# include # include # include "eoMpiNode.h" @@ -18,6 +19,8 @@ namespace eo { namespace mpi { + extern eoTimerStat timerStat; + namespace Channel { const int Commands = 0; @@ -70,6 +73,7 @@ namespace eo break; } + timerStat.start("master_wait_for_assignee"); int assignee = assignmentAlgo.get( ); while( assignee <= 0 ) { @@ -85,28 +89,34 @@ namespace eo assignmentAlgo.confirm( wrkRank ); assignee = assignmentAlgo.get( ); } + timerStat.stop("master_wait_for_assignee"); # ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Assignee : " << assignee << std::endl; # endif + timerStat.start("master_wait_for_send"); comm.send( assignee, Channel::Commands, Message::Continue ); sendTask( assignee ); + timerStat.stop("master_wait_for_send"); } # ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Frees all the idle." << std::endl; # endif // frees all the idle workers + timerStat.start("master_wait_for_idles"); std::vector idles = assignmentAlgo.idles(); for(unsigned int i = 0; i < idles.size(); ++i) { comm.send( idles[i], Channel::Commands, Message::Finish ); } + timerStat.stop("master_wait_for_idles"); # ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Waits for all responses." << std::endl; # endif // wait for all responses + timerStat.start("master_wait_for_all_responses"); while( assignmentAlgo.availableWorkers() != totalWorkers ) { bmpi::status status = comm.probe( bmpi::any_source, bmpi::any_tag ); @@ -115,6 +125,7 @@ namespace eo comm.send( wrkRank, Channel::Commands, Message::Finish ); assignmentAlgo.confirm( wrkRank ); } + timerStat.stop("master_wait_for_all_responses"); # ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Leaving master task." << std::endl; @@ -136,7 +147,9 @@ namespace eo # ifndef NDEBUG eo::log << "[W" << comm.rank() << "] Waiting for an order..." << std::endl; # endif + timerStat.start("worker_wait_for_order"); comm.recv( masterRank, Channel::Commands, order ); + timerStat.stop("worker_wait_for_order"); if ( order == Message::Finish ) { # ifndef NDEBUG diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index af0666cc6..b89da73e4 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -82,10 +82,12 @@ namespace eo int recvSize; comm.recv( masterRank, 1, recvSize ); comm.recv( masterRank, 1, tempArray, recvSize ); + timerStat.start("worker_processes"); for( int i = 0; i < recvSize ; ++i ) { func( tempArray[ i ] ); } + timerStat.stop("worker_processes"); comm.send( masterRank, 1, tempArray, recvSize ); } diff --git a/eo/src/utils/eoTimer.h b/eo/src/utils/eoTimer.h new file mode 100644 index 000000000..c97ab5d35 --- /dev/null +++ b/eo/src/utils/eoTimer.h @@ -0,0 +1,145 @@ +# ifndef __TIMER_H__ +# define __TIMER_H__ + +# include +# include + +# include +# include + +# ifdef WITH_MPI +# include +# include +# include +# endif + +// TODO TODOB commenter +class eoTimer +{ + public: + + eoTimer() + { + restart(); + } + + void restart() + { + uuremainder = 0; + usremainder = 0; + wc_start = time(NULL); + getrusage( RUSAGE_SELF, &_start ); + } + + long int usertime() + { + struct rusage _now; + getrusage( RUSAGE_SELF, &_now ); + long int result = _now.ru_utime.tv_sec - _start.ru_utime.tv_sec; + if( _now.ru_utime.tv_sec == _start.ru_utime.tv_sec ) + { + uuremainder += _now.ru_utime.tv_usec - _start.ru_utime.tv_usec; + if( uuremainder > 1000000) + { + ++result; + uuremainder = 0; + } + } + return result; + } + + long int systime() + { + struct rusage _now; + getrusage( RUSAGE_SELF, &_now ); + long int result = _now.ru_stime.tv_sec - _start.ru_stime.tv_sec; + if( _now.ru_stime.tv_sec == _start.ru_stime.tv_sec ) + { + usremainder += _now.ru_stime.tv_usec - _start.ru_stime.tv_usec; + if( usremainder > 1000000) + { + ++result; + usremainder = 0; + } + } + return result; + } + + double wallclock() + { + return std::difftime( std::time(NULL) , wc_start ); + } + + protected: + struct rusage _start; + long int uuremainder; + long int usremainder; + time_t wc_start; +}; + +class eoTimerStat +{ + public: + + struct Stat + { + std::vector utime; + std::vector stime; + std::vector wtime; +#ifdef WITH_MPI + // Gives access to boost serialization + friend class boost::serialization::access; + + /** + * Serializes the statistique in a boost archive (useful for boost::mpi) + */ + template + void serialize( Archive & ar, const unsigned int version ) + { + ar & utime & stime & wtime; + (void) version; // avoid compilation warning + } +# endif + }; + +#ifdef WITH_MPI + // Gives access to boost serialization + friend class boost::serialization::access; + + /** + * Serializes the map of statistics in a boost archive (useful for boost::mpi) + */ + template + void serialize( Archive & ar, const unsigned int version ) + { + ar & _stats; + (void) version; // avoid compilation warning + } +# endif + + void start( const std::string & key ) + { + _timers[ key ].restart(); + } + + void stop( const std::string& key ) + { + Stat & s = _stats[ key ]; + eoTimer & t = _timers[ key ]; + s.utime.push_back( t.usertime() ); + s.stime.push_back( t.systime() ); + s.wtime.push_back( t.wallclock() ); + } + + std::map< std::string, Stat > stats() + { + return _stats; + } + + protected: + std::map< std::string, Stat > _stats; + std::map< std::string, eoTimer > _timers; +}; + +# endif // __TIMER_H__ + From 4675abaa24becca1c6f5d2bdecf2dbf9a72f8b20 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 29 Jun 2012 18:19:55 +0200 Subject: [PATCH 36/76] Implementation test of functional configurable job --- eo/src/mpi/eoMpi.h | 67 ++++++++- eo/src/mpi/eoParallelApply.h | 262 ++++++++++++++++++++++++----------- 2 files changed, 245 insertions(+), 84 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index a516b442a..6d22a1bf2 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -8,6 +8,7 @@ # include # include +# include # include # include "eoMpiNode.h" @@ -32,28 +33,84 @@ namespace eo const int Finish = 1; } + template + struct SharedDataFunction + { + void data( Data & _d ) { d = _d; } + + protected: + Data d; + }; + + template + struct SendTaskFunction : public eoUF, public SharedDataFunction + { + virtual ~SendTaskFunction() {} + }; + + template + struct HandleResponseFunction : public eoUF, public SharedDataFunction + { + virtual ~HandleResponseFunction() {} + }; + + template + struct ProcessTaskFunction : public eoF, public SharedDataFunction + { + virtual ~ProcessTaskFunction() {} + }; + + template + struct IsFinishedFunction : public eoF, public SharedDataFunction + { + virtual ~IsFinishedFunction() {} + }; + + template + struct JobStore + { + virtual SendTaskFunction & sendTask() = 0; + virtual HandleResponseFunction & handleResponse() = 0; + virtual ProcessTaskFunction & processTask() = 0; + virtual IsFinishedFunction & isFinished() = 0; + }; + + template class Job { public: - Job( AssignmentAlgorithm& _algo, int _masterRank, long maxTime = 0 ) : + Job( AssignmentAlgorithm& _algo, int _masterRank, JobStore store ) : + // Job( AssignmentAlgorithm& _algo, int _masterRank, long maxTime = 0 ) : assignmentAlgo( _algo ), comm( Node::comm() ), + // _maxTime( maxTime ), masterRank( _masterRank ), - _maxTime( maxTime ) + // Functors + sendTask( store.sendTask() ), + handleResponse( store.handleResponse() ), + processTask( store.processTask() ), + isFinished( store.isFinished() ) { _isMaster = Node::comm().rank() == _masterRank; } + /* // master virtual bool isFinished() = 0; virtual void sendTask( int wrkRank ) = 0; virtual void handleResponse( int wrkRank ) = 0; // worker virtual void processTask( ) = 0; + */ protected: + SendTaskFunction & sendTask; + HandleResponseFunction & handleResponse; + ProcessTaskFunction & processTask; + IsFinishedFunction & isFinished; + void master( ) { int totalWorkers = assignmentAlgo.availableWorkers(); @@ -65,6 +122,7 @@ namespace eo while( ! isFinished() ) { // Time restrictions + /* getrusage( RUSAGE_SELF , &_usage ); _current = _usage.ru_utime.tv_sec + _usage.ru_stime.tv_sec; if( _maxTime > 0 && _current > _maxTime ) @@ -72,6 +130,7 @@ namespace eo timeStopped = true; break; } + */ timerStat.start("master_wait_for_assignee"); int assignee = assignmentAlgo.get( ); @@ -130,10 +189,12 @@ namespace eo # ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Leaving master task." << std::endl; # endif + /* if( timeStopped ) { throw eoMaxTimeException( _current ); } + */ } void worker( ) @@ -186,7 +247,7 @@ namespace eo struct rusage _usage; long _current; - const long _maxTime; + // const long _maxTime; }; } } diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index b89da73e4..6ec78c800 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -10,15 +10,177 @@ namespace eo { namespace mpi { - template< typename EOT > - class ParallelApply : public Job + struct ParallelApplyAssignment { - private: - struct ParallelApplyAssignment + int index; + int size; + }; + + template + struct ParallelApplyData + { + ParallelApplyData() {} + + ParallelApplyData( + eoUF & _proc, + std::vector& _pop, + int _masterRank, + int _packetSize + ) : + func( _proc ), + data( _pop ), + index( 0 ), + size( _pop.size() ), + packetSize( _packetSize ), + // job + masterRank( _masterRank ), + comm( Node::comm() ) + { + tempArray = new EOT[ _packetSize ]; + } + + ~ParallelApplyData() + { + delete [] tempArray; + } + + std::vector & data; + eoUF & func; + int index; + int size; + std::map< int /* worker rank */, ParallelApplyAssignment /* min indexes in vector */> assignedTasks; + + int packetSize; + EOT* tempArray; + + int masterRank; + bmpi::communicator& comm; + }; + + template + struct SendTaskParallelApply : public SendTaskFunction< Data > + { + SendTaskParallelApply( Data & _d ) + { + data( _d ); + } + + using SharedDataFunction< Data >::d; + + // futureIndex, index, packetSize, size, comm, assignedTasks, data + void operator()(int wrkRank) + { + int futureIndex; + + if( d.index + d.packetSize < d.size ) { - int index; - int size; - }; + futureIndex = d.index + d.packetSize; + } else { + futureIndex = d.size; + } + + int sentSize = futureIndex - d.index ; + + d.comm.send( wrkRank, 1, sentSize ); + + eo::log << eo::progress << "Evaluating individual " << d.index << std::endl; + + d.assignedTasks[ wrkRank ].index = d.index; + d.assignedTasks[ wrkRank ].size = sentSize; + + d.comm.send( wrkRank, 1, & (d.data[ index ]) , sentSize ); + d.index = futureIndex; + } + }; + + template + struct HandleResponseParallelApply : public HandleResponseFunction< Data > + { + HandleResponseParallelApply( Data & _d ) + { + data( _d ); + } + + using SharedDataFunction< Data >::d; + void operator()(int wrkRank) + { + d.comm.recv( wrkRank, 1, & (d.data[ d.assignedTasks[wrkRank].index ] ), d.assignedTasks[wrkRank].size ); + } + }; + + template + struct ProcessTaskParallelApply : public ProcessTaskFunction< Data > + { + ProcessTaskParallelApply( Data & _d ) + { + data( _d ); + } + + using SharedDataFunction< Data >::d; + void operator()() + { + int recvSize; + d.comm.recv( d.masterRank, 1, recvSize ); + d.comm.recv( d.masterRank, 1, d.tempArray, recvSize ); + timerStat.start("worker_processes"); + for( int i = 0; i < recvSize ; ++i ) + { + d.func( d.tempArray[ i ] ); + } + timerStat.stop("worker_processes"); + d.comm.send( d.masterRank, 1, d.tempArray, recvSize ); + } + }; + + template + struct IsFinishedParallelApply : public IsFinishedFunction< Data > + { + IsFinishedParallelApply( Data & _d ) + { + data( _d ); + } + + using SharedDataFunction< Data >::d; + bool operator()() + { + return d.index == d.size; + } + }; + + template< typename Data > + struct ParallelApplyStore : public JobStore< Data > + { + ParallelApplyStore( Data & data ) + { + stpa = new SendTaskParallelApply< Data >( data ); + hrpa = new HandleResponseParallelApply< Data >( data ); + ptpa = new ProcessTaskParallelApply< Data >( data ); + ispa = new IsFinishedParallelApply< Data >( data ); + } + + ~ParallelApplyStore() + { + delete stpa; + delete hrpa; + delete ptpa; + delete ispa; + } + + SendTaskFunction< Data > & sendTask() { return *stpa; } + HandleResponseFunction< Data > & handleResponse() { return *hrpa; } + ProcessTaskFunction< Data > & processTask() { return *ptpa; } + IsFinishedFunction< Data > & isFinished() { return *ispa; } + + protected: + SendTaskParallelApply< Data >* stpa; + HandleResponseParallelApply< Data >* hrpa; + ProcessTaskParallelApply< Data >* ptpa; + IsFinishedParallelApply< Data >* ispa; + }; + + template< typename EOT > + class ParallelApply : public Job< ParallelApplyData > + { public: ParallelApply( @@ -26,85 +188,23 @@ namespace eo std::vector& _pop, AssignmentAlgorithm & algo, int _masterRank, - int _packetSize = 1, - long _maxTime = 0 + // long _maxTime = 0, + int _packetSize = 1 ) : - Job( algo, _masterRank, _maxTime ), - func( _proc ), - index( 0 ), - size( _pop.size() ), - data( _pop ), - packetSize( _packetSize ) + + Job< ParallelApplyData >( algo, _masterRank, ParallelApplyStore< ParallelApplyData >( sharedData ) ), + // Job( algo, _masterRank, _maxTime ), + sharedData( _proc, _pop, _masterRank, _packetSize ) + + { + if ( _packetSize <= 0 ) { - if ( _packetSize <= 0 ) - { - throw std::runtime_error("Packet size should not be negative."); - } - tempArray = new EOT[ packetSize ]; - } - - virtual ~ParallelApply() - { - delete [] tempArray; - } - - virtual void sendTask( int wrkRank ) - { - int futureIndex; - - if( index + packetSize < size ) - { - futureIndex = index + packetSize; - } else { - futureIndex = size; - } - - int sentSize = futureIndex - index ; - - comm.send( wrkRank, 1, sentSize ); - - eo::log << eo::progress << "Evaluating individual " << index << std::endl; - - assignedTasks[ wrkRank ].index = index; - assignedTasks[ wrkRank ].size = sentSize; - - comm.send( wrkRank, 1, &data[ index ] , sentSize ); - index = futureIndex; - } - - virtual void handleResponse( int wrkRank ) - { - comm.recv( wrkRank, 1, &data[ assignedTasks[wrkRank].index ], assignedTasks[wrkRank].size ); - } - - virtual void processTask( ) - { - int recvSize; - comm.recv( masterRank, 1, recvSize ); - comm.recv( masterRank, 1, tempArray, recvSize ); - timerStat.start("worker_processes"); - for( int i = 0; i < recvSize ; ++i ) - { - func( tempArray[ i ] ); - } - timerStat.stop("worker_processes"); - comm.send( masterRank, 1, tempArray, recvSize ); - } - - virtual bool isFinished() - { - return index == size; + throw std::runtime_error("Packet size should not be negative."); } + } protected: - std::vector & data; - eoUF& func; - int index; - int size; - std::map< int /* worker rank */, ParallelApplyAssignment /* min indexes in vector */> assignedTasks; - - int packetSize; - EOT* tempArray; + ParallelApplyData sharedData; }; } } From d05cbfd60dc2cbe104434168c7ed9730ed50c028 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 2 Jul 2012 11:56:41 +0200 Subject: [PATCH 37/76] First version of functional job --- eo/src/mpi/eoMpi.h | 46 +++---- eo/src/mpi/eoParallelApply.h | 232 +++++++++++++++++----------------- eo/test/mpi/parallelApply.cpp | 7 +- 3 files changed, 140 insertions(+), 145 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 6d22a1bf2..433e4c272 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -33,54 +33,42 @@ namespace eo const int Finish = 1; } - template - struct SharedDataFunction - { - void data( Data & _d ) { d = _d; } - - protected: - Data d; - }; - - template - struct SendTaskFunction : public eoUF, public SharedDataFunction + class SendTaskFunction : public eoUF { + public: virtual ~SendTaskFunction() {} }; - template - struct HandleResponseFunction : public eoUF, public SharedDataFunction + class HandleResponseFunction : public eoUF { + public: virtual ~HandleResponseFunction() {} }; - template - struct ProcessTaskFunction : public eoF, public SharedDataFunction + class ProcessTaskFunction : public eoF { + public: virtual ~ProcessTaskFunction() {} }; - template - struct IsFinishedFunction : public eoF, public SharedDataFunction + class IsFinishedFunction : public eoF { + public: virtual ~IsFinishedFunction() {} }; - template struct JobStore { - virtual SendTaskFunction & sendTask() = 0; - virtual HandleResponseFunction & handleResponse() = 0; - virtual ProcessTaskFunction & processTask() = 0; - virtual IsFinishedFunction & isFinished() = 0; + virtual SendTaskFunction & sendTask() const = 0; + virtual HandleResponseFunction & handleResponse() const = 0; + virtual ProcessTaskFunction & processTask() const = 0; + virtual IsFinishedFunction & isFinished() const = 0; }; - template class Job { public: - - Job( AssignmentAlgorithm& _algo, int _masterRank, JobStore store ) : + Job( AssignmentAlgorithm& _algo, int _masterRank, const JobStore & store ) : // Job( AssignmentAlgorithm& _algo, int _masterRank, long maxTime = 0 ) : assignmentAlgo( _algo ), comm( Node::comm() ), @@ -106,10 +94,10 @@ namespace eo protected: - SendTaskFunction & sendTask; - HandleResponseFunction & handleResponse; - ProcessTaskFunction & processTask; - IsFinishedFunction & isFinished; + SendTaskFunction & sendTask; + HandleResponseFunction & handleResponse; + ProcessTaskFunction & processTask; + IsFinishedFunction & isFinished; void master( ) { diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 6ec78c800..9cc7bb5f6 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -17,145 +17,178 @@ namespace eo }; template - struct ParallelApplyData - { - ParallelApplyData() {} + class SendTaskParallelApply; - ParallelApplyData( - eoUF & _proc, - std::vector& _pop, - int _masterRank, - int _packetSize - ) : - func( _proc ), - data( _pop ), - index( 0 ), - size( _pop.size() ), - packetSize( _packetSize ), - // job - masterRank( _masterRank ), - comm( Node::comm() ) + template + class HandleResponseParallelApply; + + template + class ProcessTaskParallelApply; + + template + class IsFinishedParallelApply; + + template + class ParallelApply; + + template< class EOT > + class BaseParallelApply + { + public: + void owner(ParallelApply * job) { - tempArray = new EOT[ _packetSize ]; + j = job; } - ~ParallelApplyData() - { - delete [] tempArray; - } + protected: + ParallelApply * j; + }; + + template< typename EOT > + class ParallelApply : public Job + { + friend class SendTaskParallelApply; + friend class HandleResponseParallelApply; + friend class ProcessTaskParallelApply; + friend class IsFinishedParallelApply; + + public: + + ParallelApply( + eoUF & _proc, + std::vector& _pop, + AssignmentAlgorithm & algo, + int _masterRank, + const JobStore& store, + // long _maxTime = 0, + int _packetSize = 1 + ) : + Job( algo, _masterRank, store ), + // Job( algo, _masterRank, _maxTime ), + func( _proc ), + data( _pop ), + packetSize( _packetSize ), + index( 0 ), + size( _pop.size() ) + { + if ( _packetSize <= 0 ) + { + throw std::runtime_error("Packet size should not be negative."); + } + tempArray = new EOT [ _packetSize ]; + + dynamic_cast< BaseParallelApply& >( sendTask ).owner( this ); + dynamic_cast< BaseParallelApply& >( handleResponse ).owner( this ); + dynamic_cast< BaseParallelApply& >( processTask ).owner( this ); + dynamic_cast< BaseParallelApply& >( isFinished ).owner( this ); + } + + ~ParallelApply() + { + delete [] tempArray; + } + + protected: std::vector & data; eoUF & func; int index; int size; std::map< int /* worker rank */, ParallelApplyAssignment /* min indexes in vector */> assignedTasks; - int packetSize; EOT* tempArray; - int masterRank; - bmpi::communicator& comm; + // bmpi::communicator& comm; }; - template - struct SendTaskParallelApply : public SendTaskFunction< Data > + template< class EOT > + class SendTaskParallelApply : public SendTaskFunction, public BaseParallelApply { - SendTaskParallelApply( Data & _d ) - { - data( _d ); - } - - using SharedDataFunction< Data >::d; + public: + using BaseParallelApply::j; // futureIndex, index, packetSize, size, comm, assignedTasks, data void operator()(int wrkRank) { int futureIndex; - if( d.index + d.packetSize < d.size ) + if( j->index + j->packetSize < j->size ) { - futureIndex = d.index + d.packetSize; + futureIndex = j->index + j->packetSize; } else { - futureIndex = d.size; + futureIndex = j->size; } - int sentSize = futureIndex - d.index ; + int sentSize = futureIndex - j->index ; - d.comm.send( wrkRank, 1, sentSize ); + j->comm.send( wrkRank, 1, sentSize ); - eo::log << eo::progress << "Evaluating individual " << d.index << std::endl; + eo::log << eo::progress << "Evaluating individual " << j->index << std::endl; - d.assignedTasks[ wrkRank ].index = d.index; - d.assignedTasks[ wrkRank ].size = sentSize; + j->assignedTasks[ wrkRank ].index = j->index; + j->assignedTasks[ wrkRank ].size = sentSize; - d.comm.send( wrkRank, 1, & (d.data[ index ]) , sentSize ); - d.index = futureIndex; + j->comm.send( wrkRank, 1, & ( (j->data)[ j->index ] ) , sentSize ); + j->index = futureIndex; } }; - template - struct HandleResponseParallelApply : public HandleResponseFunction< Data > + template< class EOT > + class HandleResponseParallelApply : public HandleResponseFunction, public BaseParallelApply { - HandleResponseParallelApply( Data & _d ) - { - data( _d ); - } + public: + using BaseParallelApply::j; - using SharedDataFunction< Data >::d; void operator()(int wrkRank) { - d.comm.recv( wrkRank, 1, & (d.data[ d.assignedTasks[wrkRank].index ] ), d.assignedTasks[wrkRank].size ); + j->comm.recv( wrkRank, 1, & (j->data[ j->assignedTasks[wrkRank].index ] ), j->assignedTasks[wrkRank].size ); } }; - template - struct ProcessTaskParallelApply : public ProcessTaskFunction< Data > + template< class EOT > + class ProcessTaskParallelApply : public ProcessTaskFunction, public BaseParallelApply { - ProcessTaskParallelApply( Data & _d ) - { - data( _d ); - } + public: + using BaseParallelApply::j; - using SharedDataFunction< Data >::d; void operator()() { int recvSize; - d.comm.recv( d.masterRank, 1, recvSize ); - d.comm.recv( d.masterRank, 1, d.tempArray, recvSize ); + + j->comm.recv( j->masterRank, 1, recvSize ); + j->comm.recv( j->masterRank, 1, j->tempArray, recvSize ); timerStat.start("worker_processes"); for( int i = 0; i < recvSize ; ++i ) { - d.func( d.tempArray[ i ] ); + j->func( j->tempArray[ i ] ); } timerStat.stop("worker_processes"); - d.comm.send( d.masterRank, 1, d.tempArray, recvSize ); + j->comm.send( j->masterRank, 1, j->tempArray, recvSize ); } }; - template - struct IsFinishedParallelApply : public IsFinishedFunction< Data > + template< class EOT > + class IsFinishedParallelApply : public IsFinishedFunction, public BaseParallelApply { - IsFinishedParallelApply( Data & _d ) - { - data( _d ); - } + public: + + using BaseParallelApply::j; - using SharedDataFunction< Data >::d; bool operator()() { - return d.index == d.size; + return j->index == j->size; } }; - template< typename Data > - struct ParallelApplyStore : public JobStore< Data > + template< class EOT > + struct ParallelApplyStore : public JobStore { - ParallelApplyStore( Data & data ) + ParallelApplyStore() { - stpa = new SendTaskParallelApply< Data >( data ); - hrpa = new HandleResponseParallelApply< Data >( data ); - ptpa = new ProcessTaskParallelApply< Data >( data ); - ispa = new IsFinishedParallelApply< Data >( data ); + stpa = new SendTaskParallelApply; + hrpa = new HandleResponseParallelApply; + ptpa = new ProcessTaskParallelApply; + ispa = new IsFinishedParallelApply; } ~ParallelApplyStore() @@ -166,45 +199,16 @@ namespace eo delete ispa; } - SendTaskFunction< Data > & sendTask() { return *stpa; } - HandleResponseFunction< Data > & handleResponse() { return *hrpa; } - ProcessTaskFunction< Data > & processTask() { return *ptpa; } - IsFinishedFunction< Data > & isFinished() { return *ispa; } + SendTaskFunction& sendTask() const { return *stpa; } + HandleResponseFunction& handleResponse() const { return *hrpa; } + ProcessTaskFunction& processTask() const { return *ptpa; } + IsFinishedFunction& isFinished() const { return *ispa; } protected: - SendTaskParallelApply< Data >* stpa; - HandleResponseParallelApply< Data >* hrpa; - ProcessTaskParallelApply< Data >* ptpa; - IsFinishedParallelApply< Data >* ispa; - }; - - template< typename EOT > - class ParallelApply : public Job< ParallelApplyData > - { - public: - - ParallelApply( - eoUF & _proc, - std::vector& _pop, - AssignmentAlgorithm & algo, - int _masterRank, - // long _maxTime = 0, - int _packetSize = 1 - ) : - - Job< ParallelApplyData >( algo, _masterRank, ParallelApplyStore< ParallelApplyData >( sharedData ) ), - // Job( algo, _masterRank, _maxTime ), - sharedData( _proc, _pop, _masterRank, _packetSize ) - - { - if ( _packetSize <= 0 ) - { - throw std::runtime_error("Packet size should not be negative."); - } - } - - protected: - ParallelApplyData sharedData; + SendTaskParallelApply* stpa; + HandleResponseParallelApply* hrpa; + ProcessTaskParallelApply* ptpa; + IsFinishedParallelApply* ispa; }; } } diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 82f4e0e70..18aadd549 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -27,7 +27,8 @@ struct Test int main(int argc, char** argv) { // eo::log << eo::setlevel( eo::debug ); - bool launchOnlyOne = false; // Set this to true if you wanna launch only the first test. + 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 ); @@ -44,6 +45,8 @@ int main(int argc, char** argv) plusOne plusOneInstance; vector< Test > tests; + + ParallelApplyStore store; const int ALL = Node::comm().size(); @@ -109,7 +112,7 @@ int main(int argc, char** argv) for( unsigned int i = 0; i < tests.size(); ++i ) { - ParallelApply job( plusOneInstance, v, *(tests[i].assign), 0, 3 ); + ParallelApply job( plusOneInstance, v, *(tests[i].assign), 0, store, 3 ); if( job.isMaster() ) { From ff61676fb7ac5e17e625202732bde8773173ba48 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 2 Jul 2012 17:53:02 +0200 Subject: [PATCH 38/76] Cleaner version of functional parallel job. --- eo/src/mpi/eoMpi.h | 80 +++++++++++++--- eo/src/mpi/eoParallelApply.h | 173 ++++++++++++++++------------------ eo/test/mpi/parallelApply.cpp | 8 +- 3 files changed, 154 insertions(+), 107 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 433e4c272..9ad2bd7bf 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -33,42 +33,91 @@ namespace eo const int Finish = 1; } - class SendTaskFunction : public eoUF + template< typename JobData, typename Wrapped > + struct SharedDataFunction + { + SharedDataFunction( Wrapped * w ) + { + wrapped = w; + } + + void data( JobData* _d ) + { + d = _d; + } + + protected: + JobData* d; + Wrapped* wrapped; + }; + + template< typename JobData > + struct SendTaskFunction : public eoUF, public SharedDataFunction< JobData, SendTaskFunction > { public: + + SendTaskFunction( SendTaskFunction* w = 0 ) : SharedDataFunction >( w ) + { + // empty + } + virtual ~SendTaskFunction() {} }; - class HandleResponseFunction : public eoUF + template< typename JobData > + struct HandleResponseFunction : public eoUF, public SharedDataFunction< JobData, HandleResponseFunction > { public: + + HandleResponseFunction( HandleResponseFunction* w = 0 ) : SharedDataFunction >( w ) + { + // empty + } + virtual ~HandleResponseFunction() {} }; - class ProcessTaskFunction : public eoF + template< typename JobData > + struct ProcessTaskFunction : public eoF, public SharedDataFunction< JobData, ProcessTaskFunction > { public: + + ProcessTaskFunction( ProcessTaskFunction* w = 0 ) : SharedDataFunction >( w ) + { + // empty + } + virtual ~ProcessTaskFunction() {} }; - class IsFinishedFunction : public eoF + template< typename JobData > + struct IsFinishedFunction : public eoF, public SharedDataFunction< JobData, IsFinishedFunction > { public: + + IsFinishedFunction( IsFinishedFunction* w = 0 ) : SharedDataFunction >( w ) + { + // empty + } + virtual ~IsFinishedFunction() {} }; + template< typename JobData > struct JobStore { - virtual SendTaskFunction & sendTask() const = 0; - virtual HandleResponseFunction & handleResponse() const = 0; - virtual ProcessTaskFunction & processTask() const = 0; - virtual IsFinishedFunction & isFinished() const = 0; + virtual SendTaskFunction & sendTask() const = 0; + virtual HandleResponseFunction & handleResponse() const = 0; + virtual ProcessTaskFunction & processTask() const = 0; + virtual IsFinishedFunction & isFinished() const = 0; + virtual JobData* data() = 0; }; + template< class JobData > class Job { public: - Job( AssignmentAlgorithm& _algo, int _masterRank, const JobStore & store ) : + Job( AssignmentAlgorithm& _algo, int _masterRank, JobStore & store ) : // Job( AssignmentAlgorithm& _algo, int _masterRank, long maxTime = 0 ) : assignmentAlgo( _algo ), comm( Node::comm() ), @@ -81,6 +130,11 @@ namespace eo isFinished( store.isFinished() ) { _isMaster = Node::comm().rank() == _masterRank; + + sendTask.data( store.data() ); + handleResponse.data( store.data() ); + processTask.data( store.data() ); + isFinished.data( store.data() ); } /* @@ -94,10 +148,10 @@ namespace eo protected: - SendTaskFunction & sendTask; - HandleResponseFunction & handleResponse; - ProcessTaskFunction & processTask; - IsFinishedFunction & isFinished; + SendTaskFunction & sendTask; + HandleResponseFunction & handleResponse; + ProcessTaskFunction & processTask; + IsFinishedFunction & isFinished; void master( ) { diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 9cc7bb5f6..bc85d4387 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -17,79 +17,29 @@ namespace eo }; template - class SendTaskParallelApply; - - template - class HandleResponseParallelApply; - - template - class ProcessTaskParallelApply; - - template - class IsFinishedParallelApply; - - template - class ParallelApply; - - template< class EOT > - class BaseParallelApply + struct JobData { - public: - void owner(ParallelApply * job) - { - j = job; - } - - protected: - ParallelApply * j; - }; - - template< typename EOT > - class ParallelApply : public Job - { - friend class SendTaskParallelApply; - friend class HandleResponseParallelApply; - friend class ProcessTaskParallelApply; - friend class IsFinishedParallelApply; - - public: - - ParallelApply( + JobData( eoUF & _proc, std::vector& _pop, - AssignmentAlgorithm & algo, int _masterRank, - const JobStore& store, // long _maxTime = 0, - int _packetSize = 1 - ) : - Job( algo, _masterRank, store ), - // Job( algo, _masterRank, _maxTime ), - func( _proc ), - data( _pop ), - packetSize( _packetSize ), - index( 0 ), - size( _pop.size() ) + int _packetSize + ) : + data( _pop ), func( _proc ), index( 0 ), size( _pop.size() ), packetSize( _packetSize ), masterRank( _masterRank ), comm( Node::comm() ) { if ( _packetSize <= 0 ) { throw std::runtime_error("Packet size should not be negative."); } - tempArray = new EOT [ _packetSize ]; - - dynamic_cast< BaseParallelApply& >( sendTask ).owner( this ); - dynamic_cast< BaseParallelApply& >( handleResponse ).owner( this ); - dynamic_cast< BaseParallelApply& >( processTask ).owner( this ); - dynamic_cast< BaseParallelApply& >( isFinished ).owner( this ); + tempArray = new EOT[ _packetSize ]; } - ~ParallelApply() + ~JobData() { delete [] tempArray; } - protected: - std::vector & data; eoUF & func; int index; @@ -98,92 +48,131 @@ namespace eo int packetSize; EOT* tempArray; - // bmpi::communicator& comm; + int masterRank; + bmpi::communicator& comm; }; - template< class EOT > - class SendTaskParallelApply : public SendTaskFunction, public BaseParallelApply + /* + template< typename EOT > + class ParallelApply : public Job< JobData > { public: - using BaseParallelApply::j; + + ParallelApply( + // eoUF & _proc, + // std::vector& _pop, + AssignmentAlgorithm & algo, + int _masterRank, + const JobStore< JobData >& store + // long _maxTime = 0, + // int _packetSize = 1 + ) : + Job( algo, _masterRank, store ) + // Job( algo, _masterRank, _maxTime ), + func( _proc ), + data( _pop ), + packetSize( _packetSize ), + index( 0 ), + size( _pop.size() ) + { + // empty + } + + protected: + + // bmpi::communicator& comm; + }; + */ + + template< class EOT > + class SendTaskParallelApply : public SendTaskFunction< JobData > + { + public: + using SendTaskFunction< JobData >::d; // futureIndex, index, packetSize, size, comm, assignedTasks, data void operator()(int wrkRank) { int futureIndex; - if( j->index + j->packetSize < j->size ) + if( d->index + d->packetSize < d->size ) { - futureIndex = j->index + j->packetSize; + futureIndex = d->index + d->packetSize; } else { - futureIndex = j->size; + futureIndex = d->size; } - int sentSize = futureIndex - j->index ; + int sentSize = futureIndex - d->index ; - j->comm.send( wrkRank, 1, sentSize ); + d->comm.send( wrkRank, 1, sentSize ); - eo::log << eo::progress << "Evaluating individual " << j->index << std::endl; + eo::log << eo::progress << "Evaluating individual " << d->index << std::endl; - j->assignedTasks[ wrkRank ].index = j->index; - j->assignedTasks[ wrkRank ].size = sentSize; + d->assignedTasks[ wrkRank ].index = d->index; + d->assignedTasks[ wrkRank ].size = sentSize; - j->comm.send( wrkRank, 1, & ( (j->data)[ j->index ] ) , sentSize ); - j->index = futureIndex; + d->comm.send( wrkRank, 1, & ( (d->data)[ d->index ] ) , sentSize ); + d->index = futureIndex; } }; template< class EOT > - class HandleResponseParallelApply : public HandleResponseFunction, public BaseParallelApply + class HandleResponseParallelApply : public HandleResponseFunction< JobData > { public: - using BaseParallelApply::j; + using HandleResponseFunction< JobData >::d; void operator()(int wrkRank) { - j->comm.recv( wrkRank, 1, & (j->data[ j->assignedTasks[wrkRank].index ] ), j->assignedTasks[wrkRank].size ); + d->comm.recv( wrkRank, 1, & (d->data[ d->assignedTasks[wrkRank].index ] ), d->assignedTasks[wrkRank].size ); } }; template< class EOT > - class ProcessTaskParallelApply : public ProcessTaskFunction, public BaseParallelApply + class ProcessTaskParallelApply : public ProcessTaskFunction< JobData > { public: - using BaseParallelApply::j; + using ProcessTaskFunction< JobData >::d; void operator()() { int recvSize; - j->comm.recv( j->masterRank, 1, recvSize ); - j->comm.recv( j->masterRank, 1, j->tempArray, recvSize ); + d->comm.recv( d->masterRank, 1, recvSize ); + d->comm.recv( d->masterRank, 1, d->tempArray, recvSize ); timerStat.start("worker_processes"); for( int i = 0; i < recvSize ; ++i ) { - j->func( j->tempArray[ i ] ); + d->func( d->tempArray[ i ] ); } timerStat.stop("worker_processes"); - j->comm.send( j->masterRank, 1, j->tempArray, recvSize ); + d->comm.send( d->masterRank, 1, d->tempArray, recvSize ); } }; template< class EOT > - class IsFinishedParallelApply : public IsFinishedFunction, public BaseParallelApply + class IsFinishedParallelApply : public IsFinishedFunction< JobData > { public: - - using BaseParallelApply::j; + using IsFinishedFunction< JobData >::d; bool operator()() { - return j->index == j->size; + return d->index == d->size; } }; template< class EOT > - struct ParallelApplyStore : public JobStore + struct ParallelApplyStore : public JobStore< JobData > { - ParallelApplyStore() + ParallelApplyStore( + eoUF & _proc, + std::vector& _pop, + int _masterRank, + // long _maxTime = 0, + int _packetSize = 1 + ) + : j( _proc, _pop, _masterRank, _packetSize ) { stpa = new SendTaskParallelApply; hrpa = new HandleResponseParallelApply; @@ -199,16 +188,20 @@ namespace eo delete ispa; } - SendTaskFunction& sendTask() const { return *stpa; } - HandleResponseFunction& handleResponse() const { return *hrpa; } - ProcessTaskFunction& processTask() const { return *ptpa; } - IsFinishedFunction& isFinished() const { return *ispa; } + SendTaskFunction< JobData >& sendTask() const { return *stpa; } + HandleResponseFunction< JobData >& handleResponse() const { return *hrpa; } + ProcessTaskFunction< JobData >& processTask() const { return *ptpa; } + IsFinishedFunction< JobData >& isFinished() const { return *ispa; } + + JobData* data() { return &j; } protected: SendTaskParallelApply* stpa; HandleResponseParallelApply* hrpa; ProcessTaskParallelApply* ptpa; IsFinishedParallelApply* ispa; + + JobData j; }; } } diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 18aadd549..4e3313342 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -38,7 +38,7 @@ int main(int argc, char** argv) { v.push_back( rand() ); } - + int offset = 0; vector originalV = v; @@ -46,8 +46,6 @@ int main(int argc, char** argv) vector< Test > tests; - ParallelApplyStore store; - const int ALL = Node::comm().size(); Test tIntervalStatic; @@ -112,7 +110,9 @@ int main(int argc, char** argv) for( unsigned int i = 0; i < tests.size(); ++i ) { - ParallelApply job( plusOneInstance, v, *(tests[i].assign), 0, store, 3 ); + // ParallelApply job( plusOneInstance, v, *(tests[i].assign), 0, store, 3 ); + ParallelApplyStore< int > store( plusOneInstance, v, 0, 3 ); + Job< JobData > job( *(tests[i].assign), 0, store ); if( job.isMaster() ) { From 6bb2ccfbd6be0bbb1e2c736dd116d757f767e82d Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 3 Jul 2012 13:57:20 +0200 Subject: [PATCH 39/76] Steps of MpiJob are now functors which can be wrapped (using decorator pattern). --- eo/src/mpi/eoMpi.h | 4 ++ eo/src/mpi/eoParallelApply.h | 112 +++++++++++++++++++--------------- eo/test/mpi/parallelApply.cpp | 3 +- eo/test/mpi/wrapper.cpp | 89 +++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 50 deletions(-) create mode 100644 eo/test/mpi/wrapper.cpp diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 9ad2bd7bf..940f41292 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -44,6 +44,10 @@ namespace eo void data( JobData* _d ) { d = _d; + if( wrapped ) + { + wrapped->data( _d ); + } } protected: diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index bc85d4387..94549fc31 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -17,14 +17,14 @@ namespace eo }; template - struct JobData + struct ParallelApplyData { - JobData( + ParallelApplyData( eoUF & _proc, std::vector& _pop, int _masterRank, // long _maxTime = 0, - int _packetSize + int _packetSize // FIXME = 1 ? ) : data( _pop ), func( _proc ), index( 0 ), size( _pop.size() ), packetSize( _packetSize ), masterRank( _masterRank ), comm( Node::comm() ) { @@ -35,7 +35,7 @@ namespace eo tempArray = new EOT[ _packetSize ]; } - ~JobData() + ~ParallelApplyData() { delete [] tempArray; } @@ -52,43 +52,16 @@ namespace eo bmpi::communicator& comm; }; - /* - template< typename EOT > - class ParallelApply : public Job< JobData > + template< class EOT > + class SendTaskParallelApply : public SendTaskFunction< ParallelApplyData > { public: + using SendTaskFunction< ParallelApplyData >::d; - ParallelApply( - // eoUF & _proc, - // std::vector& _pop, - AssignmentAlgorithm & algo, - int _masterRank, - const JobStore< JobData >& store - // long _maxTime = 0, - // int _packetSize = 1 - ) : - Job( algo, _masterRank, store ) - // Job( algo, _masterRank, _maxTime ), - func( _proc ), - data( _pop ), - packetSize( _packetSize ), - index( 0 ), - size( _pop.size() ) + SendTaskParallelApply( SendTaskParallelApply * w = 0 ) : SendTaskFunction< ParallelApplyData >( w ) { // empty } - - protected: - - // bmpi::communicator& comm; - }; - */ - - template< class EOT > - class SendTaskParallelApply : public SendTaskFunction< JobData > - { - public: - using SendTaskFunction< JobData >::d; // futureIndex, index, packetSize, size, comm, assignedTasks, data void operator()(int wrkRank) @@ -117,10 +90,15 @@ namespace eo }; template< class EOT > - class HandleResponseParallelApply : public HandleResponseFunction< JobData > + class HandleResponseParallelApply : public HandleResponseFunction< ParallelApplyData > { public: - using HandleResponseFunction< JobData >::d; + using HandleResponseFunction< ParallelApplyData >::d; + + HandleResponseParallelApply( HandleResponseParallelApply * w = 0 ) : HandleResponseFunction< ParallelApplyData >( w ) + { + // empty + } void operator()(int wrkRank) { @@ -129,10 +107,15 @@ namespace eo }; template< class EOT > - class ProcessTaskParallelApply : public ProcessTaskFunction< JobData > + class ProcessTaskParallelApply : public ProcessTaskFunction< ParallelApplyData > { public: - using ProcessTaskFunction< JobData >::d; + using ProcessTaskFunction< ParallelApplyData >::d; + + ProcessTaskParallelApply( ProcessTaskParallelApply * w = 0 ) : ProcessTaskFunction< ParallelApplyData >( w ) + { + // empty + } void operator()() { @@ -151,10 +134,15 @@ namespace eo }; template< class EOT > - class IsFinishedParallelApply : public IsFinishedFunction< JobData > + class IsFinishedParallelApply : public IsFinishedFunction< ParallelApplyData > { public: - using IsFinishedFunction< JobData >::d; + using IsFinishedFunction< ParallelApplyData >::d; + + IsFinishedParallelApply( IsFinishedParallelApply * w = 0 ) : IsFinishedFunction< ParallelApplyData >( w ) + { + // empty + } bool operator()() { @@ -163,7 +151,7 @@ namespace eo }; template< class EOT > - struct ParallelApplyStore : public JobStore< JobData > + struct ParallelApplyStore : public JobStore< ParallelApplyData > { ParallelApplyStore( eoUF & _proc, @@ -172,7 +160,7 @@ namespace eo // long _maxTime = 0, int _packetSize = 1 ) - : j( _proc, _pop, _masterRank, _packetSize ) + : _data( _proc, _pop, _masterRank, _packetSize ) { stpa = new SendTaskParallelApply; hrpa = new HandleResponseParallelApply; @@ -188,20 +176,46 @@ namespace eo delete ispa; } - SendTaskFunction< JobData >& sendTask() const { return *stpa; } - HandleResponseFunction< JobData >& handleResponse() const { return *hrpa; } - ProcessTaskFunction< JobData >& processTask() const { return *ptpa; } - IsFinishedFunction< JobData >& isFinished() const { return *ispa; } + SendTaskParallelApply< EOT >& sendTask() const { return *stpa; } + HandleResponseParallelApply< EOT >& handleResponse() const { return *hrpa; } + ProcessTaskParallelApply< EOT > & processTask() const { return *ptpa; } + IsFinishedParallelApply< EOT >& isFinished() const { return *ispa; } - JobData* data() { return &j; } + void sendTask( SendTaskParallelApply< EOT >* _stpa ) { stpa = _stpa; } + void handleResponse( HandleResponseParallelApply< EOT >* _hrpa ) { hrpa = _hrpa; } + void processTask( ProcessTaskParallelApply< EOT >* _ptpa ) { ptpa = _ptpa; } + void isFinished( IsFinishedParallelApply< EOT >* _ispa ) { ispa = _ispa; } + + ParallelApplyData* data() { return &_data; } protected: + // TODO commenter : Utiliser des pointeurs pour éviter d'écraser les fonctions wrappées SendTaskParallelApply* stpa; HandleResponseParallelApply* hrpa; ProcessTaskParallelApply* ptpa; IsFinishedParallelApply* ispa; - JobData j; + ParallelApplyData _data; + }; + + template< typename EOT > + class ParallelApply : public Job< ParallelApplyData > + { + public: + + ParallelApply( + AssignmentAlgorithm & algo, + int _masterRank, + ParallelApplyStore & store + ) : + Job< ParallelApplyData >( algo, _masterRank, store ) + { + // empty + } + + protected: + + // bmpi::communicator& comm; }; } } diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 4e3313342..75b515e93 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -112,7 +112,8 @@ int main(int argc, char** argv) { // ParallelApply job( plusOneInstance, v, *(tests[i].assign), 0, store, 3 ); ParallelApplyStore< int > store( plusOneInstance, v, 0, 3 ); - Job< JobData > job( *(tests[i].assign), 0, store ); + // Job< JobData > job( *(tests[i].assign), 0, store ); + ParallelApply< int > job( *(tests[i].assign), 0, store ); if( job.isMaster() ) { diff --git a/eo/test/mpi/wrapper.cpp b/eo/test/mpi/wrapper.cpp new file mode 100644 index 000000000..c653d5f86 --- /dev/null +++ b/eo/test/mpi/wrapper.cpp @@ -0,0 +1,89 @@ +# include +# include + +# include + +# include +using namespace std; + +using namespace eo::mpi; + +struct plusOne : public eoUF< int&, void > +{ + void operator() ( int & x ) + { + ++x; + } +}; + +template< class EOT > +struct ShowWrappedResult : public IsFinishedParallelApply +{ + using IsFinishedParallelApply::wrapped; + + ShowWrappedResult ( IsFinishedParallelApply * w ) : IsFinishedParallelApply( 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; +}; + +// These tests require at least 3 processes to be launched. +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 v; + for( int i = 0; i < 1000; ++i ) + { + v.push_back( rand() ); + } + + int offset = 0; + vector originalV = v; + + plusOne plusOneInstance; + + StaticAssignmentAlgorithm assign( v.size() ); + + ParallelApplyStore< int > store( plusOneInstance, v, 0, 1 ); + IsFinishedParallelApply< int >& wrapped = store.isFinished(); + ShowWrappedResult< int >* wrapper = new ShowWrappedResult( &wrapped ); + store.isFinished( wrapper ); + + // Job< ParallelApplyData > job( assign, 0, store ); + ParallelApply job( assign, 0, store ); + job.run(); + + if( job.isMaster() ) + { + ++offset; + for(int i = 0; i < v.size(); ++i) + { + cout << v[i] << ' '; + if( originalV[i] + offset != v[i] ) + { + cout << " <-- ERROR at this point." << endl; + exit( EXIT_FAILURE ); + } + } + cout << endl; + } + + return 0; +} + From 63f12c4e1ce3bfc325527013fbee9eaae751a504 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 3 Jul 2012 14:48:47 +0200 Subject: [PATCH 40/76] JobStore has now getters, setters and wrappers methods. --- eo/src/mpi/eoMpi.h | 101 ++++++++++++++++++++++++++++------- eo/src/mpi/eoParallelApply.h | 51 +++++++----------- eo/test/mpi/wrapper.cpp | 13 +++-- 3 files changed, 108 insertions(+), 57 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 940f41292..7dafe1289 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -25,6 +25,7 @@ namespace eo namespace Channel { const int Commands = 0; + const int Messages = 1; } namespace Message @@ -33,26 +34,32 @@ namespace eo const int Finish = 1; } + template< typename JobData, typename Wrapped > struct SharedDataFunction { - SharedDataFunction( Wrapped * w ) + SharedDataFunction( Wrapped * w = 0 ) : _wrapped( w ) { - wrapped = w; + // empty + } + + void wrapped( Wrapped * w ) + { + _wrapped = w; } void data( JobData* _d ) { d = _d; - if( wrapped ) + if( _wrapped ) { - wrapped->data( _d ); + _wrapped->data( _d ); } } protected: JobData* d; - Wrapped* wrapped; + Wrapped* _wrapped; }; template< typename JobData > @@ -110,11 +117,78 @@ namespace eo template< typename JobData > struct JobStore { - virtual SendTaskFunction & sendTask() const = 0; - virtual HandleResponseFunction & handleResponse() const = 0; - virtual ProcessTaskFunction & processTask() const = 0; - virtual IsFinishedFunction & isFinished() const = 0; + JobStore( + SendTaskFunction* stf, + HandleResponseFunction* hrf, + ProcessTaskFunction* ptf, + IsFinishedFunction* iff + ) : + _stf( stf ), _hrf( hrf ), _ptf( ptf ), _iff( iff ) + { + // empty + } + + JobStore() + { + // empty + } + + SendTaskFunction & sendTask() { return *_stf; } + HandleResponseFunction & handleResponse() { return *_hrf; } + ProcessTaskFunction & processTask() { return *_ptf; } + IsFinishedFunction & isFinished() { return *_iff; } + + void sendTask( SendTaskFunction* stf ) { _stf = stf; } + void handleResponse( HandleResponseFunction* hrf ) { _hrf = hrf; } + void processTask( ProcessTaskFunction* ptf ) { _ptf = ptf; } + void isFinished( IsFinishedFunction* iff ) { _iff = iff; } + + void wrapSendTask( SendTaskFunction* stf ) + { + if( stf ) + { + stf->wrapped( _stf ); + _stf = stf; + } + } + + void wrapHandleResponse( HandleResponseFunction* hrf ) + { + if( hrf ) + { + hrf->wrapped( _hrf ); + _hrf = hrf; + } + } + + void wrapProcessTask( ProcessTaskFunction* ptf ) + { + if( ptf ) + { + ptf->wrapped( _ptf ); + _ptf = ptf; + } + } + + void wrapIsFinished( IsFinishedFunction* iff ) + { + if( iff ) + { + iff->wrapped( _iff ); + _iff = iff; + } + } + + // TODO commenter : laissé à la couche d'en dessous car impossible d'initialiser une donnée membre d'une classe mère depuis une classe fille. virtual JobData* data() = 0; + + protected: + + // TODO commenter : Utiliser des pointeurs pour éviter d'écraser les fonctions wrappées + SendTaskFunction< JobData >* _stf; + HandleResponseFunction< JobData >* _hrf; + ProcessTaskFunction< JobData >* _ptf; + IsFinishedFunction< JobData >* _iff; }; template< class JobData > @@ -141,15 +215,6 @@ namespace eo isFinished.data( store.data() ); } - /* - // master - virtual bool isFinished() = 0; - virtual void sendTask( int wrkRank ) = 0; - virtual void handleResponse( int wrkRank ) = 0; - // worker - virtual void processTask( ) = 0; - */ - protected: SendTaskFunction & sendTask; diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 94549fc31..1487da9fb 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -153,6 +153,11 @@ namespace eo template< class EOT > struct ParallelApplyStore : public JobStore< ParallelApplyData > { + using JobStore< ParallelApplyData >::_stf; + using JobStore< ParallelApplyData >::_hrf; + using JobStore< ParallelApplyData >::_ptf; + using JobStore< ParallelApplyData >::_iff; + ParallelApplyStore( eoUF & _proc, std::vector& _pop, @@ -162,42 +167,28 @@ namespace eo ) : _data( _proc, _pop, _masterRank, _packetSize ) { - stpa = new SendTaskParallelApply; - hrpa = new HandleResponseParallelApply; - ptpa = new ProcessTaskParallelApply; - ispa = new IsFinishedParallelApply; + _stf = new SendTaskParallelApply; + _hrf = new HandleResponseParallelApply; + _ptf = new ProcessTaskParallelApply; + _iff = new IsFinishedParallelApply; } - ~ParallelApplyStore() - { - delete stpa; - delete hrpa; - delete ptpa; - delete ispa; - } - - SendTaskParallelApply< EOT >& sendTask() const { return *stpa; } - HandleResponseParallelApply< EOT >& handleResponse() const { return *hrpa; } - ProcessTaskParallelApply< EOT > & processTask() const { return *ptpa; } - IsFinishedParallelApply< EOT >& isFinished() const { return *ispa; } - - void sendTask( SendTaskParallelApply< EOT >* _stpa ) { stpa = _stpa; } - void handleResponse( HandleResponseParallelApply< EOT >* _hrpa ) { hrpa = _hrpa; } - void processTask( ProcessTaskParallelApply< EOT >* _ptpa ) { ptpa = _ptpa; } - void isFinished( IsFinishedParallelApply< EOT >* _ispa ) { ispa = _ispa; } - ParallelApplyData* data() { return &_data; } - protected: - // TODO commenter : Utiliser des pointeurs pour éviter d'écraser les fonctions wrappées - SendTaskParallelApply* stpa; - HandleResponseParallelApply* hrpa; - ProcessTaskParallelApply* ptpa; - IsFinishedParallelApply* ispa; + ~ParallelApplyStore() + { + delete _stf; + delete _hrf; + delete _ptf; + delete _iff; + } + protected: ParallelApplyData _data; }; + // TODO commentaire : impossible de faire un typedef sur un template sans passer + // par un traits => complique la tâche de l'utilisateur pour rien. template< typename EOT > class ParallelApply : public Job< ParallelApplyData > { @@ -212,10 +203,6 @@ namespace eo { // empty } - - protected: - - // bmpi::communicator& comm; }; } } diff --git a/eo/test/mpi/wrapper.cpp b/eo/test/mpi/wrapper.cpp index c653d5f86..e2bf25d62 100644 --- a/eo/test/mpi/wrapper.cpp +++ b/eo/test/mpi/wrapper.cpp @@ -19,16 +19,16 @@ struct plusOne : public eoUF< int&, void > template< class EOT > struct ShowWrappedResult : public IsFinishedParallelApply { - using IsFinishedParallelApply::wrapped; + using IsFinishedParallelApply::_wrapped; - ShowWrappedResult ( IsFinishedParallelApply * w ) : IsFinishedParallelApply( w ), times( 0 ) + ShowWrappedResult ( IsFinishedParallelApply * w = 0 ) : IsFinishedParallelApply( w ), times( 0 ) { // empty } bool operator()() { - bool wrappedValue = wrapped->operator()(); // (*wrapped)(); + bool wrappedValue = _wrapped->operator()(); // (*_wrapped)(); cout << times << ") Wrapped function would say that it is " << ( wrappedValue ? "":"not ") << "finished" << std::endl; ++times; return wrappedValue; @@ -61,12 +61,11 @@ int main(int argc, char** argv) StaticAssignmentAlgorithm assign( v.size() ); ParallelApplyStore< int > store( plusOneInstance, v, 0, 1 ); - IsFinishedParallelApply< int >& wrapped = store.isFinished(); - ShowWrappedResult< int >* wrapper = new ShowWrappedResult( &wrapped ); - store.isFinished( wrapper ); + store.wrapIsFinished( new ShowWrappedResult ); - // Job< ParallelApplyData > job( assign, 0, store ); ParallelApply job( assign, 0, store ); + // Equivalent to: + // Job< ParallelApplyData > job( assign, 0, store ); job.run(); if( job.isMaster() ) From 564cc3bccdd57f5af2487c575c0c4809a208e62f Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 3 Jul 2012 14:51:34 +0200 Subject: [PATCH 41/76] Using eo::mpi::DEFAULT_MASTER instead of raw constants. --- eo/src/mpi/eoMpi.h | 1 + eo/test/mpi/parallelApply.cpp | 6 +++--- eo/test/mpi/wrapper.cpp | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 7dafe1289..8a4e86b63 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -34,6 +34,7 @@ namespace eo const int Finish = 1; } + const int DEFAULT_MASTER = 0; template< typename JobData, typename Wrapped > struct SharedDataFunction diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 75b515e93..7ccbf3d9c 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -111,9 +111,9 @@ int main(int argc, char** argv) for( unsigned int i = 0; i < tests.size(); ++i ) { // ParallelApply job( plusOneInstance, v, *(tests[i].assign), 0, store, 3 ); - ParallelApplyStore< int > store( plusOneInstance, v, 0, 3 ); - // Job< JobData > job( *(tests[i].assign), 0, store ); - ParallelApply< int > job( *(tests[i].assign), 0, store ); + ParallelApplyStore< int > store( plusOneInstance, v, eo::mpi::DEFAULT_MASTER, 3 ); + // Job< JobData > job( *(tests[i].assign), eo::mpi::DEFAULT_MASTER, store ); + ParallelApply< int > job( *(tests[i].assign), eo::mpi::DEFAULT_MASTER, store ); if( job.isMaster() ) { diff --git a/eo/test/mpi/wrapper.cpp b/eo/test/mpi/wrapper.cpp index e2bf25d62..d8b72c98e 100644 --- a/eo/test/mpi/wrapper.cpp +++ b/eo/test/mpi/wrapper.cpp @@ -60,10 +60,10 @@ int main(int argc, char** argv) StaticAssignmentAlgorithm assign( v.size() ); - ParallelApplyStore< int > store( plusOneInstance, v, 0, 1 ); + ParallelApplyStore< int > store( plusOneInstance, v, eo::mpi::DEFAULT_MASTER, 1 ); store.wrapIsFinished( new ShowWrappedResult ); - ParallelApply job( assign, 0, store ); + ParallelApply job( assign, eo::mpi::DEFAULT_MASTER, store ); // Equivalent to: // Job< ParallelApplyData > job( assign, 0, store ); job.run(); From 1d3790a0e466c193d8c74ac46af76f68671dc1c0 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 3 Jul 2012 15:07:27 +0200 Subject: [PATCH 42/76] Updating multiple roles test. --- eo/test/mpi/multipleRoles.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/multipleRoles.cpp index 5b829c890..ce10f6214 100644 --- a/eo/test/mpi/multipleRoles.cpp +++ b/eo/test/mpi/multipleRoles.cpp @@ -31,7 +31,9 @@ void subtask( vector& v, int rank ) workers.push_back( rank + 4 ); DynamicAssignmentAlgorithm algo( workers ); SubWork sw; - ParallelApply job( sw, v, algo, rank ); + + ParallelApplyStore store( sw, v, rank ); + ParallelApply job( algo, rank, store ); job.run(); } @@ -72,7 +74,8 @@ int main(int argc, char** argv) { Work w; DynamicAssignmentAlgorithm algo( 1, 2 ); - ParallelApply< vector > job( w, metaV, algo, 0 ); + ParallelApplyStore< vector > store( w, metaV, 0 ); + ParallelApply< vector > job( algo, 0, store ); job.run(); if( job.isMaster() ) { From 24c29db6f3f8caeabbda8cf5c893e3788738b93e Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 3 Jul 2012 15:53:19 +0200 Subject: [PATCH 43/76] Using again parallel apply into eoPopEvalFunc::eoParallelPopEvalFunc. --- eo/src/apply.h | 4 +- eo/src/eoPopEvalFunc.h | 9 ++++ eo/src/mpi/eoMultiParallelApply.h | 52 ++++++++++++----------- eo/src/mpi/eoParallelApply.h | 21 ++++++---- eo/src/mpi/eoTerminateJob.h | 68 +++++++++++++++++++++++++++++++ eo/test/mpi/eval.cpp | 5 ++- eo/test/mpi/parallelApply.cpp | 1 - 7 files changed, 125 insertions(+), 35 deletions(-) diff --git a/eo/src/apply.h b/eo/src/apply.h index 7e59c21b8..86fc8927d 100644 --- a/eo/src/apply.h +++ b/eo/src/apply.h @@ -36,6 +36,7 @@ # ifdef WITH_MPI # include # include +# include # endif // WITH_MPI /** @@ -94,7 +95,8 @@ void parallelApply( int _packetSize, int _maxTime) { - eo::mpi::MultiParallelApply job( _proc, _pop, _algo, _masterRank, _packetSize, _maxTime ); + eo::mpi::ParallelEvalStore store( _proc, _pop, _masterRank, _packetSize ); + eo::mpi::ParallelApply job( _algo, _masterRank, store ); job.run(); } #endif diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index 77473eb15..1b7b447fe 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -99,6 +99,15 @@ public: // empty } + ~eoParallelPopLoopEval() + { + if( eo::mpi::Node::comm().rank() == masterRank ) + { + eo::mpi::EmptyJob job( assignAlgo, masterRank ); + job.run(); + } + } + /** Do the job: simple loop over the offspring */ void operator()(eoPop & _parents, eoPop & _offspring) { diff --git a/eo/src/mpi/eoMultiParallelApply.h b/eo/src/mpi/eoMultiParallelApply.h index f3df801d0..121e0b749 100644 --- a/eo/src/mpi/eoMultiParallelApply.h +++ b/eo/src/mpi/eoMultiParallelApply.h @@ -1,4 +1,3 @@ - # ifndef __EO_MULTI_PARALLEL_APPLY_H__ # define __EO_MULTI_PARALLEL_APPLY_H__ @@ -8,36 +7,41 @@ namespace eo { namespace mpi { - template< typename EOT > - class MultiParallelApply : public ParallelApply + template< class EOT > + class ProcessTaskParallelEval : public ProcessTaskParallelApply { public: - // using ParallelApply::comm; - using ParallelApply::masterRank; + using ProcessTaskParallelApply::_wrapped; + using ProcessTaskParallelApply::d; - MultiParallelApply( - eoUF & _proc, - std::vector& _pop, - AssignmentAlgorithm & algo, - int _masterRank, - int _packetSize = 1, - long _maxTime = 0 - ) : - ParallelApply( _proc, _pop, algo, _masterRank, _packetSize, _maxTime ) + void operator()() + { + int order = Message::Continue; + while( order != Message::Finish ) { - // empty + _wrapped->operator()(); + d->comm.recv( d->masterRank, Channel::Commands, order ); } + } + }; - virtual void processTask( ) - { - int order = Message::Continue; - while( order != Message::Finish ) - { - ParallelApply::processTask( ); - ParallelApply::comm.recv( masterRank, Channel::Commands, order ); - } - } + template< class EOT > + struct ParallelEvalStore : public ParallelApplyStore< EOT > + { + using ParallelApplyStore::wrapProcessTask; + + ParallelEvalStore( + eoUF & _proc, + std::vector& _pop, + int _masterRank, + // long _maxTime = 0, + int _packetSize = 1 + ) : + ParallelApplyStore< EOT >( _proc, _pop, _masterRank, _packetSize ) + { + wrapProcessTask( new ProcessTaskParallelEval ); + } }; } } diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 1487da9fb..f195965ae 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -24,7 +24,7 @@ namespace eo std::vector& _pop, int _masterRank, // long _maxTime = 0, - int _packetSize // FIXME = 1 ? + int _packetSize ) : data( _pop ), func( _proc ), index( 0 ), size( _pop.size() ), packetSize( _packetSize ), masterRank( _masterRank ), comm( Node::comm() ) { @@ -163,14 +163,19 @@ namespace eo std::vector& _pop, int _masterRank, // long _maxTime = 0, - int _packetSize = 1 - ) - : _data( _proc, _pop, _masterRank, _packetSize ) + int _packetSize = 1, + // JobStore functors + SendTaskParallelApply * stpa = new SendTaskParallelApply, + HandleResponseParallelApply* hrpa = new HandleResponseParallelApply, + ProcessTaskParallelApply* ptpa = new ProcessTaskParallelApply, + IsFinishedParallelApply* ifpa = new IsFinishedParallelApply + ) : + _data( _proc, _pop, _masterRank, _packetSize ) { - _stf = new SendTaskParallelApply; - _hrf = new HandleResponseParallelApply; - _ptf = new ProcessTaskParallelApply; - _iff = new IsFinishedParallelApply; + _stf = stpa; + _hrf = hrpa; + _ptf = ptpa; + _iff = ifpa; } ParallelApplyData* data() { return &_data; } diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h index 4cb182255..6c63cb697 100644 --- a/eo/src/mpi/eoTerminateJob.h +++ b/eo/src/mpi/eoTerminateJob.h @@ -7,6 +7,73 @@ namespace eo { namespace mpi { + struct DummySendTaskFunction : public SendTaskFunction + { + void operator()( int _ ) + { + } + }; + + struct DummyHandleResponseFunction : public HandleResponseFunction + { + void operator()( int _ ) + { + } + }; + + struct DummyProcessTaskFunction : public ProcessTaskFunction + { + void operator()() + { + // nothing! + } + }; + + struct DummyIsFinishedFunction : public IsFinishedFunction + { + bool operator()() + { + return true; + } + }; + + struct DummyJobStore : public JobStore + { + using JobStore::_stf; + using JobStore::_hrf; + using JobStore::_ptf; + using JobStore::_iff; + + DummyJobStore() + { + _stf = new DummySendTaskFunction; + _hrf = new DummyHandleResponseFunction; + _ptf = new DummyProcessTaskFunction; + _iff = new DummyIsFinishedFunction; + } + + ~DummyJobStore() + { + delete _stf; + delete _hrf; + delete _ptf; + delete _iff; + } + + void* data() { return 0; } + }; + + struct EmptyJob : public Job + { + EmptyJob( AssignmentAlgorithm& algo, int masterRank ) : + Job( algo, masterRank, *(new DummyJobStore) ) + // FIXME memory leak => will be corrected by using const correctness + { + // empty + } + }; + + /* class TerminateJob : public Job { public: @@ -36,6 +103,7 @@ namespace eo return true; } }; + */ } } diff --git a/eo/test/mpi/eval.cpp b/eo/test/mpi/eval.cpp index 7e15ac6f3..7d8666cb1 100644 --- a/eo/test/mpi/eval.cpp +++ b/eo/test/mpi/eval.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include @@ -83,6 +85,7 @@ typedef eoRealSerializable EOT; int main(int ac, char** av) { eo::mpi::Node::init( ac, av ); + eo::log << eo::setlevel( eo::debug ); eoParser parser(ac, av); @@ -110,7 +113,7 @@ int main(int ac, char** av) eo::log << eo::setlevel( eo::debug ); eo::mpi::DynamicAssignmentAlgorithm assign; - eoParallelPopLoopEval< EOT > popEval( eval, assign, 0, 3 ); + eoParallelPopLoopEval< EOT > popEval( eval, assign, eo::mpi::DEFAULT_MASTER, 3 ); popEval( pop, pop ); eo::log << eo::quiet << "DONE!" << std::endl; diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index 7ccbf3d9c..d562ef0ec 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -110,7 +110,6 @@ int main(int argc, char** argv) for( unsigned int i = 0; i < tests.size(); ++i ) { - // ParallelApply job( plusOneInstance, v, *(tests[i].assign), 0, store, 3 ); ParallelApplyStore< int > store( plusOneInstance, v, eo::mpi::DEFAULT_MASTER, 3 ); // Job< JobData > job( *(tests[i].assign), eo::mpi::DEFAULT_MASTER, store ); ParallelApply< int > job( *(tests[i].assign), eo::mpi::DEFAULT_MASTER, store ); From 603268b053dafbe0c545486f2354b1970d683a79 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 4 Jul 2012 10:53:57 +0200 Subject: [PATCH 44/76] Added store management to eoPopEvalFunc. --- eo/src/apply.h | 8 +-- eo/src/eoPopEvalFunc.h | 100 ++++++++++++++++++------------ eo/src/mpi/eoMultiParallelApply.h | 14 ++++- eo/src/mpi/eoParallelApply.h | 12 +++- eo/test/mpi/eval.cpp | 66 +++++++++++++++++--- 5 files changed, 143 insertions(+), 57 deletions(-) diff --git a/eo/src/apply.h b/eo/src/apply.h index 86fc8927d..df3c43e1c 100644 --- a/eo/src/apply.h +++ b/eo/src/apply.h @@ -88,15 +88,13 @@ void apply(eoUF& _proc, std::vector& _pop) #ifdef WITH_MPI template void parallelApply( - eoUF& _proc, std::vector& _pop, eo::mpi::AssignmentAlgorithm& _algo, int _masterRank, - int _packetSize, - int _maxTime) + eo::mpi::ParallelEvalStore & _store ) { - eo::mpi::ParallelEvalStore store( _proc, _pop, _masterRank, _packetSize ); - eo::mpi::ParallelApply job( _algo, _masterRank, store ); + _store.data( _pop ); + eo::mpi::ParallelApply job( _algo, _masterRank, _store ); job.run(); } #endif diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index 1b7b447fe..ce4dc5cf6 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -80,48 +80,72 @@ private: #ifdef WITH_MPI // TODO TODOB commenter template -class eoParallelPopLoopEval : public eoPopEvalFunc { -public: - /** Ctor: set value of embedded eoEvalFunc */ - eoParallelPopLoopEval( - eoEvalFunc & _eval, - eo::mpi::AssignmentAlgorithm& _assignAlgo, - int _masterRank, - int _packetSize = 1, - int _maxTime = 0 - ) : - eval(_eval), - assignAlgo( _assignAlgo ), - masterRank( _masterRank ), - packetSize( _packetSize ), - maxTime( _maxTime ) - { - // empty - } +class eoParallelPopLoopEval : public eoPopEvalFunc +{ + public: + /** Ctor: set value of embedded eoEvalFunc */ + eoParallelPopLoopEval( + eoEvalFunc & _eval, + eo::mpi::AssignmentAlgorithm& _assignAlgo, + int _masterRank, + int _packetSize = 1 + ) : + eval(_eval), + assignAlgo( _assignAlgo ), + masterRank( _masterRank ), + packetSize( _packetSize ), + needToDeleteStore( true ) + { + store = new eo::mpi::ParallelEvalStore( _eval, _masterRank, _packetSize ); + } - ~eoParallelPopLoopEval() - { - if( eo::mpi::Node::comm().rank() == masterRank ) - { - eo::mpi::EmptyJob job( assignAlgo, masterRank ); - job.run(); - } - } + eoParallelPopLoopEval( + eoEvalFunc & _eval, + eo::mpi::AssignmentAlgorithm& _assignAlgo, + eo::mpi::ParallelEvalStore* _store, + int _masterRank, + int _packetSize = 1 + ) : + eval(_eval), + assignAlgo( _assignAlgo ), + masterRank( _masterRank ), + packetSize( _packetSize ), + needToDeleteStore( false ), + store( _store ) + { + // empty + } - /** Do the job: simple loop over the offspring */ - void operator()(eoPop & _parents, eoPop & _offspring) - { - (void)_parents; - parallelApply(eval, _offspring, assignAlgo, masterRank, packetSize, maxTime); - } + ~eoParallelPopLoopEval() + { + if( eo::mpi::Node::comm().rank() == masterRank ) + { + eo::mpi::EmptyJob job( assignAlgo, masterRank ); + job.run(); + } -private: - eoEvalFunc & eval; + if( needToDeleteStore ) + { + delete store; + } + } - eo::mpi::AssignmentAlgorithm & assignAlgo; - int masterRank; - int packetSize; - int maxTime; + /** Do the job: simple loop over the offspring */ + void operator()(eoPop & _parents, eoPop & _offspring) + { + (void)_parents; + parallelApply(_offspring, assignAlgo, masterRank, *store); + } + + private: + eoEvalFunc & eval; + + eo::mpi::AssignmentAlgorithm & assignAlgo; + eo::mpi::ParallelEvalStore* store; + int masterRank; + int packetSize; + + bool needToDeleteStore; }; #endif diff --git a/eo/src/mpi/eoMultiParallelApply.h b/eo/src/mpi/eoMultiParallelApply.h index 121e0b749..85b1689e6 100644 --- a/eo/src/mpi/eoMultiParallelApply.h +++ b/eo/src/mpi/eoMultiParallelApply.h @@ -24,6 +24,11 @@ namespace eo d->comm.recv( d->masterRank, Channel::Commands, order ); } } + + ~ProcessTaskParallelEval() + { + delete _wrapped; + } }; template< class EOT > @@ -33,15 +38,20 @@ namespace eo ParallelEvalStore( eoUF & _proc, - std::vector& _pop, int _masterRank, // long _maxTime = 0, int _packetSize = 1 ) : - ParallelApplyStore< EOT >( _proc, _pop, _masterRank, _packetSize ) + ParallelApplyStore< EOT >( _proc, *( new std::vector ), _masterRank, _packetSize ) + // FIXME memory leak because of vector ==> use const correctness { wrapProcessTask( new ProcessTaskParallelEval ); } + + void data( std::vector& _pop ) + { + ParallelApplyStore::_data.init( _pop ); + } }; } } diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index f195965ae..faedc7b38 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -21,7 +21,7 @@ namespace eo { ParallelApplyData( eoUF & _proc, - std::vector& _pop, + std::vector & _pop, int _masterRank, // long _maxTime = 0, int _packetSize @@ -35,6 +35,14 @@ namespace eo tempArray = new EOT[ _packetSize ]; } + void init( std::vector& _pop ) + { + index = 0; + size = _pop.size(); + data = _pop; + assignedTasks.clear(); + } + ~ParallelApplyData() { delete [] tempArray; @@ -180,7 +188,7 @@ namespace eo ParallelApplyData* data() { return &_data; } - ~ParallelApplyStore() + virtual ~ParallelApplyStore() { delete _stf; delete _hrf; diff --git a/eo/test/mpi/eval.cpp b/eo/test/mpi/eval.cpp index 7d8666cb1..06fa8ad33 100644 --- a/eo/test/mpi/eval.cpp +++ b/eo/test/mpi/eval.cpp @@ -82,10 +82,40 @@ class eoRealSerializable : public eoReal< eoMinimizingFitness >, public eoserial typedef eoRealSerializable EOT; +struct CatBestAnswers : public eo::mpi::HandleResponseParallelApply +{ + CatBestAnswers() + { + best.fitness( 1000000000. ); + } + + using eo::mpi::HandleResponseParallelApply::_wrapped; + using eo::mpi::HandleResponseParallelApply::d; + + void operator()(int wrkRank) + { + int index = d->assignedTasks[wrkRank].index; + int size = d->assignedTasks[wrkRank].size; + (*_wrapped)( wrkRank ); + for(int i = index; i < index+size; ++i) + { + if( best.fitness() < d->data[ i ].fitness() ) + { + eo::log << eo::quiet << "Better solution found:" << d->data[i].fitness() << std::endl; + best = d->data[ 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); @@ -106,17 +136,33 @@ int main(int ac, char** av) eoEvalFuncPtr< EOT, double, const std::vector< double >& > mainEval( real_value ); eoEvalFuncCounter< EOT > eval( mainEval ); - eoPop< EOT > pop( popSize, init ); - - eo::log << "Size of population : " << popSize << std::endl; - - eo::log << eo::setlevel( eo::debug ); - + int rank = eo::mpi::Node::comm().rank(); eo::mpi::DynamicAssignmentAlgorithm assign; - eoParallelPopLoopEval< EOT > popEval( eval, assign, eo::mpi::DEFAULT_MASTER, 3 ); - popEval( pop, pop ); + if( rank == eo::mpi::DEFAULT_MASTER ) + { + eoPop< EOT > pop( popSize, init ); - eo::log << eo::quiet << "DONE!" << std::endl; + eo::log << "Size of population : " << popSize << std::endl; + + eo::mpi::ParallelEvalStore< EOT > store( eval, eo::mpi::DEFAULT_MASTER ); + store.wrapHandleResponse( new CatBestAnswers ); + + eoParallelPopLoopEval< EOT > popEval( eval, assign, &store, eo::mpi::DEFAULT_MASTER, 3 ); + 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( popSize, init ); + eoParallelPopLoopEval< EOT > popEval( eval, assign, eo::mpi::DEFAULT_MASTER, 3 ); + popEval( pop, pop ); + } return 0; } From 581b24af186c12ed9e4c46062d81c1028f3e6e5c Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 4 Jul 2012 13:24:10 +0200 Subject: [PATCH 45/76] Bugfix: a reference can't be reinitialized. Different vectors weren't recopied after evaluation. --- eo/src/mpi/eoParallelApply.h | 16 ++++++++++------ eo/test/mpi/eval.cpp | 6 +++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index faedc7b38..a02254716 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -26,7 +26,7 @@ namespace eo // long _maxTime = 0, int _packetSize ) : - data( _pop ), func( _proc ), index( 0 ), size( _pop.size() ), packetSize( _packetSize ), masterRank( _masterRank ), comm( Node::comm() ) + _data( &_pop ), func( _proc ), index( 0 ), size( _pop.size() ), packetSize( _packetSize ), masterRank( _masterRank ), comm( Node::comm() ) { if ( _packetSize <= 0 ) { @@ -39,7 +39,7 @@ namespace eo { index = 0; size = _pop.size(); - data = _pop; + _data = &_pop; assignedTasks.clear(); } @@ -48,7 +48,12 @@ namespace eo delete [] tempArray; } - std::vector & data; + std::vector& data() + { + return *_data; + } + + std::vector * _data; eoUF & func; int index; int size; @@ -71,7 +76,6 @@ namespace eo // empty } - // futureIndex, index, packetSize, size, comm, assignedTasks, data void operator()(int wrkRank) { int futureIndex; @@ -92,7 +96,7 @@ namespace eo d->assignedTasks[ wrkRank ].index = d->index; d->assignedTasks[ wrkRank ].size = sentSize; - d->comm.send( wrkRank, 1, & ( (d->data)[ d->index ] ) , sentSize ); + d->comm.send( wrkRank, 1, & ( (d->data())[ d->index ] ) , sentSize ); d->index = futureIndex; } }; @@ -110,7 +114,7 @@ namespace eo void operator()(int wrkRank) { - d->comm.recv( wrkRank, 1, & (d->data[ d->assignedTasks[wrkRank].index ] ), d->assignedTasks[wrkRank].size ); + d->comm.recv( wrkRank, 1, & (d->data()[ d->assignedTasks[wrkRank].index ] ), d->assignedTasks[wrkRank].size ); } }; diff --git a/eo/test/mpi/eval.cpp b/eo/test/mpi/eval.cpp index 06fa8ad33..312c9d657 100644 --- a/eo/test/mpi/eval.cpp +++ b/eo/test/mpi/eval.cpp @@ -99,10 +99,10 @@ struct CatBestAnswers : public eo::mpi::HandleResponseParallelApply (*_wrapped)( wrkRank ); for(int i = index; i < index+size; ++i) { - if( best.fitness() < d->data[ i ].fitness() ) + if( best.fitness() < d->data()[ i ].fitness() ) { - eo::log << eo::quiet << "Better solution found:" << d->data[i].fitness() << std::endl; - best = d->data[ i ]; + eo::log << eo::quiet << "Better solution found:" << d->data()[i].fitness() << std::endl; + best = d->data()[ i ]; } } } From 8753787725c603fa7de939e794a351617f2b7ae6 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 5 Jul 2012 18:16:49 +0200 Subject: [PATCH 46/76] Cleaning code (useless comments, suppressing warnings, etc...) --- eo/src/mpi/eoMpi.h | 21 --------------------- eo/src/mpi/eoTerminateJob.h | 2 ++ 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 8a4e86b63..5f430be29 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -197,10 +197,8 @@ namespace eo { public: Job( AssignmentAlgorithm& _algo, int _masterRank, JobStore & store ) : - // Job( AssignmentAlgorithm& _algo, int _masterRank, long maxTime = 0 ) : assignmentAlgo( _algo ), comm( Node::comm() ), - // _maxTime( maxTime ), masterRank( _masterRank ), // Functors sendTask( store.sendTask() ), @@ -230,20 +228,8 @@ namespace eo eo::log << eo::debug; eo::log << "[M" << comm.rank() << "] Have " << totalWorkers << " workers." << std::endl; # endif - bool timeStopped = false; while( ! isFinished() ) { - // Time restrictions - /* - getrusage( RUSAGE_SELF , &_usage ); - _current = _usage.ru_utime.tv_sec + _usage.ru_stime.tv_sec; - if( _maxTime > 0 && _current > _maxTime ) - { - timeStopped = true; - break; - } - */ - timerStat.start("master_wait_for_assignee"); int assignee = assignmentAlgo.get( ); while( assignee <= 0 ) @@ -301,12 +287,6 @@ namespace eo # ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Leaving master task." << std::endl; # endif - /* - if( timeStopped ) - { - throw eoMaxTimeException( _current ); - } - */ } void worker( ) @@ -359,7 +339,6 @@ namespace eo struct rusage _usage; long _current; - // const long _maxTime; }; } } diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h index 6c63cb697..25e63eb76 100644 --- a/eo/src/mpi/eoTerminateJob.h +++ b/eo/src/mpi/eoTerminateJob.h @@ -11,6 +11,7 @@ namespace eo { void operator()( int _ ) { + ++_; } }; @@ -18,6 +19,7 @@ namespace eo { void operator()( int _ ) { + ++_; } }; From 23acd1a6331d5b3e46cdca1634a13cbed821f346 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 6 Jul 2012 10:06:34 +0200 Subject: [PATCH 47/76] Catching exceptions and sending them back in eoMpi loop. --- eo/src/mpi/eoMpi.h | 135 ++++++++++++++++++++++++++++----------------- 1 file changed, 85 insertions(+), 50 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 5f430be29..a2dcebf71 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -221,6 +221,63 @@ namespace eo ProcessTaskFunction & processTask; IsFinishedFunction & isFinished; + struct FinallyBlock + { + FinallyBlock( + int _totalWorkers, + AssignmentAlgorithm& _algo, + Job< JobData > & _that + ) : + totalWorkers( _totalWorkers ), + assignmentAlgo( _algo ), + comm( Node::comm() ), + that( _that ) + { + // empty + } + + ~FinallyBlock() + { +# ifndef NDEBUG + eo::log << "[M" << comm.rank() << "] Frees all the idle." << std::endl; +# endif + // frees all the idle workers + timerStat.start("master_wait_for_idles"); + std::vector idles = assignmentAlgo.idles(); + for(unsigned int i = 0; i < idles.size(); ++i) + { + comm.send( idles[i], Channel::Commands, Message::Finish ); + } + timerStat.stop("master_wait_for_idles"); + +# ifndef NDEBUG + eo::log << "[M" << comm.rank() << "] Waits for all responses." << std::endl; +# endif + // wait for all responses + timerStat.start("master_wait_for_all_responses"); + while( assignmentAlgo.availableWorkers() != totalWorkers ) + { + bmpi::status status = comm.probe( bmpi::any_source, bmpi::any_tag ); + int wrkRank = status.source(); + that.handleResponse( wrkRank ); + comm.send( wrkRank, Channel::Commands, Message::Finish ); + assignmentAlgo.confirm( wrkRank ); + } + timerStat.stop("master_wait_for_all_responses"); + +# ifndef NDEBUG + eo::log << "[M" << comm.rank() << "] Leaving master task." << std::endl; +# endif + } + + protected: + + int totalWorkers; + bmpi::communicator & comm; + Job< JobData > & that; + AssignmentAlgorithm& assignmentAlgo; + }; + void master( ) { int totalWorkers = assignmentAlgo.availableWorkers(); @@ -228,65 +285,43 @@ namespace eo eo::log << eo::debug; eo::log << "[M" << comm.rank() << "] Have " << totalWorkers << " workers." << std::endl; # endif - while( ! isFinished() ) - { - timerStat.start("master_wait_for_assignee"); - int assignee = assignmentAlgo.get( ); - while( assignee <= 0 ) + try { + FinallyBlock finally( totalWorkers, assignmentAlgo, *this ); + while( ! isFinished() ) { + timerStat.start("master_wait_for_assignee"); + int assignee = assignmentAlgo.get( ); + while( assignee <= 0 ) + { # ifndef NDEBUG - eo::log << "[M" << comm.rank() << "] Waitin' for node..." << std::endl; + eo::log << "[M" << comm.rank() << "] Waitin' for node..." << std::endl; # endif - bmpi::status status = comm.probe( bmpi::any_source, bmpi::any_tag ); - int wrkRank = status.source(); + bmpi::status status = comm.probe( bmpi::any_source, bmpi::any_tag ); + int wrkRank = status.source(); # ifndef NDEBUG - eo::log << "[M" << comm.rank() << "] Node " << wrkRank << " just terminated." << std::endl; + eo::log << "[M" << comm.rank() << "] Node " << wrkRank << " just terminated." << std::endl; # endif - handleResponse( wrkRank ); - assignmentAlgo.confirm( wrkRank ); - assignee = assignmentAlgo.get( ); + handleResponse( wrkRank ); + assignmentAlgo.confirm( wrkRank ); + assignee = assignmentAlgo.get( ); + } + timerStat.stop("master_wait_for_assignee"); +# ifndef NDEBUG + eo::log << "[M" << comm.rank() << "] Assignee : " << assignee << std::endl; +# endif + + timerStat.start("master_wait_for_send"); + comm.send( assignee, Channel::Commands, Message::Continue ); + sendTask( assignee ); + timerStat.stop("master_wait_for_send"); } - timerStat.stop("master_wait_for_assignee"); -# ifndef NDEBUG - eo::log << "[M" << comm.rank() << "] Assignee : " << assignee << std::endl; -# endif - - timerStat.start("master_wait_for_send"); - comm.send( assignee, Channel::Commands, Message::Continue ); - sendTask( assignee ); - timerStat.stop("master_wait_for_send"); - } - -# ifndef NDEBUG - eo::log << "[M" << comm.rank() << "] Frees all the idle." << std::endl; -# endif - // frees all the idle workers - timerStat.start("master_wait_for_idles"); - std::vector idles = assignmentAlgo.idles(); - for(unsigned int i = 0; i < idles.size(); ++i) + } catch( const std::exception & e ) { - comm.send( idles[i], Channel::Commands, Message::Finish ); + std::string s = e.what(); + s.append( " in eoMpi loop"); + throw std::runtime_error( s ); } - timerStat.stop("master_wait_for_idles"); -# ifndef NDEBUG - eo::log << "[M" << comm.rank() << "] Waits for all responses." << std::endl; -# endif - // wait for all responses - timerStat.start("master_wait_for_all_responses"); - while( assignmentAlgo.availableWorkers() != totalWorkers ) - { - bmpi::status status = comm.probe( bmpi::any_source, bmpi::any_tag ); - int wrkRank = status.source(); - handleResponse( wrkRank ); - comm.send( wrkRank, Channel::Commands, Message::Finish ); - assignmentAlgo.confirm( wrkRank ); - } - timerStat.stop("master_wait_for_all_responses"); - -# ifndef NDEBUG - eo::log << "[M" << comm.rank() << "] Leaving master task." << std::endl; -# endif } void worker( ) From 79c7a263a355f906cfd87a247e0d973475c72bca Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 6 Jul 2012 16:44:06 +0200 Subject: [PATCH 48/76] Static assignment algorithm works with parallel eval now. --- eo/src/apply.h | 1 + eo/src/mpi/eoMpiAssignmentAlgorithm.h | 14 +++++++++++++- eo/src/mpi/eoTerminateJob.h | 9 +++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/eo/src/apply.h b/eo/src/apply.h index df3c43e1c..7845c14ec 100644 --- a/eo/src/apply.h +++ b/eo/src/apply.h @@ -94,6 +94,7 @@ void parallelApply( eo::mpi::ParallelEvalStore & _store ) { _store.data( _pop ); + _algo.reinit( _pop.size() ); eo::mpi::ParallelApply job( _algo, _masterRank, _store ); job.run(); } diff --git a/eo/src/mpi/eoMpiAssignmentAlgorithm.h b/eo/src/mpi/eoMpiAssignmentAlgorithm.h index aa162d951..c3d24a196 100644 --- a/eo/src/mpi/eoMpiAssignmentAlgorithm.h +++ b/eo/src/mpi/eoMpiAssignmentAlgorithm.h @@ -16,6 +16,7 @@ namespace eo virtual int availableWorkers( ) = 0; virtual void confirm( int wrkRank ) = 0; virtual std::vector idles( ) = 0; + virtual void reinit( int runs ) = 0; }; struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm @@ -79,6 +80,12 @@ namespace eo return availableWrk; } + void reinit( int _ ) + { + ++_; + // nothing to do + } + protected: std::vector< int > availableWrk; }; @@ -181,7 +188,7 @@ namespace eo void confirm( int rank ) { - int i = -1; + int i = -1; // i is the real index in table for( unsigned int j = 0; j < realRank.size(); ++j ) { if( realRank[j] == rank ) @@ -196,6 +203,11 @@ namespace eo ++freeWorkers; } + void reinit( int runs ) + { + init( realRank, runs ); + } + private: std::vector attributions; std::vector realRank; diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h index 25e63eb76..d85aeeb05 100644 --- a/eo/src/mpi/eoTerminateJob.h +++ b/eo/src/mpi/eoTerminateJob.h @@ -73,6 +73,15 @@ namespace eo { // empty } + + ~EmptyJob() + { + std::vector< int > idles = assignmentAlgo.idles(); + for(unsigned i = 0, size = idles.size(); i < size; ++i) + { + comm.send( idles[i], Channel::Commands, Message::Kill ); + } + } }; /* From ad89e280f929200188911a87f18a364775217f88 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 10 Jul 2012 14:46:12 +0200 Subject: [PATCH 49/76] Serialization: using maximum precision when converting double to string. --- eo/src/serial/String.h | 2 ++ eo/src/serial/Utils.h | 1 + 2 files changed, 3 insertions(+) diff --git a/eo/src/serial/String.h b/eo/src/serial/String.h index 43a2cffa8..6d81937f3 100644 --- a/eo/src/serial/String.h +++ b/eo/src/serial/String.h @@ -3,6 +3,7 @@ # include # include +# include # include "Entity.h" @@ -60,6 +61,7 @@ template inline void String::deserialize( T & value ) { std::stringstream ss; + ss.precision(std::numeric_limits::digits10 + 1); ss << *this; ss >> value; } diff --git a/eo/src/serial/Utils.h b/eo/src/serial/Utils.h index 70fd2f75a..f40c08d92 100644 --- a/eo/src/serial/Utils.h +++ b/eo/src/serial/Utils.h @@ -76,6 +76,7 @@ namespace eoserial String* make( const T & value ) { std::stringstream ss; + ss.precision(std::numeric_limits::digits10 + 1); ss << value; return new String( ss.str() ); } From 606eef08d8eb19e6d78f98d02c127d2e72cd0fb8 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 10 Jul 2012 14:48:46 +0200 Subject: [PATCH 50/76] Difference between termination messages Kill (stops the worker) and Terminate (waits for another task). --- eo/src/mpi/eoMpi.h | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index a2dcebf71..0281c9d59 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -31,7 +31,8 @@ namespace eo namespace Message { const int Continue = 0; - const int Finish = 1; + const int Finish = 1; // TODO commentaire : différence entre finir une tâche et arrêter le worker à expliciter. + const int Kill = 2; } const int DEFAULT_MASTER = 0; @@ -239,6 +240,7 @@ namespace eo ~FinallyBlock() { # ifndef NDEBUG + eo::log << eo::debug; eo::log << "[M" << comm.rank() << "] Frees all the idle." << std::endl; # endif // frees all the idle workers @@ -264,7 +266,6 @@ namespace eo assignmentAlgo.confirm( wrkRank ); } timerStat.stop("master_wait_for_all_responses"); - # ifndef NDEBUG eo::log << "[M" << comm.rank() << "] Leaving master task." << std::endl; # endif @@ -321,7 +322,6 @@ namespace eo s.append( " in eoMpi loop"); throw std::runtime_error( s ); } - } void worker( ) @@ -330,27 +330,32 @@ namespace eo # ifndef NDEBUG eo::log << eo::debug; # endif + timerStat.start("worker_wait_for_order"); + comm.recv( masterRank, Channel::Commands, order ); + timerStat.stop("worker_wait_for_order"); + while( true ) { # ifndef NDEBUG eo::log << "[W" << comm.rank() << "] Waiting for an order..." << std::endl; # endif - timerStat.start("worker_wait_for_order"); - comm.recv( masterRank, Channel::Commands, order ); - timerStat.stop("worker_wait_for_order"); - if ( order == Message::Finish ) + if ( order == Message::Kill ) { # ifndef NDEBUG eo::log << "[W" << comm.rank() << "] Leaving worker task." << std::endl; # endif return; - } else + } else if( order == Message::Continue ) { # ifndef NDEBUG eo::log << "[W" << comm.rank() << "] Processing task..." << std::endl; # endif processTask( ); } + + timerStat.start("worker_wait_for_order"); + comm.recv( masterRank, Channel::Commands, order ); + timerStat.stop("worker_wait_for_order"); } } From 76c4f19829096b0f3f7985980952357df9158493 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 10 Jul 2012 15:38:36 +0200 Subject: [PATCH 51/76] Separating parameters of store's default constructor of job parameters in eoParallelPopEval. --- eo/src/eoPopEvalFunc.h | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index ce4dc5cf6..a728baa8f 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -85,31 +85,28 @@ class eoParallelPopLoopEval : public eoPopEvalFunc public: /** Ctor: set value of embedded eoEvalFunc */ eoParallelPopLoopEval( - eoEvalFunc & _eval, + // Job parameters eo::mpi::AssignmentAlgorithm& _assignAlgo, int _masterRank, + // Default parameters for store + eoEvalFunc & _eval, int _packetSize = 1 ) : - eval(_eval), assignAlgo( _assignAlgo ), masterRank( _masterRank ), - packetSize( _packetSize ), needToDeleteStore( true ) { store = new eo::mpi::ParallelEvalStore( _eval, _masterRank, _packetSize ); } eoParallelPopLoopEval( - eoEvalFunc & _eval, + // Job parameters eo::mpi::AssignmentAlgorithm& _assignAlgo, - eo::mpi::ParallelEvalStore* _store, int _masterRank, - int _packetSize = 1 + eo::mpi::ParallelEvalStore* _store ) : - eval(_eval), assignAlgo( _assignAlgo ), masterRank( _masterRank ), - packetSize( _packetSize ), needToDeleteStore( false ), store( _store ) { @@ -131,20 +128,17 @@ class eoParallelPopLoopEval : public eoPopEvalFunc } /** Do the job: simple loop over the offspring */ - void operator()(eoPop & _parents, eoPop & _offspring) + void operator()( eoPop & _parents, eoPop & _offspring ) { (void)_parents; parallelApply(_offspring, assignAlgo, masterRank, *store); } private: - eoEvalFunc & eval; - eo::mpi::AssignmentAlgorithm & assignAlgo; - eo::mpi::ParallelEvalStore* store; int masterRank; - int packetSize; + eo::mpi::ParallelEvalStore* store; bool needToDeleteStore; }; #endif From 6600f1db51388626e52dc73fd37a18adc083ecf5 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 10 Jul 2012 16:08:35 +0200 Subject: [PATCH 52/76] Suppressed some warnings at compilation. --- eo/src/eoPopEvalFunc.h | 4 ++-- eo/src/mpi/eoMpi.h | 38 +++++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index a728baa8f..d37eebafa 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -107,8 +107,8 @@ class eoParallelPopLoopEval : public eoPopEvalFunc ) : assignAlgo( _assignAlgo ), masterRank( _masterRank ), - needToDeleteStore( false ), - store( _store ) + store( _store ), + needToDeleteStore( false ) { // empty } diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 0281c9d59..0ef80c120 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -197,10 +197,13 @@ namespace eo class Job { public: - Job( AssignmentAlgorithm& _algo, int _masterRank, JobStore & store ) : + Job( AssignmentAlgorithm& _algo, + int _masterRank, + JobStore & store + ) : assignmentAlgo( _algo ), - comm( Node::comm() ), masterRank( _masterRank ), + comm( Node::comm() ), // Functors sendTask( store.sendTask() ), handleResponse( store.handleResponse() ), @@ -217,11 +220,6 @@ namespace eo protected: - SendTaskFunction & sendTask; - HandleResponseFunction & handleResponse; - ProcessTaskFunction & processTask; - IsFinishedFunction & isFinished; - struct FinallyBlock { FinallyBlock( @@ -231,8 +229,9 @@ namespace eo ) : totalWorkers( _totalWorkers ), assignmentAlgo( _algo ), - comm( Node::comm() ), - that( _that ) + that( _that ), + // global field + comm( Node::comm() ) { // empty } @@ -274,9 +273,10 @@ namespace eo protected: int totalWorkers; - bmpi::communicator & comm; - Job< JobData > & that; AssignmentAlgorithm& assignmentAlgo; + Job< JobData > & that; + + bmpi::communicator & comm; }; void master( ) @@ -372,13 +372,17 @@ namespace eo } protected: - AssignmentAlgorithm& assignmentAlgo; - bmpi::communicator& comm; - int masterRank; - bool _isMaster; - struct rusage _usage; - long _current; + AssignmentAlgorithm& assignmentAlgo; + int masterRank; + bmpi::communicator& comm; + + SendTaskFunction & sendTask; + HandleResponseFunction & handleResponse; + ProcessTaskFunction & processTask; + IsFinishedFunction & isFinished; + + bool _isMaster; }; } } From 008f2571b2f4e2571c13a0bf9bd6a6b5663145dd Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 10 Jul 2012 17:32:18 +0200 Subject: [PATCH 53/76] MultiJob and OneShotJob allow to choose job's way of termination (Kill for multi, Finish for one shot). --- eo/src/mpi/eoMpi.h | 34 +++++++++++++++++++++++++++++++++- eo/src/mpi/eoParallelApply.h | 4 ++-- eo/src/mpi/eoTerminateJob.h | 4 ++-- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 0ef80c120..8521d5c30 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -199,10 +199,12 @@ namespace eo public: Job( AssignmentAlgorithm& _algo, int _masterRank, + int _workerStopCondition, JobStore & store ) : assignmentAlgo( _algo ), masterRank( _masterRank ), + workerStopCondition( _workerStopCondition ), comm( Node::comm() ), // Functors sendTask( store.sendTask() ), @@ -339,7 +341,7 @@ namespace eo # ifndef NDEBUG eo::log << "[W" << comm.rank() << "] Waiting for an order..." << std::endl; # endif - if ( order == Message::Kill ) + if ( order == workerStopCondition ) { # ifndef NDEBUG eo::log << "[W" << comm.rank() << "] Leaving worker task." << std::endl; @@ -375,6 +377,7 @@ namespace eo AssignmentAlgorithm& assignmentAlgo; int masterRank; + const int workerStopCondition; bmpi::communicator& comm; SendTaskFunction & sendTask; @@ -383,6 +386,35 @@ namespace eo IsFinishedFunction & isFinished; bool _isMaster; + + }; + + template< class JobData > + class OneShotJob : public Job< JobData > + { + // TODO commentaire : ce job s'arrête sur message terminate + public: + OneShotJob( AssignmentAlgorithm& algo, + int masterRank, + JobStore & store ) + : Job( algo, masterRank, Message::Finish, store ) + { + // empty + } + }; + + template< class JobData > + class MultiJob : public Job< JobData > + { + public: + // TODO commentaire : ce job s'arrête sur message kill + MultiJob ( AssignmentAlgorithm& algo, + int masterRank, + JobStore & store ) + : Job( algo, masterRank, Message::Kill, store ) + { + // empty + } }; } } diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index a02254716..4ef588303 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -207,7 +207,7 @@ namespace eo // TODO commentaire : impossible de faire un typedef sur un template sans passer // par un traits => complique la tâche de l'utilisateur pour rien. template< typename EOT > - class ParallelApply : public Job< ParallelApplyData > + class ParallelApply : public MultiJob< ParallelApplyData > { public: @@ -216,7 +216,7 @@ namespace eo int _masterRank, ParallelApplyStore & store ) : - Job< ParallelApplyData >( algo, _masterRank, store ) + MultiJob< ParallelApplyData >( algo, _masterRank, store ) { // empty } diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h index d85aeeb05..bda085a02 100644 --- a/eo/src/mpi/eoTerminateJob.h +++ b/eo/src/mpi/eoTerminateJob.h @@ -65,10 +65,10 @@ namespace eo void* data() { return 0; } }; - struct EmptyJob : public Job + struct EmptyJob : public OneShotJob { EmptyJob( AssignmentAlgorithm& algo, int masterRank ) : - Job( algo, masterRank, *(new DummyJobStore) ) + OneShotJob( algo, masterRank, *(new DummyJobStore) ) // FIXME memory leak => will be corrected by using const correctness { // empty From 472b86bc6856bc3a33e172ae862fef0d82385d2f Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 10 Jul 2012 17:34:18 +0200 Subject: [PATCH 54/76] MPI job functors have to be created with new, so as to be deleted by delete, because of Composite / Decorator pattern. --- eo/src/mpi/eoMpi.h | 13 +++++++++++- eo/src/mpi/eoParallelApply.h | 4 ---- eo/src/mpi/eoTerminateJob.h | 40 ------------------------------------ 3 files changed, 12 insertions(+), 45 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 8521d5c30..c277ddd07 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -119,6 +119,7 @@ namespace eo template< typename JobData > struct JobStore { + // TODO commentaire: ces pointeurs devraient être alloués avec new pour éviter segfault on quit. JobStore( SendTaskFunction* stf, HandleResponseFunction* hrf, @@ -135,6 +136,16 @@ namespace eo // empty } + ~JobStore() + { + // TODO commentaire: Composition Pattern => délégation de la destruction => destruction homogène => new + // et delete partout. + delete _stf; + delete _hrf; + delete _ptf; + delete _iff; + } + SendTaskFunction & sendTask() { return *_stf; } HandleResponseFunction & handleResponse() { return *_hrf; } ProcessTaskFunction & processTask() { return *_ptf; } @@ -377,7 +388,7 @@ namespace eo AssignmentAlgorithm& assignmentAlgo; int masterRank; - const int workerStopCondition; + const int workerStopCondition; // TODO commentaire: message signifiant l'arrêt pour un worker bmpi::communicator& comm; SendTaskFunction & sendTask; diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 4ef588303..d93b5b818 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -194,10 +194,6 @@ namespace eo virtual ~ParallelApplyStore() { - delete _stf; - delete _hrf; - delete _ptf; - delete _iff; } protected: diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h index bda085a02..4dc9f421b 100644 --- a/eo/src/mpi/eoTerminateJob.h +++ b/eo/src/mpi/eoTerminateJob.h @@ -54,14 +54,6 @@ namespace eo _iff = new DummyIsFinishedFunction; } - ~DummyJobStore() - { - delete _stf; - delete _hrf; - delete _ptf; - delete _iff; - } - void* data() { return 0; } }; @@ -83,38 +75,6 @@ namespace eo } } }; - - /* - class TerminateJob : public Job - { - public: - TerminateJob( AssignmentAlgorithm& algo, int _ ) - : Job( algo, _ ) - { - // empty - } - - void sendTask( int wrkRank ) - { - // empty - } - - void handleResponse( int wrkRank ) - { - // empty - } - - void processTask( ) - { - // empty - } - - bool isFinished() - { - return true; - } - }; - */ } } From d805800731a0a9dc985071a9731dcbc7c669a2b8 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 11 Jul 2012 10:03:53 +0200 Subject: [PATCH 55/76] Added packet size to parallel parser and default argument to static assignment. --- eo/src/mpi/eoMpiAssignmentAlgorithm.h | 3 ++- eo/src/utils/eoParallel.cpp | 2 ++ eo/src/utils/eoParallel.h | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/eo/src/mpi/eoMpiAssignmentAlgorithm.h b/eo/src/mpi/eoMpiAssignmentAlgorithm.h index c3d24a196..0630a6f7a 100644 --- a/eo/src/mpi/eoMpiAssignmentAlgorithm.h +++ b/eo/src/mpi/eoMpiAssignmentAlgorithm.h @@ -114,13 +114,14 @@ namespace eo init( workers, runs ); } - StaticAssignmentAlgorithm( int runs ) + StaticAssignmentAlgorithm( int runs = 0 ) { std::vector workers; for(int i = 1; i < Node::comm().size(); ++i) { workers.push_back( i ); } + init( workers, runs ); } diff --git a/eo/src/utils/eoParallel.cpp b/eo/src/utils/eoParallel.cpp index d9d09c38b..418651984 100644 --- a/eo/src/utils/eoParallel.cpp +++ b/eo/src/utils/eoParallel.cpp @@ -37,6 +37,7 @@ eoParallel::eoParallel() : _nthreads( 0, "parallelize-nthreads", "Define the number of threads you want to use, nthreads = 0 means you want to use all threads available", '\0' ), _enableResults( false, "parallelize-enable-results", "Enable the generation of results", '\0' ), _doMeasure( false, "parallelize-do-measure", "Do some measures during execution", '\0' ), + _packetSize( 1U, "parallelize-packet-size", "Number of elements which should be sent at a time during a parallel evaluation based on message passing.", '\0'), _t_start(0) { } @@ -90,6 +91,7 @@ void eoParallel::_createParameters( eoParser& parser ) parser.processParam( _nthreads, section ); parser.processParam( _enableResults, section ); parser.processParam( _doMeasure, section ); + parser.processParam( _packetSize, section ); } void make_parallel(eoParser& parser) diff --git a/eo/src/utils/eoParallel.h b/eo/src/utils/eoParallel.h index 3f22f6c43..3aae8e430 100644 --- a/eo/src/utils/eoParallel.h +++ b/eo/src/utils/eoParallel.h @@ -54,6 +54,7 @@ public: std::string prefix() const; inline unsigned int nthreads() const { return _nthreads.value(); } + inline unsigned int packetSize() const { return _packetSize.value(); } inline bool enableResults() const { return _enableResults.value(); } inline bool doMeasure() const { return _doMeasure.value(); } @@ -68,6 +69,7 @@ private: eoValueParam _isDynamic; eoValueParam _prefix; eoValueParam _nthreads; + eoValueParam _packetSize; eoValueParam _enableResults; eoValueParam _doMeasure; double _t_start; From 108c0bcf35e9e480ef9b0bb8843ad99270c15ccf Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 11 Jul 2012 10:41:48 +0200 Subject: [PATCH 56/76] Automatic deleting functors, only if necessary. --- eo/src/mpi/eoMpi.h | 25 +++++++++++----- eo/src/mpi/eoMultiParallelApply.h | 2 -- eo/src/mpi/eoParallelApply.h | 49 ++++++++++++++++++++++++------- eo/src/mpi/eoTerminateJob.h | 4 +++ 4 files changed, 60 insertions(+), 20 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index c277ddd07..fc419b13d 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -40,11 +40,19 @@ namespace eo template< typename JobData, typename Wrapped > struct SharedDataFunction { - SharedDataFunction( Wrapped * w = 0 ) : _wrapped( w ) + SharedDataFunction( Wrapped * w = 0 ) : _wrapped( w ), _needDelete( false ) { // empty } + virtual ~SharedDataFunction() + { + if( _wrapped && _wrapped->needDelete() ) + { + delete _wrapped; + } + } + void wrapped( Wrapped * w ) { _wrapped = w; @@ -59,9 +67,13 @@ namespace eo } } + bool needDelete() { return _needDelete; } + void needDelete( bool b ) { _needDelete = b; } + protected: JobData* d; Wrapped* _wrapped; + bool _needDelete; }; template< typename JobData > @@ -119,7 +131,6 @@ namespace eo template< typename JobData > struct JobStore { - // TODO commentaire: ces pointeurs devraient être alloués avec new pour éviter segfault on quit. JobStore( SendTaskFunction* stf, HandleResponseFunction* hrf, @@ -138,12 +149,10 @@ namespace eo ~JobStore() { - // TODO commentaire: Composition Pattern => délégation de la destruction => destruction homogène => new - // et delete partout. - delete _stf; - delete _hrf; - delete _ptf; - delete _iff; + if( _stf->needDelete() ) delete _stf; + if( _hrf->needDelete() ) delete _hrf; + if( _ptf->needDelete() ) delete _ptf; + if( _iff->needDelete() ) delete _iff; } SendTaskFunction & sendTask() { return *_stf; } diff --git a/eo/src/mpi/eoMultiParallelApply.h b/eo/src/mpi/eoMultiParallelApply.h index 85b1689e6..f4fe47b51 100644 --- a/eo/src/mpi/eoMultiParallelApply.h +++ b/eo/src/mpi/eoMultiParallelApply.h @@ -27,7 +27,6 @@ namespace eo ~ProcessTaskParallelEval() { - delete _wrapped; } }; @@ -39,7 +38,6 @@ namespace eo ParallelEvalStore( eoUF & _proc, int _masterRank, - // long _maxTime = 0, int _packetSize = 1 ) : ParallelApplyStore< EOT >( _proc, *( new std::vector ), _masterRank, _packetSize ) diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index d93b5b818..9598b952f 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -21,17 +21,23 @@ namespace eo { ParallelApplyData( eoUF & _proc, - std::vector & _pop, int _masterRank, // long _maxTime = 0, - int _packetSize + int _packetSize, + std::vector * _pop = 0 ) : - _data( &_pop ), func( _proc ), index( 0 ), size( _pop.size() ), packetSize( _packetSize ), masterRank( _masterRank ), comm( Node::comm() ) + _data( _pop ), func( _proc ), index( 0 ), packetSize( _packetSize ), masterRank( _masterRank ), comm( Node::comm() ) { if ( _packetSize <= 0 ) { throw std::runtime_error("Packet size should not be negative."); } + + if( _pop ) + { + size = _pop->size(); + } + tempArray = new EOT[ _packetSize ]; } @@ -172,18 +178,36 @@ namespace eo ParallelApplyStore( eoUF & _proc, - std::vector& _pop, int _masterRank, - // long _maxTime = 0, int _packetSize = 1, // JobStore functors - SendTaskParallelApply * stpa = new SendTaskParallelApply, - HandleResponseParallelApply* hrpa = new HandleResponseParallelApply, - ProcessTaskParallelApply* ptpa = new ProcessTaskParallelApply, - IsFinishedParallelApply* ifpa = new IsFinishedParallelApply + SendTaskParallelApply * stpa = 0, + HandleResponseParallelApply* hrpa = 0, + ProcessTaskParallelApply* ptpa = 0, + IsFinishedParallelApply* ifpa = 0 ) : - _data( _proc, _pop, _masterRank, _packetSize ) + _data( _proc, _masterRank, _packetSize ) { + if( stpa == 0 ) { + stpa = new SendTaskParallelApply; + stpa->needDelete( true ); + } + + if( hrpa == 0 ) { + hrpa = new HandleResponseParallelApply; + hrpa->needDelete( true ); + } + + if( ptpa == 0 ) { + ptpa = new ProcessTaskParallelApply; + ptpa->needDelete( true ); + } + + if( ifpa == 0 ) { + ifpa = new IsFinishedParallelApply; + ifpa->needDelete( true ); + } + _stf = stpa; _hrf = hrpa; _ptf = ptpa; @@ -192,6 +216,11 @@ namespace eo ParallelApplyData* data() { return &_data; } + void data( std::vector& _pop ) + { + _data.init( _pop ); + } + virtual ~ParallelApplyStore() { } diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h index 4dc9f421b..ed45ca052 100644 --- a/eo/src/mpi/eoTerminateJob.h +++ b/eo/src/mpi/eoTerminateJob.h @@ -49,9 +49,13 @@ namespace eo DummyJobStore() { _stf = new DummySendTaskFunction; + _stf->needDelete( true ); _hrf = new DummyHandleResponseFunction; + _hrf->needDelete( true ); _ptf = new DummyProcessTaskFunction; + _ptf->needDelete( true ); _iff = new DummyIsFinishedFunction; + _iff->needDelete( true ); } void* data() { return 0; } From 414e011296c1c73d9d40ed3a4a3a64067a7a3e73 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 11 Jul 2012 10:42:35 +0200 Subject: [PATCH 57/76] Using ParallelApplyJob instead of MultiParallelApplyJob in eoPopEvalFunc. --- eo/src/apply.h | 2 +- eo/src/eoPopEvalFunc.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/eo/src/apply.h b/eo/src/apply.h index 7845c14ec..b13c434e4 100644 --- a/eo/src/apply.h +++ b/eo/src/apply.h @@ -91,7 +91,7 @@ void parallelApply( std::vector& _pop, eo::mpi::AssignmentAlgorithm& _algo, int _masterRank, - eo::mpi::ParallelEvalStore & _store ) + eo::mpi::ParallelApplyStore & _store ) { _store.data( _pop ); _algo.reinit( _pop.size() ); diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index d37eebafa..c811e3a57 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -96,14 +96,15 @@ class eoParallelPopLoopEval : public eoPopEvalFunc masterRank( _masterRank ), needToDeleteStore( true ) { - store = new eo::mpi::ParallelEvalStore( _eval, _masterRank, _packetSize ); + // FIXME memory leak because of new. + store = new eo::mpi::ParallelApplyStore( _eval, _masterRank, _packetSize ); } eoParallelPopLoopEval( // Job parameters eo::mpi::AssignmentAlgorithm& _assignAlgo, int _masterRank, - eo::mpi::ParallelEvalStore* _store + eo::mpi::ParallelApplyStore* _store ) : assignAlgo( _assignAlgo ), masterRank( _masterRank ), @@ -138,7 +139,7 @@ class eoParallelPopLoopEval : public eoPopEvalFunc eo::mpi::AssignmentAlgorithm & assignAlgo; int masterRank; - eo::mpi::ParallelEvalStore* store; + eo::mpi::ParallelApplyStore* store; bool needToDeleteStore; }; #endif From 4ab5b2fd8983eb24be122ac009e46bc9a6876cf7 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 11 Jul 2012 11:41:10 +0200 Subject: [PATCH 58/76] Taking time measures only if parallel.doMeasure() is true. --- eo/src/utils/eoTimer.h | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/eo/src/utils/eoTimer.h b/eo/src/utils/eoTimer.h index c97ab5d35..8aa09c51b 100644 --- a/eo/src/utils/eoTimer.h +++ b/eo/src/utils/eoTimer.h @@ -7,6 +7,8 @@ # include # include +# include "utils/eoParallel.h" + # ifdef WITH_MPI # include # include @@ -119,16 +121,22 @@ class eoTimerStat void start( const std::string & key ) { - _timers[ key ].restart(); + if( eo::parallel.doMeasure() ) + { + _timers[ key ].restart(); + } } void stop( const std::string& key ) { - Stat & s = _stats[ key ]; - eoTimer & t = _timers[ key ]; - s.utime.push_back( t.usertime() ); - s.stime.push_back( t.systime() ); - s.wtime.push_back( t.wallclock() ); + if( eo::parallel.doMeasure() ) + { + Stat & s = _stats[ key ]; + eoTimer & t = _timers[ key ]; + s.utime.push_back( t.usertime() ); + s.stime.push_back( t.systime() ); + s.wtime.push_back( t.wallclock() ); + } } std::map< std::string, Stat > stats() From 12560899ae6b9546b43297bc64039baae706631a Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 11 Jul 2012 12:00:36 +0200 Subject: [PATCH 59/76] Updating MPI tests. --- eo/test/mpi/eval.cpp | 8 +++----- eo/test/mpi/multipleRoles.cpp | 9 +++++++-- eo/test/mpi/parallelApply.cpp | 6 ++++-- eo/test/mpi/wrapper.cpp | 5 ++++- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/eo/test/mpi/eval.cpp b/eo/test/mpi/eval.cpp index 312c9d657..72fca7558 100644 --- a/eo/test/mpi/eval.cpp +++ b/eo/test/mpi/eval.cpp @@ -11,8 +11,6 @@ #include -#include - #include #include @@ -144,10 +142,10 @@ int main(int ac, char** av) eo::log << "Size of population : " << popSize << std::endl; - eo::mpi::ParallelEvalStore< EOT > store( eval, eo::mpi::DEFAULT_MASTER ); + eo::mpi::ParallelApplyStore< EOT > store( eval, eo::mpi::DEFAULT_MASTER ); store.wrapHandleResponse( new CatBestAnswers ); - eoParallelPopLoopEval< EOT > popEval( eval, assign, &store, eo::mpi::DEFAULT_MASTER, 3 ); + eoParallelPopLoopEval< EOT > popEval( assign, eo::mpi::DEFAULT_MASTER, &store ); eo::log << eo::quiet << "Before first evaluation." << std::endl; popEval( pop, pop ); eo::log << eo::quiet << "After first evaluation." << std::endl; @@ -160,7 +158,7 @@ int main(int ac, char** av) } else { eoPop< EOT > pop( popSize, init ); - eoParallelPopLoopEval< EOT > popEval( eval, assign, eo::mpi::DEFAULT_MASTER, 3 ); + eoParallelPopLoopEval< EOT > popEval( assign, eo::mpi::DEFAULT_MASTER, eval ); popEval( pop, pop ); } diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/multipleRoles.cpp index ce10f6214..232a61105 100644 --- a/eo/test/mpi/multipleRoles.cpp +++ b/eo/test/mpi/multipleRoles.cpp @@ -1,5 +1,6 @@ # include # include +# include # include @@ -32,9 +33,11 @@ void subtask( vector& v, int rank ) DynamicAssignmentAlgorithm algo( workers ); SubWork sw; - ParallelApplyStore store( sw, v, rank ); + ParallelApplyStore store( sw, rank ); + store.data( v ); ParallelApply job( algo, rank, store ); job.run(); + EmptyJob stop( algo, rank ); } struct Work: public eoUF< vector&, void > @@ -74,11 +77,13 @@ int main(int argc, char** argv) { Work w; DynamicAssignmentAlgorithm algo( 1, 2 ); - ParallelApplyStore< vector > store( w, metaV, 0 ); + ParallelApplyStore< vector > store( w, 0 ); + store.data( metaV ); ParallelApply< vector > job( algo, 0, store ); job.run(); if( job.isMaster() ) { + EmptyJob stop( algo, 0 ); v = metaV[0]; cout << "Results : " << endl; for(int i = 0; i < v.size(); ++i) diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/parallelApply.cpp index d562ef0ec..26b0b30ea 100644 --- a/eo/test/mpi/parallelApply.cpp +++ b/eo/test/mpi/parallelApply.cpp @@ -1,5 +1,6 @@ # include # include +# include # include @@ -110,8 +111,8 @@ int main(int argc, char** argv) for( unsigned int i = 0; i < tests.size(); ++i ) { - ParallelApplyStore< int > store( plusOneInstance, v, eo::mpi::DEFAULT_MASTER, 3 ); - // Job< JobData > job( *(tests[i].assign), eo::mpi::DEFAULT_MASTER, store ); + ParallelApplyStore< int > store( plusOneInstance, eo::mpi::DEFAULT_MASTER, 3 ); + store.data( v ); ParallelApply< int > job( *(tests[i].assign), eo::mpi::DEFAULT_MASTER, store ); if( job.isMaster() ) @@ -126,6 +127,7 @@ int main(int argc, char** argv) if( job.isMaster() ) { + EmptyJob stop( *(tests[i].assign), eo::mpi::DEFAULT_MASTER ); ++offset; for(int i = 0; i < v.size(); ++i) { diff --git a/eo/test/mpi/wrapper.cpp b/eo/test/mpi/wrapper.cpp index d8b72c98e..527c7cc6d 100644 --- a/eo/test/mpi/wrapper.cpp +++ b/eo/test/mpi/wrapper.cpp @@ -1,5 +1,6 @@ # include # include +# include # include @@ -60,13 +61,15 @@ int main(int argc, char** argv) StaticAssignmentAlgorithm assign( v.size() ); - ParallelApplyStore< int > store( plusOneInstance, v, eo::mpi::DEFAULT_MASTER, 1 ); + ParallelApplyStore< int > store( plusOneInstance, eo::mpi::DEFAULT_MASTER, 1 ); + store.data( v ); store.wrapIsFinished( new ShowWrappedResult ); ParallelApply job( assign, eo::mpi::DEFAULT_MASTER, store ); // Equivalent to: // Job< ParallelApplyData > job( assign, 0, store ); job.run(); + EmptyJob stop( assign, eo::mpi::DEFAULT_MASTER ); if( job.isMaster() ) { From 60fff427fedb40afd6009a84441ce52ad9ad42a7 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 12 Jul 2012 13:56:54 +0200 Subject: [PATCH 60/76] Documentation of serialization, utils/eoTimer and eoPopEvalFunc. --- eo/src/apply.h | 21 --- eo/src/eoEasyEA.h | 70 ++++----- eo/src/eoPopEvalFunc.h | 81 ++++++++++- eo/src/serial/Array.cpp | 70 +++++---- eo/src/serial/Array.h | 267 +++++++++++++++++++---------------- eo/src/serial/Entity.h | 39 ++++- eo/src/serial/Object.cpp | 74 ++++++---- eo/src/serial/Object.h | 111 +++++++++------ eo/src/serial/Parser.cpp | 24 +++- eo/src/serial/Parser.h | 25 ++++ eo/src/serial/Serializable.h | 80 +++++++---- eo/src/serial/String.cpp | 21 +++ eo/src/serial/String.h | 140 ++++++++++-------- eo/src/serial/Utils.h | 25 +++- eo/src/serial/eoSerial.h | 21 +++ eo/src/utils/eoParallel.cpp | 4 +- eo/src/utils/eoParallel.h | 3 +- eo/src/utils/eoTimer.h | 198 ++++++++++++++++++++++---- 18 files changed, 856 insertions(+), 418 deletions(-) diff --git a/eo/src/apply.h b/eo/src/apply.h index b13c434e4..939e62bde 100644 --- a/eo/src/apply.h +++ b/eo/src/apply.h @@ -33,12 +33,6 @@ #include #include -# ifdef WITH_MPI -# include -# include -# include -# endif // WITH_MPI - /** Applies a unary function to a std::vector of things. @@ -85,21 +79,6 @@ void apply(eoUF& _proc, std::vector& _pop) #endif // !_OPENMP } -#ifdef WITH_MPI -template -void parallelApply( - std::vector& _pop, - eo::mpi::AssignmentAlgorithm& _algo, - int _masterRank, - eo::mpi::ParallelApplyStore & _store ) -{ - _store.data( _pop ); - _algo.reinit( _pop.size() ); - eo::mpi::ParallelApply job( _algo, _masterRank, _store ); - job.run(); -} -#endif - /** This is a variant of apply which is called in parallel thanks to OpenMP. diff --git a/eo/src/eoEasyEA.h b/eo/src/eoEasyEA.h index 4d74932f7..4d9c7b6da 100644 --- a/eo/src/eoEasyEA.h +++ b/eo/src/eoEasyEA.h @@ -37,8 +37,6 @@ #include #include - - template class eoIslandsEasyEA ; template class eoDistEvalEasyEA ; @@ -106,7 +104,7 @@ template class eoEasyEA: public eoAlgo * @brief Ctor allowing to specify which pop eval function we're going to use. * * Ctor taking a breed and merge, an overload of ctor to define an offspring size, and - * the pop eval function used. This allows to precise if we would like to use the + * the pop eval function used. This allows to precise if we would like to use the * parallel evaluation, for instance. */ eoEasyEA( @@ -247,57 +245,43 @@ template class eoEasyEA: public eoAlgo virtual void operator()(eoPop& _pop) { - eo::log << "[EasyEA] Call to operator()" << std::endl; - if (isFirstCall) - { - size_t total_capacity = _pop.capacity() + offspring.capacity(); - _pop.reserve(total_capacity); - offspring.reserve(total_capacity); - isFirstCall = false; - } - - // TODO TODOB delete all log traces - std::cout << "[EasyEA] After is first call." << std::endl; - - eoPop empty_pop; - std::cout << "[EasyEA] After empty_pop." << std::endl; - - popEval(empty_pop, _pop); // A first eval of pop. - std::cout << "[EasyEA] After pop_eval." << std::endl; - - do + if (isFirstCall) { - try + size_t total_capacity = _pop.capacity() + offspring.capacity(); + _pop.reserve(total_capacity); + offspring.reserve(total_capacity); + isFirstCall = false; + } + + eoPop empty_pop; + + do + { + try { - std::cout << "[EasyEA] Beginning try." << std::endl; - unsigned pSize = _pop.size(); - std::cout << "[EasyEA] psize determinated." << std::endl; - offspring.clear(); // new offspring - std::cout << "[EasyEA] offspring cleared." << std::endl; + unsigned pSize = _pop.size(); - breed(_pop, offspring); + offspring.clear(); // new offspring - std::cout << "[EasyEA] After breed, evaluating pop." << std::endl; - popEval(_pop, offspring); // eval of parents + offspring if necessary + breed(_pop, offspring); - std::cout << "[EasyEA] After evaluation, replacing pop." << std::endl; - replace(_pop, offspring); // after replace, the new pop. is in _pop - std::cout << "[EasyEA] After replacing, continuator." << std::endl; + popEval(_pop, offspring); // eval of parents + offspring if necessary - if (pSize > _pop.size()) - throw std::runtime_error("Population shrinking!"); - else if (pSize < _pop.size()) - throw std::runtime_error("Population growing!"); + replace(_pop, offspring); // after replace, the new pop. is in _pop + if (pSize > _pop.size()) + throw std::runtime_error("Population shrinking!"); + else if (pSize < _pop.size()) + throw std::runtime_error("Population growing!"); } - catch (std::exception& e) + catch (std::exception& e) { - std::string s = e.what(); - s.append( " in eoEasyEA"); - throw std::runtime_error( s ); + std::string s = e.what(); + s.append( " in eoEasyEA"); + throw std::runtime_error( s ); } } - while ( continuator( _pop ) ); + while ( continuator( _pop ) ); } protected : diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index c811e3a57..2cc868076 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -30,6 +30,13 @@ #include #include +# ifdef WITH_MPI +#include +#include +#include +#include +# endif // WITH_MPI + /** eoPopEvalFunc: This abstract class is for GLOBAL evaluators * of a population after variation. * It takes 2 populations (typically the parents and the offspring) @@ -78,12 +85,43 @@ private: }; #ifdef WITH_MPI -// TODO TODOB commenter +/** + * @brief Evaluator of a population of EOT which uses parallelization to evaluate individuals. + * + * This class implements an instance of eoPopEvalFunc that applies a private eoEvalFunc to + * all offspring, but in a parallel way. The original process becomes the central host from a network ("master"), and + * other machines disponible in the MPI network ("slaves") are used as evaluators. Population to evaluate is splitted in + * little packets of individuals, which are sent to the evaluators, that process the effective call to eval. Once all + * the individuals have been evaluated, they are returned to the master. The whole process is entirely invisible to the + * eyes of the user, who just has to launch a certain number of processes in MPI so as to have a result. + * + * The eoEvalFunc is no more directly given, but it is stored in the eo::mpi::ParallelApplyStore, which can be + * instanciated if no one is given at construction. + * + * The use of this class requires the user to have called the eo::mpi::Node::init function, at the beginning of its + * program. + * + * @ingroup Evaluation Parallel + * + * @author Benjamin Bouvier + */ template class eoParallelPopLoopEval : public eoPopEvalFunc { public: - /** Ctor: set value of embedded eoEvalFunc */ + /** + * @brief Constructor which creates the job store for the user. + * + * This constructor is the simplest to use, as it creates the store used by the parallel job, for the user. + * The user just precises the scheduling algorithm, the rank of the master and then gives its eval function and + * the size of a packet (how many individuals should be in a single message to evaluator). + * + * @param _assignAlgo The scheduling algorithm used to give orders to evaluators. + * @param _masterRank The MPI rank of the master. + * @param _eval The evaluation functor used to evaluate each individual in the population. + * @param _packetSize The number of individuals to send in one message to evaluator, and which are evaluated at + * a time. + */ eoParallelPopLoopEval( // Job parameters eo::mpi::AssignmentAlgorithm& _assignAlgo, @@ -94,12 +132,21 @@ class eoParallelPopLoopEval : public eoPopEvalFunc ) : assignAlgo( _assignAlgo ), masterRank( _masterRank ), - needToDeleteStore( true ) + needToDeleteStore( true ) // we used new, we'll have to use delete (RAII) { - // FIXME memory leak because of new. store = new eo::mpi::ParallelApplyStore( _eval, _masterRank, _packetSize ); } + /** + * @brief Constructor which allows the user to customize its job store. + * + * This constructor allows the user to customize the store, for instance by adding wrappers and other + * functionnalities, before using it for the parallelized evaluation. + * + * @param _assignAlgo The scheduling algorithm used to give orders to evaluators. + * @param _masterRank The MPI rank of the master. + * @param _store Pointer to a parallel eval store given by the user. + */ eoParallelPopLoopEval( // Job parameters eo::mpi::AssignmentAlgorithm& _assignAlgo, @@ -109,37 +156,57 @@ class eoParallelPopLoopEval : public eoPopEvalFunc assignAlgo( _assignAlgo ), masterRank( _masterRank ), store( _store ), - needToDeleteStore( false ) + needToDeleteStore( false ) // we haven't used new for creating store, we don't care if we have to delete it (RAII). { // empty } + /** + * @brief Default destructor. Sends a message to all evaluators indicating that the global loop (eoEasyEA, for + * instance) is over. + */ ~eoParallelPopLoopEval() { + // Only the master has to send the termination message if( eo::mpi::Node::comm().rank() == masterRank ) { eo::mpi::EmptyJob job( assignAlgo, masterRank ); job.run(); } + // RAII if( needToDeleteStore ) { delete store; } } - /** Do the job: simple loop over the offspring */ + /** + * @brief Parallel implementation of the operator(). + * + * @param _parents Population of parents (ignored). + * @param _offspring Population of children, which will be evaluated. + */ void operator()( eoPop & _parents, eoPop & _offspring ) { (void)_parents; - parallelApply(_offspring, assignAlgo, masterRank, *store); + // Reinits the store and the scheduling algorithm + store->data( _offspring ); + assignAlgo.reinit( _offspring.size() ); + // Effectively launches the job. + eo::mpi::ParallelApply job( assignAlgo, masterRank, *store ); + job.run(); } private: + // Scheduling algorithm eo::mpi::AssignmentAlgorithm & assignAlgo; + // Master MPI rank int masterRank; + // Store eo::mpi::ParallelApplyStore* store; + // Do we have to delete the store by ourselves ? bool needToDeleteStore; }; #endif diff --git a/eo/src/serial/Array.cpp b/eo/src/serial/Array.cpp index f70b00e83..180aad16b 100644 --- a/eo/src/serial/Array.cpp +++ b/eo/src/serial/Array.cpp @@ -1,38 +1,58 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # include "Array.h" namespace eoserial { - -std::ostream& Array::print( std::ostream& out ) const -{ - out << "["; - bool first = true; - for (ArrayChildren::const_iterator it = begin(), - end = this->end(); - it != end; - ++it) + std::ostream& Array::print( std::ostream& out ) const { - if ( first ) + out << "["; + bool first = true; + for (ArrayChildren::const_iterator it = begin(), + end = this->end(); + it != end; + ++it) { - first = false; - } else { - out << ", "; + if ( first ) + { + first = false; + } else { + out << ", "; + } + (*it)->print( out ); } - (*it)->print( out ); + out << "]\n"; + return out; } - out << "]\n"; - return out; -} -Array::~Array() -{ - for (ArrayChildren::iterator it = begin(), - end = this->end(); - it != end; - ++it) + Array::~Array() { - delete *it; + for (ArrayChildren::iterator it = begin(), + end = this->end(); + it != end; + ++it) + { + delete *it; + } } -} } // namespace eoserial diff --git a/eo/src/serial/Array.h b/eo/src/serial/Array.h index 69231980c..d453add99 100644 --- a/eo/src/serial/Array.h +++ b/eo/src/serial/Array.h @@ -1,148 +1,169 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EOSERIAL_ARRAY_H__ # define __EOSERIAL_ARRAY_H__ # include -# include # include "Entity.h" # include "Serializable.h" - # include "Object.h" -# include "String.h" namespace eoserial { -// Forward declaration for below declarations. -class Array; - -/* - * Declarations of functions present in Utils.h - * These are put here to avoid instead of including the file Utils.h, which would - * cause a circular inclusion. - */ -template< class T > -void unpack( const Array & array, unsigned int index, T & value ); - -void unpackObject( const Array & array, unsigned int index, Persistent & value ); - -template< class Container, template class UnpackAlgorithm > -void unpackArray( const Array & array, unsigned int index, Container & container ); - -/** - * @brief Represents a JSON array. - * - * Wrapper for an array, so as to be used as a JSON object. - */ -class Array : public eoserial::Entity, public std::vector< eoserial::Entity* > -{ -protected: - typedef std::vector< eoserial::Entity* > ArrayChildren; - -public: - /** - * @brief Adds the serializable object as a JSON object. - * @param obj Object which implemnets JsonSerializable. - */ - void push_back( const eoserial::Printable* obj ) - { - ArrayChildren::push_back( obj->pack() ); - } - - /** - * @brief Proxy for vector::push_back. - */ - void push_back( eoserial::Entity* json ) - { - ArrayChildren::push_back( json ); - } - - /** - * @brief Prints the JSON array into the given stream. - * @param out The stream - */ - virtual std::ostream& print( std::ostream& out ) const; - - /** - * @brief Dtor - */ - ~Array(); + // Forward declaration for below declarations. + class Array; /* - * The following parts allows the user to automatically deserialize an eoserial::Array into a - * standard container, by giving the algorithm which will be used to deserialize contained entities. + * Declarations of functions present in Utils.h + * These are put here to avoid instead of including the file Utils.h, which would + * cause a circular inclusion. */ + template< class T > + void unpack( const Array & array, unsigned int index, T & value ); + + void unpackObject( const Array & array, unsigned int index, Persistent & value ); + + template< class Container, template class UnpackAlgorithm > + void unpackArray( const Array & array, unsigned int index, Container & container ); + /** - * @brief Functor which determines how to retrieve the real value contained in a eoserial::Entity at - * a given place. + * @brief Represents a JSON array. * - * It will be applied for each contained variable in the array. + * Wrapper for an array, so as to be used as a JSON object. + * + * @ingroup Serialization */ - template - struct BaseAlgorithm + class Array : public eoserial::Entity, public std::vector< eoserial::Entity* > { - /** - * @brief Main operator. - * - * @param array The eoserial::Array from which we're reading. - * @param i The index of the contained value. - * @param container The standard (STL) container in which we'll push back the read value. - */ - virtual void operator()( const eoserial::Array& array, unsigned int i, Container & container ) const = 0; + protected: + typedef std::vector< eoserial::Entity* > ArrayChildren; + + public: + /** + * @brief Adds the serializable object as a JSON object. + * @param obj Object which implemnets JsonSerializable. + */ + void push_back( const eoserial::Printable* obj ) + { + ArrayChildren::push_back( obj->pack() ); + } + + /** + * @brief Proxy for vector::push_back. + */ + void push_back( eoserial::Entity* json ) + { + ArrayChildren::push_back( json ); + } + + /** + * @brief Prints the JSON array into the given stream. + * @param out The stream + */ + virtual std::ostream& print( std::ostream& out ) const; + + /** + * @brief Dtor + */ + ~Array(); + + /* + * The following parts allows the user to automatically deserialize an eoserial::Array into a + * standard container, by giving the algorithm which will be used to deserialize contained entities. + */ + + /** + * @brief Functor which determines how to retrieve the real value contained in a eoserial::Entity at + * a given place. + * + * It will be applied for each contained variable in the array. + */ + template + struct BaseAlgorithm + { + /** + * @brief Main operator. + * + * @param array The eoserial::Array from which we're reading. + * @param i The index of the contained value. + * @param container The standard (STL) container in which we'll push back the read value. + */ + virtual void operator()( const eoserial::Array& array, unsigned int i, Container & container ) const = 0; + }; + + /** + * @brief BaseAlgorithm for retrieving primitive variables. + * + * This one should be used to retrieve primitive (and types which implement operator>>) variables, for instance + * int, double, std::string, etc... + */ + template + struct UnpackAlgorithm : public BaseAlgorithm + { + void operator()( const eoserial::Array& array, unsigned int i, C & container ) const + { + typename C::value_type t; + unpack( array, i, t ); + container.push_back( t ); + } + }; + + /** + * @brief BaseAlgorithm for retrieving eoserial::Persistent objects. + * + * This one should be used to retrieve objects which implement eoserial::Persistent. + */ + template + struct UnpackObjectAlgorithm : public BaseAlgorithm + { + void operator()( const eoserial::Array& array, unsigned int i, C & container ) const + { + typename C::value_type t; + unpackObject( array, i, t ); + container.push_back( t ); + } + }; + + /** + * @brief General algorithm for array deserialization. + * + * Applies the BaseAlgorithm to each contained variable in the eoserial::Array. + */ + template class UnpackAlgorithm> + inline void deserialize( Container & array ) + { + UnpackAlgorithm< Container > algo; + for( unsigned int i = 0, size = this->size(); + i < size; + ++i) + { + algo( *this, i, array ); + } + } }; - /** - * @brief BaseAlgorithm for retrieving primitive variables. - * - * This one should be used to retrieve primitive (and types which implement operator>>) variables, for instance - * int, double, std::string, etc... - */ - template - struct UnpackAlgorithm : public BaseAlgorithm - { - void operator()( const eoserial::Array& array, unsigned int i, C & container ) const - { - typename C::value_type t; - unpack( array, i, t ); - container.push_back( t ); - } - }; - - /** - * @brief BaseAlgorithm for retrieving eoserial::Persistent objects. - * - * This one should be used to retrieve objects which implement eoserial::Persistent. - */ - template - struct UnpackObjectAlgorithm : public BaseAlgorithm - { - void operator()( const eoserial::Array& array, unsigned int i, C & container ) const - { - typename C::value_type t; - unpackObject( array, i, t ); - container.push_back( t ); - } - }; - - /** - * @brief General algorithm for array deserialization. - * - * Applies the BaseAlgorithm to each contained variable in the eoserial::Array. - */ - template class UnpackAlgorithm> - inline void deserialize( Container & array ) - { - UnpackAlgorithm< Container > algo; - for( unsigned int i = 0, size = this->size(); - i < size; - ++i) - { - algo( *this, i, array ); - } - } -}; - } // namespace eoserial # endif // __EOSERIAL_ARRAY_H__ diff --git a/eo/src/serial/Entity.h b/eo/src/serial/Entity.h index df10002df..6fec34858 100644 --- a/eo/src/serial/Entity.h +++ b/eo/src/serial/Entity.h @@ -1,9 +1,42 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EOSERIAL_ENTITY_H__ # define __EOSERIAL_ENTITY_H__ -# include -# include +# include // ostream +/** + * @brief Contains all the necessary entities to serialize eo objects into JSON objects. + * + * Allows serialization from user objects into JSON objects, if they implement the interface + * eoserial::Serializable or eoserial::Persistent. The following user objects can be serialized: + * - primitive types (int, std::string, ...), in particular every type that can be written into a + * std::stringstream. + * - objects which implement eoserial::Serializable. + * - array of serializable things (primitive or serializable objects). + * + * @ingroup Utilities + * @defgroup Serialization Serialization helpers + */ namespace eoserial { @@ -12,6 +45,8 @@ namespace eoserial * * This class represents a JSON entity, which can be JSON objects, * strings or arrays. It is the base class for the JSON hierarchy. + * + * @ingroup Serialization */ class Entity { diff --git a/eo/src/serial/Object.cpp b/eo/src/serial/Object.cpp index 3e557a126..dd859052e 100644 --- a/eo/src/serial/Object.cpp +++ b/eo/src/serial/Object.cpp @@ -1,40 +1,60 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # include "Object.h" using namespace eoserial; namespace eoserial { - -std::ostream& Object::print( std::ostream& out ) const -{ - out << '{'; - bool first = true; - for(JsonValues::const_iterator it = begin(), end = this->end(); - it != end; - ++it) + std::ostream& Object::print( std::ostream& out ) const { - if ( first ) + out << '{'; + bool first = true; + for(JsonValues::const_iterator it = begin(), end = this->end(); + it != end; + ++it) { - first = false; - } else { - out << ", "; - } + if ( first ) + { + first = false; + } else { + out << ", "; + } - out << '"' << it->first << "\":"; // key - it->second->print( out ); // value + out << '"' << it->first << "\":"; // key + it->second->print( out ); // value + } + out << "}\n"; + return out; + } + + Object::~Object() + { + for(JsonValues::iterator it = begin(), end = this->end(); + it != end; + ++it) + { + delete it->second; } - out << "}\n"; - return out; -} - -Object::~Object() -{ - for(JsonValues::iterator it = begin(), end = this->end(); - it != end; - ++it) - { - delete it->second; } -} } // namespace eoserial diff --git a/eo/src/serial/Object.h b/eo/src/serial/Object.h index 367692527..20fbdbf7e 100644 --- a/eo/src/serial/Object.h +++ b/eo/src/serial/Object.h @@ -1,66 +1,87 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EOSERIAL_OBJECT_H__ # define __EOSERIAL_OBJECT_H__ # include # include -# include # include "Entity.h" # include "Serializable.h" namespace eoserial { - -/** - * @brief JSON Object - * - * This class represents a JSON object, which is basically a dictionnary - * of keys (strings) and values (JSON entities). - */ -class Object : public eoserial::Entity, public std::map< std::string, eoserial::Entity* > -{ -public: - typedef std::map JsonValues; - /** - * @brief Adds a pair into the JSON object. - * @param key The key associated with the eoserial object - * @param eoserial The JSON object as created with framework. + * @brief JSON Object + * + * This class represents a JSON object, which is basically a dictionnary + * of keys (strings) and values (JSON entities). + * + * @ingroup Serialization */ - void add( const std::string& key, eoserial::Entity* json ) + class Object : public eoserial::Entity, public std::map< std::string, eoserial::Entity* > { - (*this)[ key ] = json; - } + public: + typedef std::map JsonValues; - /** - * @brief Adds a pair into the JSON object. - * @param key The key associated with the eoserial object - * @param obj A JSON-serializable object - */ - void add( const std::string& key, const eoserial::Printable* obj ) - { - (*this)[ key ] = obj->pack(); - } + /** + * @brief Adds a pair into the JSON object. + * @param key The key associated with the eoserial object + * @param eoserial The JSON object as created with framework. + */ + void add( const std::string& key, eoserial::Entity* json ) + { + (*this)[ key ] = json; + } - /** - * @brief Deserializes a Serializable class instance from this JSON object. - * @param obj The object we want to rebuild. - */ - void deserialize( eoserial::Persistent & obj ) - { - obj.unpack( this ); - } + /** + * @brief Adds a pair into the JSON object. + * @param key The key associated with the eoserial object + * @param obj A JSON-serializable object + */ + void add( const std::string& key, const eoserial::Printable* obj ) + { + (*this)[ key ] = obj->pack(); + } - /** - * @brief Dtor - */ - ~Object(); + /** + * @brief Deserializes a Serializable class instance from this JSON object. + * @param obj The object we want to rebuild. + */ + void deserialize( eoserial::Persistent & obj ) + { + obj.unpack( this ); + } - /** - * @brief Prints the content of a JSON object into a stream. - */ - virtual std::ostream& print( std::ostream& out ) const; -}; + /** + * @brief Dtor + */ + ~Object(); + + /** + * @brief Prints the content of a JSON object into a stream. + */ + virtual std::ostream& print( std::ostream& out ) const; + }; } // namespace eoserial # endif // __EOSERIAL_OBJECT_H__ diff --git a/eo/src/serial/Parser.cpp b/eo/src/serial/Parser.cpp index c7822d292..258174c86 100644 --- a/eo/src/serial/Parser.cpp +++ b/eo/src/serial/Parser.cpp @@ -1,7 +1,25 @@ -# include +/* +(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 +*/ # include -# include -# include # include "Parser.h" diff --git a/eo/src/serial/Parser.h b/eo/src/serial/Parser.h index f0a94ee21..5bcdc5979 100644 --- a/eo/src/serial/Parser.h +++ b/eo/src/serial/Parser.h @@ -1,3 +1,24 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EOSERIAL_PARSER_H__ # define __EOSERIAL_PARSER_H__ @@ -6,6 +27,8 @@ # include "Object.h" /** + * @file Parser.h + * * This file contains a tiny JSON parser used in DAE. This parser just handles * a subset of JSON grammar, with the following restrictions : * - all strings must be surrounded by double quotes. @@ -26,6 +49,8 @@ namespace eoserial * This parser does just retrieve values and does NOT check the structure of * the input. This implies that if the input is not correct, the result is undefined * and can result to a failure on execution. + * + * @ingroup Serialization */ class Parser { diff --git a/eo/src/serial/Serializable.h b/eo/src/serial/Serializable.h index 482a918a8..715e9c973 100644 --- a/eo/src/serial/Serializable.h +++ b/eo/src/serial/Serializable.h @@ -1,43 +1,65 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EOSERIAL_SERIALIZABLE_H__ # define __EOSERIAL_SERIALIZABLE_H__ -# include - namespace eoserial { + class Object; // to avoid recursive inclusion with JsonObject -class Object; // to avoid recursive inclusion with JsonObject - -/** - * @brief Interface showing that object can be written to a eoserial type - * (currently JSON). - */ -class Printable -{ -public: /** - * @brief Serializes the object to JSON format. - * @return A JSON object created with new. + * @brief Interface showing that object can be written to a eoserial type + * (currently JSON). + * + * @ingroup Serialization */ - virtual eoserial::Object* pack() const = 0; -}; + class Printable + { + public: + /** + * @brief Serializes the object to JSON format. + * @return A JSON object created with new. + */ + virtual eoserial::Object* pack() const = 0; + }; -/** - * @brief Interface showing that object can be eoserialized (written and read - * from an input). - * - * Note : Persistent objects should have a default non-arguments constructor. - */ -class Persistent : public Printable -{ - public: /** - * @brief Loads class fields from a JSON object. - * @param json A JSON object. Programmer doesn't have to delete it, it - * is automatically done. + * @brief Interface showing that object can be eoserialized (written and read + * from an input). + * + * Note : Persistent objects should have a default non-arguments constructor. + * + * @ingroup Serialization */ - virtual void unpack(const eoserial::Object* json) = 0; -}; + class Persistent : public Printable + { + public: + /** + * @brief Loads class fields from a JSON object. + * @param json A JSON object. Programmer doesn't have to delete it, it + * is automatically done. + */ + virtual void unpack(const eoserial::Object* json) = 0; + }; } // namespace eoserial diff --git a/eo/src/serial/String.cpp b/eo/src/serial/String.cpp index deba05a06..c50882786 100644 --- a/eo/src/serial/String.cpp +++ b/eo/src/serial/String.cpp @@ -1,3 +1,24 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # include "String.h" namespace eoserial diff --git a/eo/src/serial/String.h b/eo/src/serial/String.h index 6d81937f3..526cab365 100644 --- a/eo/src/serial/String.h +++ b/eo/src/serial/String.h @@ -1,3 +1,24 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EOSERIAL_STRING_H__ # define __EOSERIAL_STRING_H__ @@ -9,72 +30,73 @@ namespace eoserial { + /** + * @brief JSON String + * + * Wrapper for string, so as to be used as a JSON object. + * + * @ingroup Serialization + */ + class String : public eoserial::Entity, public std::string + { + public: -/** - * @brief JSON String - * - * Wrapper for string, so as to be used as a JSON object. - */ -class String : public eoserial::Entity, public std::string -{ - public: + /** + * @brief Default ctor. + * @param str The string we want to wrap. + */ + String( const std::string& str ) : std::string( str ) {} - /** - * @brief Default ctor. - * @param str The string we want to wrap. - */ - String( const std::string& str ) : std::string( str ) {} + /** + * @brief Ctor used only when parsing. + */ + String( ) {} - /** - * @brief Ctor used only on parsing. - */ - String( ) {} + /** + * @brief Prints out the string. + */ + virtual std::ostream& print( std::ostream& out ) const; - /** - * @brief Prints out the string. - */ - virtual std::ostream& print( std::ostream& out ) const; + /** + * @brief Deserializes the current String into a given primitive type value. + * @param value The value in which we're writing. + */ + template + inline void deserialize( T & value ); - /** - * @brief Deserializes the current String into a given primitive type value. - * @param value The value in which we're writing. - */ - template - inline void deserialize( T & value ); + protected: + // Copy and reaffectation are forbidden + explicit String( const String& _ ); + String& operator=( const String& _ ); + }; - protected: - // Copy and reaffectation are forbidden - explicit String( const String& _ ); - String& operator=( const String& _ ); -}; + /** + * @brief Casts a eoserial::String into a primitive value, or in a type which at + * least overload operator>>. + * + * @param value A reference to the variable we're writing into. + * + * It's not necessary to specify the variable type, which can be infered by compiler when + * invoking. + */ + template + inline void String::deserialize( T & value ) + { + std::stringstream ss; + ss.precision(std::numeric_limits::digits10 + 1); + ss << *this; + ss >> value; + } -/** - * @brief Casts a eoserial::String into a primitive value, or in a type which at - * least overload operator>>. - * - * @param value A reference to the variable we're writing into. - * - * It's not necessary to specify the variable type, which can be infered by compiler when - * invoking. - */ -template -inline void String::deserialize( T & value ) -{ - std::stringstream ss; - ss.precision(std::numeric_limits::digits10 + 1); - ss << *this; - ss >> value; -} - -/** - * @brief Specialization for strings, which don't need to be converted through - * a stringstream. - */ -template<> -inline void String::deserialize( std::string & value ) -{ - value = *this; -} + /** + * @brief Specialization for strings, which don't need to be converted through + * a stringstream. + */ + template<> + inline void String::deserialize( std::string & value ) + { + value = *this; + } } // namespace eoserial diff --git a/eo/src/serial/Utils.h b/eo/src/serial/Utils.h index f40c08d92..33172a747 100644 --- a/eo/src/serial/Utils.h +++ b/eo/src/serial/Utils.h @@ -1,3 +1,24 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EOSERIAL_UTILS_H__ # define __EOSERIAL_UTILS_H__ @@ -7,7 +28,7 @@ namespace eoserial { - /***************************** + /* *************************** * DESERIALIZATION FUNCTIONS * ***************************** These functions are useful for casting eoserial::objects into simple, primitive @@ -54,7 +75,7 @@ namespace eoserial static_cast( array[ index ] )->deserialize< Container, UnpackAlgorithm >( container ); } - /******************************* + /* ***************************** *** SERIALIZATION FUNCTIONS *** ******************************* These functions are useful for casting classic objects and diff --git a/eo/src/serial/eoSerial.h b/eo/src/serial/eoSerial.h index a10f6c014..55a116f0c 100644 --- a/eo/src/serial/eoSerial.h +++ b/eo/src/serial/eoSerial.h @@ -1,3 +1,24 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EOSERIAL_HEADERS__ # define __EOSERIAL_HEADERS__ diff --git a/eo/src/utils/eoParallel.cpp b/eo/src/utils/eoParallel.cpp index 418651984..fc57910c8 100644 --- a/eo/src/utils/eoParallel.cpp +++ b/eo/src/utils/eoParallel.cpp @@ -21,7 +21,7 @@ Contact: http://eodev.sourceforge.net Authors: -Caner Candan + Caner Candan */ @@ -37,7 +37,7 @@ eoParallel::eoParallel() : _nthreads( 0, "parallelize-nthreads", "Define the number of threads you want to use, nthreads = 0 means you want to use all threads available", '\0' ), _enableResults( false, "parallelize-enable-results", "Enable the generation of results", '\0' ), _doMeasure( false, "parallelize-do-measure", "Do some measures during execution", '\0' ), - _packetSize( 1U, "parallelize-packet-size", "Number of elements which should be sent at a time during a parallel evaluation based on message passing.", '\0'), + _packetSize( 1U, "parallelize-packet-size", "Number of elements which should be sent in a single message during a parallel evaluation based on message passing.", '\0'), _t_start(0) { } diff --git a/eo/src/utils/eoParallel.h b/eo/src/utils/eoParallel.h index 3aae8e430..b812fecc9 100644 --- a/eo/src/utils/eoParallel.h +++ b/eo/src/utils/eoParallel.h @@ -20,8 +20,7 @@ Contact: http://eodev.sourceforge.net Authors: -Caner Candan - + Caner Candan */ /** @defgroup Parallel Parallel diff --git a/eo/src/utils/eoTimer.h b/eo/src/utils/eoTimer.h index 8aa09c51b..edecce62a 100644 --- a/eo/src/utils/eoTimer.h +++ b/eo/src/utils/eoTimer.h @@ -1,38 +1,84 @@ -# ifndef __TIMER_H__ -# define __TIMER_H__ +/* +(c) Thales group, 2012 -# include -# include + 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. -# include -# include + 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. -# include "utils/eoParallel.h" + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ +# ifndef __EO_TIMER_H__ +# define __EO_TIMER_H__ + +# include // time() +# include // rusage() + +# include // std::vector +# include // std::map + +# include "utils/eoParallel.h" // eo::parallel # ifdef WITH_MPI +// For serialization purposes # include # include # include # endif -// TODO TODOB commenter +/** + * @brief Timer allowing to measure time between a start point and a stop point. + * + * This timer allows the user to measure user time, system time and wallclock time + * between two points. Basically, user time is time spent in developer code ; system + * time is time spent during the IO wait and system calls ; wallclock is the difference + * of time we could observe by measuring time with a watch. + * + * @ingroup Utilities + */ class eoTimer { public: + /** + * @brief Default ctor. Begins all the timers. + */ eoTimer() - { - restart(); - } - - void restart() { uuremainder = 0; usremainder = 0; + restart(); + } + + /** + * @brief Restarts all the timers and launch the measure. + */ + void restart() + { wc_start = time(NULL); getrusage( RUSAGE_SELF, &_start ); } + /** + * @brief Measures the user time spent since the last restart(). + * + * If the number of elapsed seconds is zero, the spent milliseconds are + * added to a remainder. If the remainder exceeds one second, it is + * added to the number of elapsed seconds. + * + * @return Number of seconds elapsed in user space. + */ long int usertime() { struct rusage _now; @@ -44,12 +90,21 @@ class eoTimer if( uuremainder > 1000000) { ++result; - uuremainder = 0; + uuremainder -= 1000000; } } return result; } + /** + * @brief Measures the system time spent since the last restart(). + * + * If the number of elapsed seconds is zero, the spent milliseconds are + * added to a remainder. If the remainder exceeds one second, it is + * added to the number of elapsed seconds. + * + * @return Number of seconds elapsed in system (kernel) space. + */ long int systime() { struct rusage _now; @@ -61,28 +116,90 @@ class eoTimer if( usremainder > 1000000) { ++result; - usremainder = 0; + usremainder -= 1000000; } } return result; } + /** + * @brief Measures the wallclock time spent since the last restart(). + * + * @return Number of seconds elapsed, as a double. + */ double wallclock() { return std::difftime( std::time(NULL) , wc_start ); } protected: + // Structure used to measure user and system time. struct rusage _start; + // Remainder (in milliseconds) for user time. long int uuremainder; + // Remainder (in milliseconds) for system time. long int usremainder; + // Structure used to measure wallclock time. time_t wc_start; }; +/** + * @brief Registers a group of statistics, each statistic corresponding to user, system and wallclock times distribution. + * + * This class helps the user to measure time in different parts of an application. A name is associated to a statistic, + * on each call to start() and stop() for this name, a new number is added to the statistic, for each of the three + * measured times. + * + * The statistics are only registered if the option "--parallelized-do-measure" is set to true, which can be checked + * thanks to global object eo::parallel. + * + * This shows how the eoTimerStat can be used : + * @code + * eoTimerStat timerStat; + * timerStat.start("first_point"); + * for( int i = 0; i < 1000; ++i ) + * { + * timerStat.start("single_computation"); + * single_computation( i ); + * timerStat.stop("single_computation"); + * } + * // After this loop, timerStat contains a statistic of key "single_computation" which contains 1000 measures for + * // each type of time. + * timerStat.stop("first_point"); + * // After this line, timerStat contains another statistic of key "first_point" which counted the duration of the + * // whole loop. + * + * int singleComputationUsertimeMean = 0; + * for( int i = 0; i < 1000; ++i ) + * { + * singleComputationUsertimeMean += timerStat.stats()["single_computation"].utime[i]; + * } + * std::cout << "Mean of user time spent in single computation: " << singleComputationUsertimeMean / 1000. << std::endl; + * @endcode + * + * When using MPI, these statistics can be readily be serialized, so as to be sent over a network, for instance. + * + * Implementation details: this eoTimerStat is in fact a map of strings (key) / Stat (value). Stat is an internal + * structure directly defined in the class, which contains three vectors modeling the distributions of the different + * types of elapsed times. Another map of strings (key) / eoTimer (value) allows to effectively retrieve the different + * times. The struct Stat will be exposed to client, which will use its members ; however, + * the client doesn't have anything to do directly with the timer, that's why the two maps are splitted. + * + * @ingroup Utilities + */ class eoTimerStat { public: + /** + * @brief Statistic related to a key (name). + * + * This structure is the value in the map saved in the eoTimerStat. It contains the statistic bound to a key, + * which are the user time distribution, the system time distribution and the wallclock time distribution, as + * std::vector s. + * + * It can readily be serialized with boost when compiling with mpi. + */ struct Stat { std::vector utime; @@ -93,25 +210,27 @@ class eoTimerStat friend class boost::serialization::access; /** - * Serializes the statistique in a boost archive (useful for boost::mpi) + * Serializes the single statistic in a boost archive (useful for boost::mpi). + * Just serializes the 3 vectors. */ template - void serialize( Archive & ar, const unsigned int version ) - { - ar & utime & stime & wtime; - (void) version; // avoid compilation warning - } + void serialize( Archive & ar, const unsigned int version ) + { + ar & utime & stime & wtime; + (void) version; // avoid compilation warning + } # endif }; #ifdef WITH_MPI - // Gives access to boost serialization - friend class boost::serialization::access; + // Gives access to boost serialization + friend class boost::serialization::access; - /** - * Serializes the map of statistics in a boost archive (useful for boost::mpi) - */ - template + /** + * Serializes the timerStat object in a boost archive (useful for boost::mpi). + * Just serializes the map. + */ + template void serialize( Archive & ar, const unsigned int version ) { ar & _stats; @@ -119,6 +238,14 @@ class eoTimerStat } # endif + /** + * @brief Starts a new measure for the given key. + * + * This is only performed if parallel.doMeasure() is true, which is equivalent to the fact that + * parser found "--parallel-do-measure=1" in command line args. + * + * @param key The key of the statistic. + */ void start( const std::string & key ) { if( eo::parallel.doMeasure() ) @@ -127,6 +254,16 @@ class eoTimerStat } } + /** + * @brief Stops the mesure for the given key and saves the elapsed times. + * + * Must follow a call of start with the same key. + * + * This is only performed if parallel.doMeasure() is true, which is equivalent to the fact that + * parser found "--parallel-do-measure=1" in command line args. + * + * @param key The key of the statistic. + */ void stop( const std::string& key ) { if( eo::parallel.doMeasure() ) @@ -139,13 +276,18 @@ class eoTimerStat } } - std::map< std::string, Stat > stats() + /** + * @brief Getter for the statistics map. + */ + std::map< std::string, Stat >& stats() { return _stats; } protected: + // Statistics map: links a key (string) to a statistic. std::map< std::string, Stat > _stats; + // Timers map: links a key to its timer. std::map< std::string, eoTimer > _timers; }; From d711369f12b65969cfb8ad2478996c849922bc4a Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 12 Jul 2012 14:17:51 +0200 Subject: [PATCH 61/76] Correctly handling remainders when using eoTimer. --- eo/src/utils/eoTimer.h | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/eo/src/utils/eoTimer.h b/eo/src/utils/eoTimer.h index edecce62a..211313778 100644 --- a/eo/src/utils/eoTimer.h +++ b/eo/src/utils/eoTimer.h @@ -83,15 +83,22 @@ class eoTimer { struct rusage _now; getrusage( RUSAGE_SELF, &_now ); + long int result = _now.ru_utime.tv_sec - _start.ru_utime.tv_sec; - if( _now.ru_utime.tv_sec == _start.ru_utime.tv_sec ) + long int remainder = _now.ru_utime.tv_usec - _start.ru_utime.tv_usec; + if( remainder >= 0 ) { - uuremainder += _now.ru_utime.tv_usec - _start.ru_utime.tv_usec; - if( uuremainder > 1000000) - { - ++result; - uuremainder -= 1000000; - } + uuremainder += remainder; + } else + { + uuremainder += ( 1000000 - remainder ); + --result; + } + + if( uuremainder >= 1000000 ) + { + uuremainder -= 1000000; + ++result; } return result; } @@ -109,15 +116,22 @@ class eoTimer { struct rusage _now; getrusage( RUSAGE_SELF, &_now ); + long int result = _now.ru_stime.tv_sec - _start.ru_stime.tv_sec; - if( _now.ru_stime.tv_sec == _start.ru_stime.tv_sec ) + long int remainder = _now.ru_stime.tv_usec - _start.ru_stime.tv_usec; + if( remainder >= 0 ) { - usremainder += _now.ru_stime.tv_usec - _start.ru_stime.tv_usec; - if( usremainder > 1000000) - { - ++result; - usremainder -= 1000000; - } + usremainder += remainder; + } else + { + usremainder += ( 1000000 - remainder ); + --result; + } + + if( usremainder >= 1000000 ) + { + usremainder -= 1000000; + ++result; } return result; } From f963a15fbe23ad8e3cb4c279ac0f557ddbe70e83 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 12 Jul 2012 19:15:07 +0200 Subject: [PATCH 62/76] Fixing assignment algorithm, who gave too many assignments by worker. Now, when using static assignment, the optimal size of packet (so as each worker has to process only one packet of data) is sent. --- eo/src/eoPopEvalFunc.h | 10 +++++++++- eo/src/mpi/eoMpiAssignmentAlgorithm.h | 3 +++ eo/src/mpi/eoParallelApply.h | 14 ++++---------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index 2cc868076..b96e8cac9 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -35,6 +35,9 @@ #include #include #include +#include + +#include // ceil # endif // WITH_MPI /** eoPopEvalFunc: This abstract class is for GLOBAL evaluators @@ -192,7 +195,12 @@ class eoParallelPopLoopEval : public eoPopEvalFunc (void)_parents; // Reinits the store and the scheduling algorithm store->data( _offspring ); - assignAlgo.reinit( _offspring.size() ); + // For static scheduling, it's mandatory to reinit attributions + int nbWorkers = assignAlgo.availableWorkers(); + assignAlgo.reinit( nbWorkers ); + if( ! eo::parallel.isDynamic() ) { + store->data()->packetSize = ceil( static_cast( _offspring.size() ) / nbWorkers ); + } // Effectively launches the job. eo::mpi::ParallelApply job( assignAlgo, masterRank, *store ); job.run(); diff --git a/eo/src/mpi/eoMpiAssignmentAlgorithm.h b/eo/src/mpi/eoMpiAssignmentAlgorithm.h index 0630a6f7a..85177ea08 100644 --- a/eo/src/mpi/eoMpiAssignmentAlgorithm.h +++ b/eo/src/mpi/eoMpiAssignmentAlgorithm.h @@ -137,7 +137,10 @@ namespace eo { unsigned int nbWorkers = workers.size(); freeWorkers = nbWorkers; + + attributions.clear(); attributions.reserve( nbWorkers ); + busy.clear(); busy.resize( nbWorkers, false ); // Let be the euclidean division of runs by nbWorkers : diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 9598b952f..d0435cead 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -37,8 +37,6 @@ namespace eo { size = _pop->size(); } - - tempArray = new EOT[ _packetSize ]; } void init( std::vector& _pop ) @@ -49,11 +47,6 @@ namespace eo assignedTasks.clear(); } - ~ParallelApplyData() - { - delete [] tempArray; - } - std::vector& data() { return *_data; @@ -65,7 +58,7 @@ namespace eo int size; std::map< int /* worker rank */, ParallelApplyAssignment /* min indexes in vector */> assignedTasks; int packetSize; - EOT* tempArray; + std::vector tempArray; int masterRank; bmpi::communicator& comm; @@ -140,14 +133,15 @@ namespace eo int recvSize; d->comm.recv( d->masterRank, 1, recvSize ); - d->comm.recv( d->masterRank, 1, d->tempArray, recvSize ); + d->tempArray.resize( recvSize ); + d->comm.recv( d->masterRank, 1, & d->tempArray[0] , recvSize ); timerStat.start("worker_processes"); for( int i = 0; i < recvSize ; ++i ) { d->func( d->tempArray[ i ] ); } timerStat.stop("worker_processes"); - d->comm.send( d->masterRank, 1, d->tempArray, recvSize ); + d->comm.send( d->masterRank, 1, & d->tempArray[0], recvSize ); } }; From e7a48b8afb7b617c1f074e14492e98abca1c0745 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 12 Jul 2012 19:15:52 +0200 Subject: [PATCH 63/76] Using dynamic scheduling by default --- eo/src/utils/eoParallel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eo/src/utils/eoParallel.cpp b/eo/src/utils/eoParallel.cpp index fc57910c8..b47d3f82a 100644 --- a/eo/src/utils/eoParallel.cpp +++ b/eo/src/utils/eoParallel.cpp @@ -32,7 +32,7 @@ Authors: eoParallel::eoParallel() : _isEnabled( false, "parallelize-loop", "Enable memory shared parallelization into evaluation's loops", '\0' ), - _isDynamic( false, "parallelize-dynamic", "Enable dynamic memory shared parallelization", '\0' ), + _isDynamic( true, "parallelize-dynamic", "Enable dynamic memory shared parallelization", '\0' ), _prefix( "results", "parallelize-prefix", "Here's the prefix filename where the results are going to be stored", '\0' ), _nthreads( 0, "parallelize-nthreads", "Define the number of threads you want to use, nthreads = 0 means you want to use all threads available", '\0' ), _enableResults( false, "parallelize-enable-results", "Enable the generation of results", '\0' ), From 1eac497c4d4233e992d4dfb603e9845d5b7f8310 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 13 Jul 2012 11:54:21 +0200 Subject: [PATCH 64/76] Documentating eoMpi.h --- eo/src/mpi/eoMpi.h | 390 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 371 insertions(+), 19 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index fc419b13d..d4e0349e3 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -1,10 +1,28 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EO_MPI_H__ # define __EO_MPI_H__ -# include -# include -# include -# include +# include // std::vector # include # include @@ -14,37 +32,196 @@ # include "eoMpiNode.h" # include "eoMpiAssignmentAlgorithm.h" -// TODO TODOB comment! - namespace eo { + /** + * @ingroup Parallel + * @defgroup MPI Message Passing Interface parallelization + */ + + /** + * @brief MPI parallelization helpers for EO. + * + * This namespace contains parallelization functions which help to parallelize computations in EO. It is based on a + * generic algorithm, which is then customized with functors, corresponding to the algorithm main steps. These + * computations are centralized, i.e there is one central host whose role is to handle the steps of the algorithm ; + * we call it the "master". The other hosts just have to perform a "dummy" computation, which may be any kind of + * processing ; we call them, the "slaves", or less pejoratively, the "workers". Workers can communicate to each + * other, but they receive their orders from the Master and send him back some results. A worker can also be the + * master of a different parallelization process, as soon as it is a part of its work. Machines of the network, also + * called hosts, are identified by an unique number: their rank. At any time during the execution of the program, + * all the hosts know the total number of hosts. + * + * A parallelized Job is a set of tasks which are independant (i.e can be executed in random order without + * modifiying the result) and take a data input and compute a data output to be sent to the Master. The data can be + * of any type, however they have to be serialized to be sent over a network. It is sufficient that they can be + * serialized through boost. + * + * @todo For serialization purposes, don't depend upon boost. It would be easy to use only eoserial and send strings + * via mpi. + * + * The main steps of the algorithm are the following: + * - For the master: + * - Have we done with the treatment we are doing ? + * - If this is the case, we can quit. + * - Otherwise, send an input data to some available worker. + * - If there's no available worker, wait for a worker to be free. + * - When receiving the response, handle it (eventually compute something on the output data, store it...). + * - Go back to the first step. + * - For the worker, it is even easier: + * - Wait for an order. + * - If there's nothing to do, just quit. + * - Otherwise, eventually retrieve data and do the work. + * - Go back to the first step. + * + * There is of course some network adjustements to do and precisions to give there, but the main ideas are present. As the + * job is fully centralized, this is the master who tells the workers when to quit and when to work. + * + * The idea behind these MPI helpers is to be the most generic possible. If we look back at the steps of the + * algorithm, we found that the steps can be splitted into 2 parts: the first consists in the steps of any + * parallelization algorithm and the other consists in the specific parts of the algorithm. Ideally, the user should + * just have to implement the specific parts of the algorithm. We identified these parts to be: + * - For the master: + * - What does mean to have terminated ? There are only two alternatives, in our binary world: either it is + * terminated, or it is not. Hence we only need a function returning a boolean to know if we're done with the + * computation : we'll call it IsFinished. + * - What do we have to do when we send a task ? We don't have any a priori on the form of the sent data, or + * the number of sent data. Moreover, as the tasks are all independant, we don't care of who will do the + * computation, as soon as it's done. Knowing the rank of the worker will be sufficient to send him data. We + * have identified another function, taking a single argument which is the rank of the worker: we'll call it + * SendTask. + * - What do we have to do when we receive a response from a worker ? One more time, we don't know which form + * or structure can have the receive data, only the user can know. Also we let the user the charge to retrieve + * the data ; he just has to know from who the master will retrieve the data. Here is another function, taking + * a rank (the sender's one) as a function argument : this will be HandleResponse. + * - For the worker: + * - What is the processing ? It can have any nature. We just need to be sure that a data is sent back to the + * master, but it seems difficult to check that: it will be the role of the user to assert that data is sent by + * the worker at the end of an execution. We've got identified our last function: ProcessTask. + * + * In term of implementation, it would be annoying to have only abstract classes with these 4 methods to implement. It + * would mean that if you want to alter just one of these 4 functions, you have to implement a new sub class, with a + * new constructor which could have the same signature. Besides, this fashion doesn't allow you to add dynamic + * functionalities, using the design pattern Decorator for instance, without implement a class for each type of + * decoration you want to add. For these reasons, we decided to transform function into functors ; the user can then + * wrap the existing, basic comportments into more sophisticated computations, whenever he wants, and without the + * notion of order. We retrieve here the power of extension given by the design pattern Decorator. + * + * Our 4 functors could have a big amount of data in common (see eoParallelApply to have an idea). + * So as to make it easy for the user to implement these 4 functors, we consider that these functors + * have to share a common data structure. This data structure is referenced (as a pointer) in the 4 functors, so the + * user doesn't need to pass a lot of parameters to each functor constructor. + * + * There are two kinds of jobs: + * - The job which are launched a fixed and well known amount of times, i.e both master and workers know how many + * times they will be launched. They are "one shot jobs". + * - The job which are launched an unknown amount of times, for instance embedded in a while loop for which we don't + * know the amount of repetitions (typically, eoEasyEA loop is a good example, as we don't know the continuator + * condition). They are called "multi job". + * As the master tells the workers to quit, we have to differentiate these two kinds of jobs. When the job is of the + * kind "multi job", the workers would have to perform a while(true) loop so as to receive the orders ; but even if + * the master tells them to quit, they would begin another job and wait for another order, while the master would + * have quit: this would cause a deadlock and workers processes would be blocked, waiting for an order. + */ namespace mpi { + /** + * @brief A timer which allows user to generate statistics about computation times. + */ extern eoTimerStat timerStat; + /** + * @brief Tags used in MPI messages for framework communication + * + * These tags are used for framework communication and fits "channels", so as to differentiate when we're + * sending an order to a worker (Commands) or data (Messages). They are not reserved by the framework and can be + * used by the user, but he is not bound to. + */ namespace Channel { const int Commands = 0; const int Messages = 1; } + /** + * @brief Simple orders used by the framework. + * + * These orders are sent by the master to the workers, to indicate to them if they should receive another task + * to do (Continue), if an one shot job is done (Finish) or if a multi job is done (Kill). + */ namespace Message { const int Continue = 0; - const int Finish = 1; // TODO commentaire : différence entre finir une tâche et arrêter le worker à expliciter. + const int Finish = 1; const int Kill = 2; } + /** + * @brief If the job only has one master, the user can use this constant, so as not to worry with integer ids. + */ const int DEFAULT_MASTER = 0; + /** + * @brief Base class for the 4 algorithm functors. + * + * This class can embed a data (JobData) and a wrapper, so as to make all the 4 functors wrappable. + * We can add a wrapper at initialization or at any time when executing the program. + * + * According to RAII, the boolean needDelete helps to know if we have to use the operator delete on the wrapper + * or not. Hence, if any functor is wrapped, user has just to put this boolean to true, to indicate to wrapper + * that it should call delete. This allows to mix wrapper initialized in the heap (with new) or in the stack. + * + * @param JobData a Data type, which can have any form. It can a struct, a single int, anything. + * + * @param Wrapped the type of the functor, which will be stored as a pointer under the name _wrapped. + * This allows to wrap directly the functor in functors of the same type + * here, instead of dealing with SharedDataFunction* that we would have to cast all the time. + * Doing also allows to handle the wrapped functor as the functor we're writing, when coding the wrappers, + * instead of doing some static_cast. For instance, if there are 2 functors subclasses, fA and fB, fA + * implementing doFa() and fB implementing doFb(), we could have the following code: + * @code + * struct fA_wrapper + * { + * // some code + * void doFa() + * { + * _wrapped->doFa(); + * std::cout << "I'm a fA wrapper!" << std::endl; + * // if we didn't have the second template parameter, but a SharedDataFunction, we would have to do this: + * static_cast(_wrapped)->doFa(); + * // do other things (it's a wrapper) + * } + * }; + * + * struct fB_wrapper + * { + * // some code + * void doFb() + * { + * _wrapped->doFb(); // and not: static_cast(_wrapped)->doFb(); + * } + * }; + * @endcode + * This makes the code easier to write for the user. + */ template< typename JobData, typename Wrapped > struct SharedDataFunction { + /** + * @brief Default constructor. + * + * The user is not bound to give a wrapped functor. + */ SharedDataFunction( Wrapped * w = 0 ) : _wrapped( w ), _needDelete( false ) { // empty } + /** + * @brief Destructor. + * + * Calls delete on the wrapped function, only if necessary. + */ virtual ~SharedDataFunction() { if( _wrapped && _wrapped->needDelete() ) @@ -53,11 +230,21 @@ namespace eo } } + /** + * @brief Setter for the wrapped function. + * + * It doesn't do anything on the current wrapped function, like deleting it. + */ void wrapped( Wrapped * w ) { _wrapped = w; } + /** + * @brief Setter for the data present in the functor. + * + * Calls the setter on the functor and on the wrapped functors, in a Composite pattern fashion. + */ void data( JobData* _d ) { d = _d; @@ -67,15 +254,29 @@ namespace eo } } + /** + * @brief Returns true if we need to use operator delete on this wrapper, false otherwise. + **/ bool needDelete() { return _needDelete; } void needDelete( bool b ) { _needDelete = b; } protected: JobData* d; - Wrapped* _wrapped; + Wrapped* _wrapped; // Pointer and not a reference so as to be set at any time and to avoid affectation bool _needDelete; }; + /** + * @brief Functor (master side) used to send a task to the worker. + * + * The user doesn't have to know which worker will receive a task, so we just indicate to master the rank of the + * worker. The data used for computation have to be explicitly sent by the master to the worker, with indicated + * rank. Once this functor has been called, the worker is considered busy until it sends a return message to the + * master. + * + * This is a functor implementing void operator()(int), and also a shared data function, containing wrapper on its + * own type. + */ template< typename JobData > struct SendTaskFunction : public eoUF, public SharedDataFunction< JobData, SendTaskFunction > { @@ -86,9 +287,19 @@ namespace eo // empty } - virtual ~SendTaskFunction() {} + virtual ~SendTaskFunction() {} // for inherited classes }; + /** + * @brief Functor (master side) used to indicate what to do when receiving a response. + * + * The master calls this function as soon as it receives some data, in some channel. Thanks to MPI, we retrieve + * the rank of the data's sender. This functor is then called with this rank. There is no memoization of a link + * between sent data and rank, so the user has to implement it, if he needs it. + * + * This is a functor implementing void operator()(int), and also a shared data function, containing wrapper on + * its own type. + */ template< typename JobData > struct HandleResponseFunction : public eoUF, public SharedDataFunction< JobData, HandleResponseFunction > { @@ -99,9 +310,21 @@ namespace eo // empty } - virtual ~HandleResponseFunction() {} + virtual ~HandleResponseFunction() {} // for inherited classes }; + /** + * @brief Functor (worker side) implementing the processing to do. + * + * This is where the real computation happen. + * Whenever the master sends the command "Continue" to workers, which indicates the worker will receive a task, + * the worker calls this functor. The user has to explicitly retrieve the data, handle it and transmit it, + * processed, back to the master. If the worker does not send any data back to the master, the latter will + * consider the worker isn't done and a deadlock could occur. + * + * This is a functor implementing void operator()(), and also a shared data function, containing wrapper on its + * own type. + */ template< typename JobData > struct ProcessTaskFunction : public eoF, public SharedDataFunction< JobData, ProcessTaskFunction > { @@ -112,9 +335,19 @@ namespace eo // empty } - virtual ~ProcessTaskFunction() {} + virtual ~ProcessTaskFunction() {} // for inherited classes }; + /** + * @brief Functor (master side) indicating whether the job is done or not. + * + * The master loops on this functor to know when to stop. When this functor returns true, the master will wait + * for the last responses and properly stops the job. Whenever this functor returns false, the master will send + * tasks, until this functor returns true. + * + * This is a functor implementing bool operator()(), and also a shared function, containing wrapper on its own + * type. + */ template< typename JobData > struct IsFinishedFunction : public eoF, public SharedDataFunction< JobData, IsFinishedFunction > { @@ -125,12 +358,31 @@ namespace eo // empty } - virtual ~IsFinishedFunction() {} + virtual ~IsFinishedFunction() {} // for inherited classes }; + /** + * @brief Contains all the required data and the functors to launch a job. + * + * Splitting the functors and data from the job in itself allows to use the same functors and data for multiples + * instances of the same job. You define your store once and can use it a lot of times during your program. If + * the store was included in the job, you'd have to give again all the functors and all the datas to each + * invokation of the job. + * + * Job store contains the 4 functors (pointers, so as to be able to wrap them ; references couldn't have + * permitted that) described above and the JobData used by all these functors. It contains + * also helpers to easily wrap the functors, getters and setters on all of them. + * + * The user has to implement data(), which is the getter for retrieving JobData. We don't have any idea of who + * owns the data, moreover it is impossible to initialize it in this generic JobStore, as we don't know its + * form. As a matter of fact, the user has to define this in the JobStore subclasses. + */ template< typename JobData > struct JobStore { + /** + * @brief Default ctor with the 4 functors. + */ JobStore( SendTaskFunction* stf, HandleResponseFunction* hrf, @@ -142,11 +394,22 @@ namespace eo // empty } + /** + * @brief Empty ctor, useful for not forcing users to call the other constructor. + * + * When using this constructor, the user have to care about the 4 functors pointers, otherwise null pointer + * segfaults have to be expected. + */ JobStore() { // empty } + /** + * @brief Default destructor. + * + * JobStore is the highest layer which calls needDelete on its functors. + */ ~JobStore() { if( _stf->needDelete() ) delete _stf; @@ -155,16 +418,21 @@ namespace eo if( _iff->needDelete() ) delete _iff; } + // Getters SendTaskFunction & sendTask() { return *_stf; } HandleResponseFunction & handleResponse() { return *_hrf; } ProcessTaskFunction & processTask() { return *_ptf; } IsFinishedFunction & isFinished() { return *_iff; } + // Setters void sendTask( SendTaskFunction* stf ) { _stf = stf; } void handleResponse( HandleResponseFunction* hrf ) { _hrf = hrf; } void processTask( ProcessTaskFunction* ptf ) { _ptf = ptf; } void isFinished( IsFinishedFunction* iff ) { _iff = iff; } + /** + * @brief Helpers for wrapping send task functor. + */ void wrapSendTask( SendTaskFunction* stf ) { if( stf ) @@ -174,6 +442,9 @@ namespace eo } } + /** + * @brief Helpers for wrapping handle response functor. + */ void wrapHandleResponse( HandleResponseFunction* hrf ) { if( hrf ) @@ -183,6 +454,9 @@ namespace eo } } + /** + * @brief Helpers for wrapping process task functor. + */ void wrapProcessTask( ProcessTaskFunction* ptf ) { if( ptf ) @@ -192,6 +466,9 @@ namespace eo } } + /** + * @brief Helpers for wrapping is finished functor. + */ void wrapIsFinished( IsFinishedFunction* iff ) { if( iff ) @@ -201,22 +478,48 @@ namespace eo } } - // TODO commenter : laissé à la couche d'en dessous car impossible d'initialiser une donnée membre d'une classe mère depuis une classe fille. virtual JobData* data() = 0; protected: - // TODO commenter : Utiliser des pointeurs pour éviter d'écraser les fonctions wrappées SendTaskFunction< JobData >* _stf; HandleResponseFunction< JobData >* _hrf; ProcessTaskFunction< JobData >* _ptf; IsFinishedFunction< JobData >* _iff; }; + /** + * @brief Class implementing the centralized job algorithm. + * + * This class handles all the job algorithm. With its store and its assignment (scheduling) algorithm, it + * executes the general algorithm described above, adding some networking, so as to make the global process + * work. It initializes all the functors with the data, then launches the main loop, indicating to workers when + * they will have to work and when they will finish, by sending them a termination message (integer that can be + * customized). As the algorithm is centralized, it is also mandatory to indicate what is the MPI rank of the + * master process, hence the workers will know from who they should receive their commands. + * + * Any of the 3 master functors can launch exception, it will be catched and rethrown as a std::runtime_exception + * to the higher layers. + */ template< class JobData > class Job { public: + /** + * @brief Main constructor for Job. + * + * @param _algo The used assignment (scheduling) algorithm. It has to be initialized, with its maximum + * possible number of workers (some workers referenced in this algorithm shouldn't be busy). See + * AssignmentAlgorithm for more details. + * + * @param _masterRank The MPI rank of the master. + * @param _workerStopCondition Number of the message which will cause the workers to terminate. It could + * be one of the constants defined in eo::mpi::Commands, or any other integer. The user has to be sure + * that a message containing this integer will be sent to each worker on the Commands channel, otherwise + * deadlock will happen. Master sends Finish messages at the end of a simple job, but as a job can + * happen multiples times (multi job), workers don't have to really finish on these messages but on + * another message. This is here where you can configurate it. See also OneShotJob and MultiJob. + */ Job( AssignmentAlgorithm& _algo, int _masterRank, int _workerStopCondition, @@ -242,6 +545,16 @@ namespace eo protected: + /** + * @brief Finally block of the main algorithm + * + * Herb Sutter's trick for having a finally block, in a try/catch section: invoke a class at the + * beginning of the try, its destructor will be called in every cases. + * + * This implements the end of the master algorithm: + * - sends to all available workers that they are free, + * - waits for last responses, handles them and sends termination messages to last workers. + */ struct FinallyBlock { FinallyBlock( @@ -301,6 +614,15 @@ namespace eo bmpi::communicator & comm; }; + /** + * @brief Master part of the job. + * + * Launches the parallelized job algorithm : while there is something to do (! IsFinished ), get a + * worker who will be the assignee ; if no worker is available, wait for a response, handle it and reask + * for an assignee. Then send the command and the task. + * Once there is no more to do (IsFinished), indicate to all available workers that they're free, wait + * for all the responses and send termination messages (see also FinallyBlock). + */ void master( ) { int totalWorkers = assignmentAlgo.availableWorkers(); @@ -346,6 +668,12 @@ namespace eo } } + /** + * @brief Worker part of the algorithm. + * + * The algorithm is more much simpler: wait for an order; if it's termination message, leave. Otherwise, + * prepare to work. + */ void worker( ) { int order; @@ -383,11 +711,18 @@ namespace eo public: + /** + * @brief Launches the job algorithm, according to the role of the host (roles are deduced from the + * master rank indicated in the constructor). + */ void run( ) { ( _isMaster ) ? master( ) : worker( ); } + /** + * @brief Returns true if the current host is the master, false otherwise. + */ bool isMaster( ) { return _isMaster; @@ -397,7 +732,7 @@ namespace eo AssignmentAlgorithm& assignmentAlgo; int masterRank; - const int workerStopCondition; // TODO commentaire: message signifiant l'arrêt pour un worker + const int workerStopCondition; bmpi::communicator& comm; SendTaskFunction & sendTask; @@ -406,13 +741,20 @@ namespace eo IsFinishedFunction & isFinished; bool _isMaster; - }; + /** + * @brief Job that will be launched only once. + * + * As explained in eo::mpi documentation, jobs can happen either a well known amount of times or an unknown + * amount of times. This class implements the general case when the job is launched a well known amount of + * times. The job will be terminated on both sides (master and worker) once the master would have said it. + * + * It uses the message Message::Finish as the termination message. + */ template< class JobData > class OneShotJob : public Job< JobData > { - // TODO commentaire : ce job s'arrête sur message terminate public: OneShotJob( AssignmentAlgorithm& algo, int masterRank, @@ -423,11 +765,22 @@ namespace eo } }; + /** + * @brief Job that will be launched an unknown amount of times, in worker side. + * + * As explained in eo::mpi documentation, jobs can happen either a well known amount of times or an unknown + * amount of times. This class implements the general case when the job is launched an unknown amount of times, for + * instance in a while loop. The master will run many jobs (or the same job many times), but the workers will + * launch it only once. + * + * It uses the message Message::Kill as the termination message. This message can be launched with an EmptyJob, + * launched only by the master. If no Message::Kill is sent on the Channels::Commands, the worker will wait + * forever, which will cause a deadlock. + */ template< class JobData > class MultiJob : public Job< JobData > { public: - // TODO commentaire : ce job s'arrête sur message kill MultiJob ( AssignmentAlgorithm& algo, int masterRank, JobStore & store ) @@ -438,6 +791,5 @@ namespace eo }; } } - # endif // __EO_MPI_H__ From 5e76ba30b076b805bcde5f03c941478d2e5b88a1 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 13 Jul 2012 14:31:29 +0200 Subject: [PATCH 65/76] mDocumentation of eo::mpi classes. --- eo/src/mpi/eoMpi.cpp | 1 - eo/src/mpi/eoMpi.h | 5 + eo/src/mpi/eoMpiAssignmentAlgorithm.h | 172 +++++++++++++++++++++++++- eo/src/mpi/eoMpiNode.h | 40 ++++++ eo/src/mpi/eoParallelApply.h | 149 ++++++++++++++++++++-- eo/src/mpi/eoTerminateJob.h | 56 ++++++++- 6 files changed, 409 insertions(+), 14 deletions(-) diff --git a/eo/src/mpi/eoMpi.cpp b/eo/src/mpi/eoMpi.cpp index 9ffc84bda..ab7543284 100644 --- a/eo/src/mpi/eoMpi.cpp +++ b/eo/src/mpi/eoMpi.cpp @@ -1,6 +1,5 @@ # include "eoMpi.h" -// MpiNode* MpiNodeStore::singleton; namespace eo { namespace mpi diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index d4e0349e3..230399040 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -37,6 +37,7 @@ namespace eo /** * @ingroup Parallel * @defgroup MPI Message Passing Interface parallelization + * @{ */ /** @@ -790,6 +791,10 @@ namespace eo } }; } + + /** + * @} + */ } # endif // __EO_MPI_H__ diff --git a/eo/src/mpi/eoMpiAssignmentAlgorithm.h b/eo/src/mpi/eoMpiAssignmentAlgorithm.h index 85177ea08..d42ec723d 100644 --- a/eo/src/mpi/eoMpiAssignmentAlgorithm.h +++ b/eo/src/mpi/eoMpiAssignmentAlgorithm.h @@ -1,28 +1,118 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __MPI_ASSIGNMENT_ALGORITHM_H__ # define __MPI_ASSIGNMENT_ALGORITHM_H__ -# include +# include // std::vector # include "eoMpiNode.h" namespace eo { namespace mpi { + /** + * @brief Constant indicating to use all the resting available workers, in assignment algorithms constructor + * using an interval. + */ const int REST_OF_THE_WORLD = -1; + /** + * @brief Contains informations on the available workers and allows to find assignees for jobs. + * + * Available workers are workers who aren't processing anything. When they've received an order, workers switch + * from the state "available" to the state "busy", and the master has to wait for their response for considering + * them available again. + * + * @ingroup Parallel + */ struct AssignmentAlgorithm { + /** + * @brief Gets the rank of an available worker, so as to send it a task. + * + * @return The MPI rank of an available worker, or -1 if there is no available worker. + */ virtual int get( ) = 0; + + /** + * @brief Gets the number of total available workers. + * + * Before the first call, it is equal to the total number of present workers, as specified in the + * specific assignment algorithm constructor. It allows the Job class to know when all the responses have + * been received, by comparing this number to the total number of workers. + * + * @return Integer indicating how many workers are available. + */ virtual int availableWorkers( ) = 0; + + /** + * @brief Reinject the worker of indicated rank in the available state. + * + * @param wrkRank The MPI rank of the worker who has finished its job. + */ virtual void confirm( int wrkRank ) = 0; + + /** + * @brief Indicates who are the workers which do nothing. + * + * At the end of the algorithm, the master has to warn all the workers that it's done. All the workers mean, + * the workers which are currently processing data, and the other ones who could be waiting : the idles. + * This function indicates to the master which worker aren't doing anything. + * + * @param A std::vector containing all the MPI ranks of the idles workers. + */ virtual std::vector idles( ) = 0; + + /** + * @brief Reinitializes the assignment algorithm with the right number of runs. + * + * In fact, this is only useful for static assignment algorithm, which has to be reinitialized every time + * it's used, in the case of a Multi Job. It's the user's responsability to call this function. + * + * @todo Not really clean. Find a better way to do it. + */ virtual void reinit( int runs ) = 0; }; + /** + * @brief Assignment (scheduling) algorithm which handles workers in a queue. + * + * With this assignment algorithm, workers are put in a queue and may be called an unlimited number of times. + * Whenever a worker returns, it is added to the queue, and it becomes available for the next call to get(). + * The available workers are all located in the queue at any time, so the number of available workers is + * directly equal to the size of the queue. + * + * This kind of assignment is adapted for tasks whose execution time is stochastic or unknown, but without any + * warranty to be faster than other assignments. + * + * @ingroup Parallel + */ struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm { public: + /** + * @brief Uses all the hosts whose rank is higher to 1, inclusive, as workers. + */ DynamicAssignmentAlgorithm( ) { for(int i = 1; i < Node::comm().size(); ++i) @@ -31,16 +121,33 @@ namespace eo } } + /** + * @brief Uses the unique host with given rank as a worker. + * + * @param unique MPI rank of the unique worker. + */ DynamicAssignmentAlgorithm( int unique ) { availableWrk.push_back( unique ); } + /** + * @brief Uses the workers whose ranks are present in the argument as workers. + * + * @param workers std::vector containing MPI ranks of workers. + */ DynamicAssignmentAlgorithm( const std::vector & workers ) { availableWrk = workers; } + /** + * @brief Uses a range of ranks as workers. + * + * @param first The first worker to be included (inclusive) + * @param last The last worker to be included (inclusive). If last == eo::mpi::REST_OF_THE_WORLD, all + * hosts whose rank is higher than first are taken. + */ DynamicAssignmentAlgorithm( int first, int last ) { if( last == REST_OF_THE_WORLD ) @@ -90,14 +197,43 @@ namespace eo std::vector< int > availableWrk; }; + /** + * @brief Assignment algorithm which gives to each worker a precise number of tasks to do, in a round robin + * fashion. + * + * This scheduling algorithm attributes, at initialization or when calling reinit(), a fixed amount of runs to + * distribute to the workers. The amount of runs is then equally distributed between all workers ; if total + * number of runs is not a direct multiple of workers number, then remainding unaffected runs are distributed to + * workers from the first to the last, in a round-robin fashion. + * + * This scheduling should be used when the amount of runs can be computed or is fixed and when we guess that the + * duration of processing task will be the same for each run. There is no warranty that this algorithm is more + * or less efficient that another one. When having a doubt, use DynamicAssignmentAlgorithm. + * + * @ingroup Parallel + */ struct StaticAssignmentAlgorithm : public AssignmentAlgorithm { public: + /** + * @brief Uses a given precise set of workers. + * + * @param workers std::vector of MPI ranks of workers which will be used. + * @param runs Fixed amount of runs, strictly positive. + */ StaticAssignmentAlgorithm( std::vector& workers, int runs ) { init( workers, runs ); } + /** + * @brief Uses a range of workers. + * + * @param first The first MPI rank of worker to use + * @param last The last MPI rank of worker to use. If it's equal to REST_OF_THE_WORLD, then all the + * workers from the first one are taken as workers. + * @param runs Fixed amount of runs, strictly positive. + */ StaticAssignmentAlgorithm( int first, int last, int runs ) { std::vector workers; @@ -114,6 +250,12 @@ namespace eo init( workers, runs ); } + /** + * @brief Uses all the hosts whose rank is higher than 1 (inclusive) as workers. + * + * @param runs Fixed amount of runs, strictly positive. If it's not set, you'll have to call reinit() + * later. + */ StaticAssignmentAlgorithm( int runs = 0 ) { std::vector workers; @@ -125,6 +267,12 @@ namespace eo init( workers, runs ); } + /** + * @brief Uses an unique host as worker. + * + * @param unique The MPI rank of the host which will be the worker. + * @param runs Fixed amount of runs, strictly positive. + */ StaticAssignmentAlgorithm( int unique, int runs ) { std::vector workers; @@ -133,15 +281,31 @@ namespace eo } private: + /** + * @brief Initializes the static scheduling. + * + * Gives to each worker an equal attribution number, equal to runs / workers.size(), eventually plus one + * if number of workers is not a divisor of runs. + * + * @param workers Vector of hosts' ranks + * @param runs Fixed amount of runs, strictly positive. + */ void init( std::vector & workers, int runs ) { unsigned int nbWorkers = workers.size(); freeWorkers = nbWorkers; - attributions.clear(); - attributions.reserve( nbWorkers ); busy.clear(); busy.resize( nbWorkers, false ); + realRank = workers; + + if( runs <= 0 ) + { + return; + } + + attributions.clear(); + attributions.reserve( nbWorkers ); // Let be the euclidean division of runs by nbWorkers : // runs == q * nbWorkers + r, 0 <= r < nbWorkers @@ -151,8 +315,6 @@ namespace eo // r requests to workers, in ascending order unsigned int diff = runs - (runs / nbWorkers) * nbWorkers; for (unsigned int i = 0; i < diff; ++attributions[i++]); - - realRank = workers; } public: diff --git a/eo/src/mpi/eoMpiNode.h b/eo/src/mpi/eoMpiNode.h index 9f1ea7b53..e836637f5 100644 --- a/eo/src/mpi/eoMpiNode.h +++ b/eo/src/mpi/eoMpiNode.h @@ -1,3 +1,24 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __MPI_NODE_H__ # define __MPI_NODE_H__ @@ -8,15 +29,34 @@ namespace eo { namespace mpi { + /** + * @brief Global object used to reach boost::mpi::communicator everywhere. + * + * boost::mpi::communicator is the main object used to send and receive messages between the different hosts of + * a MPI algorithm. + * + * @ingroup Parallel + */ class Node { public: + /** + * @brief Initializes the MPI environment with argc and argv. + * + * Should be called at the beginning of every parallel program. + * + * @param argc Main's argc + * @param argv Main's argv + */ static void init( int argc, char** argv ) { static bmpi::environment env( argc, argv ); } + /** + * @brief Returns the global boost::mpi::communicator + */ static bmpi::communicator& comm() { return _comm; diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index d0435cead..6f5120f81 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -1,28 +1,100 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EO_PARALLEL_APPLY_H__ # define __EO_PARALLEL_APPLY_H__ # include "eoMpi.h" -# include -# include +# include // eoUF +# include // std::vector population + +/** + * @file eoParallelApply.h + * + * @brief Applies a functor with single parameter to elements of a table, in a parallel fashion. + * + * This file contains all the required classes to do a parallel apply of a table, in a parallel fashion. This can be + * very useful when applying the function can be made without any dependances within the data. In EO, it occurs in + * particular during the evaluation: the number of individuals to evaluate can be really high, and the evaluation of one + * individual is independant from the evaluation of other individuals. + * + * Elements in the table are directly replaced, as the table is given by reference. No new table is made during the + * process. + * + * User can tune this job, indicating how many elements of the table should be sent and evaluated by a worker, at a + * time: this is called the "packet size", as individuals are groupped into a packet of individuals which are sent to + * the worker before evaluation. The problem of choosing the optimal packet size is beyond the purposes of this documentation + * and deserves a theoritical study. + * + * This job is the parallel equivalent to the function apply, defined in apply.h. It just applies the function to + * every element of a table. In Python or Javascript, it's the equivalent of the function Map. + */ namespace eo { namespace mpi { + /** + * @brief Structure used to save assignment to a worker, i.e which slice of the table it has to process. + * + * This slice is defined by the index of the first evaluated argument and the number of processed elements. + */ struct ParallelApplyAssignment { int index; int size; }; + /** + * @brief Data useful for a parallel apply (map). + * + * A parallel apply needs at least the functor to apply to every element of the table, and the table itself, + * whereas it can be set later with the function init(). Master rank is also needed, to send it informations and + * receive informations from it, inside the functors (the job knows these values, but the functors don't). The + * size of a packet can be tuned here. + * + * Internal attributes contain: + * - (useful for master) the index of the next element to be evaluated. + * - (useful for master) a map containing links between MPI ranks and slices of the table which the worker with + * this rank has evaluated. Without this map, when receiving results from a worker, the master couldn't be + * able to replace the right elements in the table. + * + * @ingroup Parallel + */ template struct ParallelApplyData { + /** + * @brief Ctor for Parallel apply (map) data. + * + * @param _proc The functor to apply on each element in the table + * @param _masterRank The MPI rank of the master + * @param _packetSize The number of elements on which the function will be applied by the worker, at a time. + * @param _pop The table to apply. If this value is NULL, user will have to call init() before launching the + * job. + */ ParallelApplyData( eoUF & _proc, int _masterRank, - // long _maxTime = 0, int _packetSize, std::vector * _pop = 0 ) : @@ -39,6 +111,9 @@ namespace eo } } + /** + * @brief Reinitializes the data for a new table to evaluate. + */ void init( std::vector& _pop ) { index = 0; @@ -52,11 +127,12 @@ namespace eo return *_data; } + // All elements are public since functors will often use them. std::vector * _data; eoUF & func; int index; int size; - std::map< int /* worker rank */, ParallelApplyAssignment /* min indexes in vector */> assignedTasks; + std::map< int /* worker rank */, ParallelApplyAssignment /* last assignment */> assignedTasks; int packetSize; std::vector tempArray; @@ -64,6 +140,15 @@ namespace eo bmpi::communicator& comm; }; + /** + * @brief Send task functor implementation for the parallel apply (map) job. + * + * Master side: Sends a slice of the table to evaluate to the worker. + * + * Implementation details: + * Finds the next slice of data to send to the worker, sends first the size and then the data, and memorizes + * that this slice has been distributed to the worker, then updates the next position of element to evaluate. + */ template< class EOT > class SendTaskParallelApply : public SendTaskFunction< ParallelApplyData > { @@ -100,6 +185,11 @@ namespace eo } }; + /** + * @brief Handle response functor implementation for the parallel apply (map) job. + * + * Master side: Replaces the slice of data attributed to the worker in the table. + */ template< class EOT > class HandleResponseParallelApply : public HandleResponseFunction< ParallelApplyData > { @@ -117,6 +207,14 @@ namespace eo } }; + /** + * @brief Process task functor implementation for the parallel apply (map) job. + * + * Worker side: apply the function to the given slice of data. + * + * Implementation details: retrieves the number of elements to evaluate, retrieves them, applies the function + * and then returns the results. + */ template< class EOT > class ProcessTaskParallelApply : public ProcessTaskFunction< ParallelApplyData > { @@ -145,6 +243,12 @@ namespace eo } }; + /** + * @brief Is finished functor implementation for the parallel apply (map) job. + * + * Master side: returns true if and only if the whole table has been evaluated. The job is also terminated only + * when the whole table has been evaluated. + */ template< class EOT > class IsFinishedParallelApply : public IsFinishedFunction< ParallelApplyData > { @@ -162,6 +266,14 @@ namespace eo } }; + /** + * @brief Store containing all the datas and the functors for the parallel apply (map) job. + * + * User can tune functors when constructing the object. For each functor which is not given, a default one is + * generated. + * + * @ingroup Parallel + */ template< class EOT > struct ParallelApplyStore : public JobStore< ParallelApplyData > { @@ -170,6 +282,17 @@ namespace eo using JobStore< ParallelApplyData >::_ptf; using JobStore< ParallelApplyData >::_iff; + /** + * @brief Main constructor for the parallel apply (map) job. + * + * @param _proc The procedure to apply to each element of the table. + * @param _masterRank The rank of the master process. + * @param _packetSize The number of elements of the table to be evaluated at a time, by the worker. + * @param stpa Pointer to Send Task parallel apply functor descendant. If null, a default one is used. + * @param hrpa Pointer to Handle Response parallel apply functor descendant. If null, a default one is used. + * @param ptpa Pointer to Process Task parallel apply functor descendant. If null, a default one is used. + * @param ifpa Pointer to Is Finished parallel apply functor descendant. If null, a default one is used. + */ ParallelApplyStore( eoUF & _proc, int _masterRank, @@ -210,12 +333,17 @@ namespace eo ParallelApplyData* data() { return &_data; } + /** + * @brief Reinits the store with a new table to evaluate. + * + * @param _pop The table of elements to be evaluated. + */ void data( std::vector& _pop ) { _data.init( _pop ); } - virtual ~ParallelApplyStore() + virtual ~ParallelApplyStore() // for inheritance purposes only { } @@ -223,8 +351,15 @@ namespace eo ParallelApplyData _data; }; - // TODO commentaire : impossible de faire un typedef sur un template sans passer - // par un traits => complique la tâche de l'utilisateur pour rien. + /** + * @brief Parallel apply job. Present for convenience only. + * + * A typedef wouldn't have been working, as typedef on templates don't work in C++. Traits would be a + * disgraceful overload for the user. + * + * @ingroup Parallel + * @see eoParallelApply.h + */ template< typename EOT > class ParallelApply : public MultiJob< ParallelApplyData > { diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h index ed45ca052..ccbd3521e 100644 --- a/eo/src/mpi/eoTerminateJob.h +++ b/eo/src/mpi/eoTerminateJob.h @@ -1,3 +1,24 @@ +/* +(c) Thales group, 2012 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Contact: http://eodev.sourceforge.net + +Authors: + Benjamin Bouvier +*/ # ifndef __EO_TERMINATE_H__ # define __EO_TERMINATE_H__ @@ -7,6 +28,14 @@ namespace eo { namespace mpi { + /** + * @ingroup Parallel + * @{ + */ + + /** + * @brief Send task functor which does nothing. + */ struct DummySendTaskFunction : public SendTaskFunction { void operator()( int _ ) @@ -15,6 +44,9 @@ namespace eo } }; + /** + * @brief Handle response functor which does nothing. + */ struct DummyHandleResponseFunction : public HandleResponseFunction { void operator()( int _ ) @@ -23,6 +55,9 @@ namespace eo } }; + /** + * @brief Process task functor which does nothing. + */ struct DummyProcessTaskFunction : public ProcessTaskFunction { void operator()() @@ -31,6 +66,9 @@ namespace eo } }; + /** + * @brief Is finished functor which returns true everytime. + */ struct DummyIsFinishedFunction : public IsFinishedFunction { bool operator()() @@ -39,6 +77,9 @@ namespace eo } }; + /** + * @brief Job store containing all dummy functors and containing no data. + */ struct DummyJobStore : public JobStore { using JobStore::_stf; @@ -61,11 +102,20 @@ namespace eo void* data() { return 0; } }; + /** + * @brief Job to run after a Multi Job, so as to indicate that every workers should terminate. + */ struct EmptyJob : public OneShotJob { + /** + * @brief Main EmptyJob ctor + * + * @param algo Assignment (scheduling) algorithm used. + * @param masterRank The rank of the master process. + */ EmptyJob( AssignmentAlgorithm& algo, int masterRank ) : OneShotJob( algo, masterRank, *(new DummyJobStore) ) - // FIXME memory leak => will be corrected by using const correctness + // FIXME memory leak, meaningless but present { // empty } @@ -79,6 +129,10 @@ namespace eo } } }; + + /** + * @} + */ } } From f9a0084f959033ab896fe2ad19ca42a7adc7079a Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 13 Jul 2012 14:45:56 +0200 Subject: [PATCH 66/76] Taking in account Doxygen remarks, fixed some documentation errors. --- eo/src/mpi/eoMpi.h | 3 +++ eo/src/mpi/eoMpiAssignmentAlgorithm.h | 2 +- eo/src/serial/Object.h | 2 +- eo/src/serial/Parser.h | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 230399040..a040e3623 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -514,12 +514,15 @@ namespace eo * AssignmentAlgorithm for more details. * * @param _masterRank The MPI rank of the master. + * * @param _workerStopCondition Number of the message which will cause the workers to terminate. It could * be one of the constants defined in eo::mpi::Commands, or any other integer. The user has to be sure * that a message containing this integer will be sent to each worker on the Commands channel, otherwise * deadlock will happen. Master sends Finish messages at the end of a simple job, but as a job can * happen multiples times (multi job), workers don't have to really finish on these messages but on * another message. This is here where you can configurate it. See also OneShotJob and MultiJob. + * + * @param store The JobStore containing functors and data for this job. */ Job( AssignmentAlgorithm& _algo, int _masterRank, diff --git a/eo/src/mpi/eoMpiAssignmentAlgorithm.h b/eo/src/mpi/eoMpiAssignmentAlgorithm.h index d42ec723d..e052d8ab3 100644 --- a/eo/src/mpi/eoMpiAssignmentAlgorithm.h +++ b/eo/src/mpi/eoMpiAssignmentAlgorithm.h @@ -78,7 +78,7 @@ namespace eo * the workers which are currently processing data, and the other ones who could be waiting : the idles. * This function indicates to the master which worker aren't doing anything. * - * @param A std::vector containing all the MPI ranks of the idles workers. + * @return A std::vector containing all the MPI ranks of the idles workers. */ virtual std::vector idles( ) = 0; diff --git a/eo/src/serial/Object.h b/eo/src/serial/Object.h index 20fbdbf7e..9424f1c4c 100644 --- a/eo/src/serial/Object.h +++ b/eo/src/serial/Object.h @@ -46,7 +46,7 @@ namespace eoserial /** * @brief Adds a pair into the JSON object. * @param key The key associated with the eoserial object - * @param eoserial The JSON object as created with framework. + * @param json The JSON object as created with framework. */ void add( const std::string& key, eoserial::Entity* json ) { diff --git a/eo/src/serial/Parser.h b/eo/src/serial/Parser.h index 5bcdc5979..20f6a1bb2 100644 --- a/eo/src/serial/Parser.h +++ b/eo/src/serial/Parser.h @@ -84,7 +84,7 @@ class Parser * * @param str The string we're parsing. * @param pos The index of the current position in the string. - * @param eoserial The current JSON object for which we're adding a key-value pair. + * @param json The current JSON object for which we're adding a key-value pair. */ static void parseLeft(const std::string & str, size_t & pos, eoserial::Object* json); From d54e88dd165feaef3858585b03740f4ba0249c73 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 13 Jul 2012 15:07:42 +0200 Subject: [PATCH 67/76] eoMultiParallelApply is no more used, replaced with eo::mpi::MultiJob instead. --- eo/src/mpi/eoMultiParallelApply.h | 57 ------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 eo/src/mpi/eoMultiParallelApply.h diff --git a/eo/src/mpi/eoMultiParallelApply.h b/eo/src/mpi/eoMultiParallelApply.h deleted file mode 100644 index f4fe47b51..000000000 --- a/eo/src/mpi/eoMultiParallelApply.h +++ /dev/null @@ -1,57 +0,0 @@ -# ifndef __EO_MULTI_PARALLEL_APPLY_H__ -# define __EO_MULTI_PARALLEL_APPLY_H__ - -# include "eoParallelApply.h" - -namespace eo -{ - namespace mpi - { - template< class EOT > - class ProcessTaskParallelEval : public ProcessTaskParallelApply - { - public: - - using ProcessTaskParallelApply::_wrapped; - using ProcessTaskParallelApply::d; - - void operator()() - { - int order = Message::Continue; - while( order != Message::Finish ) - { - _wrapped->operator()(); - d->comm.recv( d->masterRank, Channel::Commands, order ); - } - } - - ~ProcessTaskParallelEval() - { - } - }; - - template< class EOT > - struct ParallelEvalStore : public ParallelApplyStore< EOT > - { - using ParallelApplyStore::wrapProcessTask; - - ParallelEvalStore( - eoUF & _proc, - int _masterRank, - int _packetSize = 1 - ) : - ParallelApplyStore< EOT >( _proc, *( new std::vector ), _masterRank, _packetSize ) - // FIXME memory leak because of vector ==> use const correctness - { - wrapProcessTask( new ProcessTaskParallelEval ); - } - - void data( std::vector& _pop ) - { - ParallelApplyStore::_data.init( _pop ); - } - }; - } -} -# endif // __EO_PARALLEL_APPLY_H__ - From ad74755d8e1e81676acf43a7f764eab0efce68b2 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 13 Jul 2012 15:50:51 +0200 Subject: [PATCH 68/76] Precised group MPI instead of group Parallel for Mpi classes in documentation. --- eo/src/mpi/eoMpi.h | 24 ++++++++++++++++++++++++ eo/src/mpi/eoMpiAssignmentAlgorithm.h | 8 +++++--- eo/src/mpi/eoMpiNode.h | 2 +- eo/src/mpi/eoParallelApply.h | 6 +++--- eo/src/mpi/eoTerminateJob.h | 2 +- eo/src/serial/Entity.h | 3 ++- 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index a040e3623..0293c43d8 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -137,6 +137,8 @@ namespace eo * These tags are used for framework communication and fits "channels", so as to differentiate when we're * sending an order to a worker (Commands) or data (Messages). They are not reserved by the framework and can be * used by the user, but he is not bound to. + * + * @ingroup MPI */ namespace Channel { @@ -149,6 +151,8 @@ namespace eo * * These orders are sent by the master to the workers, to indicate to them if they should receive another task * to do (Continue), if an one shot job is done (Finish) or if a multi job is done (Kill). + * + * @ingroup MPI */ namespace Message { @@ -159,6 +163,8 @@ namespace eo /** * @brief If the job only has one master, the user can use this constant, so as not to worry with integer ids. + * + * @ingroup MPI */ const int DEFAULT_MASTER = 0; @@ -204,6 +210,8 @@ namespace eo * }; * @endcode * This makes the code easier to write for the user. + * + * @ingroup MPI */ template< typename JobData, typename Wrapped > struct SharedDataFunction @@ -277,6 +285,8 @@ namespace eo * * This is a functor implementing void operator()(int), and also a shared data function, containing wrapper on its * own type. + * + * @ingroup MPI */ template< typename JobData > struct SendTaskFunction : public eoUF, public SharedDataFunction< JobData, SendTaskFunction > @@ -300,6 +310,8 @@ namespace eo * * This is a functor implementing void operator()(int), and also a shared data function, containing wrapper on * its own type. + * + * @ingroup MPI */ template< typename JobData > struct HandleResponseFunction : public eoUF, public SharedDataFunction< JobData, HandleResponseFunction > @@ -325,6 +337,8 @@ namespace eo * * This is a functor implementing void operator()(), and also a shared data function, containing wrapper on its * own type. + * + * @ingroup MPI */ template< typename JobData > struct ProcessTaskFunction : public eoF, public SharedDataFunction< JobData, ProcessTaskFunction > @@ -348,6 +362,8 @@ namespace eo * * This is a functor implementing bool operator()(), and also a shared function, containing wrapper on its own * type. + * + * @ingroup MPI */ template< typename JobData > struct IsFinishedFunction : public eoF, public SharedDataFunction< JobData, IsFinishedFunction > @@ -377,6 +393,8 @@ namespace eo * The user has to implement data(), which is the getter for retrieving JobData. We don't have any idea of who * owns the data, moreover it is impossible to initialize it in this generic JobStore, as we don't know its * form. As a matter of fact, the user has to define this in the JobStore subclasses. + * + * @ingroup MPI */ template< typename JobData > struct JobStore @@ -501,6 +519,8 @@ namespace eo * * Any of the 3 master functors can launch exception, it will be catched and rethrown as a std::runtime_exception * to the higher layers. + * + * @ingroup MPI */ template< class JobData > class Job @@ -755,6 +775,8 @@ namespace eo * times. The job will be terminated on both sides (master and worker) once the master would have said it. * * It uses the message Message::Finish as the termination message. + * + * @ingroup MPI */ template< class JobData > class OneShotJob : public Job< JobData > @@ -780,6 +802,8 @@ namespace eo * It uses the message Message::Kill as the termination message. This message can be launched with an EmptyJob, * launched only by the master. If no Message::Kill is sent on the Channels::Commands, the worker will wait * forever, which will cause a deadlock. + * + * @ingroup MPI */ template< class JobData > class MultiJob : public Job< JobData > diff --git a/eo/src/mpi/eoMpiAssignmentAlgorithm.h b/eo/src/mpi/eoMpiAssignmentAlgorithm.h index e052d8ab3..07f674157 100644 --- a/eo/src/mpi/eoMpiAssignmentAlgorithm.h +++ b/eo/src/mpi/eoMpiAssignmentAlgorithm.h @@ -32,6 +32,8 @@ namespace eo /** * @brief Constant indicating to use all the resting available workers, in assignment algorithms constructor * using an interval. + * + * @ingroup MPI */ const int REST_OF_THE_WORLD = -1; @@ -42,7 +44,7 @@ namespace eo * from the state "available" to the state "busy", and the master has to wait for their response for considering * them available again. * - * @ingroup Parallel + * @ingroup MPI */ struct AssignmentAlgorithm { @@ -104,7 +106,7 @@ namespace eo * This kind of assignment is adapted for tasks whose execution time is stochastic or unknown, but without any * warranty to be faster than other assignments. * - * @ingroup Parallel + * @ingroup MPI */ struct DynamicAssignmentAlgorithm : public AssignmentAlgorithm { @@ -210,7 +212,7 @@ namespace eo * duration of processing task will be the same for each run. There is no warranty that this algorithm is more * or less efficient that another one. When having a doubt, use DynamicAssignmentAlgorithm. * - * @ingroup Parallel + * @ingroup MPI */ struct StaticAssignmentAlgorithm : public AssignmentAlgorithm { diff --git a/eo/src/mpi/eoMpiNode.h b/eo/src/mpi/eoMpiNode.h index e836637f5..27c03312c 100644 --- a/eo/src/mpi/eoMpiNode.h +++ b/eo/src/mpi/eoMpiNode.h @@ -35,7 +35,7 @@ namespace eo * boost::mpi::communicator is the main object used to send and receive messages between the different hosts of * a MPI algorithm. * - * @ingroup Parallel + * @ingroup MPI */ class Node { diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 6f5120f81..98f113f44 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -78,7 +78,7 @@ namespace eo * this rank has evaluated. Without this map, when receiving results from a worker, the master couldn't be * able to replace the right elements in the table. * - * @ingroup Parallel + * @ingroup MPI */ template struct ParallelApplyData @@ -272,7 +272,7 @@ namespace eo * User can tune functors when constructing the object. For each functor which is not given, a default one is * generated. * - * @ingroup Parallel + * @ingroup MPI */ template< class EOT > struct ParallelApplyStore : public JobStore< ParallelApplyData > @@ -357,7 +357,7 @@ namespace eo * A typedef wouldn't have been working, as typedef on templates don't work in C++. Traits would be a * disgraceful overload for the user. * - * @ingroup Parallel + * @ingroup MPI * @see eoParallelApply.h */ template< typename EOT > diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h index ccbd3521e..e0b4a5bd3 100644 --- a/eo/src/mpi/eoTerminateJob.h +++ b/eo/src/mpi/eoTerminateJob.h @@ -29,7 +29,7 @@ namespace eo namespace mpi { /** - * @ingroup Parallel + * @ingroup MPI * @{ */ diff --git a/eo/src/serial/Entity.h b/eo/src/serial/Entity.h index 6fec34858..50155e13a 100644 --- a/eo/src/serial/Entity.h +++ b/eo/src/serial/Entity.h @@ -24,6 +24,7 @@ Authors: # include // ostream + /** * @brief Contains all the necessary entities to serialize eo objects into JSON objects. * @@ -36,7 +37,7 @@ Authors: * * @ingroup Utilities * @defgroup Serialization Serialization helpers - */ +**/ namespace eoserial { From d2816d530a58f731aab6192db958df46a6d09d8a Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 13 Jul 2012 16:47:28 +0200 Subject: [PATCH 69/76] Included MPI tests into compilation, if WITH_MPI is defined. --- eo/CMakeLists.txt | 5 ++ eo/test/CMakeLists.txt | 15 ++++- eo/test/mpi/CMakeLists.txt | 55 +++++++++++++++++++ eo/test/mpi/{eval.cpp => t-mpi-eval.cpp} | 0 ...tipleRoles.cpp => t-mpi-multipleRoles.cpp} | 0 ...allelApply.cpp => t-mpi-parallelApply.cpp} | 0 .../mpi/{wrapper.cpp => t-mpi-wrapper.cpp} | 0 7 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 eo/test/mpi/CMakeLists.txt rename eo/test/mpi/{eval.cpp => t-mpi-eval.cpp} (100%) rename eo/test/mpi/{multipleRoles.cpp => t-mpi-multipleRoles.cpp} (100%) rename eo/test/mpi/{parallelApply.cpp => t-mpi-parallelApply.cpp} (100%) rename eo/test/mpi/{wrapper.cpp => t-mpi-wrapper.cpp} (100%) diff --git a/eo/CMakeLists.txt b/eo/CMakeLists.txt index 78cab0a56..df128f134 100644 --- a/eo/CMakeLists.txt +++ b/eo/CMakeLists.txt @@ -120,6 +120,11 @@ ENDIF (ENABLE_CMAKE_TESTING) ADD_SUBDIRECTORY(doc) ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(test) + +IF(WITH_MPI) + ADD_SUBDIRECTORY(test/mpi) +ENDIF() + ADD_SUBDIRECTORY(tutorial) ###################################################################################### diff --git a/eo/test/CMakeLists.txt b/eo/test/CMakeLists.txt index 4e68b02ad..8bf8b500e 100644 --- a/eo/test/CMakeLists.txt +++ b/eo/test/CMakeLists.txt @@ -14,12 +14,23 @@ INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/contrib) INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/contrib/MGE) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +IF(WITH_MPI) + INCLUDE_DIRECTORIES(${BOOST_DIR}/include) + INCLUDE_DIRECTORIES(${MPI_DIR}/include) +ENDIF() + ###################################################################################### ### 2) Specify where CMake can find the libraries ###################################################################################### LINK_DIRECTORIES(${EO_BINARY_DIR}/lib) +IF(WITH_MPI) + LINK_DIRECTORIES(${BOOST_DIR}/lib) + LINK_DIRECTORIES(${MPI_DIR}/lib) + SET(CMAKE_CXX_COMPILER "${MPI_DIR}/bin/mpicxx") +ENDIF() + ###################################################################################### ### 3) Define your targets and link the librairies ###################################################################################### @@ -65,8 +76,8 @@ SET (TEST_LIST t-eoExtendedVelocity t-eoLogger t-eoIQRStat - t-eoParallel - t-openmp + #t-eoParallel + #t-openmp #t-eoDualFitness t-eoParser ) diff --git a/eo/test/mpi/CMakeLists.txt b/eo/test/mpi/CMakeLists.txt new file mode 100644 index 000000000..37b1227ff --- /dev/null +++ b/eo/test/mpi/CMakeLists.txt @@ -0,0 +1,55 @@ +############################################################################### +## +## CMakeLists file for eo/test/mpi +## +############################################################################### + +###################################################################################### +### 1) Include the sources +###################################################################################### + +MESSAGE("EO SOURCE DIR: ${EO_SOURCE_DIR}") +MESSAGE("OMPI: ${MPI_DIR}") +MESSAGE("BOOST: ${BOOST_DIR}") + +INCLUDE_DIRECTORIES(${MPI_DIR}/include) +INCLUDE_DIRECTORIES(${BOOST_DIR}/include) +INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +###################################################################################### +### 2) Specify where CMake can find the libraries +###################################################################################### + +LINK_DIRECTORIES(${EO_BINARY_DIR}/lib) +LINK_DIRECTORIES(${MPI_DIR}/lib) +LINK_DIRECTORIES(${BOOST_DIR}/lib) + +###################################################################################### +### 3) Define your targets and link the librairies +###################################################################################### + +SET (TEST_LIST + t-mpi-parallelApply + t-mpi-wrapper + t-mpi-multipleRoles + t-mpi-eval + ) + +FOREACH (test ${TEST_LIST}) + SET ("T_${test}_SOURCES" "${test}.cpp") +ENDFOREACH (test) + +SET(CMAKE_CXX_COMPILER "${MPI_DIR}/bin/mpicxx") +ADD_DEFINITIONS(-DWITH_MPI) + +IF(ENABLE_CMAKE_TESTING) + FOREACH (test ${TEST_LIST}) + ADD_EXECUTABLE(${test} ${T_${test}_SOURCES}) + ADD_TEST(${test} ${test}) + TARGET_LINK_LIBRARIES(${test} boost_mpi boost_serialization eoutils eompi eoserial eo) + INSTALL(TARGETS ${test} RUNTIME DESTINATION share/eo/test COMPONENT test) + ENDFOREACH (test) +ENDIF() + +###################################################################################### diff --git a/eo/test/mpi/eval.cpp b/eo/test/mpi/t-mpi-eval.cpp similarity index 100% rename from eo/test/mpi/eval.cpp rename to eo/test/mpi/t-mpi-eval.cpp diff --git a/eo/test/mpi/multipleRoles.cpp b/eo/test/mpi/t-mpi-multipleRoles.cpp similarity index 100% rename from eo/test/mpi/multipleRoles.cpp rename to eo/test/mpi/t-mpi-multipleRoles.cpp diff --git a/eo/test/mpi/parallelApply.cpp b/eo/test/mpi/t-mpi-parallelApply.cpp similarity index 100% rename from eo/test/mpi/parallelApply.cpp rename to eo/test/mpi/t-mpi-parallelApply.cpp diff --git a/eo/test/mpi/wrapper.cpp b/eo/test/mpi/t-mpi-wrapper.cpp similarity index 100% rename from eo/test/mpi/wrapper.cpp rename to eo/test/mpi/t-mpi-wrapper.cpp From b92f17fce55b7c602b796b7d796f0eccc4494648 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 16 Jul 2012 15:07:48 +0200 Subject: [PATCH 70/76] Documentation of MPI examples. --- eo/test/mpi/t-mpi-eval.cpp | 51 ++++++++++++++++--- eo/test/mpi/t-mpi-multipleRoles.cpp | 66 ++++++++++++++++++++++-- eo/test/mpi/t-mpi-parallelApply.cpp | 78 ++++++++++++++++++++++++++--- eo/test/mpi/t-mpi-wrapper.cpp | 45 ++++++++++++++++- 4 files changed, 219 insertions(+), 21 deletions(-) diff --git a/eo/test/mpi/t-mpi-eval.cpp b/eo/test/mpi/t-mpi-eval.cpp index 72fca7558..e5678104c 100644 --- a/eo/test/mpi/t-mpi-eval.cpp +++ b/eo/test/mpi/t-mpi-eval.cpp @@ -1,12 +1,35 @@ -//----------------------------------------------------------------------------- -// t-eoMpiParallel.cpp +/* +(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 +*/ + +/* + * 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 #include #include -// #include #include "../real_value.h" #include @@ -80,6 +103,16 @@ class eoRealSerializable : public eoReal< eoMinimizingFitness >, public eoserial 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 { CatBestAnswers() @@ -87,14 +120,15 @@ struct CatBestAnswers : public eo::mpi::HandleResponseParallelApply best.fitness( 1000000000. ); } - using eo::mpi::HandleResponseParallelApply::_wrapped; - using eo::mpi::HandleResponseParallelApply::d; + // if EOT were a template, we would have to do: (thank you C++ :) + // using eo::mpi::HandleResponseParallelApply::_wrapped; + // using eo::mpi::HandleResponseParallelApply::d; void operator()(int wrkRank) { int index = d->assignedTasks[wrkRank].index; int size = d->assignedTasks[wrkRank].size; - (*_wrapped)( wrkRank ); + (*_wrapped)( wrkRank ); // call to the wrapped function HERE for(int i = index; i < index+size; ++i) { if( best.fitness() < d->data()[ i ].fitness() ) @@ -134,6 +168,9 @@ int main(int ac, char** av) 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 ) @@ -157,7 +194,7 @@ int main(int ac, char** av) eo::log << eo::quiet << "DONE!" << std::endl; } else { - eoPop< EOT > pop( popSize, init ); + 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 ); } diff --git a/eo/test/mpi/t-mpi-multipleRoles.cpp b/eo/test/mpi/t-mpi-multipleRoles.cpp index 232a61105..36d588b82 100644 --- a/eo/test/mpi/t-mpi-multipleRoles.cpp +++ b/eo/test/mpi/t-mpi-multipleRoles.cpp @@ -1,3 +1,42 @@ +/* +(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 +*/ + +/* + * 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 # include # include @@ -11,11 +50,7 @@ using namespace std; using namespace eo::mpi; -// Role map -// 0 : general master -// 1, 2 : worker of general job, master of subjob -// 3 to 7 : workers of subjob - +// The real job to execute, for the subworkers: add one to each element of a table. struct SubWork: public eoUF< int&, void > { void operator() ( int & x ) @@ -25,14 +60,20 @@ struct SubWork: public eoUF< int&, void > } }; +// 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& 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 workers; workers.push_back( rank + 2 ); workers.push_back( rank + 4 ); DynamicAssignmentAlgorithm algo( workers ); SubWork sw; + // Launch the job! ParallelApplyStore store( sw, rank ); store.data( v ); ParallelApply job( algo, rank, store ); @@ -40,6 +81,10 @@ void subtask( vector& v, int rank ) EmptyJob stop( algo, rank ); } +// Functor applied by submasters. Wait for the subworkers responses and then add some random processing (here, multiply +// each result by two). +// Note that this work receives a vector of integers as an entry, while subworkers task's operator receives a simple +// integer. struct Work: public eoUF< vector&, void > { void operator() ( vector& v ) @@ -57,6 +102,10 @@ int main(int argc, char** argv) { // eo::log << eo::setlevel( eo::debug ); Node::init( argc, argv ); + if( Node::comm().size() != 7 ) { + throw std::runtime_error("World size should be 7."); + } + vector v; v.push_back(1); @@ -65,12 +114,18 @@ int main(int argc, char** argv) v.push_back(7); v.push_back(42); + // As submasters' operator receives a vector as an input, and ParallelApply takes a vector of + // operator's input as an input, we have to deal with a vector of vector of integers for the master task. vector< vector > 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: @@ -95,6 +150,7 @@ int main(int argc, char** argv) } break; + // Other nodes are implicated into the subwork task. default: { // all the other nodes are sub workers diff --git a/eo/test/mpi/t-mpi-parallelApply.cpp b/eo/test/mpi/t-mpi-parallelApply.cpp index 26b0b30ea..7cefa203a 100644 --- a/eo/test/mpi/t-mpi-parallelApply.cpp +++ b/eo/test/mpi/t-mpi-parallelApply.cpp @@ -1,3 +1,41 @@ +/* +(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 +*/ + +/* + * This file shows an example of use of parallel apply, in the following context: each element of a table is + * incremented... in a parallel fashion. While this operation is very easy to perform even on a single host, it's just + * an example for parallel apply use. + * + * Besides, this is also a test for assignment (scheduling) algorithms, in different cases. The test succeeds if and + * only if the program terminates without any segfault ; otherwise, there could be a deadlock which prevents the end or + * a segfault at any time. + * + * One important thing is to instanciate an EmptyJob after having launched a ParallelApplyJob, so as the workers to be + * aware that the job is done (as it's a MultiJob). + * + * This test needs at least 3 processes to be launched. Under this size, it will directly throw an exception, at the + * beginning; + */ + # include # include # include @@ -9,6 +47,9 @@ using namespace std; using namespace eo::mpi; +/* + * The function to be called on each element of the table: just increment the value. + */ struct plusOne : public eoUF< int&, void > { void operator() ( int & x ) @@ -17,22 +58,26 @@ struct plusOne : public eoUF< int&, void > } }; +/* + * Internal structure representating a test. + */ struct Test { - AssignmentAlgorithm * assign; - string description; - int requiredNodesNumber; // nb : chosen nodes ranks must be sequential + 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 }; -// These tests require at least 3 processes to be launched. int main(int argc, char** argv) { - // eo::log << eo::setlevel( eo::debug ); + // 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 v; for( int i = 0; i < 1000; ++i ) @@ -40,18 +85,27 @@ int main(int argc, char** argv) 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 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."; + tIntervalStatic.description = "Correct static assignment with interval."; // workers have ranks from 1 to size - 1 tIntervalStatic.requiredNodesNumber = ALL; tests.push_back( tIntervalStatic ); @@ -111,23 +165,32 @@ int main(int argc, char** argv) for( unsigned int i = 0; i < tests.size(); ++i ) { + // Instanciates a store with the functor, the master rank and size of packet (see ParallelApplyStore doc). ParallelApplyStore< int > store( plusOneInstance, eo::mpi::DEFAULT_MASTER, 3 ); + // Updates the contained data store.data( v ); + // Creates the job with the assignment algorithm, the master rank and the store ParallelApply< int > job( *(tests[i].assign), eo::mpi::DEFAULT_MASTER, store ); + // Only master writes information if( job.isMaster() ) { cout << "Test : " << tests[i].description << endl; } + // Workers whose rank is inferior to required nodes number have to run the test, the other haven't anything to + // do. if( Node::comm().rank() < tests[i].requiredNodesNumber ) { job.run(); } + // After the job run, the master checks the result with offset and originalV if( job.isMaster() ) { - EmptyJob stop( *(tests[i].assign), eo::mpi::DEFAULT_MASTER ); + // This job has to be instanciated, not launched, so as to tell the workers they're done with the parallel + // job. + EmptyJob stop( *(tests[i].assign), eo::mpi::DEFAULT_MASTER ); ++offset; for(int i = 0; i < v.size(); ++i) { @@ -141,6 +204,7 @@ int main(int argc, char** argv) cout << endl; } + // MPI synchronization (all the processes wait to be here). Node::comm().barrier(); delete tests[i].assign; diff --git a/eo/test/mpi/t-mpi-wrapper.cpp b/eo/test/mpi/t-mpi-wrapper.cpp index 527c7cc6d..dbe70261d 100644 --- a/eo/test/mpi/t-mpi-wrapper.cpp +++ b/eo/test/mpi/t-mpi-wrapper.cpp @@ -1,3 +1,34 @@ +/* +(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 +*/ + +/* + * 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 # include # include @@ -9,6 +40,7 @@ using namespace std; using namespace eo::mpi; +// Job functor. struct plusOne : public eoUF< int&, void > { void operator() ( int & x ) @@ -17,6 +49,10 @@ struct plusOne : public eoUF< int&, void > } }; +/* + * 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 { @@ -39,7 +75,6 @@ struct ShowWrappedResult : public IsFinishedParallelApply int times; }; -// These tests require at least 3 processes to be launched. int main(int argc, char** argv) { // eo::log << eo::setlevel( eo::debug ); @@ -63,7 +98,11 @@ int main(int argc, char** argv) ParallelApplyStore< int > store( plusOneInstance, eo::mpi::DEFAULT_MASTER, 1 ); store.data( v ); - store.wrapIsFinished( new ShowWrappedResult ); + // This is the only thing which changes: we wrap the IsFinished function. + // According to RAII, we'll delete the invokated wrapper at the end of the main ; the store won't delete it + // automatically. + IsFinishedParallelApply* wrapper = new ShowWrappedResult; + store.wrapIsFinished( wrapper ); ParallelApply job( assign, eo::mpi::DEFAULT_MASTER, store ); // Equivalent to: @@ -86,6 +125,8 @@ int main(int argc, char** argv) cout << endl; } + delete wrapper; + return 0; } From fb75279012e0a7591cb40bfb5d990fe54278f83e Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 16 Jul 2012 15:08:53 +0200 Subject: [PATCH 71/76] Added doxygen links to examples on MPI classes --- eo/src/eoPopEvalFunc.h | 4 ++++ eo/src/mpi/eoMpi.h | 7 ++++++- eo/src/mpi/eoParallelApply.h | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/eo/src/eoPopEvalFunc.h b/eo/src/eoPopEvalFunc.h index b96e8cac9..7fcad1467 100644 --- a/eo/src/eoPopEvalFunc.h +++ b/eo/src/eoPopEvalFunc.h @@ -217,6 +217,10 @@ class eoParallelPopLoopEval : public eoPopEvalFunc // Do we have to delete the store by ourselves ? bool needToDeleteStore; }; + +/** + * @example t-mpi-eval.cpp + */ #endif ///////////////////////////////////////////////////////////// diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index 0293c43d8..cf5cbeb54 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -36,7 +36,8 @@ namespace eo { /** * @ingroup Parallel - * @defgroup MPI Message Passing Interface parallelization + * @defgroup MPI Message Passing Interface + * @brief See namespace eo::mpi to have all explanations about this module. * @{ */ @@ -507,6 +508,10 @@ namespace eo IsFinishedFunction< JobData >* _iff; }; + /** + * @example t-mpi-wrapper.cpp + */ + /** * @brief Class implementing the centralized job algorithm. * diff --git a/eo/src/mpi/eoParallelApply.h b/eo/src/mpi/eoParallelApply.h index 98f113f44..3cfd729c8 100644 --- a/eo/src/mpi/eoParallelApply.h +++ b/eo/src/mpi/eoParallelApply.h @@ -375,6 +375,11 @@ namespace eo // empty } }; + + /** + * @example t-mpi-parallelApply.cpp + * @example t-mpi-multipleRoles.cpp + */ } } # endif // __EO_PARALLEL_APPLY_H__ From 8285d78245a83b61f46fe32797751dc0a2cd43e6 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 18 Jul 2012 13:57:13 +0200 Subject: [PATCH 72/76] Added comments in wrapper of t-mpi-eval. --- eo/test/mpi/t-mpi-eval.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/eo/test/mpi/t-mpi-eval.cpp b/eo/test/mpi/t-mpi-eval.cpp index e5678104c..be3c2d96a 100644 --- a/eo/test/mpi/t-mpi-eval.cpp +++ b/eo/test/mpi/t-mpi-eval.cpp @@ -120,15 +120,30 @@ struct CatBestAnswers : public eo::mpi::HandleResponseParallelApply 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::_wrapped; // using eo::mpi::HandleResponseParallelApply::d; void operator()(int wrkRank) { + // Retrieve informations about the slice processed by the worker int index = d->assignedTasks[wrkRank].index; int size = d->assignedTasks[wrkRank].size; - (*_wrapped)( wrkRank ); // call to the wrapped function HERE + // 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->data()[ i ].fitness() ) @@ -147,6 +162,7 @@ struct CatBestAnswers : public eo::mpi::HandleResponseParallelApply 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); @@ -179,10 +195,15 @@ int main(int ac, char** av) 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; From 766a8f40d6a416f7016f22633daa03ed5481ace9 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 18 Jul 2012 14:02:39 +0200 Subject: [PATCH 73/76] Solved memory leak on terminate job ctor --- eo/src/mpi/eoMpi.h | 22 ++++++++++++---------- eo/src/mpi/eoTerminateJob.h | 3 ++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/eo/src/mpi/eoMpi.h b/eo/src/mpi/eoMpi.h index cf5cbeb54..abb5fa6f5 100644 --- a/eo/src/mpi/eoMpi.h +++ b/eo/src/mpi/eoMpi.h @@ -539,7 +539,7 @@ namespace eo * AssignmentAlgorithm for more details. * * @param _masterRank The MPI rank of the master. - * + * * @param _workerStopCondition Number of the message which will cause the workers to terminate. It could * be one of the constants defined in eo::mpi::Commands, or any other integer. The user has to be sure * that a message containing this integer will be sent to each worker on the Commands channel, otherwise @@ -552,24 +552,25 @@ namespace eo Job( AssignmentAlgorithm& _algo, int _masterRank, int _workerStopCondition, - JobStore & store + JobStore & _store ) : assignmentAlgo( _algo ), masterRank( _masterRank ), workerStopCondition( _workerStopCondition ), comm( Node::comm() ), // Functors - sendTask( store.sendTask() ), - handleResponse( store.handleResponse() ), - processTask( store.processTask() ), - isFinished( store.isFinished() ) + store( _store ), + sendTask( _store.sendTask() ), + handleResponse( _store.handleResponse() ), + processTask( _store.processTask() ), + isFinished( _store.isFinished() ) { _isMaster = Node::comm().rank() == _masterRank; - sendTask.data( store.data() ); - handleResponse.data( store.data() ); - processTask.data( store.data() ); - isFinished.data( store.data() ); + sendTask.data( _store.data() ); + handleResponse.data( _store.data() ); + processTask.data( _store.data() ); + isFinished.data( _store.data() ); } protected: @@ -764,6 +765,7 @@ namespace eo const int workerStopCondition; bmpi::communicator& comm; + JobStore& store; SendTaskFunction & sendTask; HandleResponseFunction & handleResponse; ProcessTaskFunction & processTask; diff --git a/eo/src/mpi/eoTerminateJob.h b/eo/src/mpi/eoTerminateJob.h index e0b4a5bd3..fe231f0e3 100644 --- a/eo/src/mpi/eoTerminateJob.h +++ b/eo/src/mpi/eoTerminateJob.h @@ -115,7 +115,7 @@ namespace eo */ EmptyJob( AssignmentAlgorithm& algo, int masterRank ) : OneShotJob( algo, masterRank, *(new DummyJobStore) ) - // FIXME memory leak, meaningless but present + // the job store is deleted on destructor { // empty } @@ -127,6 +127,7 @@ namespace eo { comm.send( idles[i], Channel::Commands, Message::Kill ); } + delete & this->store; } }; From 4ae75cdef2b5083da9af1e9c4da6df148f7b58e9 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 18 Jul 2012 17:12:48 +0200 Subject: [PATCH 74/76] Adding presentation of Parallelization in EO, dated from July, 18th to tutorial section. --- eo/tutorial/Parallelization/css/deck.core.css | 405 ++++++++++++++ eo/tutorial/Parallelization/css/deck.goto.css | 41 ++ eo/tutorial/Parallelization/css/deck.hash.css | 13 + eo/tutorial/Parallelization/css/deck.menu.css | 47 ++ .../Parallelization/css/deck.navigation.css | 43 ++ .../Parallelization/css/deck.scale.css | 28 + .../Parallelization/css/deck.status.css | 18 + eo/tutorial/Parallelization/css/eompi.css | 9 + .../Parallelization/css/horizontal-slide.css | 76 +++ eo/tutorial/Parallelization/css/shjs.css | 67 +++ eo/tutorial/Parallelization/css/thales.css | 91 ++++ .../Parallelization/img/generic_parallel.dia | Bin 0 -> 2476 bytes .../Parallelization/img/generic_parallel.png | Bin 0 -> 29522 bytes .../Parallelization/img/serialisation.dia | Bin 0 -> 1528 bytes .../Parallelization/img/serialisation.png | Bin 0 -> 10806 bytes eo/tutorial/Parallelization/js/deck.core.js | 498 ++++++++++++++++++ eo/tutorial/Parallelization/js/deck.goto.js | 170 ++++++ eo/tutorial/Parallelization/js/deck.hash.js | 141 +++++ eo/tutorial/Parallelization/js/deck.menu.js | 187 +++++++ .../Parallelization/js/deck.navigation.js | 91 ++++ eo/tutorial/Parallelization/js/deck.scale.js | 170 ++++++ eo/tutorial/Parallelization/js/deck.status.js | 95 ++++ .../Parallelization/js/jquery-1.7.min.js | 4 + .../Parallelization/js/modernizr.custom.js | 4 + eo/tutorial/Parallelization/js/shjs-cpp.js | 1 + eo/tutorial/Parallelization/js/shjs.js | 1 + 26 files changed, 2200 insertions(+) create mode 100644 eo/tutorial/Parallelization/css/deck.core.css create mode 100644 eo/tutorial/Parallelization/css/deck.goto.css create mode 100644 eo/tutorial/Parallelization/css/deck.hash.css create mode 100644 eo/tutorial/Parallelization/css/deck.menu.css create mode 100644 eo/tutorial/Parallelization/css/deck.navigation.css create mode 100644 eo/tutorial/Parallelization/css/deck.scale.css create mode 100644 eo/tutorial/Parallelization/css/deck.status.css create mode 100644 eo/tutorial/Parallelization/css/eompi.css create mode 100644 eo/tutorial/Parallelization/css/horizontal-slide.css create mode 100644 eo/tutorial/Parallelization/css/shjs.css create mode 100644 eo/tutorial/Parallelization/css/thales.css create mode 100644 eo/tutorial/Parallelization/img/generic_parallel.dia create mode 100644 eo/tutorial/Parallelization/img/generic_parallel.png create mode 100644 eo/tutorial/Parallelization/img/serialisation.dia create mode 100644 eo/tutorial/Parallelization/img/serialisation.png create mode 100644 eo/tutorial/Parallelization/js/deck.core.js create mode 100644 eo/tutorial/Parallelization/js/deck.goto.js create mode 100644 eo/tutorial/Parallelization/js/deck.hash.js create mode 100644 eo/tutorial/Parallelization/js/deck.menu.js create mode 100644 eo/tutorial/Parallelization/js/deck.navigation.js create mode 100644 eo/tutorial/Parallelization/js/deck.scale.js create mode 100644 eo/tutorial/Parallelization/js/deck.status.js create mode 100644 eo/tutorial/Parallelization/js/jquery-1.7.min.js create mode 100644 eo/tutorial/Parallelization/js/modernizr.custom.js create mode 100644 eo/tutorial/Parallelization/js/shjs-cpp.js create mode 100644 eo/tutorial/Parallelization/js/shjs.js diff --git a/eo/tutorial/Parallelization/css/deck.core.css b/eo/tutorial/Parallelization/css/deck.core.css new file mode 100644 index 000000000..a188b28fd --- /dev/null +++ b/eo/tutorial/Parallelization/css/deck.core.css @@ -0,0 +1,405 @@ +html { + height: 100%; +} + +body.deck-container { + overflow-y: auto; + position: static; +} + +.deck-container { + position: relative; + min-height: 100%; + margin: 0 auto; + padding: 0 48px; + font-size: 16px; + line-height: 1.25; + overflow: hidden; + /* Resets and base styles from HTML5 Boilerplate */ + /* End HTML5 Boilerplate adaptations */ +} +.js .deck-container { + visibility: hidden; +} +.ready .deck-container { + visibility: visible; +} +.touch .deck-container { + -webkit-text-size-adjust: none; + -moz-text-size-adjust: none; +} +.deck-container div, .deck-container span, .deck-container object, .deck-container iframe, +.deck-container h1, .deck-container h2, .deck-container h3, .deck-container h4, .deck-container h5, .deck-container h6, .deck-container p, .deck-container blockquote, .deck-container pre, +.deck-container abbr, .deck-container address, .deck-container cite, .deck-container code, .deck-container del, .deck-container dfn, .deck-container em, .deck-container img, .deck-container ins, .deck-container kbd, .deck-container q, .deck-container samp, +.deck-container small, .deck-container strong, .deck-container sub, .deck-container sup, .deck-container var, .deck-container b, .deck-container i, .deck-container dl, .deck-container dt, .deck-container dd, .deck-container ol, .deck-container ul, .deck-container li, +.deck-container fieldset, .deck-container form, .deck-container label, .deck-container legend, +.deck-container table, .deck-container caption, .deck-container tbody, .deck-container tfoot, .deck-container thead, .deck-container tr, .deck-container th, .deck-container td, +.deck-container article, .deck-container aside, .deck-container canvas, .deck-container details, .deck-container figcaption, .deck-container figure, +.deck-container footer, .deck-container header, .deck-container hgroup, .deck-container menu, .deck-container nav, .deck-container section, .deck-container summary, +.deck-container time, .deck-container mark, .deck-container audio, .deck-container video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +.deck-container article, .deck-container aside, .deck-container details, .deck-container figcaption, .deck-container figure, +.deck-container footer, .deck-container header, .deck-container hgroup, .deck-container menu, .deck-container nav, .deck-container section { + display: block; +} +.deck-container blockquote, .deck-container q { + quotes: none; +} +.deck-container blockquote:before, .deck-container blockquote:after, .deck-container q:before, .deck-container q:after { + content: ""; + content: none; +} +.deck-container ins { + background-color: #ff9; + color: #000; + text-decoration: none; +} +.deck-container mark { + background-color: #ff9; + color: #000; + font-style: italic; + font-weight: bold; +} +.deck-container del { + text-decoration: line-through; +} +.deck-container abbr[title], .deck-container dfn[title] { + border-bottom: 1px dotted; + cursor: help; +} +.deck-container table { + border-collapse: collapse; + border-spacing: 0; +} +.deck-container hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #ccc; + margin: 1em 0; + padding: 0; +} +.deck-container input, .deck-container select { + vertical-align: middle; +} +.deck-container select, .deck-container input, .deck-container textarea, .deck-container button { + font: 99% sans-serif; +} +.deck-container pre, .deck-container code, .deck-container kbd, .deck-container samp { + font-family: monospace, sans-serif; +} +.deck-container a { + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +.deck-container a:hover, .deck-container a:active { + outline: none; +} +.deck-container ul, .deck-container ol { + margin-left: 2em; + vertical-align: top; +} +.deck-container ol { + list-style-type: decimal; +} +.deck-container nav ul, .deck-container nav li { + margin: 0; + list-style: none; + list-style-image: none; +} +.deck-container small { + font-size: 85%; +} +.deck-container strong, .deck-container th { + font-weight: bold; +} +.deck-container td { + vertical-align: top; +} +.deck-container sub, .deck-container sup { + font-size: 75%; + line-height: 0; + position: relative; +} +.deck-container sup { + top: -0.5em; +} +.deck-container sub { + bottom: -0.25em; +} +.deck-container textarea { + overflow: auto; +} +.ie6 .deck-container legend, .ie7 .deck-container legend { + margin-left: -7px; +} +.deck-container input[type="radio"] { + vertical-align: text-bottom; +} +.deck-container input[type="checkbox"] { + vertical-align: bottom; +} +.ie7 .deck-container input[type="checkbox"] { + vertical-align: baseline; +} +.ie6 .deck-container input { + vertical-align: text-bottom; +} +.deck-container label, .deck-container input[type="button"], .deck-container input[type="submit"], .deck-container input[type="image"], .deck-container button { + cursor: pointer; +} +.deck-container button, .deck-container input, .deck-container select, .deck-container textarea { + margin: 0; +} +.deck-container input:invalid, .deck-container textarea:invalid { + border-radius: 1px; + -moz-box-shadow: 0px 0px 5px red; + -webkit-box-shadow: 0px 0px 5px red; + box-shadow: 0px 0px 5px red; +} +.deck-container input:invalid .no-boxshadow, .deck-container textarea:invalid .no-boxshadow { + background-color: #f0dddd; +} +.deck-container button { + width: auto; + overflow: visible; +} +.ie7 .deck-container img { + -ms-interpolation-mode: bicubic; +} +.deck-container, .deck-container select, .deck-container input, .deck-container textarea { + color: #444; +} +.deck-container a { + color: #607890; +} +.deck-container a:hover, .deck-container a:focus { + color: #036; +} +.deck-container a:link { + -webkit-tap-highlight-color: #fff; +} +.deck-container.deck-loading { + display: none; +} + +.slide { + width: auto; + min-height: 100%; + position: relative; +} +.slide h1 { + font-size: 4.5em; +} +.slide h1, .slide .vcenter { + font-weight: bold; + text-align: center; + padding-top: 1em; + max-height: 100%; +} +.csstransforms .slide h1, .csstransforms .slide .vcenter { + padding: 0 48px; + position: absolute; + left: 0; + right: 0; + top: 50%; + -webkit-transform: translate(0, -50%); + -moz-transform: translate(0, -50%); + -ms-transform: translate(0, -50%); + -o-transform: translate(0, -50%); + transform: translate(0, -50%); +} +.slide .vcenter h1 { + position: relative; + top: auto; + padding: 0; + -webkit-transform: none; + -moz-transform: none; + -ms-transform: none; + -o-transform: none; + transform: none; +} +.slide h2 { + font-size: 2.25em; + font-weight: bold; + padding-top: .5em; + margin: 0 0 .66666em 0; + border-bottom: 3px solid #888; +} +.slide h3 { + font-size: 1.4375em; + font-weight: bold; + margin-bottom: .30435em; +} +.slide h4 { + font-size: 1.25em; + font-weight: bold; + margin-bottom: .25em; +} +.slide h5 { + font-size: 1.125em; + font-weight: bold; + margin-bottom: .2222em; +} +.slide h6 { + font-size: 1em; + font-weight: bold; +} +.slide img, .slide iframe, .slide video { + display: block; + max-width: 100%; +} +.slide video, .slide iframe, .slide img { + display: block; + margin: 0 auto; +} +.slide p, .slide blockquote, .slide iframe, .slide img, .slide ul, .slide ol, .slide pre, .slide video { + margin-bottom: 1em; +} +.slide pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; + padding: 1em; + border: 1px solid #888; +} +.slide em { + font-style: italic; +} +.slide li { + padding: .25em 0; + vertical-align: middle; +} + +.deck-before, .deck-previous, .deck-next, .deck-after { + position: absolute; + left: -999em; + top: -999em; +} + +.deck-current { + z-index: 2; +} + +.slide .slide { + visibility: hidden; + position: static; + min-height: 0; +} + +.deck-child-current { + position: static; + z-index: 2; +} +.deck-child-current .slide { + visibility: hidden; +} +.deck-child-current .deck-previous, .deck-child-current .deck-before, .deck-child-current .deck-current { + visibility: visible; +} + +@media screen and (max-device-width: 480px) { + /* html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */ +} +@media print { + * { + background: transparent !important; + color: black !important; + text-shadow: none !important; + filter: none !important; + -ms-filter: none !important; + -webkit-box-reflect: none !important; + -moz-box-reflect: none !important; + -webkit-box-shadow: none !important; + -moz-box-shadow: none !important; + box-shadow: none !important; + } + * :before, * :after { + display: none !important; + } + + a, a:visited { + color: #444 !important; + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { + content: ""; + } + + pre, blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; + } + + tr, img { + page-break-inside: avoid; + } + + @page { + margin: 0.5cm; +} + + p, h2, h3 { + orphans: 3; + widows: 3; + } + + h2, h3 { + page-break-after: avoid; + } + + .slide { + position: static !important; + visibility: visible !important; + display: block !important; + -webkit-transform: none !important; + -moz-transform: none !important; + -o-transform: none !important; + -ms-transform: none !important; + transform: none !important; + opacity: 1 !important; + } + + h1, .vcenter { + -webkit-transform: none !important; + -moz-transform: none !important; + -o-transform: none !important; + -ms-transform: none !important; + transform: none !important; + padding: 0 !important; + position: static !important; + } + + .deck-container > .slide { + page-break-after: always; + } + + .deck-container { + width: 100% !important; + height: auto !important; + padding: 0 !important; + display: block !important; + } + + script { + display: none; + } +} diff --git a/eo/tutorial/Parallelization/css/deck.goto.css b/eo/tutorial/Parallelization/css/deck.goto.css new file mode 100644 index 000000000..108e4f9c4 --- /dev/null +++ b/eo/tutorial/Parallelization/css/deck.goto.css @@ -0,0 +1,41 @@ +.deck-container .goto-form { + position: absolute; + z-index: 3; + bottom: 10px; + left: 50%; + height: 1.75em; + margin: 0 0 0 -9.125em; + line-height: 1.75em; + padding: 0.625em; + display: none; + background: #ccc; + overflow: hidden; +} +.borderradius .deck-container .goto-form { + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; +} +.deck-container .goto-form label { + font-weight: bold; +} +.deck-container .goto-form label, .deck-container .goto-form input { + display: inline-block; + font-family: inherit; +} + +.deck-goto .goto-form { + display: block; +} + +#goto-slide { + width: 8.375em; + margin: 0 0.625em; + height: 1.4375em; +} + +@media print { + .goto-form, #goto-slide { + display: none !important; + } +} diff --git a/eo/tutorial/Parallelization/css/deck.hash.css b/eo/tutorial/Parallelization/css/deck.hash.css new file mode 100644 index 000000000..28f07326b --- /dev/null +++ b/eo/tutorial/Parallelization/css/deck.hash.css @@ -0,0 +1,13 @@ +.deck-container .deck-permalink { + display: none; + position: absolute; + z-index: 4; + bottom: 30px; + right: 0; + width: 48px; + text-align: center; +} + +.no-history .deck-container:hover .deck-permalink { + display: block; +} diff --git a/eo/tutorial/Parallelization/css/deck.menu.css b/eo/tutorial/Parallelization/css/deck.menu.css new file mode 100644 index 000000000..c664a3f8e --- /dev/null +++ b/eo/tutorial/Parallelization/css/deck.menu.css @@ -0,0 +1,47 @@ +.deck-menu .slide { + background: #eee; + position: relative; + left: 0; + top: 0; + visibility: visible; + cursor: pointer; +} +.no-csstransforms .deck-menu > .slide { + float: left; + width: 22%; + height: 22%; + min-height: 0; + margin: 1%; + font-size: 0.22em; + overflow: hidden; + padding: 0 0.5%; +} +.csstransforms .deck-menu > .slide { + -webkit-transform: scale(0.22) !important; + -moz-transform: scale(0.22) !important; + -o-transform: scale(0.22) !important; + -ms-transform: scale(0.22) !important; + transform: scale(0.22) !important; + -webkit-transform-origin: 0 0; + -moz-transform-origin: 0 0; + -o-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + height: 100%; + overflow: hidden; + padding: 0 48px; + margin: 12px; +} +.deck-menu iframe, .deck-menu img, .deck-menu video { + max-width: 100%; +} +.deck-menu .deck-current, .no-touch .deck-menu .slide:hover { + background: #ddf; +} +.deck-menu.deck-container:hover .deck-prev-link, .deck-menu.deck-container:hover .deck-next-link { + display: none; +} diff --git a/eo/tutorial/Parallelization/css/deck.navigation.css b/eo/tutorial/Parallelization/css/deck.navigation.css new file mode 100644 index 000000000..e1ebad8b8 --- /dev/null +++ b/eo/tutorial/Parallelization/css/deck.navigation.css @@ -0,0 +1,43 @@ +.deck-container .deck-prev-link, .deck-container .deck-next-link { + display: none; + position: absolute; + z-index: 3; + top: 50%; + width: 32px; + height: 32px; + margin-top: -16px; + font-size: 20px; + font-weight: bold; + line-height: 32px; + vertical-align: middle; + text-align: center; + text-decoration: none; + color: #fff; + background: #888; +} +.borderradius .deck-container .deck-prev-link, .borderradius .deck-container .deck-next-link { + -webkit-border-radius: 16px; + -moz-border-radius: 16px; + border-radius: 16px; +} +.deck-container .deck-prev-link:hover, .deck-container .deck-prev-link:focus, .deck-container .deck-prev-link:active, .deck-container .deck-prev-link:visited, .deck-container .deck-next-link:hover, .deck-container .deck-next-link:focus, .deck-container .deck-next-link:active, .deck-container .deck-next-link:visited { + color: #fff; +} +.deck-container .deck-prev-link { + left: 8px; +} +.deck-container .deck-next-link { + right: 8px; +} +.deck-container:hover .deck-prev-link, .deck-container:hover .deck-next-link { + display: block; +} +.deck-container:hover .deck-prev-link.deck-nav-disabled, .touch .deck-container:hover .deck-prev-link, .deck-container:hover .deck-next-link.deck-nav-disabled, .touch .deck-container:hover .deck-next-link { + display: none; +} + +@media print { + .deck-prev-link, .deck-next-link { + display: none !important; + } +} diff --git a/eo/tutorial/Parallelization/css/deck.scale.css b/eo/tutorial/Parallelization/css/deck.scale.css new file mode 100644 index 000000000..d6a4eb0be --- /dev/null +++ b/eo/tutorial/Parallelization/css/deck.scale.css @@ -0,0 +1,28 @@ +/* Remove this line if you are embedding deck.js in a page and +using the scale extension. */ +.csstransforms { + overflow: hidden; +} + +.csstransforms .deck-container.deck-scale:not(.deck-menu) > .slide { + -webkit-box-sizing: padding-box; + -moz-box-sizing: padding-box; + box-sizing: padding-box; + width: 100%; + padding-bottom: 20px; +} +.csstransforms .deck-container.deck-scale:not(.deck-menu) > .slide > .deck-slide-scaler { + -webkit-transform-origin: 50% 0; + -moz-transform-origin: 50% 0; + -o-transform-origin: 50% 0; + -ms-transform-origin: 50% 0; + transform-origin: 50% 0; +} + +.csstransforms .deck-container.deck-menu .deck-slide-scaler { + -webkit-transform: none !important; + -moz-transform: none !important; + -o-transform: none !important; + -ms-transform: none !important; + transform: none !important; +} diff --git a/eo/tutorial/Parallelization/css/deck.status.css b/eo/tutorial/Parallelization/css/deck.status.css new file mode 100644 index 000000000..17d55ad0d --- /dev/null +++ b/eo/tutorial/Parallelization/css/deck.status.css @@ -0,0 +1,18 @@ +.deck-container .deck-status { + position: absolute; + bottom: 10px; + right: 5px; + color: #888; + z-index: 3; + margin: 0; +} + +body.deck-container .deck-status { + position: fixed; +} + +@media print { + .deck-status { + display: none; + } +} diff --git a/eo/tutorial/Parallelization/css/eompi.css b/eo/tutorial/Parallelization/css/eompi.css new file mode 100644 index 000000000..d089362ce --- /dev/null +++ b/eo/tutorial/Parallelization/css/eompi.css @@ -0,0 +1,9 @@ +.changed +{ + color: green; +} + +.specific +{ + color: red; +} diff --git a/eo/tutorial/Parallelization/css/horizontal-slide.css b/eo/tutorial/Parallelization/css/horizontal-slide.css new file mode 100644 index 000000000..4a4c6adf6 --- /dev/null +++ b/eo/tutorial/Parallelization/css/horizontal-slide.css @@ -0,0 +1,76 @@ +.csstransitions.csstransforms { + overflow-x: hidden; +} +.csstransitions.csstransforms .deck-container > .slide { + -webkit-transition: -webkit-transform 500ms ease-in-out; + -moz-transition: -moz-transform 500ms ease-in-out; + -ms-transition: -ms-transform 500ms ease-in-out; + -o-transition: -o-transform 500ms ease-in-out; + transition: transform 500ms ease-in-out; +} +.csstransitions.csstransforms .deck-container:not(.deck-menu) > .slide { + position: absolute; + top: 0; + left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + padding: 0 48px; +} +.csstransitions.csstransforms .deck-container:not(.deck-menu) > .slide .slide { + position: relative; + left: 0; + top: 0; + -webkit-transition: -webkit-transform 500ms ease-in-out, opacity 500ms ease-in-out; + -moz-transition: -moz-transform 500ms ease-in-out, opacity 500ms ease-in-out; + -ms-transition: -ms-transform 500ms ease-in-out, opacity 500ms ease-in-out; + -o-transition: -o-transform 500ms ease-in-out, opacity 500ms ease-in-out; + transition: -webkit-transform 500ms ease-in-out, opacity 500ms ease-in-out; +} +.csstransitions.csstransforms .deck-container:not(.deck-menu) > .slide .deck-next, .csstransitions.csstransforms .deck-container:not(.deck-menu) > .slide .deck-after { + visibility: visible; + -webkit-transform: translate3d(200%, 0, 0); + -moz-transform: translate(200%, 0); + -ms-transform: translate(200%, 0); + -o-transform: translate(200%, 0); + transform: translate3d(200%, 0, 0); +} +.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-previous { + -webkit-transform: translate3d(-200%, 0, 0); + -moz-transform: translate(-200%, 0); + -ms-transform: translate(-200%, 0); + -o-transform: translate(-200%, 0); + transform: translate3d(-200%, 0, 0); +} +.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-before { + -webkit-transform: translate3d(-400%, 0, 0); + -moz-transform: translate(-400%, 0); + -ms-transform: translate(-400%, 0); + -o-transform: translate(-400%, 0); + transform: translate3d(-400%, 0, 0); +} +.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-next { + -webkit-transform: translate3d(200%, 0, 0); + -moz-transform: translate(200%, 0); + -ms-transform: translate(200%, 0); + -o-transform: translate(200%, 0); + transform: translate3d(200%, 0, 0); +} +.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-after { + -webkit-transform: translate3d(400%, 0, 0); + -moz-transform: translate(400%, 0); + -ms-transform: translate(400%, 0); + -o-transform: translate(400%, 0); + transform: translate3d(400%, 0, 0); +} +.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-before .slide, .csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-previous .slide { + visibility: visible; +} +.csstransitions.csstransforms .deck-container:not(.deck-menu) > .deck-child-current { + -webkit-transform: none; + -moz-transform: none; + -ms-transform: none; + -o-transform: none; + transform: none; +} diff --git a/eo/tutorial/Parallelization/css/shjs.css b/eo/tutorial/Parallelization/css/shjs.css new file mode 100644 index 000000000..ec9d4088b --- /dev/null +++ b/eo/tutorial/Parallelization/css/shjs.css @@ -0,0 +1,67 @@ +pre.sh_sourceCode { + background-color: white; + color: black; + font-style: normal; + font-weight: normal; +} + +pre.sh_sourceCode .sh_keyword { color: blue; font-weight: bold; } /* language keywords */ +pre.sh_sourceCode .sh_type { color: darkgreen; } /* basic types */ +pre.sh_sourceCode .sh_usertype { color: teal; } /* user defined types */ +pre.sh_sourceCode .sh_string { color: red; font-family: monospace; } /* strings and chars */ +pre.sh_sourceCode .sh_regexp { color: orange; font-family: monospace; } /* regular expressions */ +pre.sh_sourceCode .sh_specialchar { color: pink; font-family: monospace; } /* e.g., \n, \t, \\ */ +pre.sh_sourceCode .sh_comment { color: brown; font-style: italic; } /* comments */ +pre.sh_sourceCode .sh_number { color: purple; } /* literal numbers */ +pre.sh_sourceCode .sh_preproc { color: darkblue; font-weight: bold; } /* e.g., #include, import */ +pre.sh_sourceCode .sh_symbol { color: darkred; } /* e.g., <, >, + */ +pre.sh_sourceCode .sh_function { color: black; font-weight: bold; } /* function calls and declarations */ +pre.sh_sourceCode .sh_cbracket { color: red; } /* block brackets (e.g., {, }) */ +pre.sh_sourceCode .sh_todo { font-weight: bold; background-color: cyan; } /* TODO and FIXME */ + +/* Predefined variables and functions (for instance glsl) */ +pre.sh_sourceCode .sh_predef_var { color: darkblue; } +pre.sh_sourceCode .sh_predef_func { color: darkblue; font-weight: bold; } + +/* for OOP */ +pre.sh_sourceCode .sh_classname { color: teal; } + +/* line numbers (not yet implemented) */ +pre.sh_sourceCode .sh_linenum { color: black; font-family: monospace; } + +/* Internet related */ +pre.sh_sourceCode .sh_url { color: blue; text-decoration: underline; font-family: monospace; } + +/* for ChangeLog and Log files */ +pre.sh_sourceCode .sh_date { color: blue; font-weight: bold; } +pre.sh_sourceCode .sh_time, pre.sh_sourceCode .sh_file { color: darkblue; font-weight: bold; } +pre.sh_sourceCode .sh_ip, pre.sh_sourceCode .sh_name { color: darkgreen; } + +/* for Prolog, Perl... */ +pre.sh_sourceCode .sh_variable { color: darkgreen; } + +/* for LaTeX */ +pre.sh_sourceCode .sh_italics { color: darkgreen; font-style: italic; } +pre.sh_sourceCode .sh_bold { color: darkgreen; font-weight: bold; } +pre.sh_sourceCode .sh_underline { color: darkgreen; text-decoration: underline; } +pre.sh_sourceCode .sh_fixed { color: green; font-family: monospace; } +pre.sh_sourceCode .sh_argument { color: darkgreen; } +pre.sh_sourceCode .sh_optionalargument { color: purple; } +pre.sh_sourceCode .sh_math { color: orange; } +pre.sh_sourceCode .sh_bibtex { color: blue; } + +/* for diffs */ +pre.sh_sourceCode .sh_oldfile { color: orange; } +pre.sh_sourceCode .sh_newfile { color: darkgreen; } +pre.sh_sourceCode .sh_difflines { color: blue; } + +/* for css */ +pre.sh_sourceCode .sh_selector { color: purple; } +pre.sh_sourceCode .sh_property { color: blue; } +pre.sh_sourceCode .sh_value { color: darkgreen; font-style: italic; } + +/* other */ +pre.sh_sourceCode .sh_section { color: black; font-weight: bold; } +pre.sh_sourceCode .sh_paren { color: red; } +pre.sh_sourceCode .sh_attribute { color: darkgreen; } + diff --git a/eo/tutorial/Parallelization/css/thales.css b/eo/tutorial/Parallelization/css/thales.css new file mode 100644 index 000000000..e30282966 --- /dev/null +++ b/eo/tutorial/Parallelization/css/thales.css @@ -0,0 +1,91 @@ +.deck-container em { + color:rgb(255,115,0); +} + +.deck-container s { + color:rgb(180,180,211); +} + +.deck-container { + font-family: "Helvetica Neue", sans-serif; + font-size: 1.75em; + color:RGB(50,50,101); + background:white url("./img/thales.jpg") no-repeat fixed bottom left; +} +.deck-container .slide { +} +.deck-container .slide h1 { + color:rgb(50,50,101); +} +.deck-container .slide h2 { + color:rgb(255,115,0); + border-bottom-color:lightgray; +} +.deck-container .slide h3 { + color:RGB(57,138,199); +} +.deck-container .slide pre { + border-color: #ccc; +} +.deck-container .slide blockquote { + font-size: 2em; + font-style: italic; + padding: 1em 2em; + color: #000; + border-left: 5px solid #ccc; + font-family:serif; +} +.deck-container .slide blockquote p { + margin: 0; +} +.deck-container .slide blockquote cite { + font-size: .5em; + font-style: normal; + font-weight: bold; + color: #888; +} +.deck-container .slide ::-moz-selection { + background: #c00; + color: #fff; +} +.deck-container .slide ::selection { + background: #c00; + color: #fff; +} +.deck-container .slide a, .deck-container .slide a:hover, .deck-container .slide a:focus, .deck-container .slide a:active, .deck-container .slide a:visited { + color:RGB(152,191,12); + text-decoration: none; +} +.deck-container .slide a:hover, .deck-container .slide a:focus { + text-decoration: underline; +} +.deck-container > .slide .deck-before, .deck-container > .slide .deck-previous { + opacity: 0.4; +} +.deck-container > .slide .deck-before:not(.deck-child-current) .deck-before, .deck-container > .slide .deck-before:not(.deck-child-current) .deck-previous, .deck-container > .slide .deck-previous:not(.deck-child-current) .deck-before, .deck-container > .slide .deck-previous:not(.deck-child-current) .deck-previous { + opacity: 1; +} +.deck-container > .slide .deck-child-current { + opacity: 1; +} +.deck-container .deck-prev-link, .deck-container .deck-next-link { + background: #ccc; + font-family: serif; +} +.deck-container .deck-prev-link, .deck-container .deck-prev-link:hover, .deck-container .deck-prev-link:focus, .deck-container .deck-prev-link:active, .deck-container .deck-prev-link:visited, .deck-container .deck-next-link, .deck-container .deck-next-link:hover, .deck-container .deck-next-link:focus, .deck-container .deck-next-link:active, .deck-container .deck-next-link:visited { + color: #fff; +} +.deck-container .deck-prev-link:hover, .deck-container .deck-prev-link:focus, .deck-container .deck-next-link:hover, .deck-container .deck-next-link:focus { + background: #c00; + text-decoration: none; +} +.deck-container .deck-status { + font-size: 0.6666em; +} +.deck-container.deck-menu .slide { + background: #eee; +} +.deck-container.deck-menu .deck-current, .no-touch .deck-container.deck-menu .slide:hover { + background: #ddf; +} + diff --git a/eo/tutorial/Parallelization/img/generic_parallel.dia b/eo/tutorial/Parallelization/img/generic_parallel.dia new file mode 100644 index 0000000000000000000000000000000000000000..693475c5b437cd12e500ea48e11eb4af3d4f7f28 GIT binary patch literal 2476 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 literal 0 HcmV?d00001 diff --git a/eo/tutorial/Parallelization/img/serialisation.png b/eo/tutorial/Parallelization/img/serialisation.png new file mode 100644 index 0000000000000000000000000000000000000000..76ac0a9c0b67ec7d03b8d686348a465dfeda77c0 GIT binary patch literal 10806 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 literal 0 HcmV?d00001 diff --git a/eo/tutorial/Parallelization/js/deck.core.js b/eo/tutorial/Parallelization/js/deck.core.js new file mode 100644 index 000000000..6fbeb5ce0 --- /dev/null +++ b/eo/tutorial/Parallelization/js/deck.core.js @@ -0,0 +1,498 @@ +/*! +Deck JS - deck.core +Copyright (c) 2011 Caleb Troughton +Dual licensed under the MIT license and GPL license. +https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt +https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt +*/ + +/* +The deck.core module provides all the basic functionality for creating and +moving through a deck. It does so by applying classes to indicate the state of +the deck and its slides, allowing CSS to take care of the visual representation +of each state. It also provides methods for navigating the deck and inspecting +its state, as well as basic key bindings for going to the next and previous +slides. More functionality is provided by wholly separate extension modules +that use the API provided by core. +*/ +(function($, deck, document, undefined) { + var slides, // Array of all the uh, slides... + current, // Array index of the current slide + $container, // Keeping this cached + + events = { + /* + This event fires whenever the current slide changes, whether by way of + next, prev, or go. The callback function is passed two parameters, from + and to, equal to the indices of the old slide and the new slide + respectively. If preventDefault is called on the event within this handler + the slide change does not occur. + + $(document).bind('deck.change', function(event, from, to) { + alert('Moving from slide ' + from + ' to ' + to); + }); + */ + change: 'deck.change', + + /* + This event fires at the beginning of deck initialization, after the options + are set but before the slides array is created. This event makes a good hook + for preprocessing extensions looking to modify the deck. + */ + beforeInitialize: 'deck.beforeInit', + + /* + This event fires at the end of deck initialization. Extensions should + implement any code that relies on user extensible options (key bindings, + element selectors, classes) within a handler for this event. Native + events associated with Deck JS should be scoped under a .deck event + namespace, as with the example below: + + var $d = $(document); + $.deck.defaults.keys.myExtensionKeycode = 70; // 'h' + $d.bind('deck.init', function() { + $d.bind('keydown.deck', function(event) { + if (event.which === $.deck.getOptions().keys.myExtensionKeycode) { + // Rock out + } + }); + }); + */ + initialize: 'deck.init' + }, + + options = {}, + $d = $(document), + + /* + Internal function. Updates slide and container classes based on which + slide is the current slide. + */ + updateStates = function() { + var oc = options.classes, + osc = options.selectors.container, + old = $container.data('onSlide'), + $all = $(); + + // Container state + $container.removeClass(oc.onPrefix + old) + .addClass(oc.onPrefix + current) + .data('onSlide', current); + + // Remove and re-add child-current classes for nesting + $('.' + oc.current).parentsUntil(osc).removeClass(oc.childCurrent); + slides[current].parentsUntil(osc).addClass(oc.childCurrent); + + // Remove previous states + $.each(slides, function(i, el) { + $all = $all.add(el); + }); + $all.removeClass([ + oc.before, + oc.previous, + oc.current, + oc.next, + oc.after + ].join(" ")); + + // Add new states back in + slides[current].addClass(oc.current); + if (current > 0) { + slides[current-1].addClass(oc.previous); + } + if (current + 1 < slides.length) { + slides[current+1].addClass(oc.next); + } + if (current > 1) { + $.each(slides.slice(0, current - 1), function(i, el) { + el.addClass(oc.before); + }); + } + if (current + 2 < slides.length) { + $.each(slides.slice(current+2), function(i, el) { + el.addClass(oc.after); + }); + } + }, + + /* Methods exposed in the jQuery.deck namespace */ + methods = { + + /* + jQuery.deck(selector, options) + + selector: string | jQuery | array + options: object, optional + + Initializes the deck, using each element matched by selector as a slide. + May also be passed an array of string selectors or jQuery objects, in + which case each selector in the array is considered a slide. The second + parameter is an optional options object which will extend the default + values. + + $.deck('.slide'); + + or + + $.deck([ + '#first-slide', + '#second-slide', + '#etc' + ]); + */ + init: function(elements, opts) { + var startTouch, + tolerance, + esp = function(e) { + e.stopPropagation(); + }; + + options = $.extend(true, {}, $[deck].defaults, opts); + slides = []; + current = 0; + $container = $(options.selectors.container); + tolerance = options.touch.swipeTolerance; + + // Pre init event for preprocessing hooks + $d.trigger(events.beforeInitialize); + + // Hide the deck while states are being applied to kill transitions + $container.addClass(options.classes.loading); + + // Fill slides array depending on parameter type + if ($.isArray(elements)) { + $.each(elements, function(i, e) { + slides.push($(e)); + }); + } + else { + $(elements).each(function(i, e) { + slides.push($(e)); + }); + } + + /* Remove any previous bindings, and rebind key events */ + $d.unbind('keydown.deck').bind('keydown.deck', function(e) { + if (e.which === options.keys.next || $.inArray(e.which, options.keys.next) > -1) { + methods.next(); + e.preventDefault(); + } + else if (e.which === options.keys.previous || $.inArray(e.which, options.keys.previous) > -1) { + methods.prev(); + e.preventDefault(); + } + }); + + /* Bind touch events for swiping between slides on touch devices */ + $container.unbind('touchstart.deck').bind('touchstart.deck', function(e) { + if (!startTouch) { + startTouch = $.extend({}, e.originalEvent.targetTouches[0]); + } + }) + .unbind('touchmove.deck').bind('touchmove.deck', function(e) { + $.each(e.originalEvent.changedTouches, function(i, t) { + if (startTouch && t.identifier === startTouch.identifier) { + if (t.screenX - startTouch.screenX > tolerance || t.screenY - startTouch.screenY > tolerance) { + $[deck]('prev'); + startTouch = undefined; + } + else if (t.screenX - startTouch.screenX < -1 * tolerance || t.screenY - startTouch.screenY < -1 * tolerance) { + $[deck]('next'); + startTouch = undefined; + } + return false; + } + }); + e.preventDefault(); + }) + .unbind('touchend.deck').bind('touchend.deck', function(t) { + $.each(t.originalEvent.changedTouches, function(i, t) { + if (startTouch && t.identifier === startTouch.identifier) { + startTouch = undefined; + } + }); + }) + .scrollLeft(0).scrollTop(0) + /* Stop propagation of key events within editable elements of slides */ + .undelegate('input, textarea, select, button, meter, progress, [contentEditable]', 'keydown', esp) + .delegate('input, textarea, select, button, meter, progress, [contentEditable]', 'keydown', esp); + + /* + Kick iframe videos, which dont like to redraw w/ transforms. + Remove this if Webkit ever fixes it. + */ + $.each(slides, function(i, $el) { + $el.unbind('webkitTransitionEnd.deck').bind('webkitTransitionEnd.deck', + function(event) { + if ($el.hasClass($[deck]('getOptions').classes.current)) { + var embeds = $(this).find('iframe').css('opacity', 0); + window.setTimeout(function() { + embeds.css('opacity', 1); + }, 100); + } + }); + }); + + if (slides.length) { + updateStates(); + } + + // Show deck again now that slides are in place + $container.removeClass(options.classes.loading); + $d.trigger(events.initialize); + }, + + /* + jQuery.deck('go', index) + + index: integer | string + + Moves to the slide at the specified index if index is a number. Index is + 0-based, so $.deck('go', 0); will move to the first slide. If index is a + string this will move to the slide with the specified id. If index is out + of bounds or doesn't match a slide id the call is ignored. + */ + go: function(index) { + var e = $.Event(events.change), + ndx; + + /* Number index, easy. */ + if (typeof index === 'number' && index >= 0 && index < slides.length) { + ndx = index; + } + /* Id string index, search for it and set integer index */ + else if (typeof index === 'string') { + $.each(slides, function(i, $slide) { + if ($slide.attr('id') === index) { + ndx = i; + return false; + } + }); + }; + + /* Out of bounds, id doesn't exist, illegal input, eject */ + if (typeof ndx === 'undefined') return; + + $d.trigger(e, [current, ndx]); + if (e.isDefaultPrevented()) { + /* Trigger the event again and undo the damage done by extensions. */ + $d.trigger(events.change, [ndx, current]); + } + else { + current = ndx; + updateStates(); + } + }, + + /* + jQuery.deck('next') + + Moves to the next slide. If the last slide is already active, the call + is ignored. + */ + next: function() { + methods.go(current+1); + }, + + /* + jQuery.deck('prev') + + Moves to the previous slide. If the first slide is already active, the + call is ignored. + */ + prev: function() { + methods.go(current-1); + }, + + /* + jQuery.deck('getSlide', index) + + index: integer, optional + + Returns a jQuery object containing the slide at index. If index is not + specified, the current slide is returned. + */ + getSlide: function(index) { + var i = typeof index !== 'undefined' ? index : current; + if (typeof i != 'number' || i < 0 || i >= slides.length) return null; + return slides[i]; + }, + + /* + jQuery.deck('getSlides') + + Returns all slides as an array of jQuery objects. + */ + getSlides: function() { + return slides; + }, + + /* + jQuery.deck('getContainer') + + Returns a jQuery object containing the deck container as defined by the + container option. + */ + getContainer: function() { + return $container; + }, + + /* + jQuery.deck('getOptions') + + Returns the options object for the deck, including any overrides that + were defined at initialization. + */ + getOptions: function() { + return options; + }, + + /* + jQuery.deck('extend', name, method) + + name: string + method: function + + Adds method to the deck namespace with the key of name. This doesn’t + give access to any private member data — public methods must still be + used within method — but lets extension authors piggyback on the deck + namespace rather than pollute jQuery. + + $.deck('extend', 'alert', function(msg) { + alert(msg); + }); + + // Alerts 'boom' + $.deck('alert', 'boom'); + */ + extend: function(name, method) { + methods[name] = method; + } + }; + + /* jQuery extension */ + $[deck] = function(method, arg) { + if (methods[method]) { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } + else { + return methods.init(method, arg); + } + }; + + /* + The default settings object for a deck. All deck extensions should extend + this object to add defaults for any of their options. + + options.classes.after + This class is added to all slides that appear after the 'next' slide. + + options.classes.before + This class is added to all slides that appear before the 'previous' + slide. + + options.classes.childCurrent + This class is added to all elements in the DOM tree between the + 'current' slide and the deck container. For standard slides, this is + mostly seen and used for nested slides. + + options.classes.current + This class is added to the current slide. + + options.classes.loading + This class is applied to the deck container during loading phases and is + primarily used as a way to short circuit transitions between states + where such transitions are distracting or unwanted. For example, this + class is applied during deck initialization and then removed to prevent + all the slides from appearing stacked and transitioning into place + on load. + + options.classes.next + This class is added to the slide immediately following the 'current' + slide. + + options.classes.onPrefix + This prefix, concatenated with the current slide index, is added to the + deck container as you change slides. + + options.classes.previous + This class is added to the slide immediately preceding the 'current' + slide. + + options.selectors.container + Elements matched by this CSS selector will be considered the deck + container. The deck container is used to scope certain states of the + deck, as with the onPrefix option, or with extensions such as deck.goto + and deck.menu. + + options.keys.next + The numeric keycode used to go to the next slide. + + options.keys.previous + The numeric keycode used to go to the previous slide. + + options.touch.swipeTolerance + The number of pixels the users finger must travel to produce a swipe + gesture. + */ + $[deck].defaults = { + classes: { + after: 'deck-after', + before: 'deck-before', + childCurrent: 'deck-child-current', + current: 'deck-current', + loading: 'deck-loading', + next: 'deck-next', + onPrefix: 'on-slide-', + previous: 'deck-previous' + }, + + selectors: { + container: '.deck-container' + }, + + keys: { + // enter, space, page down, right arrow, down arrow, + next: [13, 32, 34, 39, 40], + // backspace, page up, left arrow, up arrow + previous: [8, 33, 37, 38] + }, + + touch: { + swipeTolerance: 60 + } + }; + + $d.ready(function() { + $('html').addClass('ready'); + }); + + /* + FF + Transforms + Flash video don't get along... + Firefox will reload and start playing certain videos after a + transform. Blanking the src when a previously shown slide goes out + of view prevents this. + */ + $d.bind('deck.change', function(e, from, to) { + var oldFrames = $[deck]('getSlide', from).find('iframe'), + newFrames = $[deck]('getSlide', to).find('iframe'); + + oldFrames.each(function() { + var $this = $(this), + curSrc = $this.attr('src'); + + if(curSrc) { + $this.data('deck-src', curSrc).attr('src', ''); + } + }); + + newFrames.each(function() { + var $this = $(this), + originalSrc = $this.data('deck-src'); + + if (originalSrc) { + $this.attr('src', originalSrc); + } + }); + }); +})(jQuery, 'deck', document); diff --git a/eo/tutorial/Parallelization/js/deck.goto.js b/eo/tutorial/Parallelization/js/deck.goto.js new file mode 100644 index 000000000..eedba10b1 --- /dev/null +++ b/eo/tutorial/Parallelization/js/deck.goto.js @@ -0,0 +1,170 @@ +/*! +Deck JS - deck.goto +Copyright (c) 2011 Caleb Troughton +Dual licensed under the MIT license and GPL license. +https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt +https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt +*/ + +/* +This module adds the necessary methods and key bindings to show and hide a form +for jumping to any slide number/id in the deck (and processes that form +accordingly). The form-showing state is indicated by the presence of a class on +the deck container. +*/ +(function($, deck, undefined) { + var $d = $(document); + + /* + Extends defaults/options. + + options.classes.goto + This class is added to the deck container when showing the Go To Slide + form. + + options.selectors.gotoDatalist + The element that matches this selector is the datalist element that will + be populated with options for each of the slide ids. In browsers that + support the datalist element, this provides a drop list of slide ids to + aid the user in selecting a slide. + + options.selectors.gotoForm + The element that matches this selector is the form that is submitted + when a user hits enter after typing a slide number/id in the gotoInput + element. + + options.selectors.gotoInput + The element that matches this selector is the text input field for + entering a slide number/id in the Go To Slide form. + + options.keys.goto + The numeric keycode used to show the Go To Slide form. + + options.countNested + If false, only top level slides will be counted when entering a + slide number. + */ + $.extend(true, $[deck].defaults, { + classes: { + goto: 'deck-goto' + }, + + selectors: { + gotoDatalist: '#goto-datalist', + gotoForm: '.goto-form', + gotoInput: '#goto-slide' + }, + + keys: { + goto: 71 // g + }, + + countNested: true + }); + + /* + jQuery.deck('showGoTo') + + Shows the Go To Slide form by adding the class specified by the goto class + option to the deck container. + */ + $[deck]('extend', 'showGoTo', function() { + $[deck]('getContainer').addClass($[deck]('getOptions').classes.goto); + $($[deck]('getOptions').selectors.gotoInput).focus(); + }); + + /* + jQuery.deck('hideGoTo') + + Hides the Go To Slide form by removing the class specified by the goto class + option from the deck container. + */ + $[deck]('extend', 'hideGoTo', function() { + $($[deck]('getOptions').selectors.gotoInput).blur(); + $[deck]('getContainer').removeClass($[deck]('getOptions').classes.goto); + }); + + /* + jQuery.deck('toggleGoTo') + + Toggles between showing and hiding the Go To Slide form. + */ + $[deck]('extend', 'toggleGoTo', function() { + $[deck]($[deck]('getContainer').hasClass($[deck]('getOptions').classes.goto) ? 'hideGoTo' : 'showGoTo'); + }); + + $d.bind('deck.init', function() { + var opts = $[deck]('getOptions'), + $datalist = $(opts.selectors.gotoDatalist), + slideTest = $.map([ + opts.classes.before, + opts.classes.previous, + opts.classes.current, + opts.classes.next, + opts.classes.after + ], function(el, i) { + return '.' + el; + }).join(', '), + rootCounter = 1; + + // Bind key events + $d.unbind('keydown.deckgoto').bind('keydown.deckgoto', function(e) { + var key = $[deck]('getOptions').keys.goto; + + if (e.which === key || $.inArray(e.which, key) > -1) { + e.preventDefault(); + $[deck]('toggleGoTo'); + } + }); + + /* Populate datalist and work out countNested*/ + $.each($[deck]('getSlides'), function(i, $slide) { + var id = $slide.attr('id'), + $parentSlides = $slide.parentsUntil(opts.selectors.container, slideTest); + + if (id) { + $datalist.append('