Author SHA1 Message Date
hoo2 888fcba64c Small changes to HW1 2024-12-24 17:54:36 +02:00
50 changed files with 32 additions and 32185 deletions
+4 -4
View File
@@ -51,13 +51,13 @@ struct session_t {
std::string queryMtxFile {}; //!< optional query matrix file name in HDF5 format
std::string queryDataSet {}; //!< optional query dataset name in HDF5 matrix file
bool queryMtx {false}; //!< Flag to indicate that there is a separate query matrix
size_t k {1}; //!< The number of nearest neighbors to find
size_t k {1UL}; //!< The number of nearest neighbors to find
std::string outMtxFile {"out.hdf5"}; //!< output matrix file name in HDF5 format
std::string outMtxIdxDataSet {"/Idx"}; //!< Index output dataset name in HDF5 matrix file
std::string outMtxDstDataSet {"/Dst"}; //!< Distance output dataset name in HDF5 matrix file
std::size_t max_threads {0}; //!< Maximum threads to use
std::size_t slices {0}; //!< Slices/threads to use
std::size_t accuracy {100}; //!< The neighbor finding accuracy
std::size_t max_threads {0UL}; //!< Maximum threads to use
std::size_t slices {0UL}; //!< Slices/threads to use
std::size_t accuracy {100UL}; //!< The neighbor finding accuracy
bool timing {false}; //!< Enable timing prints of the program
bool verbose {false}; //!< Flag to enable verbose output to stdout
};
+6 -6
View File
@@ -39,7 +39,8 @@ void pdist2(const Matrix& X, const Matrix& Y, Matrix& D2) {
int d = X.columns();
// Compute the squared norms of each row in X and Y
std::vector<DataType> X_norms(M), Y_norms(N);
std::vector<DataType> X_norms(M);
std::vector<DataType> Y_norms(N);
for (int i = 0; i < M ; ++i) {
X_norms[i] = cblas_ddot(d, X.data() + i * d, 1, X.data() + i * d, 1);
}
@@ -58,7 +59,6 @@ void pdist2(const Matrix& X, const Matrix& Y, Matrix& D2) {
D2.set(std::sqrt(D2.get(i, j)), i, j); // Take the square root of each
}
}
M++;
}
/*!
@@ -92,7 +92,7 @@ void quickselect(std::vector<std::pair<DataType, IndexType>>& vec, int k) {
* point of Q
*/
template<typename MatrixD, typename MatrixI>
void knnsearch(MatrixD& C, MatrixD& Q, size_t idx_offset, size_t k, size_t m, MatrixI& idx, MatrixD& dst) {
void knnsearch(MatrixD& C, MatrixD& Q, size_t idx_offset, size_t k, [[maybe_unused]] size_t m, MatrixI& idx, MatrixD& dst) {
using DstType = typename MatrixD::dataType;
using IdxType = typename MatrixI::dataType;
@@ -104,10 +104,10 @@ void knnsearch(MatrixD& C, MatrixD& Q, size_t idx_offset, size_t k, size_t m, Ma
pdist2(C, Q, D);
for (size_t j = 0; j < N; ++j) {
for (size_t j = 0UL; j < N; ++j) {
// Create a vector of pairs (distance, index) for the j-th query
std::vector<std::pair<DstType, IdxType>> dst_idx(M);
for (size_t i = 0; i < M; ++i) {
for (size_t i = 0UL; i < M; ++i) {
dst_idx[i] = {D.data()[i * N + j], i};
}
// Find the k smallest distances using quickSelectKSmallest
@@ -117,7 +117,7 @@ void knnsearch(MatrixD& C, MatrixD& Q, size_t idx_offset, size_t k, size_t m, Ma
std::sort(dst_idx.begin(), dst_idx.end());
// Store the indices and distances
for (size_t i = 0; i < k; ++i) {
for (size_t i = 0UL; i < k; ++i) {
dst.set(dst_idx[i].first, j, i);
idx.set(dst_idx[i].second + idx_offset, j, i);
}
+21 -20
View File
@@ -60,17 +60,17 @@ void mergeResultsWithM(mtx::Matrix<IndexType>& N1, mtx::Matrix<DataType>& D1,
size_t k, size_t m,
mtx::Matrix<IndexType>& N, mtx::Matrix<DataType>& D) {
size_t numQueries = N1.rows();
size_t maxCandidates = std::min((IndexType)m, (IndexType)(N1.columns() + N2.columns()));
size_t maxCandidates = std::min(static_cast<IndexType>(m), static_cast<IndexType>(N1.columns() + N2.columns()));
for (size_t q = 0; q < numQueries; ++q) {
for (size_t q = 0UL; q < numQueries; ++q) {
// Combine distances and neighbors
std::vector<std::pair<DataType, IndexType>> candidates(N1.columns() + N2.columns());
// Concatenate N1 and N2 rows
for (size_t i = 0; i < N1.columns(); ++i) {
for (size_t i = 0UL; i < N1.columns(); ++i) {
candidates[i] = {D1.get(q, i), N1.get(q, i)};
}
for (size_t i = 0; i < N2.columns(); ++i) {
for (size_t i = 0UL; i < N2.columns(); ++i) {
candidates[i + N1.columns()] = {D2.get(q, i), N2.get(q, i)};
}
@@ -81,7 +81,7 @@ void mergeResultsWithM(mtx::Matrix<IndexType>& N1, mtx::Matrix<DataType>& D1,
std::sort(candidates.begin(), candidates.begin() + maxCandidates);
// If m < k, pad the remaining slots with invalid values
for (size_t i = 0; i < k; ++i) {
for (size_t i = 0UL; i < k; ++i) {
if (i < maxCandidates) {
D.set(candidates[i].first, q, i);
N.set(candidates[i].second, q, i);
@@ -110,7 +110,7 @@ void worker_body (std::vector<MatrixD>& corpus_slices,
using IdxType = typename MatrixI::dataType;
for (size_t ci = 0; ci < num_slices; ++ci) {
for (size_t ci = 0UL; ci < num_slices; ++ci) {
size_t idx_offset = ci * corpus_slice_size;
// Intermediate matrixes for intermediate results
@@ -121,8 +121,8 @@ void worker_body (std::vector<MatrixD>& corpus_slices,
v0::knnsearch(corpus_slices[ci], query_slices[slice], idx_offset, k, m, temp_idx, temp_dst);
// Merge temporary results to final results
MatrixI idx_slice((IdxType*)idx.data(), slice * query_slice_size, query_slices[slice].rows(), k);
MatrixD dst_slice((DstType*)dst.data(), slice * query_slice_size, query_slices[slice].rows(), k);
MatrixI idx_slice(static_cast<IdxType*>(idx.data()), slice * query_slice_size, query_slices[slice].rows(), k);
MatrixD dst_slice(static_cast<DstType*>(dst.data()), slice * query_slice_size, query_slices[slice].rows(), k);
mergeResultsWithM(idx_slice, dst_slice, temp_idx, temp_dst, k, m, idx_slice, dst_slice);
}
@@ -145,29 +145,29 @@ void knnsearch(MatrixD& C, MatrixD& Q, size_t num_slices, size_t k, size_t m, Ma
using IdxType = typename MatrixI::dataType;
//Slice calculations
size_t corpus_slice_size = C.rows() / ((num_slices == 0)? 1:num_slices);
size_t query_slice_size = Q.rows() / ((num_slices == 0)? 1:num_slices);
size_t corpus_slice_size = C.rows() / ((num_slices == 0UL)? 1UL:num_slices);
size_t query_slice_size = Q.rows() / ((num_slices == 0UL)? 1UL:num_slices);
// Make slices
std::vector<MatrixD> corpus_slices;
std::vector<MatrixD> query_slices;
std::vector<MatrixD> corpus_slices{};
std::vector<MatrixD> query_slices{};
for (size_t i = 0; i < num_slices; ++i) {
for (size_t i = 0UL; i < num_slices; ++i) {
corpus_slices.emplace_back(
(DstType*)C.data(),
static_cast<DstType*>(C.data()),
i * corpus_slice_size,
(i == num_slices - 1 ? C.rows() - i * corpus_slice_size : corpus_slice_size),
(i == num_slices - 1UL ? C.rows() - i * corpus_slice_size : corpus_slice_size),
C.columns());
query_slices.emplace_back(
(DstType*)Q.data(),
static_cast<DstType*>(Q.data()),
i * query_slice_size,
(i == num_slices - 1 ? Q.rows() - i * query_slice_size : query_slice_size),
(i == num_slices - 1UL ? Q.rows() - i * query_slice_size : query_slice_size),
Q.columns());
}
// Initialize results
for (size_t i = 0; i < dst.rows(); ++i) {
for (size_t j = 0; j < dst.columns(); ++j) {
for (size_t i = 0UL; i < dst.rows(); ++i) {
for (size_t j = 0UL; j < dst.columns(); ++j) {
dst.set(std::numeric_limits<DstType>::infinity(), i, j);
idx.set(static_cast<IdxType>(-1), i, j);
}
@@ -176,7 +176,7 @@ void knnsearch(MatrixD& C, MatrixD& Q, size_t num_slices, size_t k, size_t m, Ma
// Main loop
#if defined OMP
#pragma omp parallel for
for (size_t qi = 0; qi < num_slices; ++qi) {
for (size_t qi = 0UL; qi < num_slices; ++qi) {
worker_body (corpus_slices, query_slices, idx, dst, qi, num_slices, corpus_slice_size, query_slice_size, k, m);
}
#elif defined CILK
@@ -185,6 +185,7 @@ void knnsearch(MatrixD& C, MatrixD& Q, size_t num_slices, size_t k, size_t m, Ma
}
#elif defined PTHREADS
std::vector<std::thread> workers;
workers.reserve(num_slices);
for (size_t qi = 0; qi < num_slices; ++qi) {
workers.push_back(
std::thread (worker_body<MatrixD, MatrixI>,
-23
View File
@@ -1,23 +0,0 @@
# project
bin/
out/
mat/
mtx/
.unused/
various/
# hpc
# IDEs
.idea/
.clangd
# eclipse
.project
.cproject
.settings/
.vs/
.vscode/
-250
View File
@@ -1,250 +0,0 @@
#
# PDS HW2 Makefile
#
# Copyright (C) 2024 Christos Choutouridis <christos@choutouridis.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# ============== Project settings ==============
# Project's name
PROJECT := PDS_homework_2
# Excecutable's name
TARGET := bitonic
# Source directories list(space seperated). Makefile-relative path, UNDER current directory.
SRC_DIR_LIST := src test test/gtest
# Include directories list(space seperated). Makefile-relative path.
INC_DIR_LIST := include \
test \
test/gtest/ \
/usr/lib/x86_64-linux-gnu/openmpi/include/ \
src
# Exclude files list(space seperated). Filenames only.
# EXC_FILE_LIST := bad.cpp old.cpp
# Build directories
BUILD_DIR := bin
OBJ_DIR := $(BUILD_DIR)/obj
DEP_DIR := $(BUILD_DIR)/.dep
# ========== Compiler settings ==========
# Compiler flags for debug and release
DEB_CFLAGS := -DDEBUG -g3 -Wall -Wextra -std=c11 -fopenmp
REL_CFLAGS := -Wall -Wextra -O3 -std=c11 -fopenmp
DEB_CXXFLAGS := -DDEBUG -g3 -Wall -Wextra -std=c++17 -fopenmp
REL_CXXFLAGS := -Wall -Wextra -O3 -std=c++17 -fopenmp
# Pre-defines
# PRE_DEFS := MYCAB=1729 SUPER_MODE
PRE_DEFS := _GLIBCXX_PARALLEL
# ============== Linker settings ==============
# Linker flags (example: -pthread -lm)
LDFLAGS := -pthread -fopenmp
# Map output file
MAP_FILE := output.map
MAP_FLAG := -Xlinker -Map=$(BUILD_DIR)/$(MAP_FILE)
# ============== Docker settings ==============
# We need:
# - Bind the entire project directory(the dir that icludes all the code) as volume.
# - In docker instance, change to working directory(where the makefile is).
DOCKER_VOL_DIR := $(shell pwd)
DOCKER_WRK_DIR :=
DOCKER_RUN := docker run --rm
DOCKER_FLAGS := -v $(DOCKER_VOL_DIR):/usr/src/$(PROJECT) -w /usr/src/$(PROJECT)/$(DOCKER_WRK_DIR)
# docker invoke mechanism (edit with care)
# note:
# Here, `DOCKER` variable is empty. Rules can assign `DOCKER := DOCKER_CMD` when docker
# functionality is needed.
DOCKER_CMD = $(DOCKER_RUN) $(DOCKER_FLAGS) $(IMAGE)
DOCKER :=
# ============== Tool selection ==============
# compiler and compiler flags.
CSIZE := size
CFLAGS := $(DEB_CFLAGS)
CXXFLAGS := $(DEB_CXXFLAGS)
CXX := g++ #mpic++
CC := gcc #mpicc
#
# =========== Main body and Patterns ===========
#
#ifeq ($(OS), Windows_NT)
# TARGET := $(TARGET).exe
#endif
INC := $(foreach dir,$(INC_DIR_LIST),-I$(dir))
DEF := $(foreach def,$(PRE_DEFS),-D$(def))
EXC := $(foreach fil,$(EXC_FILE_LIST), \
$(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/$(fil))) \
)
# source files. object and dependencies list
# recursive search into current and source directories
SRC := $(wildcard *.cpp)
SRC += $(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/*.cpp))
SRC += $(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/**/*.cpp))
SRC := $(filter-out $(EXC),$(SRC))
#SRC := $(abspath $(SRC))
OBJ := $(foreach file,$(SRC:%.cpp=%.o),$(OBJ_DIR)/$(file))
DEP := $(foreach file,$(SRC:%.cpp=%.d),$(DEP_DIR)/$(file))
# Make Dependencies pattern.
# This little trick enables recompilation only when dependencies change
# and it does so for changes both in source AND header files ;)
#
# It is based on Tom Tromey's method.
#
# Invoke cpp to create makefile rules with dependencies for each source file
$(DEP_DIR)/%.d: %.c
@mkdir -p $(@D)
@$(DOCKER) $(CC) -E $(CFLAGS) $(INC) $(DEF) -MM -MT $(OBJ_DIR)/$(<:.c=.o) -MF $@ $<
# c file objects depent on .c AND dependency files, which have an empty recipe
$(OBJ_DIR)/%.o: %.c $(DEP_DIR)/%.d
@mkdir -p $(@D)
@$(DOCKER) $(CC) -c $(CFLAGS) $(INC) $(DEF) -o $@ $<
$(DEP_DIR)/%.d: %.cpp
@mkdir -p $(@D)
@$(DOCKER) $(CXX) -E $(CXXFLAGS) $(INC) $(DEF) -MM -MT $(OBJ_DIR)/$(<:.cpp=.o) -MF $@ $<
# cpp file objects depent on .cpp AND dependency files, which have an empty recipe
$(OBJ_DIR)/%.o: %.cpp $(DEP_DIR)/%.d
@mkdir -p $(@D)
@$(DOCKER) $(CXX) -c $(CXXFLAGS) $(INC) $(DEF) -o $@ $<
# empty recipe for dependency files. This prevents make errors
$(DEP):
# now include all dependencies
# After all they are makefile dependency rules ;)
include $(wildcard $(DEP))
# main target rule
$(BUILD_DIR)/$(TARGET): $(OBJ)
@mkdir -p $(@D)
@echo Linking to target: $(TARGET)
@echo $(DOCKER) $(CXX) '$$(OBJ)' $(LDFLAGS) $(MAP_FLAG) -o $(@D)/$(TARGET)
@$(DOCKER) $(CXX) $(OBJ) $(LDFLAGS) $(MAP_FLAG) -o $(@D)/$(TARGET)
@echo
@echo Print size information
@$(CSIZE) $(@D)/$(TARGET)
@echo Done
#
# ================ Default local build rules =================
# example:
# make debug
.DEFAULT_GOAL := all
.PHONY: clean
clean:
@echo Cleaning build directories
@rm -rf $(OBJ_DIR)
@rm -rf $(DEP_DIR)
@rm -rf $(BUILD_DIR)
debug: CFLAGS := $(DEB_CFLAGS)
debug: $(BUILD_DIR)/$(TARGET)
release: CFLAGS := $(REL_CFLAGS)
release: $(BUILD_DIR)/$(TARGET)
#
# ================ 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)
@mkdir -p out
cp $(BUILD_DIR)/$(TARGET) out/$(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)
@mkdir -p out
cp $(BUILD_DIR)/$(TARGET) out/$(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)
@mkdir -p out
cp $(BUILD_DIR)/$(TARGET) out/$(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)
@mkdir -p out
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET)
tests: CC := mpicc
tests: CXX := mpic++
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)
@mkdir -p out
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET)
perfbitonic: CC := mpicc
perfbitonic: CXX := mpic++
perfbitonic: CFLAGS := $(REL_CFLAGS) -g -DCODE_VERSION=BITONIC
perfbitonic: CXXFLAGS := $(REL_CXXFLAGS) -g -DCODE_VERSION=BITONIC
perfbitonic: TARGET := perfbitonic
perfbitonic: $(BUILD_DIR)/$(TARGET)
@mkdir -p out
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET)
hpc-build:
make clean
make distbubbletonic
make clean
make distbitonic
make clean
make tests
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.
-15
View File
@@ -1,15 +0,0 @@
#! /usr/bin/env bash
if [[ $# -lt 2 ]]; then
echo "Error: You must pass the directory with the scripts and partition"
echo "you want to run to "
echo "example $ enqueueAll.sh ./hpc/Q23 batch"
echo "example $ enqueueAll.sh ./hpc/Q20 rome"
exit 1;
fi
# Enqueue
for file in $(ls $1); do
echo "sbatch -p $2 --qos=small $file";
eval "sbatch -p $2 --qos=small $file";
done
-33
View File
@@ -1,33 +0,0 @@
Parallel & Distributed Computer Systems HW2
December 6, 2024
Write a distributed program that sorts $N$ integers in ascending order, using MPI. The inter-process communications should be defined by the Bitonic sort algorithm as presented in our class notes.
The program must perform the following tasks:
- The user specifies two positive integers $q$ and $p$.
- Start $2^p$ processes with an array of $2^q$ random integers is each processes.
- Sort all $N = \left ( 2^{(q + p)} \right)$ elements int ascending order.
- Check the correctness of the final result.
Your implementation should be based on the following steps:
- Start p processes, each process gets n/p data and sorts (ascending or descending depending on the process id) them using sort from any library. Use two buffers, to sort from one to the other, to send from one and receive in the other.
- Repeat $O((log_2(p))^2)$:
* Exchange data with the corresponding partner and keep the min or max elements, depending on the process id and phase of the computation
* Sort locally the bitonic sequence using a modification of merge sort, starting from the "elbow" of the bitonic.
You may use the C standard library function stdlib qsort() to check the correctness of your results and as the initial local sorting routine for each process.
You must deliver:
- A report (about $3-4$ pages) that describes your parallel algorithm and implementation.
- Your comments on the speed of your parallel program compared to the serial sort, after trying you program on aristotelis for $p = [1:7]$ and $q = [20:27]$.
- The source code of your program uploaded online.
Ethics: If you use code found on the web or by an LLM, you should mention your source and the changes you made. You may work in pairs; both partners must submit a single report with both names.
Deadline: 7 January, $2025$.
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=2
#SBATCH --cpus-per-task=4
#SBATCH --time=1:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 20 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=1:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 20 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 20 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=16
#SBATCH --cpus-per-task=4
#SBATCH --time=5:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 20 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=32
#SBATCH --cpus-per-task=4
#SBATCH --time=5:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 20 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 20 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=8
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 20 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=2
#SBATCH --cpus-per-task=4
#SBATCH --time=1:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 23 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=1:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 23 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 23 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=16
#SBATCH --cpus-per-task=4
#SBATCH --time=5:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 23 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=32
#SBATCH --cpus-per-task=4
#SBATCH --time=5:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 23 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 23 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=8
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 23 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=2
#SBATCH --cpus-per-task=4
#SBATCH --time=1:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 25 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=1:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 25 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 25 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=16
#SBATCH --cpus-per-task=4
#SBATCH --time=5:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 25 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=32
#SBATCH --cpus-per-task=4
#SBATCH --time=5:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 25 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 25 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=8
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 25 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=2
#SBATCH --cpus-per-task=4
#SBATCH --time=1:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 27 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=1:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 27 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 27 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=16
#SBATCH --cpus-per-task=4
#SBATCH --time=5:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 27 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=32
#SBATCH --cpus-per-task=4
#SBATCH --time=5:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 27 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=4
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 27 --perf --validation
-28
View File
@@ -1,28 +0,0 @@
#! /usr/bin/env bash
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=8
#SBATCH --cpus-per-task=4
#SBATCH --time=2:00
# Use this as following
# $> sbatch -p batch|rome <this file>
#
# NOTE:
# First compile in aristotle with
# $> module load gcc/9.2.0 openmpi/4.0.3
# $> make -j hpc-build
#
module load gcc/9.2.0 openmpi/4.0.3
# Note:
# The above versions are matching w/ my system's
# versions, thus making compiling/debugging easier.
# Suppress unused UCX_ROOT warning
export UCX_WARN_UNUSED_ENV_VARS=n
# Suppress CUDA-aware support is disabled warning
export OMPI_MCA_opal_warn_on_missing_libcuda=0
srun ./out/distbitonic -q 27 --perf --validation
-61
View File
@@ -1,61 +0,0 @@
/*!
* \file
* \brief Build configuration file.
*
* \author
* Christos Choutouridis AEM:8997
* <cchoutou@ece.auth.gr>
*/
#ifndef CONFIG_H_
#define CONFIG_H_
#include <cstdint>
/*
* Defines for different version of the exercise
*/
#define BITONIC (1)
#define BUBBLETONIC (2)
// Fail-safe version selection
#if !defined CODE_VERSION
#define CODE_VERSION BITONIC
#endif
// Default Data size (in case -q <N> is not present)
#define DEFAULT_DATA_SIZE (1 << 16)
/*!
* 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;
/*!
* Session option for each invocation of the executable
*/
struct config_t {
size_t arraySize{DEFAULT_DATA_SIZE}; //!< The array size of the local data to sort.
bool validation{false}; //!< Request a full validation at the end, performed by process rank 0.
bool ndebug{false}; //!< Skips debug trap on DEBUG builds.
bool perf{false}; //!< Enable performance timing measurements and prints.
bool verbose{false}; //!< Flag to enable verbose output to stdout.
};
/*
* Exported data types
*/
extern config_t config;
#endif /* CONFIG_H_ */
-380
View File
@@ -1,380 +0,0 @@
/*!
* \file
* \brief Distributed sort implementation header
*
* \author
* Christos Choutouridis AEM:8997
* <cchoutou@ece.auth.gr>
*/
#ifndef DISTBITONIC_H_
#define DISTBITONIC_H_
#include <vector>
#include <algorithm>
#include <parallel/algorithm>
#include <cmath>
#include <cstdint>
#if !defined DEBUG
#define NDEBUG
#endif
#include <cassert>
#include "utils.hpp"
extern Timing TfullSort, Texchange, Tminmax, TelbowSort; // make timers public
/*!
* Enumerator for the different versions of the sorting method
*/
enum class SortMode {
Bubbletonic, //!< The v0.5 of the algorithm where we use a bubble-sort like approach
Bitonic //!< The v1.0 of the algorithm where we use the bitonic data-exchange approach
};
/*
* ============================== Sort utilities ==============================
*/
/*!
* The primary function template of ascending(). It is DISABLED since , it is explicitly specialized
* for each of the \c SortMode
*/
template <SortMode Mode> inline bool ascending(mpi_id_t, [[maybe_unused]] size_t) noexcept = delete;
/*!
* 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 [mpi_id_t] The current node (MPI process)
* @return [bool] True if we need ascending configuration, false otherwise
*/
template <> inline
bool ascending<SortMode::Bubbletonic>(mpi_id_t node, [[maybe_unused]] size_t depth) noexcept {
return (node % 2) == 0;
}
/*!
* 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 [mpi_id_t] The current node (MPI process)
* @param depth [size_t] The total depth of the sorting network (same for each step for a given network)
* @return [bool] True if we need ascending configuration, false otherwise
*/
template <> inline
bool ascending<SortMode::Bitonic>(mpi_id_t node, size_t depth) noexcept {
return !(node & (1 << depth));
}
/*!
* The primary function template of partner(). It is DISABLED since , it is explicitly specialized
* for each of the \c SortMode
*/
template <SortMode Mode> inline mpi_id_t partner(mpi_id_t, size_t) noexcept = delete;
/*!
* Returns the node's partner for data exchange during the sorting network iterations
* of Bubbletonic
*
* @param node [mpi_id_t] The current node
* @param step [size_t] The step of the sorting network
* @return [mpi_id_t] The node id of the partner for data exchange
*/
template <> inline
mpi_id_t partner<SortMode::Bubbletonic>(mpi_id_t node, size_t step) noexcept {
//return (node % 2 == step % 2) ? node + 1 : node - 1;
return (((node+step) % 2) == 0) ? node + 1 : node - 1;
}
/*!
* Returns the node's partner for data exchange during the sorting network iterations
* of Bitonic
*
* @param node [mpi_id_t] The current node
* @param step [size_t] The step of the sorting network
* @return [mpi_id_t] The node id of the partner for data exchange
*/
template <> inline
mpi_id_t partner<SortMode::Bitonic>(mpi_id_t node, size_t step) noexcept {
return (node ^ (1 << step));
}
/*!
* The primary function template of keepSmall(). It is DISABLED since , it is explicitly specialized
* for each of the \c SortMode
*/
template<SortMode Mode> inline bool keepSmall(mpi_id_t, mpi_id_t, [[maybe_unused]] size_t) = delete;
/*!
* Predicate to check if a node keeps the small numbers during the bubbletonic sort network exchange.
*
* @param node [mpi_id_t] The node for which we check
* @param partner [mpi_id_t] The partner of the data exchange
* @return [bool] True if the node should keep the small values, false otherwise
*/
template <> inline
bool keepSmall<SortMode::Bubbletonic>(mpi_id_t node, mpi_id_t partner, [[maybe_unused]] size_t depth) {
if (node == partner)
throw std::runtime_error("(keepSmall) Node and Partner can not be the same\n");
return (node < partner);
}
/*!
* Predicate to check if a node keeps the small numbers during the bitonic sort network exchange.
*
* @param node [mpi_id_t] The node for which we check
* @param partner [mpi_id_t] The partner of the data exchange
* @param depth [size_t] The total depth of the sorting network (same for each step for a given network)
* @return [bool] True if the node should keep the small values, false otherwise
*/
template <> inline
bool keepSmall<SortMode::Bitonic>(mpi_id_t node, mpi_id_t partner, size_t depth) {
if (node == partner)
throw std::runtime_error("(keepSmall) Node and Partner can not be the same\n");
return ascending<SortMode::Bitonic>(node, depth) == (node < partner);
}
/*!
* Predicate to check if the node is active in the current iteration of the bubbletonic
* sort exchange.
*
* @param node [mpi_id_t] The node to check
* @param nodes [size_t] The total number of nodes
* @return [bool] True if the node is active, false otherwise
*/
bool isActive(mpi_id_t node, size_t nodes);
/*
* ============================== Data utilities ==============================
*/
/*!
* Sort a range using the build-in O(Nlog(N)) algorithm
*
* @tparam RangeT A range type with random access iterator
*
* @param data [RangeT] The data to be sorted
* @param ascending [bool] Flag to indicate the sorting order
*/
template<typename RangeT>
void fullSort(RangeT& data, bool ascending) noexcept {
// Use introsort from stdlib++ here, unless ... __gnu_parallel
if (ascending) {
__gnu_parallel::sort(data.begin(), data.end(), std::less<>());
}
else {
__gnu_parallel::sort(data.begin(), data.end(), std::greater<>());
}
}
/*!
* Core functionality of sort for shadowed buffer types using
* the "elbow sort" algorithm.
*
* @note:
* This algorithm can not work "in place".
* We use the active buffer as source and the shadow as target.
* At the end we switch which buffer is active and which is the shadow.
* @note
* This is the core functionality. Use the elbowSort() function instead
*
* @tparam ShadowedDataT A Shadowed buffer type with random access iterator.
* @tparam CompT A Comparison type for binary operation comparisons
*
* @param data [ShadowedDataT] The data to sort
* @param ascending [bool] Flag to indicate the sorting order
* @param comp [CompT] The binary operator object
*/
template<typename ShadowedDataT, typename CompT>
void elbowSortCore(ShadowedDataT& data, bool ascending, CompT comp) noexcept {
auto& active = data.getActive(); // Get the source vector (the data to sort)
auto& shadow = data.getShadow(); // Get the target vector (the sorted data)
size_t N = data.size(); // The total size is the same or both vectors
size_t left = std::distance(
active.begin(),
(ascending) ?
std::min_element(active.begin(), active.end()) :
std::max_element(active.begin(), active.end())
); // start 'left' from elbow of the bitonic
size_t right = (left == N-1) ? 0 : left + 1;
// Walk in opposite directions from elbow and insert-sort to target vector
for (size_t i = 0 ; i<N ; ++i) {
if (comp(active[left], active[right])) {
shadow[i] = active[left];
left = (left == 0) ? N-1 : left -1; // cycle decrease
}
else {
shadow[i] = active[right];
right = (right + 1) % N; // cycle increase
}
}
data.switch_active(); // Switch active-shadow buffers
}
/*!
* Sort a shadowed buffer using the "elbow sort" algorithm.
*
* @tparam ShadowedDataT A Shadowed buffer type with random access iterator.
*
* @param data [ShadowedDataT] The data to sort
* @param ascending [bool] Flag to indicate the sorting order
*/
template<typename ShadowedDataT>
void elbowSort(ShadowedDataT& data, bool ascending) noexcept {
if (ascending)
elbowSortCore(data, ascending, std::less<>());
else
elbowSortCore(data, ascending, std::greater<>());
}
/*!
* Predicate for exchange optimization. Returns true only if an exchange between partners is needed.
* In order to do that we exchange min and max statistics of the partner's data.
*
* @tparam StatT Statistics data type (for min-max)
*
* @param lstat [const StatT] Reference to the local statistic data
* @param rstat [StatT] Reference to the remote statistic data to fill
* @param part [mpi_id_t] The partner for the exchange
* @param tag [int] The tag to use for the exchange of stats
* @param keepSmall [bool] Flag to indicate if the local thread keeps the small ro the large values
* @return True if we need data exchange, false otherwise
*/
template<typename StatT>
bool needsExchange(const StatT& lstat, StatT& rstat, mpi_id_t part, int tag, bool keepSmall) {
timeCall(Texchange, mpi.exchange_it, lstat, rstat, part, tag);
return (keepSmall) ?
rstat.min < lstat.max // Lmin: rstat.min - Smax: lstat.max
: lstat.min < rstat.max; // Lmin: lstat.min - Smax: rstat.max
}
/*!
* Update stats utility
*
* @tparam RangeT A range type with random access iterator
* @tparam StatT Statistics data type (for min-max)
*
* @param stat [StatT] Reference to the statistic data to update
* @param data [const RangeT] Reference to the sequence to extract stats from
*/
template<typename RangeT, typename StatT>
void updateMinMax(StatT& stat, const RangeT& data) noexcept {
auto [min, max] = std::minmax_element(data.begin(), data.end());
stat.min = *min;
stat.max = *max;
}
/*!
* Takes two sorted sequences where one is in increasing and the other is in decreasing order
* and selects either the larger or the smaller items in one-to-one comparison between them.
* The result is a bitonic sequence.
*
* @tparam RangeT A range type with random access iterator
*
* @param local [RangeT] Reference to the local sequence
* @param remote [const RangeT] Reference to the remote sequence (copied locally by MPI)
* @param keepSmall [bool] Flag to indicate if we keep the small items in local sequence
*/
template<typename RangeT>
void keepMinOrMax(RangeT& local, const RangeT& remote, bool keepSmall) noexcept {
using value_t = typename RangeT::value_type;
std::transform(
local.begin(), local.end(),
remote.begin(),
local.begin(),
[&keepSmall](const value_t& a, const value_t& b){
return (keepSmall) ? std::min(a, b) : std::max(a, b);
});
}
/*
* ============================== Sort algorithms ==============================
*/
/*!
* A distributed version of the Bubbletonic sort algorithm.
*
* @note
* Each MPI process should run an instance of this function.
*
* @tparam ShadowedDataT A Shadowed buffer type with random access iterator.
*
* @param data [ShadowedDataT] The local to MPI process data to sort
* @param Processes [mpi_id_t] The total number of MPI processes
* @param rank [mpi_id_t] The current process id
*/
template<typename ShadowedDataT>
void distBubbletonic(ShadowedDataT& data, mpi_id_t Processes, mpi_id_t rank) {
// Initially sort to create a half part of a bitonic sequence
timeCall(TfullSort, fullSort, data, ascending<SortMode::Bubbletonic>(rank, 0));
updateMinMax(localStat, data);
// Sort network (O(N) iterations)
for (size_t step = 0; step < static_cast<size_t>(Processes); ++step) {
// Find out exchange configuration
auto part = partner<SortMode::Bubbletonic>(rank, step);
auto ks = keepSmall<SortMode::Bubbletonic>(rank, part, Processes);
if ( isActive(rank, Processes) &&
isActive(part, Processes) ) {
// Exchange with partner, keep nim-or-max and sort - O(N)
int tag = static_cast<int>(2 * step);
if (needsExchange(localStat, remoteStat, part, tag, ks)) {
timeCall(Texchange, mpi.exchange_data, data.getActive(), data.getShadow(), part, ++tag);
timeCall(Tminmax, keepMinOrMax, data.getActive(), data.getShadow(), ks);
updateMinMax(localStat, data);
}
timeCall(TelbowSort, elbowSort, data, ascending<SortMode::Bubbletonic>(rank, Processes));
}
}
// Invert if the node was descending.
if (!ascending<SortMode::Bubbletonic>(rank, 0))
elbowSort(data, true);
}
/*!
* A distributed version of the Bitonic sort algorithm.
*
* @note
* Each MPI process should run an instance of this function.
*
* @tparam ShadowedDataT A Shadowed buffer type with random access iterator.
*
* @param data [ShadowedDataT] The local to MPI process data to sort
* @param Processes [mpi_id_t] The total number of MPI processes
* @param rank [mpi_id_t] The current process id
*/
template<typename ShadowedDataT>
void distBitonic(ShadowedDataT& data, mpi_id_t Processes, mpi_id_t rank) {
// Initially sort to create a half part of a bitonic sequence
timeCall(TfullSort, fullSort, data, ascending<SortMode::Bitonic>(rank, 0));
updateMinMax(localStat, data);
// Run through sort network using elbow-sort ( O(LogN * LogN) iterations )
auto p = static_cast<uint32_t>(std::log2(Processes));
for (size_t depth = 1; depth <= p; ++depth) {
for (size_t step = depth; step > 0;) {
--step;
// Find out exchange configuration
auto part = partner<SortMode::Bitonic>(rank, step);
auto ks = keepSmall<SortMode::Bitonic>(rank, part, depth);
// Exchange with partner, keep nim-or-max
int tag = static_cast<int>( (2*p*depth) + (2*step) );
if (needsExchange(localStat, remoteStat, part, tag, ks)) {
timeCall(Texchange, mpi.exchange_data, data.getActive(), data.getShadow(), part, tag);
timeCall(Tminmax, keepMinOrMax, data.getActive(), data.getShadow(), ks);
updateMinMax(localStat, data);
}
}
// sort - O(N)
timeCall(TelbowSort, elbowSort, data, ascending<SortMode::Bitonic>(rank, depth));
}
}
#endif //DISTBITONIC_H_
-421
View File
@@ -1,421 +0,0 @@
/**
* \file
* \brief Utilities header
*
* \author
* Christos Choutouridis AEM:8997
* <cchoutou@ece.auth.gr>
*/
#ifndef UTILS_HPP_
#define UTILS_HPP_
#include <vector>
#include <iostream>
#include <chrono>
#include <unistd.h>
#include <mpi.h>
#include "config.h"
/*!
* Min-Max statistics data for exchange optimization
* @tparam Value_t The underlying data type of the sequence data
*/
template <typename Value_t>
struct Stat_t {
using value_type = Value_t; //!< meta-export the type
Value_t min{}; //!< The minimum value of the sequence
Value_t max{}; //!< The maximum value of the sequence
};
//! Application data selection alias
using distStat_t = Stat_t<distValue_t>;
extern distStat_t localStat, remoteStat; // Make stats public
/*
* 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<short> { static MPI_Datatype getType() { return MPI_SHORT; } };
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 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 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 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<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>
struct MPI_t {
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 [char***] POINTER to main's argv argument
*/
void init(int* argc, char*** argv) {
// Initialize the MPI environment
int err;
if ((err = MPI_Init(argc, argv)) != MPI_SUCCESS)
mpi_throw(err, "(MPI) MPI_Init() - ");
initialized_ = true;
// Get the number of processes
int size_value, rank_value;
if ((err = MPI_Comm_size(MPI_COMM_WORLD, &size_value)) != MPI_SUCCESS)
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);
rank_ = static_cast<ID_t>(rank_value);
// Get the name of the processor
char processor_name[MPI_MAX_PROCESSOR_NAME];
int 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);
}
/*!
* 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 ldata [std::vector<T>] Reference to local data to send
* @param rdata [std::vector<T>] Reference to buffer to receive data from partner
* @param partner [mpi_id_t] The partner for the exchange
* @param tag [int] The tag to use for the MPI communication
*/
template<typename T>
void exchange_data(const std::vector<T>& ldata, std::vector<T>& rdata, ID_t partner, int tag) {
if (tag < 0)
throw std::runtime_error("(MPI) exchange_data() [tag] - Out of bound");
MPI_Datatype datatype = MPI_TypeMapper<T>::getType();
int count = static_cast<int>(ldata.size());
MPI_Status status;
int err;
if ((err = MPI_Sendrecv(
ldata.data(), count, datatype, partner, tag,
rdata.data(), count, datatype, partner, tag,
MPI_COMM_WORLD, &status
)) != MPI_SUCCESS)
mpi_throw(err, "(MPI) MPI_Sendrecv() [data] - ");
}
/*!
* Exchange a data object 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 the data object
* between current node and partner.
*
* @tparam T The object type
*
* @param local [const T&] Reference to the local object to send
* @param remote [T&] Reference to the object to receive data from partner
* @param partner [mpi_id_t] The partner for the exchange
* @param tag [int] The tag to use for the MPI communication
*/
template<typename T>
void exchange_it(const T& local, T& remote, ID_t partner, int tag) {
if (tag < 0)
throw std::runtime_error("(MPI) exchange_it() [tag] - Out of bound");
MPI_Status status;
int err;
if ((err = MPI_Sendrecv(
&local, sizeof(T), MPI_BYTE, partner, tag,
&remote, sizeof(T), MPI_BYTE, partner, tag,
MPI_COMM_WORLD, &status
)) != MPI_SUCCESS)
mpi_throw(err, "(MPI) MPI_Sendrecv() [item] - ");
}
// Accessors
[[nodiscard]] ID_t rank() const noexcept { return rank_; }
[[nodiscard]] ID_t size() const noexcept { return size_; }
[[nodiscard]] const std::string& name() const noexcept { return name_; }
// Mutators
ID_t rank(ID_t rank) noexcept { return rank_ = rank; }
ID_t size(ID_t size) noexcept { return size_ = size; }
std::string& name(const std::string& name) noexcept { return name_ = name; }
/*!
* Finalized the MPI
*/
void finalize() {
// Finalize the MPI environment
initialized_ = false;
MPI_Finalize();
}
//! RAII MPI finalization
~MPI_t() {
// Finalize the MPI environment even on unexpected errors
if (initialized_)
MPI_Finalize();
}
// Local functionality
private:
/*!
* Throw exception helper. It bundles the prefix msg with the MPI error string retrieved by
* MPI API.
*
* @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');
}
private:
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;
using mpi_id_t = MPI_t<>::ID_t;
/*!
* @brief 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 underlying data type of the vectors
*/
template <typename Value_t>
struct ShadowedVec_t {
// STL requirements
using value_type = Value_t;
using iterator = typename std::vector<Value_t>::iterator;
using const_iterator = typename std::vector<Value_t>::const_iterator;
using size_type = typename std::vector<Value_t>::size_type;
// Default constructor
ShadowedVec_t() = default;
// Constructor from an std::vector
explicit ShadowedVec_t(const std::vector<Value_t>& vec)
: North(vec), South(), active(north) {
South.resize(North.size());
}
explicit ShadowedVec_t(std::vector<Value_t>&& vec)
: North(std::move(vec)), South(), active(north) {
South.resize(North.size());
}
// Copy assignment operator
ShadowedVec_t& operator=(const ShadowedVec_t& other) {
if (this != &other) { // Avoid self-assignment
North = other.North;
South = other.South;
active = other.active;
}
return *this;
}
// Move assignment operator
ShadowedVec_t& operator=(ShadowedVec_t&& other) noexcept {
if (this != &other) { // Avoid self-assignment
North = std::move(other.North);
South = std::move(other.South);
active = other.active;
// There is no need to zero out other since it is valid but in a non-defined state
}
return *this;
}
// Type accessors
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; }
// Swap vectors
void switch_active() { active = (active == north) ? south : north; }
// Dispatch vector functionality to active vector
Value_t& operator[](size_type index) { return getActive()[index]; }
const Value_t& operator[](size_type index) const { return getActive()[index]; }
Value_t& at(size_type index) { return getActive().at(index); }
const Value_t& at(size_type index) const { return getActive().at(index); }
void push_back(const Value_t& value) { getActive().push_back(value); }
void push_back(Value_t&& value) { getActive().push_back(std::move(value)); }
void pop_back() { getActive().pop_back(); }
Value_t& front() { return getActive().front(); }
Value_t& back() { return getActive().back(); }
const Value_t& front() const { return getActive().front(); }
const Value_t& back() const { return getActive().back(); }
iterator begin() { return getActive().begin(); }
const_iterator begin() const { return getActive().begin(); }
iterator end() { return getActive().end(); }
const_iterator end() const { return getActive().end(); }
size_type size() const { return getActive().size(); }
void resize(size_t new_size) {
North.resize(new_size);
South.resize(new_size);
}
void reserve(size_t new_capacity) {
North.reserve(new_capacity);
South.reserve(new_capacity);
}
[[nodiscard]] size_t capacity() const { return getActive().capacity(); }
[[nodiscard]] bool empty() const { return getActive().empty(); }
void clear() { getActive().clear(); }
void swap(std::vector<Value_t>& other) { getActive().swap(other); }
// Comparisons
bool operator== (const ShadowedVec_t& other) { 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 std::vector<value_type>& other) { return getActive() != other; }
private:
std::vector<Value_t> North{}; //!< Actual buffer to be used either as active or shadow
std::vector<Value_t> South{}; //!< Actual buffer to be used either as active or shadow
enum {
north, south
} active{north}; //!< Flag to select between North and South buffer
};
/*
* Exported data types
*/
using distBuffer_t = ShadowedVec_t<distValue_t>;
extern distBuffer_t Data;
/*!
* A Logger for entire program.
*/
struct Log {
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 (config.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 (config.verbose) {
std::cout << '\n';
line_ = true;
}
return *this;
}
private:
bool line_{true};
};
extern Log logger;
/*!
* A small timing utility based on chrono.
*/
struct Timing {
using Tpoint = std::chrono::steady_clock::time_point;
using Tduration = std::chrono::microseconds;
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 mark_ = std::chrono::steady_clock::now(); }
//! tool to mark the ending point
Tpoint stop() noexcept {
Tpoint now = std::chrono::steady_clock::now();
duration_ += dt(now, mark_);
return now;
}
//! A duration calculation utility
static Tduration dt(Tpoint t2, Tpoint t1) noexcept {
return std::chrono::duration_cast<Tduration>(t2 - t1);
}
//! Tool to print the time interval
void print_duration(const char *what, mpi_id_t rank) noexcept {
if (std::chrono::duration_cast<microseconds>(duration_).count() < 10000)
std::cout << "[Timing] (Rank " << rank << ") " << what << ": "
<< std::to_string(std::chrono::duration_cast<microseconds>(duration_).count()) << " [usec]\n";
else if (std::chrono::duration_cast<milliseconds>(duration_).count() < 10000)
std::cout << "[Timing] (Rank " << rank << ") " << what << ": "
<< std::to_string(std::chrono::duration_cast<milliseconds>(duration_).count()) << " [msec]\n";
else
std::cout << "[Timing] (Rank " << rank << ") " << what << ": "
<< std::to_string(std::chrono::duration_cast<seconds>(duration_).count()) << " [sec]\n";
}
private:
Tpoint mark_{};
Tduration duration_{};
};
/*!
* Utility "high level function"-like macro to forward a function call
* and accumulate the execution time to the corresponding timing object.
*
* @param Tim The Timing object [Needs to have methods start() and stop()]
* @param Func The function name
* @param ... The arguments to pass to function (the preprocessor way)
*/
#define timeCall(Tim, Func, ...) \
Tim.start(); \
Func(__VA_ARGS__); \
Tim.stop(); \
#endif /* UTILS_HPP_ */
-114
View File
@@ -1,114 +0,0 @@
#
# ---------------------------------------------
# Bitonic v0.5 functionality
#
function partner(node, step)
partner = node + ((((node+step) % 2) == 0) ? 1 : -1)
end
function active(id, p)
ret = (id >= 0) && (id < p)
end
function exchange(localid, remoteid)
if verbose
println("Exchange local data from $localid with partner $remoteid")
end
nothing # We have all data here ;)
end
function minmax(data, localid, remoteid, keepsmall)
# Keep min-max on local data
temp = copy(data[localid+1, :])
if (keepsmall)
view(data, localid+1, :) .= min.(temp, data[remoteid+1, :])
view(data, remoteid+1, :) .= max.(temp, data[remoteid+1, :])
else
view(data, localid+1, :) .= max.(temp, data[remoteid+1, :])
view(data, remoteid+1, :) .= min.(temp, data[remoteid+1, :])
end
end
"""
distbubletonic!(p, data)
distributed bitonic v0.5 sort using a "bubble sort"-like functionality to propagate large and small
items between nodes.
p: The number of processes
data: (p, N/p) array
"""
function distbubletonic!(p, data)
pid = 0:p-1
ascending = mod.(pid,2) .== 0
if verbose
println("ascending: $ascending")
end
# local full sort here (run all MPI nodes)
for i in 1:p
sort!(view(data, i, :), rev = !ascending[i])
end
for step in 0:p-1
direction = [true for x = 1:p]
partnerid = partner.(pid, step)
activeids = active.(partnerid, p)
keepsmall = pid .< partnerid
if verbose
println("step: $step | active ids: $activeids | partner: $partnerid | keepsmall: $keepsmall")
end
# exchange with partner and keep small or large (run all MPI nodes)
for i in 0:p-1
l_idx = i+1
r_idx = partnerid[i+1]+1
if activeids[l_idx] && i < partnerid[l_idx]
exchange(i, partnerid[l_idx])
minmax(data, i, partnerid[l_idx], keepsmall[l_idx])
sort!(view(data, l_idx, :), rev = !ascending[l_idx]) # elbow sort here
sort!(view(data, r_idx, :), rev = !ascending[r_idx]) # elbow sort here
end
end
end
# [optional] reverse the odd positions (run all MPI nodes)
for i in 1:p
if !ascending[i]
sort!(view(data, i, :))
end
end
nothing
end
#
# Homework setup
# ---------------------------------------------
#
p::Int8 = 3 # The order of number of "processors"
q::Int8 = 8 # The data size order (power of 2) of each "processor"
verbose = false;
# Run Script
# ---------------------------------------------
P::Int = 2^p
Q::Int = 2^q
N::Int = 2^(q+p)
println("Distributed Bubbletonic (v0.5) test")
println("p: $p -> Number of processors: $P")
println("q: $q -> Data length for each node: $Q, Total: $(P*Q)")
println("Create an $P x $Q array")
Data = rand(Int8, P, Q)
println("Sort array with $P (MPI) nodes")
@time distbubletonic!(P, Data)
# Test
if issorted(vec(permutedims(Data)))
println("Test: Passed")
else
println("Test: Failed")
end
-200
View File
@@ -1,200 +0,0 @@
#
# ---------------------------------------------
# Bitonic v0.5 functionality
#
function exchange(localid, remoteid)
# if verbose
# println("Exchange local data from $localid with partner $remoteid")
# end
nothing # We have all data here ;)
end
function minmax(data, localid, remoteid, keepsmall)
# Keep min-max on local data
temp = copy(data[localid+1, :])
if (keepsmall)
view(data, localid+1, :) .= min.(temp, data[remoteid+1, :])
view(data, remoteid+1, :) .= max.(temp, data[remoteid+1, :])
else
view(data, localid+1, :) .= max.(temp, data[remoteid+1, :])
view(data, remoteid+1, :) .= min.(temp, data[remoteid+1, :])
end
end
function is_bitonic(arr)
n = length(arr)
if n <= 2
return true # Any sequence of length <= 2 is bitonic
end
# State for state machine. 1: inc, -1: dec, 0: z-state
state = 0
inc_count = 0
dec_count = 0
ret = false
for i in 1:n-1
# Find the first order
if state == 0
if arr[i] > arr[i+1]
state = -1
dec_count += 1
elseif arr[i] < arr[i+1]
state = 1
inc_count += 1
end
elseif state == -1 # decreasing
if arr[i] < arr[i + 1]
state = 1
inc_count += 1
end
elseif state == 1 # increasing
if arr[i] > arr[i+1]
state = -1
dec_count += 1
end
end
end
if inc_count <= 1 && dec_count <= 1
ret = true # Sequence is bitonic
elseif inc_count == 2 && dec_count == 1
ret = (arr[1] >= arr[n])
elseif inc_count == 1 && dec_count == 2
ret = (arr[1] <= arr[n])
end
ret
end
function is_sort(arr)
# State for state machine. 1: inc, -1: dec, 0: z-state
state = 0
inc_count = 0
dec_count = 0
for i in 1:length(arr)-1
# Find the first order
if state == 0
if arr[i] > arr[i+1]
state = -1
dec_count += 1
elseif arr[i] < arr[i+1]
state = 1
inc_count += 1
end
elseif state == -1 # decreasing
if arr[i] < arr[i + 1]
state = 1
inc_count += 1
end
elseif state == 1 # increasing
if arr[i] > arr[i+1]
state = -1
dec_count += 1
end
end
end
ret = ((inc_count + dec_count) == 1) ? state : 0
ret
end
function sort_network!(data, n, depth)
nodes = 0:n-1
bitonicFlag = zeros(Int8, size(data, 1))
sortFlag = zeros(Int8, size(data, 1))
for step = depth-1:-1:0
partnerid = nodes .⊻ (1 << step)
direction = (nodes .& (1 << depth)) .== 0 .& (nodes .< partnerid)
keepsmall = ((nodes .< partnerid) .& direction) .| ((nodes .> partnerid) .& .!direction)
if verbose
println("depth: $depth | step: $step | partner: $partnerid | keepsmall: $keepsmall")
end
# exchange with partner and keep small or large (run all MPI nodes)
for i in 0:n-1
if (i < partnerid[i+1])
exchange(i, partnerid[i+1])
minmax(data, i, partnerid[i+1], keepsmall[i+1])
end
end
if verbose
for i in 1:size(data, 1)
bitonicFlag[i] = is_bitonic(data[i, :])
sortFlag[i] = is_sort(data[i, :])
end
println("depth: $depth | step: $step | bitonicFlag: $bitonicFlag | sorfFlag: $sortFlag")
end
end
end
"""
distbitonic!(p, data)
distributed bitonic sort v1 using elbow merge locally except for the first step
p: The number of processes
data: (p, N/p) array
"""
function distbitonic!(p, data)
q = Int(log2(p)) # CPU order
pid = 0:p-1
ascending = mod.(pid,2) .== 0
if verbose
println("ascending: $ascending")
end
# local full sort here (run all MPI nodes)
for i in 1:p
sort!(view(data, i, :), rev = !ascending[i])
end
for depth = 1:q
sort_network!(data, p, depth)
ascending = (pid .& (1 << depth)) .== 0
if verbose
println("ascending: $ascending")
end
# local elbowmerge here (run all MPI nodes)
for i in 1:p
sort!(view(data, i, :), rev = !ascending[i])
end
end
nothing
end
#
# Homework setup
# ---------------------------------------------
#
p::Int8 = 3 # The order of number of "processors"
q::Int8 = 8 # The data size order (power of 2) of each "processor"
verbose = false;
# Run Script
# ---------------------------------------------
P::Int = 2^p
Q::Int = 2^q
N::Int = 2^(q+p)
println("Distributed bitonic (v1) test")
println("p: $p -> Number of processors: $P")
println("q: $q -> Data length for each node: $Q, Total: $(P*Q)")
println("Create an $P x $Q array")
Data = rand(Int8, P, Q)
println("Sort array with $P (MPI) nodes")
@time distbitonic!(P, Data)
# Test
if issorted(vec(permutedims(Data)))
println("Test: Passed")
else
println("Test: Failed")
end
-27
View File
@@ -1,27 +0,0 @@
# distributed bitonic sort using elbow merge locally except for the first step
function distbitonic!(p)
q = Int(log2(p))
pid = 0:p-1
ascending = mod.(pid,2) .== 0
println("ascending: $ascending")
# local full sort here
for k = 1:q
kk = 1 << k
for j = k-1:-1:0
jj = 1 << j
partnerid = pid .⊻ jj
direction = (pid .& kk) .== 0 .& (pid .< partnerid)
keepsmall = ((pid .< partnerid) .& direction) .| ((pid .> partnerid) .& .!direction)
println("k: $k | j: $j | partner: $partnerid | keepsmall: $keepsmall")
# exchange with partner and keep small or large
end
ascending = (pid .& kk) .== 0
println("ascending: $ascending")
# local elbowmerge here
end
nothing
end
-23
View File
@@ -1,23 +0,0 @@
# given a bitonic sequence b, merge it into a sorted sequence s
@inbounds function elbowmerge!(s, b)
n = length(b)
l = argmin(b)
r = l == n ? 1 : l + 1
i = 1
while i <= n
if b[l] < b[r]
s[i] = b[l]
l = l == 1 ? n : l - 1
else
s[i] = b[r]
r = r == n ? 1 : r + 1
end
i += 1
end
nothing
end
-27
View File
@@ -1,27 +0,0 @@
/*!
* \file
* \brief Distributed sort implementation
*
* \author
* Christos Choutouridis AEM:8997
* <cchoutou@ece.auth.gr>
*/
#include "utils.hpp"
#include "distsort.hpp"
//! Statistic variables for exchange optimization
distStat_t localStat, remoteStat;
//! Performance timers for each one of the "costly" functions
Timing TfullSort, Texchange, Tminmax, TelbowSort;
bool isActive(mpi_id_t node, size_t nodes) {
if (!((nodes > 0) &&
(nodes <= std::numeric_limits<mpi_id_t>::max()) ))
throw std::runtime_error("(isActive) Non-acceptable value of MPI Nodes\n");
// ^ Assert that mpi_id_t can hold nodes, and thus we can cast without data loss!
return (node >= 0) && (node < static_cast<mpi_id_t>(nodes));
}
-243
View File
@@ -1,243 +0,0 @@
/*!
* \file
* \brief Main application file for PDS HW2 (MPI)
*
* \author
* Christos Choutouridis AEM:8997
* <cchoutou@ece.auth.gr>
*/
#include <exception>
#include <iostream>
#include <algorithm>
#include <random>
#include "utils.hpp"
#include "config.h"
#include "distsort.hpp"
// Global config data
config_t config;
MPI_t<> mpi;
distBuffer_t Data;
Log logger;
Timing Ttotal;
/*!
* A small command line argument parser
* \return The status of the operation
*/
bool get_options(int argc, char* argv[]){
bool status =true;
// iterate over the passed arguments
for (int i=1 ; i<argc ; ++i) {
std::string arg(argv[i]); // get current argument
if (arg == "-q" || arg == "--array-size") {
if (i+1 < argc) {
config.arraySize = 1 << atoi(argv[++i]);
}
else {
status = false;
}
}
else if (arg == "--validation") {
config.validation = true;
}
else if (arg == "--ndebug") {
config.ndebug = true;
}
else if (arg == "--perf") {
config.perf = true;
}
else if (arg == "-v" || arg == "--verbose") {
config.verbose = true;
}
else if (arg == "-h" || arg == "--help") {
std::cout << "distbitonic/distbubbletonic - A distributed bitonic sort\n\n";
std::cout << "distbitonic -q <N> [--validation] [--ndebug] [-v]\n";
std::cout << "distbitonic -h\n";
std::cout << "distbubbletonic -q <N> [--validation] [--ndebug] [-v]\n";
std::cout << "distbubbletonic -h\n";
std::cout << '\n';
std::cout << "Options:\n\n";
std::cout << " -q | --array-size <N>\n";
std::cout << " Selects the array size according to size = 2^N\n\n";
std::cout << " --par-sort\n";
std::cout << " Request a parallel full sorting algorithm\n\n";
std::cout << " --validation\n";
std::cout << " Request a full validation at the end, performed by process rank 0\n\n";
std::cout << " --ndebug\n";
std::cout << " Skip debug breakpoint when on debug build.\n\n";
std::cout << " -t | --timing\n";
std::cout << " Request timing measurements output to stdout.\n\n";
std::cout << " -v | --verbose\n";
std::cout << " Request a more verbose output to stdout.\n\n";
std::cout << " -h | --help\n";
std::cout << " Prints this and exit.\n\n";
std::cout << "Examples:\n\n";
std::cout << " mpirun -np 4 distbitonic -q 24\n";
std::cout << " Runs distbitonic in 4 MPI processes with 2^24 array points each\n\n";
std::cout << " mpirun -np 16 distbubbletonic -q 20\n";
std::cout << " Runs distbubbletonic in 16 MPI processes with 2^20 array points each\n\n";
exit(0);
}
else { // parse error
std::cout << "Invocation error. Try -h for details.\n";
status = false;
}
}
return status;
}
/*!
* A simple validator for the entire distributed process
*
* @tparam ShadowedDataT A Shadowed buffer type with random access iterator.
*
* @param data [ShadowedDataT] The local to MPI process
* @param Processes [mpi_id_t] The total number of MPI processes
* @param rank [mpi_id_t] The current process id
*
* @return [bool] True if all are sorted and in total ascending order
*/
template<typename ShadowedDataT>
bool validator(ShadowedDataT& data, mpi_id_t Processes, mpi_id_t rank) {
using value_t = typename ShadowedDataT::value_type;
bool ret = true; // Have faith!
// Local results
value_t lmin = data.front();
value_t lmax = data.back();
value_t lsort = static_cast<value_t>(std::is_sorted(data.begin(), data.end()));
// Gather min/max/sort to rank 0
std::vector<value_t> mins(Processes);
std::vector<value_t> maxes(Processes);
std::vector<value_t> sorts(Processes);
MPI_Datatype datatype = MPI_TypeMapper<value_t>::getType();
MPI_Gather(&lmin, 1, datatype, mins.data(), 1, datatype, 0, MPI_COMM_WORLD);
MPI_Gather(&lmax, 1, datatype, maxes.data(), 1, datatype, 0, MPI_COMM_WORLD);
MPI_Gather(&lsort, 1, datatype, sorts.data(), 1, datatype, 0, MPI_COMM_WORLD);
// Check all results
if (rank == 0) {
for (mpi_id_t r = 1; r < Processes; ++r) {
if (sorts[r] == 0)
ret = false;
if (maxes[r - 1] > mins[r])
ret = false;
}
}
return ret;
}
#if !defined TESTING
/*!
* @return Returns 0, but.... we may throw or exit(1)
*/
int main(int argc, char* argv[]) try {
// Initialize MPI environment
mpi.init(&argc, &argv);
// try to read command line (after MPI parsing)
if (!get_options(argc, argv))
exit(1);
logger << "MPI environment initialized." <<
" Rank: " << mpi.rank() <<
" Size: " << mpi.size() <<
logger.endl;
#if defined DEBUG
#if defined TESTING
/*
* 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>
*/
volatile bool sleep_wait = false;
#else
volatile bool sleep_wait = true;
#endif
while (sleep_wait && !config.ndebug)
sleep(1);
#endif
// Initialize local data
logger << "Initialize local array of " << config.arraySize << " elements" << logger.endl;
std::random_device rd; // Mersenne seeded from hw if possible. range: [type_min, type_max]
std::mt19937 gen(rd());
std::uniform_int_distribution<distValue_t > dis(
std::numeric_limits<distValue_t>::min(),
std::numeric_limits<distValue_t>::max()
);
// Fill vector
Data.resize(config.arraySize);
std::generate(Data.begin(), Data.end(), [&]() { return dis(gen); });
// Run distributed sort
if (mpi.rank() == 0)
logger << "Starting distributed sorting ... ";
Ttotal.start();
#if CODE_VERSION == BUBBLETONIC
distBubbletonic(Data, mpi.size(), mpi.rank());
#else
distBitonic (Data, mpi.size(), mpi.rank());
#endif
Ttotal.stop();
if (mpi.rank() == 0)
logger << " Done." << logger.endl;
// Print-outs and validation
if (config.perf) {
Ttotal.print_duration("Total ", mpi.rank());
TfullSort.print_duration("Full-Sort ", mpi.rank());
Texchange.print_duration("Exchange ", mpi.rank());
Tminmax.print_duration("Min-Max ", mpi.rank());
TelbowSort.print_duration("Elbow-Sort", mpi.rank());
}
if (config.validation) {
// If requested, we have the chance to fail!
if (mpi.rank() == 0)
std::cout << "Results validation ...";
bool val = validator(Data, mpi.size(), mpi.rank());
if (mpi.rank() == 0)
std::cout << ((val) ? "\x1B[32m [PASS] \x1B[0m\n" : " \x1B[32m [FAIL] \x1B[0m\n");
}
mpi.finalize();
return 0;
}
catch (std::exception& e) {
//we probably pollute the user's screen. Comment `cerr << ...` if you don't like it.
std::cerr << "Error: " << e.what() << '\n';
exit(1);
}
#else
#include <gtest/gtest.h>
#include <exception>
/*!
* The testing version of our program
*/
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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-330
View File
@@ -1,330 +0,0 @@
/**
* \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 "distsort.hpp"
/* ================================== ascending ================================== */
/*
* bool ascending<SortMode::Bitonic>(mpi_id_t node, size_t depth);
* depth 0 (the initial ascending pattern)
*/
TEST(TdistBitonic_UT, ascending_test1) {
EXPECT_EQ(ascending<SortMode::Bitonic>(0, 0), true);
EXPECT_EQ(ascending<SortMode::Bitonic>(1, 0), false);
EXPECT_EQ(ascending<SortMode::Bitonic>(2, 0), true);
EXPECT_EQ(ascending<SortMode::Bitonic>(3, 0), false);
EXPECT_EQ(ascending<SortMode::Bitonic>(4, 0), true);
EXPECT_EQ(ascending<SortMode::Bitonic>(5, 0), false);
EXPECT_EQ(ascending<SortMode::Bitonic>(6, 0), true);
EXPECT_EQ(ascending<SortMode::Bitonic>(7, 0), false);
for (mpi_id_t node = 0 ; node < 256 ; ++node) {
EXPECT_EQ(ascending<SortMode::Bitonic>(node, 0), ((node % 2) ? false : true) );
}
}
/*
* bool ascending<SortMode::Bitonic>(mpi_id_t node, size_t depth);
* depth 1
*/
TEST(TdistBitonic_UT, ascending_test2) {
EXPECT_EQ(ascending<SortMode::Bitonic>(0, 1), true);
EXPECT_EQ(ascending<SortMode::Bitonic>(1, 1), true);
EXPECT_EQ(ascending<SortMode::Bitonic>(2, 1), false);
EXPECT_EQ(ascending<SortMode::Bitonic>(3, 1), false);
EXPECT_EQ(ascending<SortMode::Bitonic>(4, 1), true);
EXPECT_EQ(ascending<SortMode::Bitonic>(5, 1), true);
EXPECT_EQ(ascending<SortMode::Bitonic>(6, 1), false);
EXPECT_EQ(ascending<SortMode::Bitonic>(7, 1), false);
for (mpi_id_t node = 0 ; node < 256 ; ++node) {
EXPECT_EQ(ascending<SortMode::Bitonic>(2*node, 1), ((node % 2) ? false:true));
EXPECT_EQ(ascending<SortMode::Bitonic>(2*node+1, 1), ((node % 2) ? false:true));
}
}
/*
* bool ascending<SortMode::Bitonic>(mpi_id_t node, size_t depth);
* various depths
*/
TEST(TdistBitonic_UT, ascending_test3) {
// Depth = 3
size_t ts_depth = 3;
for (mpi_id_t n = 0 ; n < (1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), true);
for (mpi_id_t n = (1<<(ts_depth)) ; n < 2*(1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), false);
for (mpi_id_t n = 2*(1<<(ts_depth)) ; n < 3*(1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), true);
for (mpi_id_t n = 3*(1<<(ts_depth)) ; n < 4*(1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), false);
// Depth = 4
ts_depth = 4;
for (mpi_id_t n = 0L ; n < (1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), true);
for (mpi_id_t n = (1<<(ts_depth)) ; n < 2*(1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), false);
for (mpi_id_t n = 2*(1<<(ts_depth)) ; n < 3*(1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), true);
for (mpi_id_t n = 3*(1<<(ts_depth)) ; n < 4*(1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), false);
// Depth = 8
ts_depth = 8;
for (mpi_id_t n = 0L ; n < (1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), true);
for (mpi_id_t n = (1<<(ts_depth)) ; n < 2*(1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), false);
for (mpi_id_t n = 2*(1<<(ts_depth)) ; n < 3*(1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), true);
for (mpi_id_t n = 3*(1<<(ts_depth)) ; n < 4*(1<<(ts_depth)) ; ++n)
EXPECT_EQ(ascending<SortMode::Bitonic>(n, ts_depth), false);
}
/* ================================== partner ================================== */
/*
* mpi_id_t partner<SortMode::Bitonic>(mpi_id_t node, size_t step);
* step = 0
*/
TEST(TdistBitonic_UT, partner_test1) {
EXPECT_EQ(partner<SortMode::Bitonic>(0, 0), 1);
EXPECT_EQ(partner<SortMode::Bitonic>(1, 0), 0);
EXPECT_EQ(partner<SortMode::Bitonic>(2, 0), 3);
EXPECT_EQ(partner<SortMode::Bitonic>(3, 0), 2);
EXPECT_EQ(partner<SortMode::Bitonic>(4, 0), 5);
EXPECT_EQ(partner<SortMode::Bitonic>(5, 0), 4);
EXPECT_EQ(partner<SortMode::Bitonic>(6, 0), 7);
EXPECT_EQ(partner<SortMode::Bitonic>(7, 0), 6);
for (mpi_id_t node = 0 ; node < 256 ; ++node) {
EXPECT_EQ(partner<SortMode::Bitonic>(node, 0), (node % 2) ? node-1 : node+1);
}
}
/*
* mpi_id_t partner<SortMode::Bitonic>(mpi_id_t node, size_t step);
* step = 1
*/
TEST(TdistBitonic_UT, partner_test2) {
EXPECT_EQ(partner<SortMode::Bitonic>(0, 1), 2);
EXPECT_EQ(partner<SortMode::Bitonic>(1, 1), 3);
EXPECT_EQ(partner<SortMode::Bitonic>(2, 1), 0);
EXPECT_EQ(partner<SortMode::Bitonic>(3, 1), 1);
EXPECT_EQ(partner<SortMode::Bitonic>(4, 1), 6);
EXPECT_EQ(partner<SortMode::Bitonic>(5, 1), 7);
EXPECT_EQ(partner<SortMode::Bitonic>(6, 1), 4);
EXPECT_EQ(partner<SortMode::Bitonic>(7, 1), 5);
for (mpi_id_t n1 = 0 ; n1 < 256 ; n1 += 2) {
auto n2 = n1 + 1;
EXPECT_EQ(partner<SortMode::Bitonic>(n1, 1), ((n1 % 4) ? n1-2 : n1+2));
EXPECT_EQ(partner<SortMode::Bitonic>(n2, 1), ((n1 % 4) ? n2-2 : n2+2));
}
}
/*
* mpi_id_t partner(mpi_id_t node, size_t step);
* various steps
*/
TEST(TdistBitonic_UT, partner_test3) {
// step = 2
size_t ts_step = 2;
for (mpi_id_t n1 = 0 ; n1 < 256 ; n1 += 4) {
auto n2 = n1 + 1;
auto n3 = n1 + 2;
auto n4 = n1 + 3;
EXPECT_EQ(partner<SortMode::Bitonic>(n1, ts_step), ((n1 % 8) ? n1-4 : n1+4));
EXPECT_EQ(partner<SortMode::Bitonic>(n2, ts_step), ((n1 % 8) ? n2-4 : n2+4));
EXPECT_EQ(partner<SortMode::Bitonic>(n3, ts_step), ((n1 % 8) ? n3-4 : n3+4));
EXPECT_EQ(partner<SortMode::Bitonic>(n4, ts_step), ((n1 % 8) ? n4-4 : n4+4));
}
// step = 3
ts_step = 3;
for (mpi_id_t n1 = 0 ; n1 < 256 ; n1 += 8) {
auto n2 = n1 + 1;
auto n3 = n1 + 2;
auto n4 = n1 + 3;
auto n5 = n1 + 4;
auto n6 = n1 + 5;
auto n7 = n1 + 6;
auto n8 = n1 + 7;
EXPECT_EQ(partner<SortMode::Bitonic>(n1, ts_step), ((n1 % 16) ? n1-8 : n1+8));
EXPECT_EQ(partner<SortMode::Bitonic>(n2, ts_step), ((n1 % 16) ? n2-8 : n2+8));
EXPECT_EQ(partner<SortMode::Bitonic>(n3, ts_step), ((n1 % 16) ? n3-8 : n3+8));
EXPECT_EQ(partner<SortMode::Bitonic>(n4, ts_step), ((n1 % 16) ? n4-8 : n4+8));
EXPECT_EQ(partner<SortMode::Bitonic>(n5, ts_step), ((n1 % 16) ? n5-8 : n5+8));
EXPECT_EQ(partner<SortMode::Bitonic>(n6, ts_step), ((n1 % 16) ? n6-8 : n6+8));
EXPECT_EQ(partner<SortMode::Bitonic>(n7, ts_step), ((n1 % 16) ? n7-8 : n7+8));
EXPECT_EQ(partner<SortMode::Bitonic>(n8, ts_step), ((n1 % 16) ? n8-8 : n8+8));
}
// step = 4
ts_step = 4;
for (mpi_id_t n1 = 0 ; n1 < 256 ; n1 += 16) {
auto n2 = n1 + 1;
auto n3 = n1 + 2;
auto n4 = n1 + 3;
auto n5 = n1 + 4;
auto n6 = n1 + 5;
auto n7 = n1 + 6;
auto n8 = n1 + 7;
auto n9 = n1 + 8;
auto n10 = n1 + 9;
auto n11 = n1 + 10;
auto n12 = n1 + 11;
auto n13 = n1 + 12;
auto n14 = n1 + 13;
auto n15 = n1 + 14;
auto n16 = n1 + 15;
EXPECT_EQ(partner<SortMode::Bitonic>(n1, ts_step), ((n1 % 32) ? n1-16 : n1+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n2, ts_step), ((n1 % 32) ? n2-16 : n2+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n3, ts_step), ((n1 % 32) ? n3-16 : n3+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n4, ts_step), ((n1 % 32) ? n4-16 : n4+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n5, ts_step), ((n1 % 32) ? n5-16 : n5+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n6, ts_step), ((n1 % 32) ? n6-16 : n6+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n7, ts_step), ((n1 % 32) ? n7-16 : n7+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n8, ts_step), ((n1 % 32) ? n8-16 : n8+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n9, ts_step), ((n1 % 32) ? n9-16 : n9+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n10, ts_step), ((n1 % 32) ? n10-16 : n10+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n11, ts_step), ((n1 % 32) ? n11-16 : n11+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n12, ts_step), ((n1 % 32) ? n12-16 : n12+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n13, ts_step), ((n1 % 32) ? n13-16 : n13+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n14, ts_step), ((n1 % 32) ? n14-16 : n14+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n15, ts_step), ((n1 % 32) ? n15-16 : n15+16));
EXPECT_EQ(partner<SortMode::Bitonic>(n16, ts_step), ((n1 % 32) ? n16-16 : n16+16));
}
}
/* ================================== keepSmall ================================== */
/*
* bool keepSmall(mpi_id_t node, mpi_id_t partner, size_t depth);
* Throw check (Not assert - ASSERT_DEATH)
*/
TEST(TdistBitonic_UT, keepsmall_test1) {
// node and partner must differ or else ...
EXPECT_THROW(keepSmall<SortMode::Bitonic>(0, 0, 0), std::runtime_error);
EXPECT_THROW(keepSmall<SortMode::Bitonic>(1, 1, 42), std::runtime_error);
EXPECT_THROW(keepSmall<SortMode::Bitonic>(7, 7, 42), std::runtime_error);
}
/*
* bool keepsmall(mpi_id_t node, mpi_id_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 = 1;
mpi_id_t ts_partner[] = {1, 0, 3, 2, 5, 4, 7, 6};
bool ts_expected[] = {1, 0, 0, 1, 1, 0, 0, 1};
for (mpi_id_t node = 0 ; node < 8 ; ++node ) {
EXPECT_EQ(ts_expected[node], keepSmall<SortMode::Bitonic>(node, ts_partner[node], ts_depth));
}
}
/*
* bool keepsmall(mpi_id_t node, mpi_id_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 = 2;
mpi_id_t ts_partner[] = {2, 3, 0, 1, 6, 7, 4, 5};
bool ts_expected[] = {1, 1, 0, 0, 0, 0, 1, 1};
for (mpi_id_t node = 0 ; node < 8 ; ++node ) {
EXPECT_EQ(ts_expected[node], keepSmall<SortMode::Bitonic>(node, ts_partner[node], ts_depth));
}
}
/*
* bool keepsmall(mpi_id_t node, mpi_id_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 = 2;
mpi_id_t ts_partner[] = {1, 0, 3, 2, 5, 4, 7, 6};
bool ts_expected[] = {1, 0, 1, 0, 0, 1, 0, 1};
for (mpi_id_t node = 0 ; node < 8 ; ++node ) {
EXPECT_EQ(ts_expected[node], keepSmall<SortMode::Bitonic>(node, ts_partner[node], ts_depth));
}
}
/*
* bool keepSmall(mpi_id_t node, mpi_id_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 = 3;
mpi_id_t ts_partner[] = {4, 5, 6, 7, 0, 1, 2, 3};
bool ts_expected[] = {1, 1, 1, 1, 0, 0, 0, 0};
for (mpi_id_t node = 0 ; node < 8 ; ++node ) {
EXPECT_EQ(ts_expected[node], keepSmall<SortMode::Bitonic>(node, ts_partner[node], ts_depth));
}
}
/*
* bool keepSmall(mpi_id_t node, mpi_id_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 = 3;
mpi_id_t ts_partner[] = {2, 3, 0, 1, 6, 7, 4, 5};
bool ts_expected[] = {1, 1, 0, 0, 1, 1, 0, 0};
for (mpi_id_t node = 0 ; node < 8 ; ++node ) {
EXPECT_EQ(ts_expected[node], keepSmall<SortMode::Bitonic>(node, ts_partner[node], ts_depth));
}
}
/*
* bool keepSmall(mpi_id_t node, mpi_id_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 = 3;
mpi_id_t ts_partner[] = {1, 0, 3, 2, 5, 4, 7, 6};
bool ts_expected[] = {1, 0, 1, 0, 1, 0, 1, 0};
for (mpi_id_t node = 0 ; node < 8 ; ++node ) {
EXPECT_EQ(ts_expected[node], keepSmall<SortMode::Bitonic>(node, ts_partner[node], ts_depth));
}
}
-144
View File
@@ -1,144 +0,0 @@
/**
* \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 "distsort.hpp"
/* ================================== ascending ================================== */
/*
* bool ascending<SortMode::Bubbletonic>(mpi_id_t node, size_t depth);
*/
TEST(TdistBubbletonic_UT, ascending_Bubbletonic_test1) {
EXPECT_EQ(ascending<SortMode::Bubbletonic>(0, 0), true);
EXPECT_EQ(ascending<SortMode::Bubbletonic>(1, 0), false);
EXPECT_EQ(ascending<SortMode::Bubbletonic>(2, 0), true);
EXPECT_EQ(ascending<SortMode::Bubbletonic>(3, 0), false);
EXPECT_EQ(ascending<SortMode::Bubbletonic>(4, 0), true);
EXPECT_EQ(ascending<SortMode::Bubbletonic>(5, 0), false);
EXPECT_EQ(ascending<SortMode::Bubbletonic>(6, 0), true);
EXPECT_EQ(ascending<SortMode::Bubbletonic>(7, 0), false);
for (mpi_id_t node = 0 ; node < 256 ; ++node) {
EXPECT_EQ(ascending<SortMode::Bubbletonic>(node, 7), ((node % 2) ? false : true) );
}
}
/* ================================== partner ================================== */
/*
* mpi_id_t partner<SortMode::Bubbletonic>(mpi_id_t node, size_t step);
* step = 0
*/
TEST(TdistBubbletonic_UT, partner_Bubbletonic_test1) {
size_t ts_step = 0;
mpi_id_t ts_expected[] = {1, 0, 3, 2, 5, 4, 7, 6};
for (mpi_id_t node = 0 ; node < 8 ; ++node) {
EXPECT_EQ(partner<SortMode::Bubbletonic>(node, ts_step), ts_expected[node]);
}
}
/*
* mpi_id_t partner<SortMode::Bubbletonic>(mpi_id_t node, size_t step);
* step = 1
*/
TEST(TdistBubbletonic_UT, partner_Bubbletonic_test2) {
size_t ts_step = 1;
mpi_id_t ts_expected[] = {(mpi_id_t)-1, 2, 1, 4, 3, 6, 5, 8};
for (mpi_id_t node = 0 ; node < 8 ; ++node) {
EXPECT_EQ(partner<SortMode::Bubbletonic>(node, ts_step), ts_expected[node]);
}
}
/*
* mpi_id_t partner<SortMode::Bubbletonic>(mpi_id_t node, size_t step);
* various steps
*/
TEST(TdistBubbletonic_UT, partner_Bubbletonic_test3) {
mpi_id_t ts_even_expected[] = {
1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14
};
mpi_id_t ts_odd_expected[] = {
(mpi_id_t)-1, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16
};
for (size_t step = 0 ; step < 32 ; ++step) {
if (step % 2) {
for (mpi_id_t node = 0; node < 16; ++node) {
EXPECT_EQ(partner<SortMode::Bubbletonic>(node, step), ts_odd_expected[node]);
}
}
else {
for (mpi_id_t node = 0; node < 16; ++node) {
EXPECT_EQ(partner<SortMode::Bubbletonic>(node, step), ts_even_expected[node]);
}
}
}
}
/* ================================== keepSmall ================================== */
/*
* bool keepSmall<SortMode::Bubbletonic>(mpi_id_t node, mpi_id_t partner, size_t depth);
* Throw check (Not assert - ASSERT_DEATH)
*/
TEST(TdistBubbletonic_UT, keepsmall_test1) {
// node and partner must differ or else ...
EXPECT_THROW(keepSmall<SortMode::Bubbletonic>(0, 0, 0), std::runtime_error);
EXPECT_THROW(keepSmall<SortMode::Bubbletonic>(1, 1, 42), std::runtime_error);
EXPECT_THROW(keepSmall<SortMode::Bubbletonic>(7, 7, 42), std::runtime_error);
}
/*
* bool keepSmall<SortMode::Bubbletonic>(mpi_id_t node, mpi_id_t partner, size_t depth);
*/
TEST(TdistBubbletonic_UT, keepsmall_test2) {
// Check various combinations
EXPECT_EQ(keepSmall<SortMode::Bubbletonic>(0, 1, 42), true);
EXPECT_EQ(keepSmall<SortMode::Bubbletonic>(0, 3, 42), true);
EXPECT_EQ(keepSmall<SortMode::Bubbletonic>(2, 1, 42), false);
EXPECT_EQ(keepSmall<SortMode::Bubbletonic>(7, 1, 42), false);
EXPECT_EQ(keepSmall<SortMode::Bubbletonic>(0, 1, 42), true);
EXPECT_EQ(keepSmall<SortMode::Bubbletonic>(7, 32, 42), true);
EXPECT_EQ(keepSmall<SortMode::Bubbletonic>(7, 1, 42), false);
EXPECT_EQ(keepSmall<SortMode::Bubbletonic>(4, 0, 42), false);
EXPECT_EQ(keepSmall<SortMode::Bubbletonic>(4, 9, 42), true);
}
/* ================================== isActive ================================== */
/*
* bool isActive(mpi_id_t node, size_t nodes);
* Throw check
*/
TEST(TdistBubbletonic_UT, isActive_test1) {
EXPECT_THROW(isActive(0, 0), std::runtime_error);
EXPECT_THROW(isActive(0, static_cast<size_t>(std::numeric_limits<mpi_id_t>::max()) + 1), std::runtime_error);
}
/*
* bool isActive(mpi_id_t node, size_t nodes);
* Boundary 3 BVA
*/
TEST(TdistBubbletonic_UT, isActive_test2) {
EXPECT_EQ(isActive(-1, 8), false);
EXPECT_EQ(isActive(0, 8), true);
EXPECT_EQ(isActive(1, 8), true);
EXPECT_EQ(isActive(7, 8), true);
EXPECT_EQ(isActive(8, 8), false);
EXPECT_EQ(isActive(9, 8), false);
}
-93
View File
@@ -1,93 +0,0 @@
/**
* \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 "distsort.hpp"
/* ================================== fullSort ================================== */
/*
*
*/
TEST(TdistCommonUT, fullSort_test1) {
std::vector<uint8_t> ts_data = {3, 2, 1, 4, 5, 7, 8, 6};
std::vector<uint8_t> ts_expected = {1, 2, 3, 4, 5, 6, 7, 8};
bool ts_ascending = true;
fullSort(ts_data, ts_ascending);
EXPECT_EQ((ts_data == ts_expected), true);
}
TEST(TdistCommonUT, fullSort_test2) {
std::vector<uint8_t> ts_data = {3, 2, 1, 4, 5, 7, 8, 6};
std::vector<uint8_t> ts_expected = {8, 7, 6, 5, 4, 3, 2, 1};
bool ts_ascending = false;
fullSort(ts_data, ts_ascending);
EXPECT_EQ((ts_data == ts_expected), true);
}
/* ================================== elbowSort ================================== */
TEST(TdistCommonUT, elbowSort_test1) {
ShadowedVec_t<uint8_t> ts_data1(std::vector<uint8_t>{3, 2, 1, 4, 5, 5, 7, 8});
ShadowedVec_t<uint8_t> ts_data2(std::vector<uint8_t>{4, 5, 7, 8, 5, 3, 2, 1});
ShadowedVec_t<uint8_t> ts_data3(std::vector<uint8_t>{1, 2, 3, 4, 5, 5, 7, 8});
ShadowedVec_t<uint8_t> ts_data4(std::vector<uint8_t>{8, 7, 5, 5, 4, 3, 2, 1});
std::vector<uint8_t> ts_expected = {1, 2, 3, 4, 5, 5, 7, 8};
bool ts_ascending = true;
elbowSort(ts_data1, ts_ascending);
elbowSort(ts_data2, ts_ascending);
elbowSort(ts_data3, ts_ascending);
elbowSort(ts_data4, ts_ascending);
EXPECT_EQ((ts_data1 == ts_expected), true);
EXPECT_EQ((ts_data2 == ts_expected), true);
EXPECT_EQ((ts_data3 == ts_expected), true);
EXPECT_EQ((ts_data4 == ts_expected), true);
}
TEST(TdistCommonUT, elbowSort_test2) {
ShadowedVec_t<uint8_t> ts_data1(std::vector<uint8_t>{3, 2, 1, 4, 5, 5, 7, 8});
ShadowedVec_t<uint8_t> ts_data2(std::vector<uint8_t>{4, 5, 7, 8, 5, 3, 2, 1});
ShadowedVec_t<uint8_t> ts_data3(std::vector<uint8_t>{1, 2, 3, 4, 5, 5, 7, 8});
ShadowedVec_t<uint8_t> ts_data4(std::vector<uint8_t>{8, 7, 5, 5, 4, 3, 2, 1});
std::vector<uint8_t> ts_expected = {8, 7, 5, 5, 4, 3, 2, 1};
bool ts_ascending = false;
elbowSort(ts_data1, ts_ascending);
elbowSort(ts_data2, ts_ascending);
elbowSort(ts_data3, ts_ascending);
elbowSort(ts_data4, ts_ascending);
EXPECT_EQ((ts_data1 == ts_expected), true);
EXPECT_EQ((ts_data2 == ts_expected), true);
EXPECT_EQ((ts_data3 == ts_expected), true);
EXPECT_EQ((ts_data4 == ts_expected), true);
}
TEST(TdistCommonUT, elbowSort_test3) {
ShadowedVec_t<uint8_t> ts_data(std::vector<uint8_t>{8, 7, 5, 5, 4, 3, 2, 1});
std::vector<uint8_t> ts_expected_asc = {1, 2, 3, 4, 5, 5, 7, 8};
std::vector<uint8_t> ts_expected_des = {8, 7, 5, 5, 4, 3, 2, 1};
// Check alternation for active-shadow vector inside Buffer and elbow algorithm
elbowSort(ts_data, true);
EXPECT_EQ((ts_data == ts_expected_asc), true);
elbowSort(ts_data, false);
EXPECT_EQ((ts_data == ts_expected_des), true);
elbowSort(ts_data, true);
EXPECT_EQ((ts_data == ts_expected_asc), true);
elbowSort(ts_data, false);
EXPECT_EQ((ts_data == ts_expected_des), true);
}
-210
View File
@@ -1,210 +0,0 @@
/**
* \file
* \brief PDS HW2 tests
*
* To run these test execute:
* make tests
* mpirun -np <N> ./out/tests
*
* Note:
* Yes each process runs the entire test suite!!
*
* \author
* Christos Choutouridis AEM:8997
* <cchoutou@ece.auth.gr>
*/
#include <gtest/gtest.h>
#include <mpi.h>
#include <random>
#include "distsort.hpp"
/*
* Global fixtures
*/
// MPI handler for the test session
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;
ts_mpi.init(&argc, &argv);
}
static void TearDownTestSuite() {
ts_mpi.finalize();
}
};
/*
* MPI: SysTest (acceptance)
* Each process executes distBubbletonic for uin8_t [16]
*/
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]);
}
}
}
/*
* MPI: SysTest (acceptance)
* Each process executes distBubbletonic for uin32_t [1 << 16]
*/
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]);
}
}
}
/*
* MPI: SysTest (acceptance)
* Each process executes distBitonic for uin8_t [16]
*/
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]);
}
}
}
/*
* MPI: SysTest (acceptance)
* Each process executes distBitonic for uin32_t [1 << 16]
*/
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]);
}
}
}