HW2: Added some test and fix bubblesort <number of iteration> bug
This commit is contained in:
parent
c5ffec9cca
commit
9dd3eb737f
@ -24,7 +24,18 @@
|
|||||||
#define CODE_VERSION BITONIC
|
#define CODE_VERSION BITONIC
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Value type selection
|
/*!
|
||||||
|
* Value type selection
|
||||||
|
*
|
||||||
|
* We support the following compiler types or the <cstdint> that translate to them:
|
||||||
|
* char - unsigned char
|
||||||
|
* short - unsigned short
|
||||||
|
* int - unsigned int
|
||||||
|
* long - unsigned long
|
||||||
|
* long long - unsigned long long
|
||||||
|
* float
|
||||||
|
* double
|
||||||
|
*/
|
||||||
using distValue_t = uint32_t;
|
using distValue_t = uint32_t;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -267,26 +267,26 @@ void minmax(RangeT& local, const RangeT& remote, bool keepSmall) noexcept {
|
|||||||
* @param Processes [mpi_id_t] The total number of MPI processes
|
* @param Processes [mpi_id_t] The total number of MPI processes
|
||||||
*/
|
*/
|
||||||
template<typename ShadowedT>
|
template<typename ShadowedT>
|
||||||
void distBubbletonic(ShadowedT& data, mpi_id_t Processes) {
|
void distBubbletonic(ShadowedT& data, mpi_id_t Processes, mpi_id_t rank) {
|
||||||
// Initially sort to create a half part of a bitonic sequence
|
// Initially sort to create a half part of a bitonic sequence
|
||||||
fullSort(data, ascending<SortMode::Bubbletonic>(mpi.rank(), 0));
|
fullSort(data, ascending<SortMode::Bubbletonic>(rank, 0));
|
||||||
|
|
||||||
// Sort network (O(N) iterations)
|
// Sort network (O(N) iterations)
|
||||||
for (size_t step = 0; step < static_cast<size_t>(Processes-1); ++step) {
|
for (size_t step = 0; step < static_cast<size_t>(Processes); ++step) {
|
||||||
// Find out exchange configuration
|
// Find out exchange configuration
|
||||||
auto part = partner<SortMode::Bubbletonic>(mpi.rank(), step);
|
auto part = partner<SortMode::Bubbletonic>(rank, step);
|
||||||
auto ks = keepSmall<SortMode::Bubbletonic>(mpi.rank(), part, Processes);
|
auto ks = keepSmall<SortMode::Bubbletonic>(rank, part, Processes);
|
||||||
if ( isActive(mpi.rank(), Processes) &&
|
if ( isActive(rank, Processes) &&
|
||||||
isActive(part, Processes) ) {
|
isActive(part, Processes) ) {
|
||||||
// Exchange with partner, keep nim-or-max and sort - O(N)
|
// Exchange with partner, keep nim-or-max and sort - O(N)
|
||||||
mpi.exchange(part, data.getActive(), data.getShadow(), step);
|
mpi.exchange(part, data.getActive(), data.getShadow(), step);
|
||||||
minmax(data.getActive(), data.getShadow(), ks);
|
minmax(data.getActive(), data.getShadow(), ks);
|
||||||
elbowSort(data, ascending<SortMode::Bubbletonic>(mpi.rank(), Processes));
|
elbowSort(data, ascending<SortMode::Bubbletonic>(rank, Processes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invert if the node was descending.
|
// Invert if the node was descending.
|
||||||
if (!ascending<SortMode::Bubbletonic>(mpi.rank(), 0))
|
if (!ascending<SortMode::Bubbletonic>(rank, 0))
|
||||||
elbowSort(data, true);
|
elbowSort(data, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -304,9 +304,9 @@ void distBubbletonic(ShadowedT& data, mpi_id_t Processes) {
|
|||||||
* @param Processes [mpi_id_t] The total number of MPI processes
|
* @param Processes [mpi_id_t] The total number of MPI processes
|
||||||
*/
|
*/
|
||||||
template<typename ShadowedT>
|
template<typename ShadowedT>
|
||||||
void distBitonic(ShadowedT& data, mpi_id_t Processes) {
|
void distBitonic(ShadowedT& data, mpi_id_t Processes, mpi_id_t rank) {
|
||||||
// Initially sort to create a half part of a bitonic sequence
|
// Initially sort to create a half part of a bitonic sequence
|
||||||
fullSort(data, ascending<SortMode::Bitonic>(mpi.rank(), 0));
|
fullSort(data, ascending<SortMode::Bitonic>(rank, 0));
|
||||||
|
|
||||||
// Run through sort network using elbow-sort ( O(LogN * LogN) iterations )
|
// Run through sort network using elbow-sort ( O(LogN * LogN) iterations )
|
||||||
auto p = static_cast<uint32_t>(std::log2(Processes));
|
auto p = static_cast<uint32_t>(std::log2(Processes));
|
||||||
@ -314,14 +314,14 @@ void distBitonic(ShadowedT& data, mpi_id_t Processes) {
|
|||||||
for (size_t step = depth; step > 0;) {
|
for (size_t step = depth; step > 0;) {
|
||||||
--step;
|
--step;
|
||||||
// Find out exchange configuration
|
// Find out exchange configuration
|
||||||
auto part = partner<SortMode::Bitonic>(mpi.rank(), step);
|
auto part = partner<SortMode::Bitonic>(rank, step);
|
||||||
auto ks = keepSmall<SortMode::Bitonic>(mpi.rank(), part, depth);
|
auto ks = keepSmall<SortMode::Bitonic>(rank, part, depth);
|
||||||
// Exchange with partner, keep nim-or-max
|
// Exchange with partner, keep nim-or-max
|
||||||
mpi.exchange(part, data.getActive(), data.getShadow(), (depth << 8) | step);
|
mpi.exchange(part, data.getActive(), data.getShadow(), (depth << 8) | step);
|
||||||
minmax(data.getActive(), data.getShadow(), ks);
|
minmax(data.getActive(), data.getShadow(), ks);
|
||||||
}
|
}
|
||||||
// sort - O(N)
|
// sort - O(N)
|
||||||
elbowSort (data, ascending<SortMode::Bitonic>(mpi.rank(), depth));
|
elbowSort (data, ascending<SortMode::Bitonic>(rank, depth));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,68 +15,94 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <mpi.h>
|
#include <mpi.h>
|
||||||
|
|
||||||
//#include "matrix.hpp"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
template <typename T> struct MPI_TypeMapper;
|
|
||||||
|
|
||||||
// Specializations for supported types
|
/*
|
||||||
|
* MPI_<type> dispatcher mechanism
|
||||||
|
*/
|
||||||
|
template <typename T> struct MPI_TypeMapper { };
|
||||||
|
|
||||||
template <> struct MPI_TypeMapper<char> { static MPI_Datatype getType() { return MPI_CHAR; } };
|
template <> struct MPI_TypeMapper<char> { static MPI_Datatype getType() { return MPI_CHAR; } };
|
||||||
template <> struct MPI_TypeMapper<unsigned char> { static MPI_Datatype getType() { return MPI_UNSIGNED_CHAR; } };
|
|
||||||
|
|
||||||
template <> struct MPI_TypeMapper<short> { static MPI_Datatype getType() { return MPI_SHORT; } };
|
template <> struct MPI_TypeMapper<short> { static MPI_Datatype getType() { return MPI_SHORT; } };
|
||||||
template <> struct MPI_TypeMapper<int> { static MPI_Datatype getType() { return MPI_INT; } };
|
template <> struct MPI_TypeMapper<int> { static MPI_Datatype getType() { return MPI_INT; } };
|
||||||
template <> struct MPI_TypeMapper<long> { static MPI_Datatype getType() { return MPI_LONG; } };
|
template <> struct MPI_TypeMapper<long> { static MPI_Datatype getType() { return MPI_LONG; } };
|
||||||
template <> struct MPI_TypeMapper<long long> { static MPI_Datatype getType() { return MPI_LONG_LONG; } };
|
template <> struct MPI_TypeMapper<long long> { static MPI_Datatype getType() { return MPI_LONG_LONG; } };
|
||||||
|
template <> struct MPI_TypeMapper<unsigned char> { static MPI_Datatype getType() { return MPI_UNSIGNED_CHAR; } };
|
||||||
template <> struct MPI_TypeMapper<unsigned short>{ static MPI_Datatype getType() { return MPI_UNSIGNED_SHORT; } };
|
template <> struct MPI_TypeMapper<unsigned short>{ static MPI_Datatype getType() { return MPI_UNSIGNED_SHORT; } };
|
||||||
template <> struct MPI_TypeMapper<unsigned int> { static MPI_Datatype getType() { return MPI_UNSIGNED; } };
|
template <> struct MPI_TypeMapper<unsigned int> { static MPI_Datatype getType() { return MPI_UNSIGNED; } };
|
||||||
template <> struct MPI_TypeMapper<unsigned long> { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG; } };
|
template <> struct MPI_TypeMapper<unsigned long> { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG; } };
|
||||||
template <> struct MPI_TypeMapper<unsigned long long> { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG_LONG; } };
|
template <> struct MPI_TypeMapper<unsigned long long> { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG_LONG; } };
|
||||||
|
|
||||||
template <> struct MPI_TypeMapper<float> { static MPI_Datatype getType() { return MPI_FLOAT; } };
|
template <> struct MPI_TypeMapper<float> { static MPI_Datatype getType() { return MPI_FLOAT; } };
|
||||||
template <> struct MPI_TypeMapper<double> { static MPI_Datatype getType() { return MPI_DOUBLE; } };
|
template <> struct MPI_TypeMapper<double> { static MPI_Datatype getType() { return MPI_DOUBLE; } };
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* MPI wrapper type to provide MPI functionality and RAII to MPI as a resource
|
||||||
|
*
|
||||||
|
* @tparam TID The MPI type for process id [default: int]
|
||||||
|
*/
|
||||||
template<typename TID = int>
|
template<typename TID = int>
|
||||||
struct MPI_t {
|
struct MPI_t {
|
||||||
using ID_t = TID; // Export TID type (currently int defined by the standard)
|
using ID_t = TID; // Export TID type (currently int defined by the standard)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Initializes the MPI environment, must called from each process
|
||||||
|
*
|
||||||
|
* @param argc [int*] POINTER to main's argc argument
|
||||||
|
* @param argv [car***] POINTER to main's argv argument
|
||||||
|
*/
|
||||||
void init(int *argc, char ***argv) {
|
void init(int *argc, char ***argv) {
|
||||||
// Initialize the MPI environment
|
// Initialize the MPI environment
|
||||||
MPI_Init(argc, argv);
|
int err;
|
||||||
|
if ((err = MPI_Init(argc, argv)) != MPI_SUCCESS)
|
||||||
|
mpi_throw(err, "(MPI) MPI_Init() - ");
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
|
|
||||||
// Get the number of processes
|
// Get the number of processes
|
||||||
int size_value, rank_value;
|
int size_value, rank_value;
|
||||||
MPI_Comm_size(MPI_COMM_WORLD, &size_value);
|
if ((err = MPI_Comm_size(MPI_COMM_WORLD, &size_value)) != MPI_SUCCESS)
|
||||||
MPI_Comm_rank(MPI_COMM_WORLD, &rank_value);
|
mpi_throw(err, "(MPI) MPI_Comm_size() - ");
|
||||||
|
if ((err = MPI_Comm_rank(MPI_COMM_WORLD, &rank_value)) != MPI_SUCCESS)
|
||||||
|
mpi_throw(err, "(MPI) MPI_Comm_rank() - ");
|
||||||
size_ = static_cast<ID_t>(size_value);
|
size_ = static_cast<ID_t>(size_value);
|
||||||
rank_ = static_cast<ID_t>(rank_value);
|
rank_ = static_cast<ID_t>(rank_value);
|
||||||
|
|
||||||
// Get the name of the processor
|
// Get the name of the processor
|
||||||
char processor_name[MPI_MAX_PROCESSOR_NAME];
|
char processor_name[MPI_MAX_PROCESSOR_NAME];
|
||||||
int name_len;
|
int name_len;
|
||||||
MPI_Get_processor_name(processor_name, &name_len);
|
if ((err = MPI_Get_processor_name(processor_name, &name_len)) != MPI_SUCCESS)
|
||||||
|
mpi_throw(err, "(MPI) MPI_Get_processor_name() - ");
|
||||||
name_ = std::string (processor_name, name_len);
|
name_ = std::string (processor_name, name_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Exchange data with partner as part of the sorting network of both bubbletonic or bitonic
|
||||||
|
* sorting algorithms.
|
||||||
|
*
|
||||||
|
* This function matches a transmit and a receive in order for fully exchanged data between
|
||||||
|
* current node and partner.
|
||||||
|
*
|
||||||
|
* @tparam T The inner valur type used in buffer
|
||||||
|
*
|
||||||
|
* @param partner [mpi_id_t] The partner for the exchange
|
||||||
|
* @param send_data [std::vector<T>] Reference to local data to send
|
||||||
|
* @param recv_data [std::vector<T>] Reference to buffer to receive data from partner
|
||||||
|
* @param tag [int] The tag to use for the MPI communication
|
||||||
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void exchange(ID_t partner, const std::vector<T>& send_data, std::vector<T>& recv_data, int tag) {
|
void exchange(ID_t partner, const std::vector<T>& send_data, std::vector<T>& recv_data, int tag) {
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
|
||||||
MPI_Status status;
|
|
||||||
MPI_Datatype datatype = MPI_TypeMapper<T>::getType();
|
MPI_Datatype datatype = MPI_TypeMapper<T>::getType();
|
||||||
int send_count = static_cast<int>(send_data.size());
|
int send_count = static_cast<int>(send_data.size());
|
||||||
int err = MPI_Sendrecv(
|
MPI_Status status;
|
||||||
|
int err;
|
||||||
|
if ((err = MPI_Sendrecv(
|
||||||
send_data.data(), send_count, datatype, partner, tag,
|
send_data.data(), send_count, datatype, partner, tag,
|
||||||
recv_data.data(), send_count, datatype, partner, tag,
|
recv_data.data(), send_count, datatype, partner, tag,
|
||||||
MPI_COMM_WORLD, &status
|
MPI_COMM_WORLD, &status
|
||||||
);
|
)) != MPI_SUCCESS)
|
||||||
if (err != MPI_SUCCESS) {
|
mpi_throw(err, "(MPI) MPI_Sendrecv() - ");
|
||||||
char err_msg[MPI_MAX_ERROR_STRING];
|
|
||||||
int msg_len;
|
|
||||||
MPI_Error_string(err, err_msg, &msg_len);
|
|
||||||
throw std::runtime_error("(MPI) MPI_Sendrecv() - " + std::string (err_msg) + '\n');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
@ -84,26 +110,65 @@ struct MPI_t {
|
|||||||
[[nodiscard]] ID_t size() const noexcept { return size_; }
|
[[nodiscard]] ID_t size() const noexcept { return size_; }
|
||||||
[[nodiscard]] const std::string& name() const noexcept { return name_; }
|
[[nodiscard]] const std::string& name() const noexcept { return name_; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Finalized the MPI
|
||||||
|
*/
|
||||||
void finalize() {
|
void finalize() {
|
||||||
// Finalize the MPI environment
|
// Finalize the MPI environment
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
MPI_Finalize();
|
MPI_Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! RAII MPI finalization
|
||||||
~MPI_t() {
|
~MPI_t() {
|
||||||
// Finalize the MPI environment even on unexpected errors
|
// Finalize the MPI environment even on unexpected errors
|
||||||
if (initialized_)
|
if (initialized_)
|
||||||
MPI_Finalize();
|
MPI_Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Local functionality
|
||||||
private:
|
private:
|
||||||
ID_t rank_{};
|
/*!
|
||||||
ID_t size_{};
|
* Throw exception helper. It bundles the prefix with the MPI error string retrieved by
|
||||||
std::string name_{};
|
* MPI API.
|
||||||
bool initialized_{};
|
*
|
||||||
|
* @param err The MPI error code
|
||||||
|
* @param prefixMsg The prefix text for the exception error message
|
||||||
|
*/
|
||||||
|
void mpi_throw(int err, const char* prefixMsg) {
|
||||||
|
char err_msg[MPI_MAX_ERROR_STRING];
|
||||||
|
int msg_len;
|
||||||
|
MPI_Error_string(err, err_msg, &msg_len);
|
||||||
|
throw std::runtime_error(prefixMsg + std::string (err_msg) + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined TESTING
|
||||||
|
private:
|
||||||
|
#else
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
ID_t rank_{}; //!< MPI rank of the process
|
||||||
|
ID_t size_{}; //!< MPI total size of the execution
|
||||||
|
std::string name_{}; //!< The name of the local machine
|
||||||
|
bool initialized_{}; //!< RAII helper flag
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exported data types
|
||||||
|
*/
|
||||||
extern MPI_t<> mpi;
|
extern MPI_t<> mpi;
|
||||||
using mpi_id_t = MPI_t<>::ID_t;
|
using mpi_id_t = MPI_t<>::ID_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A std::vector wrapper with 2 vectors, an active and a shadow. This type exposes the standard vector
|
||||||
|
* functionality of the active vector. The shadow can be used when we need to use the vector as mutable
|
||||||
|
* data in algorithms that can not support "in-place" editing (like elbow-sort for example)
|
||||||
|
*
|
||||||
|
* @tparam Value_t the inner data type of the vectors
|
||||||
|
*/
|
||||||
template <typename Value_t>
|
template <typename Value_t>
|
||||||
struct ShadowedVec_t {
|
struct ShadowedVec_t {
|
||||||
// STL requirements
|
// STL requirements
|
||||||
@ -148,7 +213,18 @@ struct ShadowedVec_t {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch to active vector
|
// Type accessors
|
||||||
|
const std::vector<Value_t>& getNorth() const { return North; }
|
||||||
|
const std::vector<Value_t>& getSouth() const { return South; }
|
||||||
|
std::vector<Value_t>& getActive() { return (active == north) ? North : South; }
|
||||||
|
std::vector<Value_t>& getShadow() { return (active == north) ? South : North; }
|
||||||
|
const std::vector<Value_t>& getActive() const { return (active == north) ? North : South; }
|
||||||
|
const std::vector<Value_t>& getShadow() const { return (active == north) ? South : North; }
|
||||||
|
|
||||||
|
// Switching vectors
|
||||||
|
void switch_active() { active = (active == north) ? south : north; }
|
||||||
|
|
||||||
|
// Dispatch to active vector functionality
|
||||||
Value_t& operator[](size_type index) { return getActive()[index]; }
|
Value_t& operator[](size_type index) { return getActive()[index]; }
|
||||||
const Value_t& operator[](size_type index) const { return getActive()[index]; }
|
const Value_t& operator[](size_type index) const { return getActive()[index]; }
|
||||||
|
|
||||||
@ -159,8 +235,8 @@ struct ShadowedVec_t {
|
|||||||
void push_back(Value_t&& value) { getActive().push_back(std::move(value)); }
|
void push_back(Value_t&& value) { getActive().push_back(std::move(value)); }
|
||||||
void pop_back() { getActive().pop_back(); }
|
void pop_back() { getActive().pop_back(); }
|
||||||
Value_t& front() { return getActive().front(); }
|
Value_t& front() { return getActive().front(); }
|
||||||
const Value_t& front() const { return getActive().front(); }
|
|
||||||
Value_t& back() { return getActive().back(); }
|
Value_t& back() { return getActive().back(); }
|
||||||
|
const Value_t& front() const { return getActive().front(); }
|
||||||
const Value_t& back() const { return getActive().back(); }
|
const Value_t& back() const { return getActive().back(); }
|
||||||
|
|
||||||
iterator begin() { return getActive().begin(); }
|
iterator begin() { return getActive().begin(); }
|
||||||
@ -182,46 +258,20 @@ struct ShadowedVec_t {
|
|||||||
[[nodiscard]] bool empty() const { return getActive().empty(); }
|
[[nodiscard]] bool empty() const { return getActive().empty(); }
|
||||||
|
|
||||||
void clear() { getActive().clear(); }
|
void clear() { getActive().clear(); }
|
||||||
|
|
||||||
void swap(std::vector<Value_t>& other) { getActive().swap(other); }
|
void swap(std::vector<Value_t>& other) { getActive().swap(other); }
|
||||||
|
|
||||||
// Switching vectors
|
|
||||||
void switch_active() { active = (active == north) ? south : north; }
|
|
||||||
|
|
||||||
// Accessors
|
|
||||||
const std::vector<Value_t>& getNorth() const { return North; }
|
|
||||||
const std::vector<Value_t>& getSouth() const { return South; }
|
|
||||||
std::vector<Value_t>& getActive() {
|
|
||||||
return (active == north) ? North : South;
|
|
||||||
}
|
|
||||||
const std::vector<Value_t>& getActive() const {
|
|
||||||
return (active == north) ? North : South;
|
|
||||||
}
|
|
||||||
std::vector<Value_t>& getShadow() {
|
|
||||||
return (active == north) ? South : North;
|
|
||||||
}
|
|
||||||
const std::vector<Value_t>& getShadow() const {
|
|
||||||
return (active == north) ? South : North;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comparisons
|
// Comparisons
|
||||||
bool operator== (const ShadowedVec_t& other) {
|
bool operator== (const ShadowedVec_t& other) { return getActive() == other.getActive(); }
|
||||||
return getActive() == other.getActive();
|
bool operator!= (const ShadowedVec_t& other) { return getActive() != other.getActive(); }
|
||||||
}
|
bool operator== (const std::vector<value_type>& other) { return getActive() == other; }
|
||||||
bool operator!= (const ShadowedVec_t& other) {
|
bool operator!= (const std::vector<value_type>& other) { return getActive() != other; }
|
||||||
return getActive() != other.getActive();
|
|
||||||
}
|
|
||||||
bool operator== (const std::vector<value_type>& other) {
|
|
||||||
return getActive() == other;
|
|
||||||
}
|
|
||||||
bool operator!= (const std::vector<value_type>& other) {
|
|
||||||
return getActive() != other;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Value_t> North{};
|
std::vector<Value_t> North{}; //!< Actual buffer to be used either as active or shadow
|
||||||
std::vector<Value_t> South{};
|
std::vector<Value_t> South{}; //!< Actual buffer to be used either as active or shadow
|
||||||
enum { north, south } active{north};
|
enum {
|
||||||
|
north, south
|
||||||
|
} active{north}; //!< Flag to select between North and South buffer
|
||||||
};
|
};
|
||||||
|
|
||||||
using distBuffer_t = ShadowedVec_t<distValue_t>;
|
using distBuffer_t = ShadowedVec_t<distValue_t>;
|
||||||
|
@ -136,9 +136,9 @@ int main(int argc, char* argv[]) try {
|
|||||||
logger << "Starting distributed sorting ... ";
|
logger << "Starting distributed sorting ... ";
|
||||||
timer.start();
|
timer.start();
|
||||||
#if CODE_VERSION == BUBBLETONIC
|
#if CODE_VERSION == BUBBLETONIC
|
||||||
distBubbletonic(Data, mpi.size());
|
distBubbletonic(Data, mpi.size(), mpi.rank());
|
||||||
#else
|
#else
|
||||||
distBitonic (Data, mpi.size());
|
distBitonic (Data, mpi.size(), mpi.rank());
|
||||||
#endif
|
#endif
|
||||||
timer.stop();
|
timer.stop();
|
||||||
if (mpi.rank() == 0)
|
if (mpi.rank() == 0)
|
||||||
|
@ -328,67 +328,3 @@ TEST(TdistBitonic_UT, keepsmall_test7) {
|
|||||||
EXPECT_EQ(ts_expected[node], keepSmall<SortMode::Bitonic>(node, ts_partner[node], ts_depth));
|
EXPECT_EQ(ts_expected[node], keepSmall<SortMode::Bitonic>(node, ts_partner[node], ts_depth));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
TEST(TdistBitonic_UT, distBitonic_test1) {
|
|
||||||
AllData_t ts_Data {
|
|
||||||
ShadowedVec_t (8), ShadowedVec_t (8)
|
|
||||||
};
|
|
||||||
(unsigned(std::time(nullptr)));
|
|
||||||
for (auto
|
|
||||||
std::srand& v : ts_Data) {
|
|
||||||
std::generate(v.begin(), v.end(), std::rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
distBitonic(2, ts_Data);
|
|
||||||
|
|
||||||
auto max = std::numeric_limits<ShadowedVec_t::value_type>::min();
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
EXPECT_EQ((max <= v[0]), true);
|
|
||||||
EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true);
|
|
||||||
max = v.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TdistBitonic_UT, distBitonic_test2) {
|
|
||||||
AllData_t ts_Data {
|
|
||||||
ShadowedVec_t (8), ShadowedVec_t (8), ShadowedVec_t (8), ShadowedVec_t (8)
|
|
||||||
};
|
|
||||||
|
|
||||||
std::srand(unsigned(std::time(nullptr)));
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
std::generate(v.begin(), v.end(), std::rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
distBitonic(4, ts_Data);
|
|
||||||
|
|
||||||
auto max = std::numeric_limits<ShadowedVec_t::value_type>::min();
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
EXPECT_EQ((max <= v[0]), true);
|
|
||||||
EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true);
|
|
||||||
max = v.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TdistBitonic_UT, distBitonic_test3) {
|
|
||||||
AllData_t ts_Data {
|
|
||||||
ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32),
|
|
||||||
ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32)
|
|
||||||
};
|
|
||||||
|
|
||||||
std::srand(unsigned(std::time(nullptr)));
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
std::generate(v.begin(), v.end(), std::rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
distBitonic(8, ts_Data);
|
|
||||||
|
|
||||||
auto max = std::numeric_limits<ShadowedVec_t::value_type>::min();
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
EXPECT_EQ((max <= v[0]), true);
|
|
||||||
EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true);
|
|
||||||
max = v.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -142,69 +142,3 @@ TEST(TdistBubbletonic_UT, isActive_test2) {
|
|||||||
EXPECT_EQ(isActive(8, 8), false);
|
EXPECT_EQ(isActive(8, 8), false);
|
||||||
EXPECT_EQ(isActive(9, 8), false);
|
EXPECT_EQ(isActive(9, 8), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
TEST(TdistBubbletonic_UT, distBubbletonic_test1) {
|
|
||||||
AllData_t ts_Data {
|
|
||||||
ShadowedVec_t (8), ShadowedVec_t (8)
|
|
||||||
};
|
|
||||||
|
|
||||||
std::srand(unsigned(std::time(nullptr)));
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
std::generate(v.begin(), v.end(), std::rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
distBubbletonic(2, ts_Data);
|
|
||||||
|
|
||||||
auto max = std::numeric_limits<ShadowedVec_t::value_type>::min();
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
EXPECT_EQ((max <= v[0]), true);
|
|
||||||
EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true);
|
|
||||||
max = v.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TEST(TdistBubbletonic_UT, distBubbletonic_test2) {
|
|
||||||
AllData_t ts_Data {
|
|
||||||
ShadowedVec_t (8), ShadowedVec_t (8), ShadowedVec_t (8), ShadowedVec_t (8)
|
|
||||||
};
|
|
||||||
|
|
||||||
std::srand(unsigned(std::time(nullptr)));
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
std::generate(v.begin(), v.end(), std::rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
distBubbletonic(4, ts_Data);
|
|
||||||
|
|
||||||
auto max = std::numeric_limits<ShadowedVec_t::value_type>::min();
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
EXPECT_EQ((max <= v[0]), true);
|
|
||||||
EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true);
|
|
||||||
max = v.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TdistBubbletonic_UT, distBubbletonic_test3) {
|
|
||||||
AllData_t ts_Data {
|
|
||||||
ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32),
|
|
||||||
ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32)
|
|
||||||
};
|
|
||||||
|
|
||||||
std::srand(unsigned(std::time(nullptr)));
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
std::generate(v.begin(), v.end(), std::rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
distBubbletonic(8, ts_Data);
|
|
||||||
|
|
||||||
auto max = std::numeric_limits<ShadowedVec_t::value_type>::min();
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
EXPECT_EQ((max <= v[0]), true);
|
|
||||||
EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true);
|
|
||||||
max = v.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -91,66 +91,3 @@ TEST(TdistCommonUT, elbowSort_test3) {
|
|||||||
EXPECT_EQ((ts_data == ts_expected_des), true);
|
EXPECT_EQ((ts_data == ts_expected_des), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
TEST(TdistBubbletonic_UT, distBubbletonic_test1) {
|
|
||||||
AllData_t ts_Data {
|
|
||||||
ShadowedVec_t (8), ShadowedVec_t (8)
|
|
||||||
};
|
|
||||||
|
|
||||||
std::srand(unsigned(std::time(nullptr)));
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
std::generate(v.begin(), v.end(), std::rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
distBubbletonic(2, ts_Data);
|
|
||||||
|
|
||||||
auto max = std::numeric_limits<ShadowedVec_t::value_type>::min();
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
EXPECT_EQ((max <= v[0]), true);
|
|
||||||
EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true);
|
|
||||||
max = v.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TEST(TdistBubbletonic_UT, distBubbletonic_test2) {
|
|
||||||
AllData_t ts_Data {
|
|
||||||
ShadowedVec_t (8), ShadowedVec_t (8), ShadowedVec_t (8), ShadowedVec_t (8)
|
|
||||||
};
|
|
||||||
|
|
||||||
std::srand(unsigned(std::time(nullptr)));
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
std::generate(v.begin(), v.end(), std::rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
distBubbletonic(4, ts_Data);
|
|
||||||
|
|
||||||
auto max = std::numeric_limits<ShadowedVec_t::value_type>::min();
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
EXPECT_EQ((max <= v[0]), true);
|
|
||||||
EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true);
|
|
||||||
max = v.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TdistBubbletonic_UT, distBubbletonic_test3) {
|
|
||||||
AllData_t ts_Data {
|
|
||||||
ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32),
|
|
||||||
ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32)
|
|
||||||
};
|
|
||||||
|
|
||||||
std::srand(unsigned(std::time(nullptr)));
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
std::generate(v.begin(), v.end(), std::rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
distBubbletonic(8, ts_Data);
|
|
||||||
|
|
||||||
auto max = std::numeric_limits<ShadowedVec_t::value_type>::min();
|
|
||||||
for (auto& v : ts_Data) {
|
|
||||||
EXPECT_EQ((max <= v[0]), true);
|
|
||||||
EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true);
|
|
||||||
max = v.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
205
homework_2/test/tests_MPI.cpp
Normal file
205
homework_2/test/tests_MPI.cpp
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief PDS HW2 tests
|
||||||
|
*
|
||||||
|
* \author
|
||||||
|
* Christos Choutouridis AEM:8997
|
||||||
|
* <cchoutou@ece.auth.gr>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <mpi.h>
|
||||||
|
#include <random>
|
||||||
|
#include "distsort.hpp"
|
||||||
|
|
||||||
|
MPI_t<> ts_mpi;
|
||||||
|
|
||||||
|
// Mersenne seeded from hw if possible. range: [type_min, type_max]
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 gen(rd());
|
||||||
|
|
||||||
|
class TMPIdistSort : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
static void SetUpTestSuite() {
|
||||||
|
int argc = 0;
|
||||||
|
char** argv = nullptr;
|
||||||
|
MPI_Init(&argc, &argv);
|
||||||
|
|
||||||
|
int rank, size;
|
||||||
|
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
|
||||||
|
MPI_Comm_size(MPI_COMM_WORLD, &size);
|
||||||
|
ts_mpi.rank_ = rank;
|
||||||
|
ts_mpi.size_ = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TearDownTestSuite() {
|
||||||
|
MPI_Finalize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To run thiese test execute:
|
||||||
|
* mpirun -np <N> ./bit/tests
|
||||||
|
*/
|
||||||
|
TEST_F(TMPIdistSort, distBubbletonic_test1) {
|
||||||
|
// Create and fill vector
|
||||||
|
using tsValue_t = uint8_t; // Test parameters
|
||||||
|
size_t ts_buffer_size = 16;
|
||||||
|
|
||||||
|
ShadowedVec_t<tsValue_t> ts_Data;
|
||||||
|
std::uniform_int_distribution<tsValue_t > dis(
|
||||||
|
std::numeric_limits<tsValue_t>::min(),
|
||||||
|
std::numeric_limits<tsValue_t>::max()
|
||||||
|
);
|
||||||
|
ts_Data.resize(ts_buffer_size);
|
||||||
|
std::generate(ts_Data.begin(), ts_Data.end(), [&]() { return dis(gen); });
|
||||||
|
|
||||||
|
// Execute function under test in all processes
|
||||||
|
distBubbletonic(ts_Data, ts_mpi.size(), ts_mpi.rank());
|
||||||
|
|
||||||
|
// Local min and max
|
||||||
|
auto local_min = *std::min_element(ts_Data.begin(), ts_Data.end());
|
||||||
|
auto local_max = *std::max_element(ts_Data.begin(), ts_Data.end());
|
||||||
|
|
||||||
|
// Gather min/max to rank 0
|
||||||
|
std::vector<tsValue_t> global_mins(ts_mpi.size());
|
||||||
|
std::vector<tsValue_t> global_maxes(ts_mpi.size());
|
||||||
|
MPI_Datatype datatype = MPI_TypeMapper<tsValue_t>::getType();
|
||||||
|
|
||||||
|
MPI_Gather(&local_min, 1, datatype, global_mins.data(), 1, datatype, 0, MPI_COMM_WORLD);
|
||||||
|
MPI_Gather(&local_max, 1, datatype, global_maxes.data(), 1, datatype, 0, MPI_COMM_WORLD);
|
||||||
|
|
||||||
|
// Check results
|
||||||
|
EXPECT_EQ(std::is_sorted(ts_Data.begin(), ts_Data.end()), true);
|
||||||
|
if (ts_mpi.rank() == 0) {
|
||||||
|
for (size_t i = 1; i < global_mins.size(); ++i) {
|
||||||
|
EXPECT_LE(global_maxes[i - 1], global_mins[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To run thiese test execute:
|
||||||
|
* mpirun -np <N> ./bit/tests
|
||||||
|
*/
|
||||||
|
TEST_F(TMPIdistSort, distBubbletonic_test2) {
|
||||||
|
// Create and fill vector
|
||||||
|
using tsValue_t = uint32_t; // Test parameters
|
||||||
|
size_t ts_buffer_size = 1 << 16;
|
||||||
|
|
||||||
|
ShadowedVec_t<tsValue_t> ts_Data;
|
||||||
|
std::uniform_int_distribution<tsValue_t > dis(
|
||||||
|
std::numeric_limits<tsValue_t>::min(),
|
||||||
|
std::numeric_limits<tsValue_t>::max()
|
||||||
|
);
|
||||||
|
ts_Data.resize(ts_buffer_size);
|
||||||
|
std::generate(ts_Data.begin(), ts_Data.end(), [&]() { return dis(gen); });
|
||||||
|
|
||||||
|
// Execute function under test in all processes
|
||||||
|
distBubbletonic(ts_Data, ts_mpi.size(), ts_mpi.rank());
|
||||||
|
|
||||||
|
// Local min and max
|
||||||
|
auto local_min = *std::min_element(ts_Data.begin(), ts_Data.end());
|
||||||
|
auto local_max = *std::max_element(ts_Data.begin(), ts_Data.end());
|
||||||
|
|
||||||
|
// Gather min/max to rank 0
|
||||||
|
std::vector<tsValue_t> global_mins(ts_mpi.size());
|
||||||
|
std::vector<tsValue_t> global_maxes(ts_mpi.size());
|
||||||
|
MPI_Datatype datatype = MPI_TypeMapper<tsValue_t>::getType();
|
||||||
|
|
||||||
|
MPI_Gather(&local_min, 1, datatype, global_mins.data(), 1, datatype, 0, MPI_COMM_WORLD);
|
||||||
|
MPI_Gather(&local_max, 1, datatype, global_maxes.data(), 1, datatype, 0, MPI_COMM_WORLD);
|
||||||
|
|
||||||
|
// Check results
|
||||||
|
EXPECT_EQ(std::is_sorted(ts_Data.begin(), ts_Data.end()), true);
|
||||||
|
if (ts_mpi.rank() == 0) {
|
||||||
|
for (size_t i = 1; i < global_mins.size(); ++i) {
|
||||||
|
EXPECT_LE(global_maxes[i - 1], global_mins[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To run thiese test execute:
|
||||||
|
* mpirun -np <N> ./bit/tests
|
||||||
|
*/
|
||||||
|
TEST_F(TMPIdistSort, distBitonic_test1) {
|
||||||
|
// Create and fill vector
|
||||||
|
using tsValue_t = uint8_t; // Test parameters
|
||||||
|
size_t ts_buffer_size = 16;
|
||||||
|
|
||||||
|
ShadowedVec_t<tsValue_t> ts_Data;
|
||||||
|
std::uniform_int_distribution<tsValue_t > dis(
|
||||||
|
std::numeric_limits<tsValue_t>::min(),
|
||||||
|
std::numeric_limits<tsValue_t>::max()
|
||||||
|
);
|
||||||
|
ts_Data.resize(ts_buffer_size);
|
||||||
|
std::generate(ts_Data.begin(), ts_Data.end(), [&]() { return dis(gen); });
|
||||||
|
|
||||||
|
// Execute function under test in all processes
|
||||||
|
distBitonic(ts_Data, ts_mpi.size(), ts_mpi.rank());
|
||||||
|
|
||||||
|
// Local min and max
|
||||||
|
auto local_min = *std::min_element(ts_Data.begin(), ts_Data.end());
|
||||||
|
auto local_max = *std::max_element(ts_Data.begin(), ts_Data.end());
|
||||||
|
|
||||||
|
// Gather min/max to rank 0
|
||||||
|
std::vector<tsValue_t> global_mins(ts_mpi.size());
|
||||||
|
std::vector<tsValue_t> global_maxes(ts_mpi.size());
|
||||||
|
MPI_Datatype datatype = MPI_TypeMapper<tsValue_t>::getType();
|
||||||
|
|
||||||
|
MPI_Gather(&local_min, 1, datatype, global_mins.data(), 1, datatype, 0, MPI_COMM_WORLD);
|
||||||
|
MPI_Gather(&local_max, 1, datatype, global_maxes.data(), 1, datatype, 0, MPI_COMM_WORLD);
|
||||||
|
|
||||||
|
// Check results
|
||||||
|
EXPECT_EQ(std::is_sorted(ts_Data.begin(), ts_Data.end()), true);
|
||||||
|
if (ts_mpi.rank() == 0) {
|
||||||
|
for (size_t i = 1; i < global_mins.size(); ++i) {
|
||||||
|
EXPECT_LE(global_maxes[i - 1], global_mins[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To run thiese test execute:
|
||||||
|
* mpirun -np <N> ./bit/tests
|
||||||
|
*/
|
||||||
|
TEST_F(TMPIdistSort, distBitonic_test2) {
|
||||||
|
// Create and fill vector
|
||||||
|
using tsValue_t = uint32_t; // Test parameters
|
||||||
|
size_t ts_buffer_size = 1 << 16;
|
||||||
|
|
||||||
|
ShadowedVec_t<tsValue_t> ts_Data;
|
||||||
|
std::uniform_int_distribution<tsValue_t > dis(
|
||||||
|
std::numeric_limits<tsValue_t>::min(),
|
||||||
|
std::numeric_limits<tsValue_t>::max()
|
||||||
|
);
|
||||||
|
ts_Data.resize(ts_buffer_size);
|
||||||
|
std::generate(ts_Data.begin(), ts_Data.end(), [&]() { return dis(gen); });
|
||||||
|
|
||||||
|
// Execute function under test in all processes
|
||||||
|
distBitonic(ts_Data, ts_mpi.size(), ts_mpi.rank());
|
||||||
|
|
||||||
|
// Local min and max
|
||||||
|
auto local_min = *std::min_element(ts_Data.begin(), ts_Data.end());
|
||||||
|
auto local_max = *std::max_element(ts_Data.begin(), ts_Data.end());
|
||||||
|
|
||||||
|
// Gather min/max to rank 0
|
||||||
|
std::vector<tsValue_t> global_mins(ts_mpi.size());
|
||||||
|
std::vector<tsValue_t> global_maxes(ts_mpi.size());
|
||||||
|
MPI_Datatype datatype = MPI_TypeMapper<tsValue_t>::getType();
|
||||||
|
|
||||||
|
MPI_Gather(&local_min, 1, datatype, global_mins.data(), 1, datatype, 0, MPI_COMM_WORLD);
|
||||||
|
MPI_Gather(&local_max, 1, datatype, global_maxes.data(), 1, datatype, 0, MPI_COMM_WORLD);
|
||||||
|
|
||||||
|
// Check results
|
||||||
|
EXPECT_EQ(std::is_sorted(ts_Data.begin(), ts_Data.end()), true);
|
||||||
|
if (ts_mpi.rank() == 0) {
|
||||||
|
for (size_t i = 1; i < global_mins.size(); ++i) {
|
||||||
|
EXPECT_LE(global_maxes[i - 1], global_mins[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user