Skip to content

Commit

Permalink
Merge branch 'vectorArrayOverloads'
Browse files Browse the repository at this point in the history
  • Loading branch information
klalumiere committed Feb 5, 2017
2 parents 2dcd52d + c38d6de commit 6760a77
Show file tree
Hide file tree
Showing 4 changed files with 342 additions and 36 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ std::vector<MyStruct> allGatheredvTwo = mpiWorld().varyingAllGather(vecToSend,
receiveCounts,displacements);
```

Every functions defined for a single [POD](http://en.cppreference.com/w/cpp/concept/PODType) type is also defined for a collection of [POD](http://en.cppreference.com/w/cpp/concept/PODType)s. This collection can either be held in a `std::vector` or in a `std::array`. For instance,

```c++
const int count = 2;
if(mpiWorld().rank() == sourceIndex) {
const std::vector<MyStruct> collection(count,toSend);
mpiWorld().sendAndBlock(collection,destinationIndex);
}
if(mpiWorld().rank() == destinationIndex) {
const std::vector<MyStruct> received =
mpiWorld().receiveAndBlock<std::vector<MyStruct>>(count,sourceIndex);
}
```

# Communicator

## Identical v.s. Congruent communicators
Expand Down
132 changes: 121 additions & 11 deletions include/NiceMPI/NiceMPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ SOFTWARE. */
#ifndef NICEMPI_H
#define NICEMPI_H

#include <array>
#include <cstddef> // std::size_t
#include <memory> // unique_ptr
#include <type_traits> // std::is_pod, std::enable_if
#include <utility> // std::move
Expand All @@ -44,6 +46,46 @@ Communicator &mpiSelf(); // Forward declaration
Communicator createProxy(MPI_Comm mpiCommunicator); // Forward declaration



/** \brief std::array that contains PODs are both POD and collection, so we need to distinguish them. */
template<class T>
struct is_std_array {
static constexpr bool value = false;
};
/** \brief std::array that contains PODs are both POD and collection, so we need to distinguish them.
*Specialization*.*/
template<class T, std::size_t N>
struct is_std_array<std::array<T,N>> {
static constexpr bool value = true;
};



/** \brief Type traits that defines an alias for the type contained in a container. Usefull in ReceiveRequest.
*Specialization*.*/
template<class T>
struct to_contained_type {
using type = T;
};
/** \brief Type traits that defines an alias for the type contained in a container. Usefull in ReceiveRequest.
*Specialization*.*/
template<class T, class Allocator>
struct to_contained_type<std::vector<T,Allocator>> {
using type = typename std::vector<T,Allocator>::value_type;
};
/** \brief Type traits that defines an alias for the type contained in a container. Usefull in ReceiveRequest.
*Specialization*.*/
template<class T, std::size_t N>
struct to_contained_type<std::array<T,N>> {
using type = typename std::array<T,N>::value_type;
};
/** \brief Type traits that defines an alias for the type contained in a container. Usefull in ReceiveRequest.
*Alias*.*/
template<class T>
using to_contained_type_t = typename to_contained_type<T>::type;



/** \brief Returns after an asyncSend call, this object allows to control the status of the call. */
class SendRequest {
public:
Expand Down Expand Up @@ -73,7 +115,7 @@ template<class Type>
class ReceiveRequest {
public:
/** \brief The function asyncReceive initializes the member of the request directly. */
ReceiveRequest(): dataPtr(new Type)
ReceiveRequest(int count): data(count)
{}
/** \brief Destroys the handle \p rhs. Only there because of the
[rule of 5](http://en.cppreference.com/w/cpp/language/rule_of_three). */
Expand All @@ -98,8 +140,8 @@ class ReceiveRequest {
handleError(MPI_Wait(&value,MPI_STATUS_IGNORE));
}
/** Returns the data, assuming that the user made sure that the receiving operation was completed. */
std::unique_ptr<Type> take() {
return std::move(dataPtr);
std::vector<to_contained_type_t<Type>> take() {
return std::move(data);
}

/** \brief The function asyncReceive needs the address of \p data. */
Expand All @@ -109,7 +151,7 @@ class ReceiveRequest {
/** \brief MPI implementation. */
MPI_Request value;
/** \brief \p data to be received. */
std::unique_ptr<Type> dataPtr;
std::vector<to_contained_type_t<Type>> data;
};


Expand All @@ -132,44 +174,106 @@ class Communicator {


/** \brief Regroups the \p data of every processes in a single vector and returns it. */
template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type = true>
template<typename Type,
typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type = true
>
std::vector<Type> allGather(Type data);

/** \brief Regroups the \p data of every processes in a single vector and returns it. */
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type = true
>
std::vector<typename Collection::value_type> allGather(const Collection& data);

/** \brief Starts to receive data of type \p Type from the \p source. A \p tag can be required to be provided
with the data. \p MPI_ANY_TAG can be used. Returns a ReceiveRequest object that can be used to find out if
the data were received, or to wait until they are received and get them.*/
template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type = true>
template<typename Type,
typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type = true
>
ReceiveRequest<Type> asyncReceive(int source, int tag = 0);

/** \brief Starts to receive data of type \p Type from the \p source. A \p tag can be required to be provided
with the data. \p MPI_ANY_TAG can be used. Returns a ReceiveRequest object that can be used to find out if
the data were received, or to wait until they are received and get them.*/
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type = true
>
ReceiveRequest<Collection> asyncReceive(int count, int source, int tag = 0);

/** \brief Starts to send \p data to the \p destination. A \p tag can be required to be provided with the data.
\p MPI_ANY_TAG can be used. Returns a SendRequest object that can be used to find out if the data were sent, or
to wait until they are sent.*/
template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type = true>
template<typename Type,
typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type = true
>
SendRequest asyncSend(Type data, int destination, int tag = 0);

/** \brief Starts to send \p data to the \p destination. A \p tag can be required to be provided with the data.
\p MPI_ANY_TAG can be used. Returns a SendRequest object that can be used to find out if the data were sent, or
to wait until they are sent.*/
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type = true
>
SendRequest asyncSend(const Collection& data, int destination, int tag = 0);

/** \brief The \p source broadcast its \p data to every processes. */
template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type = true>
template<typename Type,
typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type = true
>
Type broadcast(int source, Type data);

/** \brief The \p source broadcast its \p data to every processes. */
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type = true
>
Collection broadcast(int source, Collection data);

/** \brief The \p source gathers the \p data of every processes. */
template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type = true>
template<typename Type,
typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type = true
>
std::vector<Type> gather(int source, Type data);

/** \brief The \p source gathers the \p data of every processes. */
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type = true
>
std::vector<typename Collection::value_type> gather(int source, const Collection& data);

/** \brief Wait to receive data of type \p Type from the \p source. A \p tag can be required to be provided with
the data. \p MPI_ANY_TAG can be used.*/
template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type = true>
template<typename Type,
typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type = true
>
Type receiveAndBlock(int source, int tag = 0);

/** \brief Wait to receive data of type \p Type from the \p source. A \p tag can be required to be provided with
the data. \p MPI_ANY_TAG can be used.*/
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type = true
>
Collection receiveAndBlock(int count, int source, int tag = 0);

/** \brief The \p source scatters \p sendCount of its data \p toSend to every processes. Hence, the process with
rank \p i receives the data from \p toSend[i] to toSend[i+\p sendCount].*/
template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type = true>
std::vector<Type> scatter(int source, const std::vector<Type>& toSend, int sendCount);

/** \brief Wait to send \p data to the \p destination. A \p tag can be required to be provided with
the data. \p MPI_ANY_TAG can be used.*/
template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type = true>
template<typename Type,
typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type = true
>
void sendAndBlock(Type data, int destination, int tag = 0);

/** \brief Wait to send \p data to the \p destination. A \p tag can be required to be provided with
the data. \p MPI_ANY_TAG can be used.*/
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type = true
>
void sendAndBlock(const Collection& data, int destination, int tag = 0);

/** \brief Regroups the \p data of every processes in a single vector and returns it. \p receiveCounts[i] data
is received from the process with rank \p i. These data starts at the index \p displacements[i] of the
returned vector.*/
Expand Down Expand Up @@ -217,6 +321,12 @@ class Communicator {
/** \brief Returns the \p displacements scaled by the size of \p Type.*/
template<typename Type>
static std::vector<int> createScaledDisplacements(std::vector<int> displacements);
/** \brief Initializes the collection with \p count elements. */
template<typename Type>
static std::vector<Type> initializeWithCount(std::vector<Type>, int count);
/** \brief Initializes the collection with \p count elements. */
template<typename Type, std::size_t N>
static std::array<Type,N> initializeWithCount(std::array<Type,N> a, int /*count*/);
/** \brief Returns the sum of the \p data. */
static int sum(const std::vector<int>& data);
/** \brief Implements \p varyingScatter(). */
Expand Down
99 changes: 90 additions & 9 deletions include/NiceMPI/private/NiceMPI.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,35 +54,77 @@ inline Communicator Communicator::split(int color, int key) const {
}


template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type>
template<typename Type, typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type>
inline std::vector<Type> Communicator::allGather(Type data) {
std::vector<Type> result(size());
handleError(MPI_Allgather(&data,sizeof(Type),MPI_UNSIGNED_CHAR,result.data(),sizeof(Type),MPI_UNSIGNED_CHAR,
handle.get() ));
return result;
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type>
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type
>
inline std::vector<typename Collection::value_type> Communicator::allGather(const Collection& data) {
using Type = typename Collection::value_type;
std::vector<Type> result(size()*data.size());
handleError(MPI_Allgather(data.data(),sizeof(Type)*data.size(),MPI_UNSIGNED_CHAR,result.data(),
sizeof(Type)*data.size(),MPI_UNSIGNED_CHAR, handle.get() ));
return result;
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type>
inline ReceiveRequest<Type> Communicator::asyncReceive(int source, int tag) {
ReceiveRequest<Type> r;
handleError(MPI_Irecv(r.dataPtr.get(),sizeof(Type),MPI_UNSIGNED_CHAR,source,tag,handle.get(),&r.value));
ReceiveRequest<Type> r(1);
handleError(MPI_Irecv(r.data.data(),sizeof(Type),MPI_UNSIGNED_CHAR,source,tag,handle.get(),&r.value));
return r;
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type>
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type
>
inline ReceiveRequest<Collection> Communicator::asyncReceive(int count, int source, int tag) {
using Type = typename Collection::value_type;
ReceiveRequest<Collection> r(count);
handleError(MPI_Irecv(r.data.data(),sizeof(Type)*count,MPI_UNSIGNED_CHAR,source,tag,handle.get(),&r.value));
return r;
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type>
inline SendRequest Communicator::asyncSend(Type data, int destination, int tag) {
MPI_Request x;
handleError(MPI_Isend(&data,sizeof(Type),MPI_UNSIGNED_CHAR,destination,tag,handle.get(),&x));
return SendRequest(x);
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type>
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type
>
inline SendRequest Communicator::asyncSend(const Collection& data, int destination, int tag) {
using Type = typename Collection::value_type;
MPI_Request x;
handleError(MPI_Isend(data.data(),sizeof(Type)*data.size(),MPI_UNSIGNED_CHAR,destination,tag,handle.get(),&x));
return SendRequest(x);
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type>
inline Type Communicator::broadcast(int source, Type data) {
handleError(MPI_Bcast(&data,sizeof(Type),MPI_UNSIGNED_CHAR,source,handle.get() ));
return data;
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type>
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type
>
inline Collection Communicator::broadcast(int source, Collection data) {
using Type = typename Collection::value_type;
auto sizeToBroadcast = broadcast(source,data.size());
if(rank() != source) data = initializeWithCount(Collection{},sizeToBroadcast);
handleError(MPI_Bcast(data.data(),sizeof(Type)*sizeToBroadcast,MPI_UNSIGNED_CHAR,source,handle.get() ));
return data;
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type>
inline std::vector<Type> Communicator::gather(int source, Type data) {
std::vector<Type> result;
if(rank() == source) result.resize(size());
Expand All @@ -91,13 +133,35 @@ inline std::vector<Type> Communicator::gather(int source, Type data) {
return result;
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type>
template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type
>
std::vector<typename Collection::value_type> Communicator::gather(int source, const Collection& data) {
using Type = typename Collection::value_type;
std::vector<Type> result;
if(rank() == source) result.resize(size()*data.size());
handleError(MPI_Gather(data.data(),sizeof(Type)*data.size(),MPI_UNSIGNED_CHAR,result.data(),
sizeof(Type)*data.size(),MPI_UNSIGNED_CHAR,source,handle.get() ));
return result;
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type>
inline Type Communicator::receiveAndBlock(int source, int tag) {
Type data;
handleError(MPI_Recv(&data,sizeof(Type),MPI_UNSIGNED_CHAR,source,tag,handle.get() ,MPI_STATUS_IGNORE));
return data;
}

template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type
>
Collection Communicator::receiveAndBlock(int count, int source, int tag) {
Collection data = initializeWithCount(Collection{},count);
using Type = typename Collection::value_type;
handleError(MPI_Recv(data.data(),sizeof(Type)*count,MPI_UNSIGNED_CHAR,source,tag,handle.get() ,MPI_STATUS_IGNORE));
return data;
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type>
inline std::vector<Type> Communicator::scatter(int source, const std::vector<Type>& toSend, int sendCount) {
const bool enoughDataToSend = (static_cast<int>(toSend.size()) - sendCount*size()) >= 0;
Expand All @@ -108,11 +172,19 @@ inline std::vector<Type> Communicator::scatter(int source, const std::vector<Typ
return result;
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type>
template<typename Type, typename std::enable_if<std::is_pod<Type>::value and !is_std_array<Type>::value,bool>::type>
inline void Communicator::sendAndBlock(Type data, int destination, int tag) {
handleError(MPI_Send(&data,sizeof(Type),MPI_UNSIGNED_CHAR,destination,tag,handle.get() ));
}

template<class Collection,
typename std::enable_if<std::is_pod<typename Collection::value_type>::value,bool>::type
>
inline void Communicator::sendAndBlock(const Collection& data, int destination, int tag) {
using Type = typename Collection::value_type;
handleError(MPI_Send(data.data(),sizeof(Type)*data.size(),MPI_UNSIGNED_CHAR,destination,tag,handle.get() ));
}

template<typename Type, typename std::enable_if<std::is_pod<Type>::value,bool>::type>
inline std::vector<Type> Communicator::varyingAllGather(const std::vector<Type>& data,
const std::vector<int>& receiveCounts, const std::vector<int>& displacements)
Expand Down Expand Up @@ -187,6 +259,15 @@ inline std::vector<int> Communicator::createScaledDisplacements(std::vector<int>
return displacements;
}

template<typename Type>
inline std::vector<Type> Communicator::initializeWithCount(std::vector<Type>, int count) {
return std::vector<Type>(count);
}
template<typename Type, std::size_t N>
inline std::array<Type,N> Communicator::initializeWithCount(std::array<Type,N> a, int /*count*/) {
return a;
}

inline int Communicator::sum(const std::vector<int>& data) {
int theSum = 0;
for(auto&& x: data) theSum += x;
Expand Down
Loading

0 comments on commit 6760a77

Please sign in to comment.