Skip to content

Commit

Permalink
WIP: OpenMP state #5, algebra #6 and resizer #7
Browse files Browse the repository at this point in the history
State splits a given Range into an InnerState, one for each thread. The algebra's for_eachN calls for_eachN in parallel on each part, using the InnerState's algebra. There's an openmp_wrapper to parallelize the system function; this needs a way to pass on the offset.

The idea was that this design should allow using OpenMP on each MPI node with a single-threaded inner_state: mpi_state< openmp_state< inner_state > > with mpi_wrapper(openmp_wrapper(system_function))
  • Loading branch information
Pascal Germroth committed Jun 18, 2013
1 parent 2277080 commit 6d8e71d
Show file tree
Hide file tree
Showing 7 changed files with 395 additions and 49 deletions.
47 changes: 41 additions & 6 deletions boost/numeric/odeint/external/openmp/openmp_algebra.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,27 @@

#include <boost/numeric/odeint/algebra/norm_result_type.hpp>
#include <boost/numeric/odeint/util/n_ary_helper.hpp>
#include "openmp_state.hpp"

namespace boost {
namespace numeric {
namespace odeint {

/** \brief OpenMP-parallelized algebra.
/** \brief Basic OpenMP-parallelized algebra.
*
* Requires `s.size()` and `s[n]`, i.e. a Random Access Container.
*/
struct openmp_algebra
struct basic_openmp_algebra
{

// FIXME: _Pragma is C++11.
#define OPENMP_ALGEBRA(n) \
#define BOOST_ODEINT_GEN_BODY(n) \
const size_t len = s0.size(); \
_Pragma("omp parallel for") \
_Pragma("omp parallel for schedule(dynamic)") \
for( size_t i = 0 ; i < len ; i++ ) \
op( BOOST_PP_ENUM_BINARY_PARAMS(n, s, [i] BOOST_PP_INTERCEPT) );
BOOST_ODEINT_GEN_FOR_EACH(OPENMP_ALGEBRA)
#undef OPENMP_ALGEBRA
BOOST_ODEINT_GEN_FOR_EACH(BOOST_ODEINT_GEN_BODY)
#undef BOOST_ODEINT_GEN_BODY


template< class S >
Expand All @@ -58,6 +59,40 @@ BOOST_ODEINT_GEN_FOR_EACH(OPENMP_ALGEBRA)
};


/** \brief OpenMP-parallelized algebra, wrapping another, non-parallelized algebra.
*/
template< class InnerAlgebra >
struct openmp_algebra
{

// FIXME: _Pragma is C++11.
#define BOOST_ODEINT_GEN_BODY(n) \
const size_t len = s0.size(); \
_Pragma("omp parallel for schedule(static,1)") \
for( size_t i = 0 ; i < len ; i++ ) \
InnerAlgebra::for_each##n( \
BOOST_PP_ENUM_BINARY_PARAMS(n, s, [i] BOOST_PP_INTERCEPT) \
);
BOOST_ODEINT_GEN_FOR_EACH(BOOST_ODEINT_GEN_BODY)
#undef BOOST_ODEINT_GEN_BODY


template< class InnerState >
static typename norm_result_type< T >::type norm_inf( const openmp_state< InnerState > &s )
{
using std::max;
using std::abs;
typedef typename norm_result_type< S >::type result_type;
result_type init = static_cast< result_type >( 0 );
# pragma omp parallel for reduction(max: init) schedule(static,1)
for( size_t i = 0 ; i < s.size() ; i++ )
init = max( init , InnerAlgebra::norm_inf( s[i] ) );
return init;
}

}


}
}
}
Expand Down
40 changes: 40 additions & 0 deletions boost/numeric/odeint/external/openmp/openmp_algebra_dispatcher.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
[auto_generated]
boost/numeric/odeint/external/openmp/openmp_algebra_dispatcher.hpp
[begin_description]
Algebra dispatcher to automatically chose suitable algebra.
[end_description]
Copyright 2009-2013 Karsten Ahnert
Copyright 2009-2013 Mario Mulansky
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or
copy at http://www.boost.org/LICENSE_1_0.txt)
*/

#ifndef BOOST_NUMERIC_ODEINT_OPENMP_OPENMP_ALGEBRA_DISPATCHER_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_OPENMP_OPENMP_ALGEBRA_DISPATCHER_HPP_INCLUDED

#include <boost/numeric/odeint/external/openmp/openmp_algebra.hpp>
#include <boost/numeric/odeint/algebra/algebra_dispatcher.hpp>
#include "openmp_state.hpp"

namespace boost {
namespace numeric {
namespace odeint {

template< class Inner >
struct algebra_dispatcher< openmp_state< Inner > >
{
typedef openmp_algebra<
algebra_dispatcher< Inner >::algebra_type
> algebra_type;
};

}
}
}

#endif
80 changes: 80 additions & 0 deletions boost/numeric/odeint/external/openmp/openmp_resize.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
[auto_generated]
boost/numeric/odeint/external/openmp/openmp_resize.hpp
[begin_description]
Delegate resizing of OpenMP-state to inner state.
[end_description]
Copyright 2009-2011 Karsten Ahnert
Copyright 2009-2011 Mario Mulansky
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or
copy at http://www.boost.org/LICENSE_1_0.txt)
*/


#ifndef BOOST_NUMERIC_ODEINT_EXTERNAL_OPENMP_OPENMP_RESIZE_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_EXTERNAL_OPENMP_OPENMP_RESIZE_HPP_INCLUDED

#include <boost/numeric/odeint/util/resizer.hpp>
#include "openmp_state.hpp"

namespace boost {
namespace numeric {
namespace odeint {


template< class InnerState >
struct is_resizeable< openmp_state< InnerState > >
{
const static bool value = is_resizable< InnerState >::value;
};


template< class InnerState1, class InnerState2 >
struct same_size_impl< openmp_state< InnerState1 > , openmp_state< InnerState2 > >
{
static bool same_size( const openmp_state< InnerState1 > &x , const openmp_state< InnerState2 > &y )
{
if( x.size() != y.size() ) return false;
for( size_t i = 0 ; i != x.size() ; i++ )
if( !same_size(x[i], y[i]) ) return false;
return true;
}
};


template< class InnerStateOut, class InnerStateIn >
struct resize_impl< openmp_state< InnerStateOut > , openmp_state< InnerStateIn > >
{
static void resize( openmp_state< InnerStateOut > &x , const openmp_state< InnerStateIn > &y )
{
x.m_parts.resize( y.size() );
# pragma omp parallel for schedule(static,1)
for(size_t i = 0 ; i != x.size() ; i++)
resize( x[i], y[i] );
}
};


template< class InnerStateIn , class InnerStateOut >
struct copy_impl< openmp_state< InnerStateIn >, openmp_state< InnerStateOut > >
{
static void copy( const openmp_state< InnerStateIn > &from, openmp_state< InnerStateOut > &to )
{
# pragma omp parallel for schedule(static,1)
for(size_t i = 0 ; i != from.size() ; i++)
copy( from[i], to[i] );
}
};


} // odeint
} // numeric
} // boost


#endif // BOOST_NUMERIC_ODEINT_EXTERNAL_THRUST_THRUST_RESIZE_HPP_INCLUDED

81 changes: 81 additions & 0 deletions boost/numeric/odeint/external/openmp/openmp_state.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
[auto_generated]
boost/numeric/odeint/external/openmp/openmp_state.hpp
[begin_description]
Wrappers for OpenMP.
[end_description]
Copyright 2009-2011 Karsten Ahnert
Copyright 2009-2011 Mario Mulansky
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or
copy at http://www.boost.org/LICENSE_1_0.txt)
*/


#ifndef BOOST_NUMERIC_ODEINT_EXTERNAL_OPENMP_OPENMP_STATE_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_EXTERNAL_OPENMP_OPENMP_STATE_HPP_INCLUDED

#include <omp.h>
#include <vector>
#include <algorithm>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/numeric/odeint/util/copy.hpp>
#include <boost/numeric/odeint/util/resize.hpp>


namespace boost {
namespace numeric {
namespace odeint {

/** \brief A container that is split into distinct parts, for threading.
*/
template< class InnerState >
struct openmp_state
{
std::vector< InnerState > &m_parts;

template< class Container >
openmp_state( const Container &data )
: m_parts( omp_get_num_threads() )
{
const size_t size = last - first;
const size_t part = size / m_parts.size();
# pragma omp parallel for schedule(static,1)
for(size_t i = 0 ; i != m_parts.size() ; i++) {
const size_t start = i * part;
const size_t end = (std::min)( (i + 1) * part, size );
const boost::sliced_range<Container> sl = boost::adaptors::slice(data, start, end);
resize( m_parts[i], sl );
copy( sl, m_parts[i] );
}
}

InnerState & operator[](size_t i)
{
return m_parts[i];
}

const InnerState & operator[](size_t i) const
{
return m_parts[i];
}

size_t size() const
{
return m_parts.size();
}

};



}
}
}


#endif

52 changes: 52 additions & 0 deletions boost/numeric/odeint/external/openmp/openmp_system.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
[auto_generated]
boost/numeric/odeint/external/openmp/openmp_state.hpp
[begin_description]
Parallelizing wrapper for system functions
[end_description]
Copyright 2009-2011 Karsten Ahnert
Copyright 2009-2011 Mario Mulansky
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or
copy at http://www.boost.org/LICENSE_1_0.txt)
*/


#ifndef BOOST_NUMERIC_ODEINT_EXTERNAL_OPENMP_OPENMP_SYSTEM_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_EXTERNAL_OPENMP_OPENMP_SYSTEM_HPP_INCLUDED

#include "openmp_state.hpp"
#include <boost/function.hpp>


template< class State, class Deriv, class Time >
struct system_function_wrapper_impl
{
typedef boost::function<void(const State &, Deriv &, const Time &)> sys_fun_t;
const sys_fun_t &f;
system_function_wrapper_impl(const sys_fun_t &f) : f(f) {}

inline void operator(const openmp_state< State > &s, openmp_state< Deriv > &d, const Time &t) const
{
# pragma omp parallel for schedule(static,1)
for(size_t i = 0 ; i < s.size() ; i++)
f(s[i], d[i], t);
}
};


template< class State, class Deriv, class Time >
inline openmp_wrapper_impl< State, Deriv, Time >
openmp_wrapper( boost::function<void(const State &, Deriv &, const Time &)> &f )
{
return openmp_wrapper_impl<State, Deriv, Time>(f);
}





#endif
Loading

0 comments on commit 6d8e71d

Please sign in to comment.