@@ -20,12 +20,17 @@ | |||||
# ============== Project settings ============== | # ============== Project settings ============== | ||||
# Project's name | # Project's name | ||||
PROJECT := PDS_homework_2 | PROJECT := PDS_homework_2 | ||||
# Excecutable's name | # Excecutable's name | ||||
TARGET := distbitonic | |||||
TARGET := bitonic | |||||
# Source directories list(space seperated). Makefile-relative path, UNDER current directory. | # Source directories list(space seperated). Makefile-relative path, UNDER current directory. | ||||
SRC_DIR_LIST := src | |||||
SRC_DIR_LIST := src test test/gtest | |||||
# Include directories list(space seperated). Makefile-relative path. | # Include directories list(space seperated). Makefile-relative path. | ||||
INC_DIR_LIST := include \ | INC_DIR_LIST := include \ | ||||
test \ | |||||
test/gtest/ \ | |||||
/usr/lib/x86_64-linux-gnu/openmpi/include/ \ | /usr/lib/x86_64-linux-gnu/openmpi/include/ \ | ||||
src | src | ||||
@@ -148,6 +153,14 @@ $(BUILD_DIR)/$(TARGET): $(OBJ) | |||||
@$(CSIZE) $(@D)/$(TARGET) | @$(CSIZE) $(@D)/$(TARGET) | ||||
@echo Done | @echo Done | ||||
# | |||||
# ================ Default local build rules ================= | |||||
# example: | |||||
# make debug | |||||
.DEFAULT_GOAL := all | |||||
.PHONY: clean | .PHONY: clean | ||||
clean: | clean: | ||||
@echo Cleaning build directories | @echo Cleaning build directories | ||||
@@ -155,67 +168,53 @@ clean: | |||||
@rm -rf $(DEP_DIR) | @rm -rf $(DEP_DIR) | ||||
@rm -rf $(BUILD_DIR) | @rm -rf $(BUILD_DIR) | ||||
# | |||||
# ================ Local build rules ================= | |||||
# example: | |||||
# make debug | |||||
debug: CFLAGS := $(DEB_CFLAGS) | debug: CFLAGS := $(DEB_CFLAGS) | ||||
debug: $(BUILD_DIR)/$(TARGET) | debug: $(BUILD_DIR)/$(TARGET) | ||||
release: CFLAGS := $(REL_CFLAGS) | release: CFLAGS := $(REL_CFLAGS) | ||||
release: $(BUILD_DIR)/$(TARGET) | release: $(BUILD_DIR)/$(TARGET) | ||||
all: release | |||||
hpc-results/post: | |||||
$(CXX) $(CFLAGS) -o $@ hpc-results/main.cpp | |||||
hpc-clean: | |||||
rm hpc-results/post | |||||
# | |||||
# ================ Local (and/or) via docker build rules ================= | |||||
# | |||||
# examples: | |||||
# make IMAGE=hpcimage v0 | |||||
# make IMAGE=hpcimage v1_cilk | |||||
# | |||||
dist_v05: CC := mpicc | |||||
dist_v05: CXX := mpic++ | |||||
dist_v05: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=50 | |||||
dist_v05: CXXFLAGS := $(DEB_CXXFLAGS) -DCODE_VERSION=50 | |||||
dist_v05: TARGET := dist_v05 | |||||
dist_v05: $(BUILD_DIR)/$(TARGET) | |||||
dist_v1: CC := mpicc | |||||
dist_v1: CXX := mpic++ | |||||
dist_v1: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=100 | |||||
dist_v1: CXXFLAGS := $(DEB_CXXFLAGS) -DCODE_VERSION=100 | |||||
dist_v1: TARGET := dist_v1 | |||||
dist_v1: $(BUILD_DIR)/$(TARGET) | |||||
# | |||||
# ========= Inside CSAL Image build rules =========== | |||||
# | |||||
# 1) first jump into image (make sure you are in the directory where Makefile is): | |||||
# > docker run -it -v ${PWD}:/usr/src/exercise_1 -w /usr/src/exercise_1/ hpcimage | |||||
# 2) Clean binaries first **important** | |||||
# > make clean | |||||
# 3) for v4 cilk for example: | |||||
# > make csal_v4_cilk | |||||
# 4) run executables from `bin/`. Examples: | |||||
# > ./bin/tcount_ompv3 -i mtx/NACA0015.mtx --timing -r 3 -o /dev/null | |||||
# > ./bin/tcount_pthv4 -i mtx/com_Youtube.mtx --timing --dynamic --print_count | |||||
# | |||||
# ======== Run from container ========= | |||||
# | |||||
# examples: | |||||
# | |||||
# make IMAGE=hpcimage EXEC=knnsearch_v1 run | |||||
# make IMAGE=hpcimage EXEC=knnsearch_v1 run | |||||
# | # | ||||
# ================ Build rules ================= | |||||
# | |||||
# Local or inside HPC rules | |||||
distbubbletonic: CC := mpicc | |||||
distbubbletonic: CXX := mpic++ | |||||
distbubbletonic: CFLAGS := $(REL_CFLAGS) -DCODE_VERSION=BUBBLETONIC | |||||
distbubbletonic: CXXFLAGS := $(REL_CXXFLAGS) -DCODE_VERSION=BUBBLETONIC | |||||
distbubbletonic: TARGET := distbubbletonic | |||||
distbubbletonic: $(BUILD_DIR)/$(TARGET) | |||||
distbitonic: CC := mpicc | |||||
distbitonic: CXX := mpic++ | |||||
distbitonic: CFLAGS := $(REL_CFLAGS) -DCODE_VERSION=BITONIC | |||||
distbitonic: CXXFLAGS := $(REL_CXXFLAGS) -DCODE_VERSION=BITONIC | |||||
distbitonic: TARGET := distbitonic | |||||
distbitonic: $(BUILD_DIR)/$(TARGET) | |||||
deb_distbubbletonic: CC := mpicc | |||||
deb_distbubbletonic: CXX := mpic++ | |||||
deb_distbubbletonic: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=BUBBLETONIC -DDEBUG | |||||
deb_distbubbletonic: CXXFLAGS := $(DEB_CXXFLAGS) -DCODE_VERSION=BUBBLETONIC -DDEBUG | |||||
deb_distbubbletonic: TARGET := deb_distbubbletonic | |||||
deb_distbubbletonic: $(BUILD_DIR)/$(TARGET) | |||||
deb_distbitonic: CC := mpicc | |||||
deb_distbitonic: CXX := mpic++ | |||||
deb_distbitonic: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=BITONIC -DDEBUG | |||||
deb_distbitonic: CXXFLAGS := $(DEB_CXXFLAGS) -DCODE_VERSION=BITONIC -DDEBUG | |||||
deb_distbitonic: TARGET := deb_distbitonic | |||||
deb_distbitonic: $(BUILD_DIR)/$(TARGET) | |||||
tests: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=BITONIC -DDEBUG -DTESTING | |||||
tests: CXXFLAGS := $(DEB_CXXFLAGS) -DCODE_VERSION=BITONIC -DDEBUG -DTESTING | |||||
tests: TARGET := tests | |||||
tests: $(BUILD_DIR)/$(TARGET) | |||||
all: debug distbubbletonic distbitonic | |||||
# Note: | |||||
# Add a gcc based make rule here in order for clangd to successfully scan the project files. | |||||
# Otherwise we do not need the gcc build. | |||||
@@ -1,5 +1,5 @@ | |||||
/*! | /*! | ||||
* \file config,h | |||||
* \file config.h | |||||
* \brief Build configuration file. | * \brief Build configuration file. | ||||
* | * | ||||
* \author | * \author | ||||
@@ -16,13 +16,13 @@ | |||||
/* | /* | ||||
* Defines for different version of the exercise | * Defines for different version of the exercise | ||||
*/ | */ | ||||
#define V50 (50) | |||||
#define V100 (100) | |||||
#define BITONIC (1) | |||||
#define BUBBLETONIC (2) | |||||
// Fail-safe version selection | // Fail-safe version selection | ||||
#if !defined CODE_VERSION | #if !defined CODE_VERSION | ||||
#define CODE_VERSION V1 | |||||
#define CODE_VERSION BITONIC | |||||
#endif | #endif | ||||
@@ -30,10 +30,11 @@ | |||||
* Session option for each invocation of the executable | * Session option for each invocation of the executable | ||||
*/ | */ | ||||
struct session_t { | struct session_t { | ||||
bool timing {false}; | |||||
bool verbose {false}; //!< Flag to enable verbose output to stdout | |||||
bool timing{false}; | |||||
bool verbose{false}; //!< Flag to enable verbose output to stdout | |||||
}; | }; | ||||
extern session_t session; | extern session_t session; | ||||
#endif /* CONFIG_H_ */ | #endif /* CONFIG_H_ */ |
@@ -0,0 +1,44 @@ | |||||
/*! | |||||
* \file | |||||
* \brief Distributed bitonic implementation header | |||||
* | |||||
* \author | |||||
* Christos Choutouridis AEM:8997 | |||||
* <cchoutou@ece.auth.gr> | |||||
*/ | |||||
#ifndef DISTBITONIC_H_ | |||||
#define DISTBITONIC_H_ | |||||
#if !defined DEBUG | |||||
#define NDEBUG | |||||
#endif | |||||
#include <cassert> | |||||
#include <vector> | |||||
#if !defined TESTING | |||||
#include <mpi.h> | |||||
#endif | |||||
using Data_t = std::vector<uint8_t>; | |||||
using AllData_t = std::vector<Data_t>; | |||||
struct mpi_t { | |||||
size_t world_size{}; | |||||
size_t world_rank{}; | |||||
std::string processor_name {}; | |||||
}; | |||||
extern mpi_t mpi; | |||||
bool ascending(size_t node, size_t depth) noexcept; | |||||
size_t partner(size_t node, size_t step) noexcept; | |||||
bool keepsmall(size_t node, size_t partner, size_t depth) noexcept; | |||||
void exchange(size_t node, size_t partner); | |||||
void minmax(AllData_t& data, size_t node, size_t partner, bool keepsmall); | |||||
void sort_network(AllData_t& data, size_t nodes, size_t depth); | |||||
void distbitonic(size_t P, AllData_t& data); | |||||
#endif //DISTBITONIC_H_ |
@@ -0,0 +1,14 @@ | |||||
/*! | |||||
* \file | |||||
* \brief The distributed bitonic implementation header | |||||
* | |||||
* \author | |||||
* Christos Choutouridis AEM:8997 | |||||
* <cchoutou@ece.auth.gr> | |||||
*/ | |||||
#ifndef IMPL_H_ | |||||
#define IMPL_H_ | |||||
#endif //IMPL_H_ |
@@ -19,32 +19,36 @@ | |||||
/*! | /*! | ||||
* A Logger for entire program. | * A Logger for entire program. | ||||
*/ | */ | ||||
struct Log { | struct Log { | ||||
struct Endl {} endl; //!< a tag object to to use it as a new line request. | |||||
struct Endl { | |||||
} endl; //!< a tag object to to use it as a new line request. | |||||
//! We provide logging via << operator | |||||
template<typename T> | |||||
Log &operator<<(T &&t) { | |||||
if (session.verbose) { | |||||
if (line_) { | |||||
std::cout << "[Log]: " << t; | |||||
line_ = false; | |||||
} else | |||||
std::cout << t; | |||||
} | |||||
return *this; | |||||
} | |||||
// overload for special end line handling | |||||
Log &operator<<(Endl e) { | |||||
(void) e; | |||||
if (session.verbose) { | |||||
std::cout << '\n'; | |||||
line_ = true; | |||||
} | |||||
return *this; | |||||
} | |||||
//! We provide logging via << operator | |||||
template<typename T> | |||||
Log& operator<< (T&& t) { | |||||
if (session.verbose) { | |||||
if (line_) { | |||||
std::cout << "[Log]: " << t; | |||||
line_ = false; | |||||
} | |||||
else | |||||
std::cout << t; | |||||
} | |||||
return *this; | |||||
} | |||||
// overload for special end line handling | |||||
Log& operator<< (Endl e) { (void)e; | |||||
if (session.verbose) { | |||||
std::cout << '\n'; | |||||
line_ = true; | |||||
} | |||||
return *this; | |||||
} | |||||
private: | private: | ||||
bool line_ {true}; | |||||
bool line_{true}; | |||||
}; | }; | ||||
extern Log logger; | extern Log logger; | ||||
@@ -52,36 +56,41 @@ extern Log logger; | |||||
/*! | /*! | ||||
* A small timing utility based on chrono. | * A small timing utility based on chrono. | ||||
*/ | */ | ||||
struct Timing{ | |||||
using Tpoint = std::chrono::steady_clock::time_point; | |||||
using microseconds = std::chrono::microseconds; | |||||
using milliseconds = std::chrono::milliseconds; | |||||
using seconds = std::chrono::seconds; | |||||
struct Timing { | |||||
using Tpoint = std::chrono::steady_clock::time_point; | |||||
using microseconds = std::chrono::microseconds; | |||||
using milliseconds = std::chrono::milliseconds; | |||||
using seconds = std::chrono::seconds; | |||||
//! tool to mark the starting point | |||||
Tpoint start() noexcept { return start_ = std::chrono::steady_clock::now(); } | |||||
//! tool to mark the starting point | |||||
Tpoint start () noexcept { return start_ = std::chrono::steady_clock::now(); } | |||||
//! tool to mark the ending point | |||||
Tpoint stop () noexcept { return stop_ = std::chrono::steady_clock::now(); } | |||||
//! tool to mark the ending point | |||||
Tpoint stop() noexcept { return stop_ = std::chrono::steady_clock::now(); } | |||||
auto dt() noexcept { | |||||
return std::chrono::duration_cast<std::chrono::microseconds>(stop_ - start_).count(); | |||||
} | |||||
//! tool to print the time interval | |||||
void print_dt(const char *what) noexcept { | |||||
if (session.timing) { | |||||
auto t = stop_ - start_; | |||||
if (std::chrono::duration_cast<microseconds>(t).count() < 10000) | |||||
std::cout << "[Timing]: " << what << ": " | |||||
<< std::to_string(std::chrono::duration_cast<microseconds>(t).count()) << " [usec]\n"; | |||||
else if (std::chrono::duration_cast<milliseconds>(t).count() < 10000) | |||||
std::cout << "[Timing]: " << what << ": " | |||||
<< std::to_string(std::chrono::duration_cast<milliseconds>(t).count()) << " [msec]\n"; | |||||
else | |||||
std::cout << "[Timing]: " << what << ": " | |||||
<< std::to_string(std::chrono::duration_cast<seconds>(t).count()) << " [sec]\n"; | |||||
} | |||||
} | |||||
auto dt () noexcept { | |||||
return std::chrono::duration_cast<std::chrono::microseconds>(stop_ - start_).count(); | |||||
} | |||||
//! tool to print the time interval | |||||
void print_dt (const char* what) noexcept { | |||||
if (session.timing) { | |||||
auto t = stop_ - start_; | |||||
if (std::chrono::duration_cast<microseconds>(t).count() < 10000) | |||||
std::cout << "[Timing]: " << what << ": " << std::to_string(std::chrono::duration_cast<microseconds>(t).count()) << " [usec]\n"; | |||||
else if (std::chrono::duration_cast<milliseconds>(t).count() < 10000) | |||||
std::cout << "[Timing]: " << what << ": " << std::to_string(std::chrono::duration_cast<milliseconds>(t).count()) << " [msec]\n"; | |||||
else | |||||
std::cout << "[Timing]: " << what << ": " << std::to_string(std::chrono::duration_cast<seconds>(t).count()) << " [sec]\n"; | |||||
} | |||||
} | |||||
private: | private: | ||||
Tpoint start_; | |||||
Tpoint stop_; | |||||
Tpoint start_; | |||||
Tpoint stop_; | |||||
}; | }; | ||||
#endif /* UTILS_HPP_ */ | #endif /* UTILS_HPP_ */ |
@@ -0,0 +1,106 @@ | |||||
/*! | |||||
* \file | |||||
* \brief Distributed bitonic implementation. | |||||
* | |||||
* \author | |||||
* Christos Choutouridis AEM:8997 | |||||
* <cchoutou@ece.auth.gr> | |||||
*/ | |||||
#include <vector> | |||||
#include <algorithm> | |||||
#include <cmath> | |||||
#include <cassert> | |||||
#include "distbitonic.hpp" | |||||
/*! | |||||
* Returns the ascending or descending configuration of the node's sequence based on | |||||
* the current node (MPI process) and the depth of the sorting network | |||||
* | |||||
* @param node The current node (MPI process) | |||||
* @param depth The total depth of the sorting network (same for each step for a given network) | |||||
* | |||||
* @return True if we need ascending configuration, false otherwise | |||||
*/ | |||||
bool ascending(size_t node, size_t depth) noexcept { | |||||
return !(node & (1 << depth)); | |||||
} | |||||
/*! | |||||
* Returns the node's partner for data exchange during the sorting network iterations | |||||
* | |||||
* @param node The current node | |||||
* @param step The step of the sorting network | |||||
* @return The node id of the partner for data exchange | |||||
*/ | |||||
size_t partner(size_t node, size_t step) noexcept { | |||||
return (node ^ (1 << step)); | |||||
} | |||||
/*! | |||||
* Predicate to check if a node keeps the small numbers during the bitonic | |||||
* sort network exchange. | |||||
* | |||||
* @param node The node for which we check | |||||
* @param partner The partner of the data exchange | |||||
* @param depth The total depth of the sorting network (same for each step for a given network) | |||||
* @return True if the node should keep the small values, false otherwise | |||||
*/ | |||||
bool keepsmall(size_t node, size_t partner, size_t depth) noexcept { | |||||
assert(node != partner); | |||||
return ascending(node, depth) == (node < partner); | |||||
} | |||||
void exchange(size_t node, size_t partner) { | |||||
assert(node != partner); | |||||
} | |||||
void minmax(AllData_t& data, size_t node, size_t partner, bool keepsmall) { | |||||
for (size_t i = 0; i < data[node].size(); ++i) { | |||||
if (keepsmall && data[node][i] > data[partner][i]) | |||||
std::swap(data[node][i], data[partner][i]); | |||||
if (!keepsmall && data[node][i] < data[partner][i]) | |||||
std::swap(data[node][i], data[partner][i]); | |||||
} | |||||
} | |||||
void sort_network(AllData_t& data, size_t nodes, size_t depth) { | |||||
for (size_t step = depth; step > 0;) { | |||||
--step; | |||||
for (size_t node = 0; node < nodes; ++node) { | |||||
auto part = partner(node, step); | |||||
auto ks = keepsmall(node, part, depth); | |||||
if (node < part) { | |||||
exchange(node, part); | |||||
minmax(data, node, part, ks); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
void distbitonic(size_t P, AllData_t& data) { | |||||
auto p = static_cast<uint32_t>(std::log2(P)); | |||||
for (size_t node = 0 ; node < P ; ++node) { // Currently we do all nodes here! | |||||
// Initially sort to create the half part of a bitonic | |||||
if (ascending(node, 0)) | |||||
std::sort(data[node].begin(), data[node].end(), std::less<>()); | |||||
else | |||||
std::sort(data[node].begin(), data[node].end(), std::greater<>()); | |||||
} | |||||
// Run through sort network using elbow-sort | |||||
for (size_t depth = 1; depth <= p; ++depth) { | |||||
sort_network(data, P, depth); | |||||
for (size_t node = 0 ; node < P ; ++node) { // Currently we do all nodes here! | |||||
// elbow-sort here | |||||
if (ascending(node, depth)) | |||||
std::sort(data[node].begin(), data[node].end(), std::less<>()); | |||||
else | |||||
std::sort(data[node].begin(), data[node].end(), std::greater<>()); | |||||
} | |||||
} | |||||
} |
@@ -1,22 +1,43 @@ | |||||
/*! | /*! | ||||
* \file main.cpp | |||||
* \file | |||||
* \brief Main application file for PDS HW2 (MPI) | * \brief Main application file for PDS HW2 (MPI) | ||||
* | * | ||||
* \author | * \author | ||||
* Christos Choutouridis AEM:8997 | * Christos Choutouridis AEM:8997 | ||||
* <cchoutou@ece.auth.gr> | * <cchoutou@ece.auth.gr> | ||||
*/ | */ | ||||
#include <exception> | #include <exception> | ||||
#include <iostream> | #include <iostream> | ||||
#include <algorithm> // rand/srand | |||||
//#include <ctime> // rand/srand | |||||
#if !defined TESTING | |||||
#include <mpi.h> | |||||
#endif | |||||
#include <mpi.h> | |||||
#include "matrix.hpp" | |||||
#include "distbitonic.hpp" | |||||
#include "utils.hpp" | #include "utils.hpp" | ||||
#include "config.h" | #include "config.h" | ||||
//#include "matrix.hpp" | |||||
// Global session data | // Global session data | ||||
session_t session; | |||||
session_t session; | |||||
mpi_t mpi; | |||||
/* | |||||
* Sorting data for up to 8 processes | |||||
*/ | |||||
AllData_t Data { | |||||
Data_t (8), | |||||
Data_t (8), | |||||
Data_t (8), | |||||
Data_t (8), | |||||
Data_t (8), | |||||
Data_t (8), | |||||
Data_t (8), | |||||
Data_t (8) | |||||
}; | |||||
/*! | /*! | ||||
* A small command line argument parser | * A small command line argument parser | ||||
@@ -64,6 +85,9 @@ bool get_options(int argc, char* argv[]){ | |||||
return status; | return status; | ||||
} | } | ||||
#if !defined TESTING | |||||
int main(int argc, char* argv[]) try { | int main(int argc, char* argv[]) try { | ||||
// try to read command line | // try to read command line | ||||
if (!get_options(argc, argv)) | if (!get_options(argc, argv)) | ||||
@@ -72,22 +96,49 @@ int main(int argc, char* argv[]) try { | |||||
// Initialize the MPI environment | // Initialize the MPI environment | ||||
MPI_Init(NULL, NULL); | MPI_Init(NULL, NULL); | ||||
#if defined DEBUG | |||||
/* | |||||
* In case of a debug build we will wait here until sleep_wait | |||||
* will reset via debugger. In order to do that the user must attach | |||||
* debugger to all processes. For example: | |||||
* $> mpirun -np 2 ./<program path> | |||||
* $> ps aux | grep <program> | |||||
* $> gdb <program> <PID1> | |||||
* $> gdb <program> <PID2> | |||||
*/ | |||||
#if defined TESTING | |||||
volatile bool sleep_wait = false; | |||||
#else | |||||
volatile bool sleep_wait = true; | |||||
#endif | |||||
while (sleep_wait) | |||||
sleep(1); | |||||
#endif | |||||
// Get the number of processes | // Get the number of processes | ||||
int world_size; | |||||
MPI_Comm_size(MPI_COMM_WORLD, &world_size); | |||||
MPI_Comm_size(MPI_COMM_WORLD, reinterpret_cast<int *>(&mpi.world_size)); | |||||
// Get the rank of the process | // Get the rank of the process | ||||
int world_rank; | |||||
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); | |||||
MPI_Comm_rank(MPI_COMM_WORLD, reinterpret_cast<int *>(&mpi.world_rank)); | |||||
// 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); | MPI_Get_processor_name(processor_name, &name_len); | ||||
mpi.processor_name = std::string (processor_name, name_len); | |||||
// Print off a hello world message | // Print off a hello world message | ||||
printf("Hello world from processor %s, rank %d out of %d processors\n", | |||||
processor_name, world_rank, world_size); | |||||
std::cout << "Hello world from processor: " << mpi.processor_name | |||||
<< " rank " << mpi.world_rank | |||||
<< " out of " << mpi.world_size << " processors\n"; | |||||
// std::srand(unsigned(std::time(nullptr))); | |||||
// for (auto& v : Data) { | |||||
// std::generate(v.begin(), v.end(), std::rand); | |||||
// } | |||||
// | |||||
// distbitonic (2, Data); | |||||
// distbitonic (4, Data); | |||||
// Finalize the MPI environment. | // Finalize the MPI environment. | ||||
MPI_Finalize(); | MPI_Finalize(); | ||||
@@ -97,4 +148,19 @@ catch (std::exception& e) { | |||||
//we probably pollute the user's screen. Comment `cerr << ...` if you don't like it. | //we probably pollute the user's screen. Comment `cerr << ...` if you don't like it. | ||||
std::cerr << "Error: " << e.what() << '\n'; | std::cerr << "Error: " << e.what() << '\n'; | ||||
exit(1); | exit(1); | ||||
} | |||||
} | |||||
#else | |||||
#include <gtest/gtest.h> | |||||
#include <exception> | |||||
GTEST_API_ int main(int argc, char **argv) try { | |||||
testing::InitGoogleTest(&argc, argv); | |||||
return RUN_ALL_TESTS(); | |||||
} | |||||
catch (std::exception& e) { | |||||
std::cout << "Exception: " << e.what() << '\n'; | |||||
} | |||||
#endif |
@@ -0,0 +1,384 @@ | |||||
/** | |||||
* \file | |||||
* \brief PDS HW2 tests | |||||
* | |||||
* \author | |||||
* Christos Choutouridis AEM:8997 | |||||
* <cchoutou@ece.auth.gr> | |||||
*/ | |||||
#include <gtest/gtest.h> | |||||
#include <algorithm> // rand/srand | |||||
#include <ctime> // rand/srand | |||||
#include "distbitonic.hpp" | |||||
/* | |||||
* ========================================== | |||||
*/ | |||||
/* | |||||
* bool ascending(size_t node, size_t depth); | |||||
* depth 0 (the initial ascending pattern) | |||||
*/ | |||||
TEST(Tdistbitonic_UT, ascending_test1) { | |||||
EXPECT_EQ(ascending(0, 0), true); | |||||
EXPECT_EQ(ascending(1, 0), false); | |||||
EXPECT_EQ(ascending(2, 0), true); | |||||
EXPECT_EQ(ascending(3, 0), false); | |||||
EXPECT_EQ(ascending(4, 0), true); | |||||
EXPECT_EQ(ascending(5, 0), false); | |||||
EXPECT_EQ(ascending(6, 0), true); | |||||
EXPECT_EQ(ascending(7, 0), false); | |||||
for (size_t node = 0 ; node < 256 ; ++node) { | |||||
EXPECT_EQ(ascending(node, 0), ((node % 2) ? false : true) ); | |||||
} | |||||
} | |||||
/* | |||||
* bool ascending(size_t node, size_t depth); | |||||
* depth 1 | |||||
*/ | |||||
TEST(Tdistbitonic_UT, ascending_test2) { | |||||
EXPECT_EQ(ascending(0, 1), true); | |||||
EXPECT_EQ(ascending(1, 1), true); | |||||
EXPECT_EQ(ascending(2, 1), false); | |||||
EXPECT_EQ(ascending(3, 1), false); | |||||
EXPECT_EQ(ascending(4, 1), true); | |||||
EXPECT_EQ(ascending(5, 1), true); | |||||
EXPECT_EQ(ascending(6, 1), false); | |||||
EXPECT_EQ(ascending(7, 1), false); | |||||
for (size_t node = 0 ; node < 256 ; ++node) { | |||||
EXPECT_EQ(ascending(2*node, 1), ((node % 2) ? false:true)); | |||||
EXPECT_EQ(ascending(2*node+1, 1), ((node % 2) ? false:true)); | |||||
} | |||||
} | |||||
/* | |||||
* bool ascending(size_t node, size_t depth); | |||||
* various depths | |||||
*/ | |||||
TEST(Tdistbitonic_UT, ascending_test3) { | |||||
// Depth = 3 | |||||
size_t ts_depth = 3; | |||||
for (size_t n = 0UL ; n < (1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), true); | |||||
for (size_t n = (1UL<<(ts_depth)) ; n < 2*(1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), false); | |||||
for (size_t n = 2*(1UL<<(ts_depth)) ; n < 3*(1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), true); | |||||
for (size_t n = 3*(1UL<<(ts_depth)) ; n < 4*(1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), false); | |||||
// Depth = 4 | |||||
ts_depth = 4; | |||||
for (size_t n = 0UL ; n < (1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), true); | |||||
for (size_t n = (1UL<<(ts_depth)) ; n < 2*(1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), false); | |||||
for (size_t n = 2*(1UL<<(ts_depth)) ; n < 3*(1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), true); | |||||
for (size_t n = 3*(1UL<<(ts_depth)) ; n < 4*(1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), false); | |||||
// Depth = 8 | |||||
ts_depth = 8; | |||||
for (size_t n = 0UL ; n < (1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), true); | |||||
for (size_t n = (1UL<<(ts_depth)) ; n < 2*(1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), false); | |||||
for (size_t n = 2*(1UL<<(ts_depth)) ; n < 3*(1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), true); | |||||
for (size_t n = 3*(1UL<<(ts_depth)) ; n < 4*(1UL<<(ts_depth)) ; ++n) | |||||
EXPECT_EQ(ascending(n, ts_depth), false); | |||||
} | |||||
/* | |||||
* size_t partner(size_t node, size_t step); | |||||
* step = 0 | |||||
*/ | |||||
TEST(Tdistbitonic_UT, partner_test1) { | |||||
EXPECT_EQ(partner(0, 0), 1UL); | |||||
EXPECT_EQ(partner(1, 0), 0UL); | |||||
EXPECT_EQ(partner(2, 0), 3UL); | |||||
EXPECT_EQ(partner(3, 0), 2UL); | |||||
EXPECT_EQ(partner(4, 0), 5UL); | |||||
EXPECT_EQ(partner(5, 0), 4UL); | |||||
EXPECT_EQ(partner(6, 0), 7UL); | |||||
EXPECT_EQ(partner(7, 0), 6UL); | |||||
for (size_t node = 0 ; node < 256 ; ++node) { | |||||
EXPECT_EQ(partner(node, 0), (node % 2) ? node-1 : node+1); | |||||
} | |||||
} | |||||
/* | |||||
* size_t partner(size_t node, size_t step); | |||||
* step = 1 | |||||
*/ | |||||
TEST(Tdistbitonic_UT, partner_test2) { | |||||
EXPECT_EQ(partner(0, 1), 2UL); | |||||
EXPECT_EQ(partner(1, 1), 3UL); | |||||
EXPECT_EQ(partner(2, 1), 0UL); | |||||
EXPECT_EQ(partner(3, 1), 1UL); | |||||
EXPECT_EQ(partner(4, 1), 6UL); | |||||
EXPECT_EQ(partner(5, 1), 7UL); | |||||
EXPECT_EQ(partner(6, 1), 4UL); | |||||
EXPECT_EQ(partner(7, 1), 5UL); | |||||
for (size_t n1 = 0 ; n1 < 256 ; n1 += 2) { | |||||
auto n2 = n1 + 1UL; | |||||
EXPECT_EQ(partner(n1, 1), ((n1 % 4) ? n1-2 : n1+2)); | |||||
EXPECT_EQ(partner(n2, 1), ((n1 % 4) ? n2-2 : n2+2)); | |||||
} | |||||
} | |||||
/* | |||||
* size_t partner(size_t node, size_t step); | |||||
* various steps | |||||
*/ | |||||
TEST(Tdistbitonic_UT, partner_test3) { | |||||
// step = 2 | |||||
size_t ts_step = 2; | |||||
for (size_t n1 = 0 ; n1 < 256 ; n1 += 4) { | |||||
auto n2 = n1 + 1UL; | |||||
auto n3 = n1 + 2UL; | |||||
auto n4 = n1 + 3UL; | |||||
EXPECT_EQ(partner(n1, ts_step), ((n1 % 8) ? n1-4 : n1+4)); | |||||
EXPECT_EQ(partner(n2, ts_step), ((n1 % 8) ? n2-4 : n2+4)); | |||||
EXPECT_EQ(partner(n3, ts_step), ((n1 % 8) ? n3-4 : n3+4)); | |||||
EXPECT_EQ(partner(n4, ts_step), ((n1 % 8) ? n4-4 : n4+4)); | |||||
} | |||||
// step = 3 | |||||
ts_step = 3; | |||||
for (size_t n1 = 0 ; n1 < 256 ; n1 += 8) { | |||||
auto n2 = n1 + 1UL; | |||||
auto n3 = n1 + 2UL; | |||||
auto n4 = n1 + 3UL; | |||||
auto n5 = n1 + 4UL; | |||||
auto n6 = n1 + 5UL; | |||||
auto n7 = n1 + 6UL; | |||||
auto n8 = n1 + 7UL; | |||||
EXPECT_EQ(partner(n1, ts_step), ((n1 % 16) ? n1-8 : n1+8)); | |||||
EXPECT_EQ(partner(n2, ts_step), ((n1 % 16) ? n2-8 : n2+8)); | |||||
EXPECT_EQ(partner(n3, ts_step), ((n1 % 16) ? n3-8 : n3+8)); | |||||
EXPECT_EQ(partner(n4, ts_step), ((n1 % 16) ? n4-8 : n4+8)); | |||||
EXPECT_EQ(partner(n5, ts_step), ((n1 % 16) ? n5-8 : n5+8)); | |||||
EXPECT_EQ(partner(n6, ts_step), ((n1 % 16) ? n6-8 : n6+8)); | |||||
EXPECT_EQ(partner(n7, ts_step), ((n1 % 16) ? n7-8 : n7+8)); | |||||
EXPECT_EQ(partner(n8, ts_step), ((n1 % 16) ? n8-8 : n8+8)); | |||||
} | |||||
// step = 4 | |||||
ts_step = 4; | |||||
for (size_t n1 = 0 ; n1 < 256 ; n1 += 16) { | |||||
auto n2 = n1 + 1UL; | |||||
auto n3 = n1 + 2UL; | |||||
auto n4 = n1 + 3UL; | |||||
auto n5 = n1 + 4UL; | |||||
auto n6 = n1 + 5UL; | |||||
auto n7 = n1 + 6UL; | |||||
auto n8 = n1 + 7UL; | |||||
auto n9 = n1 + 8UL; | |||||
auto n10 = n1 + 9UL; | |||||
auto n11 = n1 + 10UL; | |||||
auto n12 = n1 + 11UL; | |||||
auto n13 = n1 + 12UL; | |||||
auto n14 = n1 + 13UL; | |||||
auto n15 = n1 + 14UL; | |||||
auto n16 = n1 + 15UL; | |||||
EXPECT_EQ(partner(n1, ts_step), ((n1 % 32) ? n1-16 : n1+16)); | |||||
EXPECT_EQ(partner(n2, ts_step), ((n1 % 32) ? n2-16 : n2+16)); | |||||
EXPECT_EQ(partner(n3, ts_step), ((n1 % 32) ? n3-16 : n3+16)); | |||||
EXPECT_EQ(partner(n4, ts_step), ((n1 % 32) ? n4-16 : n4+16)); | |||||
EXPECT_EQ(partner(n5, ts_step), ((n1 % 32) ? n5-16 : n5+16)); | |||||
EXPECT_EQ(partner(n6, ts_step), ((n1 % 32) ? n6-16 : n6+16)); | |||||
EXPECT_EQ(partner(n7, ts_step), ((n1 % 32) ? n7-16 : n7+16)); | |||||
EXPECT_EQ(partner(n8, ts_step), ((n1 % 32) ? n8-16 : n8+16)); | |||||
EXPECT_EQ(partner(n9, ts_step), ((n1 % 32) ? n9-16 : n9+16)); | |||||
EXPECT_EQ(partner(n10, ts_step), ((n1 % 32) ? n10-16 : n10+16)); | |||||
EXPECT_EQ(partner(n11, ts_step), ((n1 % 32) ? n11-16 : n11+16)); | |||||
EXPECT_EQ(partner(n12, ts_step), ((n1 % 32) ? n12-16 : n12+16)); | |||||
EXPECT_EQ(partner(n13, ts_step), ((n1 % 32) ? n13-16 : n13+16)); | |||||
EXPECT_EQ(partner(n14, ts_step), ((n1 % 32) ? n14-16 : n14+16)); | |||||
EXPECT_EQ(partner(n15, ts_step), ((n1 % 32) ? n15-16 : n15+16)); | |||||
EXPECT_EQ(partner(n16, ts_step), ((n1 % 32) ? n16-16 : n16+16)); | |||||
} | |||||
} | |||||
/* | |||||
* bool keepsmall(size_t node, size_t partner, size_t depth); | |||||
* Assertion check | |||||
*/ | |||||
TEST(Tdistbitonic_UT, keepsmall_test1) { | |||||
ASSERT_DEATH(keepsmall(0, 0, 0), ""); | |||||
ASSERT_DEATH(keepsmall(1, 1, 42), ""); | |||||
ASSERT_DEATH(keepsmall(7, 7, 42), ""); | |||||
} | |||||
/* | |||||
* bool keepsmall(size_t node, size_t partner, size_t depth); | |||||
* | |||||
* depth: 1 | step: 0 | partner: [1, 0, 3, 2, 5, 4, 7, 6] | keepsmall: Bool[1, 0, 0, 1, 1, 0, 0, 1] | |||||
*/ | |||||
TEST(Tdistbitonic_UT, keepsmall_test2) { | |||||
size_t ts_depth = 1UL; | |||||
size_t ts_partner[] = { 1, 0, 3, 2, 5, 4, 7, 6}; | |||||
bool ts_expected[] = {1, 0, 0, 1, 1, 0, 0, 1}; | |||||
for (size_t node = 0 ; node < 8UL ; ++node ) { | |||||
EXPECT_EQ(ts_expected[node], keepsmall(node, ts_partner[node], ts_depth)); | |||||
} | |||||
} | |||||
/* | |||||
* bool keepsmall(size_t node, size_t partner, size_t depth); | |||||
* | |||||
* depth: 2 | step: 1 | partner: [2, 3, 0, 1, 6, 7, 4, 5] | keepsmall: Bool[1, 1, 0, 0, 0, 0, 1, 1] | |||||
*/ | |||||
TEST(Tdistbitonic_UT, keepsmall_test3) { | |||||
size_t ts_depth = 2UL; | |||||
size_t ts_partner[] = { 2, 3, 0, 1, 6, 7, 4, 5}; | |||||
bool ts_expected[] = {1, 1, 0, 0, 0, 0, 1, 1}; | |||||
for (size_t node = 0 ; node < 8UL ; ++node ) { | |||||
EXPECT_EQ(ts_expected[node], keepsmall(node, ts_partner[node], ts_depth)); | |||||
} | |||||
} | |||||
/* | |||||
* bool keepsmall(size_t node, size_t partner, size_t depth); | |||||
* | |||||
* depth: 2 | step: 0 | partner: [1, 0, 3, 2, 5, 4, 7, 6] | keepsmall: Bool[1, 0, 1, 0, 0, 1, 0, 1] | |||||
*/ | |||||
TEST(Tdistbitonic_UT, keepsmall_test4) { | |||||
size_t ts_depth = 2UL; | |||||
size_t ts_partner[] = { 1, 0, 3, 2, 5, 4, 7, 6}; | |||||
bool ts_expected[] = {1, 0, 1, 0, 0, 1, 0, 1}; | |||||
for (size_t node = 0 ; node < 8UL ; ++node ) { | |||||
EXPECT_EQ(ts_expected[node], keepsmall(node, ts_partner[node], ts_depth)); | |||||
} | |||||
} | |||||
/* | |||||
* bool keepsmall(size_t node, size_t partner, size_t depth); | |||||
* | |||||
* depth: 3 | step: 2 | partner: [4, 5, 6, 7, 0, 1, 2, 3] | keepsmall: Bool[1, 1, 1, 1, 0, 0, 0, 0] | |||||
*/ | |||||
TEST(Tdistbitonic_UT, keepsmall_test5) { | |||||
size_t ts_depth = 3UL; | |||||
size_t ts_partner[] = { 4, 5, 6, 7, 0, 1, 2, 3}; | |||||
bool ts_expected[] = {1, 1, 1, 1, 0, 0, 0, 0}; | |||||
for (size_t node = 0 ; node < 8UL ; ++node ) { | |||||
EXPECT_EQ(ts_expected[node], keepsmall(node, ts_partner[node], ts_depth)); | |||||
} | |||||
} | |||||
/* | |||||
* bool keepsmall(size_t node, size_t partner, size_t depth); | |||||
* | |||||
* depth: 3 | step: 1 | partner: [2, 3, 0, 1, 6, 7, 4, 5] | keepsmall: Bool[1, 1, 0, 0, 1, 1, 0, 0] | |||||
*/ | |||||
TEST(Tdistbitonic_UT, keepsmall_test6) { | |||||
size_t ts_depth = 3UL; | |||||
size_t ts_partner[] = { 2, 3, 0, 1, 6, 7, 4, 5}; | |||||
bool ts_expected[] = {1, 1, 0, 0, 1, 1, 0, 0}; | |||||
for (size_t node = 0 ; node < 8UL ; ++node ) { | |||||
EXPECT_EQ(ts_expected[node], keepsmall(node, ts_partner[node], ts_depth)); | |||||
} | |||||
} | |||||
/* | |||||
* bool keepsmall(size_t node, size_t partner, size_t depth); | |||||
* | |||||
* depth: 3 | step: 0 | partner: [1, 0, 3, 2, 5, 4, 7, 6] | keepsmall: Bool[1, 0, 1, 0, 1, 0, 1, 0] | |||||
*/ | |||||
TEST(Tdistbitonic_UT, keepsmall_test7) { | |||||
size_t ts_depth = 3UL; | |||||
size_t ts_partner[] = { 1, 0, 3, 2, 5, 4, 7, 6}; | |||||
bool ts_expected[] = {1, 0, 1, 0, 1, 0, 1, 0}; | |||||
for (size_t node = 0 ; node < 8UL ; ++node ) { | |||||
EXPECT_EQ(ts_expected[node], keepsmall(node, ts_partner[node], ts_depth)); | |||||
} | |||||
} | |||||
TEST(Tdistbitonic_UT, distbitonic_test1) { | |||||
AllData_t ts_Data { | |||||
Data_t (8), Data_t (8) | |||||
}; | |||||
std::srand(unsigned(std::time(nullptr))); | |||||
for (auto& v : ts_Data) { | |||||
std::generate(v.begin(), v.end(), std::rand); | |||||
} | |||||
distbitonic(2, ts_Data); | |||||
auto max = std::numeric_limits<Data_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 { | |||||
Data_t (8), Data_t (8), Data_t (8), Data_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<Data_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 { | |||||
Data_t (32), Data_t (32), Data_t (32), Data_t (32), | |||||
Data_t (32), Data_t (32), Data_t (32), Data_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<Data_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(); | |||||
} | |||||
} |