Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebffa8b6ea | ||
|
|
89212d2e47 | ||
|
|
4ff19eae01 | ||
|
|
be2aaf594a | ||
|
|
a12b9a6bec | ||
|
|
9d77f828cc |
@@ -0,0 +1,9 @@
|
|||||||
|
## NOTE ##
|
||||||
|
This folder contains the output files from tests.
|
||||||
|
|
||||||
|
In order to populate it, run the tests
|
||||||
|
|
||||||
|
$> make tests
|
||||||
|
$> ./tests
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
+66
-95
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# PDS HW2 Makefile
|
# PDS homework_1 Makefile
|
||||||
#
|
#
|
||||||
# Copyright (C) 2024 Christos Choutouridis <christos@choutouridis.net>
|
# Copyright (C) 2024 Christos Choutouridis <christos@choutouridis.net>
|
||||||
#
|
#
|
||||||
@@ -20,18 +20,13 @@
|
|||||||
# ============== Project settings ==============
|
# ============== Project settings ==============
|
||||||
# Project's name
|
# Project's name
|
||||||
PROJECT := PDS_homework_2
|
PROJECT := PDS_homework_2
|
||||||
|
|
||||||
# Excecutable's name
|
# Excecutable's name
|
||||||
TARGET := bitonic
|
TARGET := distbitonic
|
||||||
|
|
||||||
# Source directories list(space seperated). Makefile-relative path, UNDER current directory.
|
# Source directories list(space seperated). Makefile-relative path, UNDER current directory.
|
||||||
SRC_DIR_LIST := src test test/gtest
|
SRC_DIR_LIST := src
|
||||||
|
|
||||||
# Include directories list(space seperated). Makefile-relative path.
|
# Include directories list(space seperated). Makefile-relative path.
|
||||||
INC_DIR_LIST := include \
|
INC_DIR_LIST := include \
|
||||||
test \
|
/usr/lib/x86_64-linux-gnu/openmpi/include/ \
|
||||||
test/gtest/ \
|
|
||||||
/usr/lib/x86_64-linux-gnu/openmpi/include/ \
|
|
||||||
src
|
src
|
||||||
|
|
||||||
# Exclude files list(space seperated). Filenames only.
|
# Exclude files list(space seperated). Filenames only.
|
||||||
@@ -45,18 +40,18 @@ DEP_DIR := $(BUILD_DIR)/.dep
|
|||||||
|
|
||||||
# ========== Compiler settings ==========
|
# ========== Compiler settings ==========
|
||||||
# Compiler flags for debug and release
|
# Compiler flags for debug and release
|
||||||
DEB_CFLAGS := -DDEBUG -g3 -Wall -Wextra -std=c11 -fopenmp
|
DEB_CFLAGS := -DDEBUG -g3 -Wall -Wextra -std=c11
|
||||||
REL_CFLAGS := -Wall -Wextra -O3 -std=c11 -fopenmp
|
REL_CFLAGS := -Wall -Wextra -O3 -std=c11
|
||||||
DEB_CXXFLAGS := -DDEBUG -g3 -Wall -Wextra -std=c++17 -fopenmp
|
DEB_CXXFLAGS := -DDEBUG -g3 -Wall -Wextra -std=c++17
|
||||||
REL_CXXFLAGS := -Wall -Wextra -O3 -std=c++17 -fopenmp
|
REL_CXXFLAGS := -Wall -Wextra -O3 -std=c++17
|
||||||
|
|
||||||
# Pre-defines
|
# Pre-defines
|
||||||
# PRE_DEFS := MYCAB=1729 SUPER_MODE
|
# PRE_DEFS := MYCAB=1729 SUPER_MODE
|
||||||
PRE_DEFS := _GLIBCXX_PARALLEL
|
PRE_DEFS :=
|
||||||
|
|
||||||
# ============== Linker settings ==============
|
# ============== Linker settings ==============
|
||||||
# Linker flags (example: -pthread -lm)
|
# Linker flags (example: -pthread -lm)
|
||||||
LDFLAGS := -pthread -fopenmp
|
LDFLAGS := -pthread
|
||||||
|
|
||||||
# Map output file
|
# Map output file
|
||||||
MAP_FILE := output.map
|
MAP_FILE := output.map
|
||||||
@@ -82,7 +77,7 @@ DOCKER :=
|
|||||||
# compiler and compiler flags.
|
# compiler and compiler flags.
|
||||||
CSIZE := size
|
CSIZE := size
|
||||||
CFLAGS := $(DEB_CFLAGS)
|
CFLAGS := $(DEB_CFLAGS)
|
||||||
CXXFLAGS := $(DEB_CXXFLAGS)
|
CXXFLAGS := $(DEB_CXXFLAGS)
|
||||||
CXX := g++ #mpic++
|
CXX := g++ #mpic++
|
||||||
CC := gcc #mpicc
|
CC := gcc #mpicc
|
||||||
|
|
||||||
@@ -153,14 +148,6 @@ $(BUILD_DIR)/$(TARGET): $(OBJ)
|
|||||||
@$(CSIZE) $(@D)/$(TARGET)
|
@$(CSIZE) $(@D)/$(TARGET)
|
||||||
@echo Done
|
@echo Done
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# ================ Default local build rules =================
|
|
||||||
# example:
|
|
||||||
# make debug
|
|
||||||
|
|
||||||
.DEFAULT_GOAL := all
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@echo Cleaning build directories
|
@echo Cleaning build directories
|
||||||
@@ -168,83 +155,67 @@ clean:
|
|||||||
@rm -rf $(DEP_DIR)
|
@rm -rf $(DEP_DIR)
|
||||||
@rm -rf $(BUILD_DIR)
|
@rm -rf $(BUILD_DIR)
|
||||||
|
|
||||||
|
#
|
||||||
|
# ================ Local build rules =================
|
||||||
|
# example:
|
||||||
|
# make debug
|
||||||
|
|
||||||
debug: CFLAGS := $(DEB_CFLAGS)
|
debug: CFLAGS := $(DEB_CFLAGS)
|
||||||
debug: $(BUILD_DIR)/$(TARGET)
|
debug: $(BUILD_DIR)/$(TARGET)
|
||||||
|
|
||||||
release: CFLAGS := $(REL_CFLAGS)
|
release: CFLAGS := $(REL_CFLAGS)
|
||||||
release: $(BUILD_DIR)/$(TARGET)
|
release: $(BUILD_DIR)/$(TARGET)
|
||||||
|
|
||||||
|
all: release
|
||||||
|
|
||||||
|
hpc-results/post:
|
||||||
|
$(CXX) $(CFLAGS) -o $@ hpc-results/main.cpp
|
||||||
|
|
||||||
|
hpc-clean:
|
||||||
|
rm hpc-results/post
|
||||||
|
|
||||||
#
|
#
|
||||||
# ================ Build rules =================
|
# ================ Local (and/or) via docker build rules =================
|
||||||
|
#
|
||||||
|
# examples:
|
||||||
|
# make IMAGE=hpcimage v0
|
||||||
|
# make IMAGE=hpcimage v1_cilk
|
||||||
|
#
|
||||||
|
dist_v05: CC := mpicc
|
||||||
|
dist_v05: CXX := mpic++
|
||||||
|
dist_v05: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=50
|
||||||
|
dist_v05: CXXFLAGS := $(DEB_CXXFLAGS) -DCODE_VERSION=50
|
||||||
|
dist_v05: TARGET := dist_v05
|
||||||
|
dist_v05: $(BUILD_DIR)/$(TARGET)
|
||||||
|
|
||||||
|
dist_v1: CC := mpicc
|
||||||
|
dist_v1: CXX := mpic++
|
||||||
|
dist_v1: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=100
|
||||||
|
dist_v1: CXXFLAGS := $(DEB_CXXFLAGS) -DCODE_VERSION=100
|
||||||
|
dist_v1: TARGET := dist_v1
|
||||||
|
dist_v1: $(BUILD_DIR)/$(TARGET)
|
||||||
|
|
||||||
|
#
|
||||||
|
# ========= Inside CSAL Image build rules ===========
|
||||||
|
#
|
||||||
|
# 1) first jump into image (make sure you are in the directory where Makefile is):
|
||||||
|
# > docker run -it -v ${PWD}:/usr/src/exercise_1 -w /usr/src/exercise_1/ hpcimage
|
||||||
|
# 2) Clean binaries first **important**
|
||||||
|
# > make clean
|
||||||
|
# 3) for v4 cilk for example:
|
||||||
|
# > make csal_v4_cilk
|
||||||
|
# 4) run executables from `bin/`. Examples:
|
||||||
|
# > ./bin/tcount_ompv3 -i mtx/NACA0015.mtx --timing -r 3 -o /dev/null
|
||||||
|
# > ./bin/tcount_pthv4 -i mtx/com_Youtube.mtx --timing --dynamic --print_count
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# ======== Run from container =========
|
||||||
|
#
|
||||||
|
# examples:
|
||||||
|
#
|
||||||
|
# make IMAGE=hpcimage EXEC=knnsearch_v1 run
|
||||||
|
# make IMAGE=hpcimage EXEC=knnsearch_v1 run
|
||||||
#
|
#
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
+10
-32
@@ -1,5 +1,5 @@
|
|||||||
/*!
|
/*!
|
||||||
* \file
|
* \file config,h
|
||||||
* \brief Build configuration file.
|
* \brief Build configuration file.
|
||||||
*
|
*
|
||||||
* \author
|
* \author
|
||||||
@@ -10,52 +10,30 @@
|
|||||||
#ifndef CONFIG_H_
|
#ifndef CONFIG_H_
|
||||||
#define CONFIG_H_
|
#define CONFIG_H_
|
||||||
|
|
||||||
#include <cstdint>
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Defines for different version of the exercise
|
* Defines for different version of the exercise
|
||||||
*/
|
*/
|
||||||
#define BITONIC (1)
|
#define V50 (50)
|
||||||
#define BUBBLETONIC (2)
|
#define V100 (100)
|
||||||
|
|
||||||
|
|
||||||
// Fail-safe version selection
|
// Fail-safe version selection
|
||||||
#if !defined CODE_VERSION
|
#if !defined CODE_VERSION
|
||||||
#define CODE_VERSION BITONIC
|
#define CODE_VERSION V1
|
||||||
#endif
|
#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
|
* Session option for each invocation of the executable
|
||||||
*/
|
*/
|
||||||
struct config_t {
|
struct session_t {
|
||||||
size_t arraySize{DEFAULT_DATA_SIZE}; //!< The array size of the local data to sort.
|
bool timing {false};
|
||||||
bool validation{false}; //!< Request a full validation at the end, performed by process rank 0.
|
bool verbose {false}; //!< Flag to enable verbose output to stdout
|
||||||
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.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
extern session_t session;
|
||||||
* Exported data types
|
|
||||||
*/
|
|
||||||
extern config_t config;
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* CONFIG_H_ */
|
#endif /* CONFIG_H_ */
|
||||||
|
|||||||
@@ -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_
|
|
||||||
@@ -0,0 +1,804 @@
|
|||||||
|
/**
|
||||||
|
* \file matrix.hpp
|
||||||
|
* \brief A matrix abstraction implementation
|
||||||
|
*
|
||||||
|
* \author
|
||||||
|
* Christos Choutouridis AEM:8997
|
||||||
|
* <cchoutou@ece.auth.gr>
|
||||||
|
*/
|
||||||
|
#ifndef MATRIX_HPP_
|
||||||
|
#define MATRIX_HPP_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace mtx {
|
||||||
|
|
||||||
|
using std::size_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Small helper to strip types
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct remove_cvref {
|
||||||
|
typedef std::remove_cv_t<std::remove_reference_t<T>> type;
|
||||||
|
};
|
||||||
|
template<typename T>
|
||||||
|
using remove_cvref_t = typename remove_cvref<T>::type;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Enumerator to denote the storage type of the array to use.
|
||||||
|
*/
|
||||||
|
enum class MatrixType {
|
||||||
|
DENSE, /*!< Matrix is dense */
|
||||||
|
SPARSE, /*!< Matrix is sparse */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Enumerator to denote the storage type of the array to use.
|
||||||
|
*/
|
||||||
|
enum class MatrixOrder {
|
||||||
|
COLMAJOR, /*!< Matrix is column major */
|
||||||
|
ROWMAJOR, /*!< Matrix is row major */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forward type declarations
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<typename MatrixType> struct MatCol;
|
||||||
|
template<typename MatrixType> struct MatRow;
|
||||||
|
template<typename MatrixType> struct MatVal;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A 2-D matrix functionality over a 1-D array
|
||||||
|
*
|
||||||
|
* This is a very thin abstraction layer over a native array.
|
||||||
|
* This is tested using compiler explorer and our template produce
|
||||||
|
* almost identical assembly.
|
||||||
|
*
|
||||||
|
* The penalty hit we have is due to the fact that we use a one dimension array
|
||||||
|
* and we have to calculate the actual position from an (i,j) pair.
|
||||||
|
* The use of 1D array was our intention from the beginning, so the penalty
|
||||||
|
* was pretty much unavoidable.
|
||||||
|
*
|
||||||
|
* \tparam DataType The underling data type of the array
|
||||||
|
* \tparam IndexType The underling type for the index variables and sizes
|
||||||
|
* \tparam Type The storage type of the array
|
||||||
|
* \arg FULL For full matrix
|
||||||
|
* \arg SYMMETRIC For symmetric matrix (we use only the lower part)
|
||||||
|
*/
|
||||||
|
template<typename DataType,
|
||||||
|
typename IndexType = size_t,
|
||||||
|
MatrixType Type = MatrixType::DENSE,
|
||||||
|
MatrixOrder Order = MatrixOrder::ROWMAJOR,
|
||||||
|
bool Symmetric = false>
|
||||||
|
struct Matrix {
|
||||||
|
|
||||||
|
using dataType = DataType; //!< meta:export of underling data type
|
||||||
|
using indexType = IndexType; //!< meta:export of underling index type
|
||||||
|
static constexpr MatrixOrder matrixOrder = Order; //!< meta:export of array order
|
||||||
|
static constexpr MatrixType matrixType = Type; //!< meta:export of array type
|
||||||
|
static constexpr bool symmetric = Symmetric; //!< meta:export symmetric flag
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \name Obj lifetime
|
||||||
|
*/
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Construct an empty matrix with dimensions rows x columns
|
||||||
|
Matrix(IndexType rows = IndexType{}, IndexType columns = IndexType{}) noexcept
|
||||||
|
: vector_storage_(capacity(rows, columns)),
|
||||||
|
raw_storage_(nullptr),
|
||||||
|
use_vector_(true),
|
||||||
|
rows_(rows),
|
||||||
|
cols_(columns) {
|
||||||
|
data_ = vector_storage_.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Construct a matrix by copying existing data with dimensions rows x columns
|
||||||
|
Matrix(DataType* data, IndexType major_start, IndexType major_length, IndexType minor_length) noexcept
|
||||||
|
: vector_storage_(),
|
||||||
|
raw_storage_ (data + major_start * minor_length),
|
||||||
|
use_vector_ (false) {
|
||||||
|
if constexpr (Order == MatrixOrder::ROWMAJOR) {
|
||||||
|
rows_ = major_length;
|
||||||
|
cols_ = minor_length;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rows_ = minor_length;
|
||||||
|
cols_ = major_length;
|
||||||
|
}
|
||||||
|
data_ = raw_storage_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Construct a matrix using an initializer list
|
||||||
|
Matrix(IndexType rows, IndexType columns, std::initializer_list<DataType> list)
|
||||||
|
: vector_storage_(list),
|
||||||
|
raw_storage_(nullptr),
|
||||||
|
use_vector_(true),
|
||||||
|
rows_(rows),
|
||||||
|
cols_(columns) {
|
||||||
|
if (list.size() != capacity(rows, columns)) {
|
||||||
|
throw std::invalid_argument("Matrix initializer list size does not match matrix dimensions.");
|
||||||
|
}
|
||||||
|
data_ = vector_storage_.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! move ctor
|
||||||
|
Matrix(Matrix&& m) noexcept { moves(std::move(m)); }
|
||||||
|
//! move
|
||||||
|
Matrix& operator=(Matrix&& m) noexcept { moves(std::move(m)); return *this; }
|
||||||
|
Matrix(const Matrix& m) = delete; //!< No copy ctor
|
||||||
|
Matrix& operator=(const Matrix& m) = delete; //!< No copy
|
||||||
|
//Matrix(const Matrix& m);
|
||||||
|
//Matrix& operator=(const Matrix& m) { copy(m); }
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//! \name Data exposure
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
|
||||||
|
//! Get/Set the size of each dimension
|
||||||
|
IndexType rows() const noexcept { return rows_; }
|
||||||
|
IndexType columns() const noexcept { return cols_; }
|
||||||
|
|
||||||
|
//! Get the interface size of the Matrix (what appears to be the size)
|
||||||
|
IndexType size() const {
|
||||||
|
return rows_ * cols_;
|
||||||
|
}
|
||||||
|
//! Set the interface size of the Matrix (what appears to be the size)
|
||||||
|
IndexType resize(IndexType rows, IndexType columns) {
|
||||||
|
if (use_vector_) {
|
||||||
|
rows_ = rows;
|
||||||
|
cols_ = columns;
|
||||||
|
vector_storage_.resize(capacity(rows_, cols_));
|
||||||
|
data_ = vector_storage_.data();
|
||||||
|
}
|
||||||
|
return capacity(rows_, cols_);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Actual memory capacity of the symmetric matrix
|
||||||
|
static constexpr IndexType capacity(IndexType M, IndexType N) {
|
||||||
|
if constexpr (Symmetric)
|
||||||
|
return (M+1)*N/2;
|
||||||
|
else
|
||||||
|
return M*N;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* virtual 2D accessors
|
||||||
|
*/
|
||||||
|
DataType get (IndexType i, IndexType j) {
|
||||||
|
if constexpr (Symmetric) {
|
||||||
|
auto T = [](size_t i)->size_t { return i*(i+1)/2; }; // Triangular number of i
|
||||||
|
if constexpr (Order == MatrixOrder::COLMAJOR) {
|
||||||
|
// In column major we use the lower triangle of the matrix
|
||||||
|
if (i>=j) return data_[j*rows_ - T(j) + i]; // Lower, use our notation
|
||||||
|
else return data_[i*rows_ - T(i) + j]; // Upper, use opposite index
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// In row major we use the upper triangle of the matrix
|
||||||
|
if (i<=j) return data_[i*cols_ - T(i) + j]; // Upper, use our notation
|
||||||
|
else return data_[j*cols_ - T(j) + i]; // Lower, use opposite index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if constexpr (Order == MatrixOrder::COLMAJOR)
|
||||||
|
return data_[i + j*rows_];
|
||||||
|
else
|
||||||
|
return data_[i*cols_ + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \fn DataType set(DataType, IndexType, IndexType)
|
||||||
|
* \param v
|
||||||
|
* \param i
|
||||||
|
* \param j
|
||||||
|
* \return
|
||||||
|
*/
|
||||||
|
DataType set (DataType v, IndexType i, IndexType j) {
|
||||||
|
if constexpr (Symmetric) {
|
||||||
|
auto T = [](size_t i)->size_t { return i*(i+1)/2; }; // Triangular number of i
|
||||||
|
if constexpr (Order == MatrixOrder::COLMAJOR) {
|
||||||
|
// In column major we use the lower triangle of the matrix
|
||||||
|
if (i>=j) return data_[j*rows_ - T(j) + i] = v; // Lower, use our notation
|
||||||
|
else return data_[i*rows_ - T(i) + j] = v; // Upper, use opposite index
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// In row major we use the upper triangle of the matrix
|
||||||
|
if (i<=j) return data_[i*cols_ - T(i) + j] = v; // Upper, use our notation
|
||||||
|
else return data_[j*cols_ - T(j) + i] = v; // Lower, use opposite index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if constexpr (Order == MatrixOrder::COLMAJOR)
|
||||||
|
return data_[i + j*rows_] = v;
|
||||||
|
else
|
||||||
|
return data_[i*cols_ + j] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// DataType operator()(IndexType i, IndexType j) { return get(i, j); }
|
||||||
|
/*!
|
||||||
|
* Return a proxy MatVal object with read and write capabilities.
|
||||||
|
* @param i The row number
|
||||||
|
* @param j The column number
|
||||||
|
* @return tHE MatVal object
|
||||||
|
*/
|
||||||
|
MatVal<Matrix> operator()(IndexType i, IndexType j) noexcept {
|
||||||
|
return MatVal<Matrix>(this, get(i, j), i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a basic serial iterator support
|
||||||
|
DataType* data() noexcept { return data_; }
|
||||||
|
DataType* begin() noexcept { return data_; }
|
||||||
|
const DataType* begin() const noexcept { return data_; }
|
||||||
|
DataType* end() noexcept { return data_ + capacity(rows_, cols_); }
|
||||||
|
const DataType* end() const noexcept { return data_ + capacity(rows_, cols_); }
|
||||||
|
|
||||||
|
// IndexType begin_idx() noexcept { return 0; }
|
||||||
|
// IndexType end_idx() noexcept { return capacity(rows_, cols_); }
|
||||||
|
|
||||||
|
const DataType* data() const noexcept { return data_; }
|
||||||
|
const IndexType begin_idx() const noexcept { return 0; }
|
||||||
|
const IndexType end_idx() const noexcept { return capacity(rows_, cols_); }
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \name Safe iteration API
|
||||||
|
*
|
||||||
|
* This api automates the iteration over the array based on
|
||||||
|
* MatrixType
|
||||||
|
*/
|
||||||
|
//! @{
|
||||||
|
template<typename F, typename... Args>
|
||||||
|
void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) {
|
||||||
|
for (IndexType it=begin ; it<end ; ++it) {
|
||||||
|
std::forward<F>(lambda)(std::forward<Args>(args)..., it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//
|
||||||
|
void swap(Matrix& src) noexcept {
|
||||||
|
std::swap(vector_storage_, src.vector_storage_);
|
||||||
|
std::swap(raw_storage_, src.raw_storage_);
|
||||||
|
std::swap(data_, src.data_);
|
||||||
|
std::swap(use_vector_, src.use_vector_);
|
||||||
|
std::swap(rows_, src.rows_);
|
||||||
|
std::swap(cols_, src.cols_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! move helper
|
||||||
|
void moves(Matrix&& src) noexcept {
|
||||||
|
vector_storage_ = std::move(src.vector_storage_);
|
||||||
|
raw_storage_ = std::move(src.raw_storage_);
|
||||||
|
data_ = std::move(src.data_);
|
||||||
|
use_vector_ = std::move(src.use_vector_);
|
||||||
|
rows_ = std::move(src.rows_);
|
||||||
|
cols_ = std::move(src.cols_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage
|
||||||
|
std::vector<DataType>
|
||||||
|
vector_storage_; //!< Internal storage (if used).
|
||||||
|
DataType* raw_storage_; //!< External storage (if used).
|
||||||
|
DataType* data_; //!< Pointer to active storage.
|
||||||
|
bool use_vector_; //!< True if using vector storage, false for raw pointer.
|
||||||
|
IndexType rows_{}; //!< the virtual size of rows.
|
||||||
|
IndexType cols_{}; //!< the virtual size of columns.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple sparse matrix specialization.
|
||||||
|
*
|
||||||
|
* We use CSC format and provide get/set functionalities for each (i,j) item
|
||||||
|
* on the matrix. We also provide a () overload using a proxy MatVal object.
|
||||||
|
* This way the user can:
|
||||||
|
* \code
|
||||||
|
* auto v = A(3,4);
|
||||||
|
* A(3, 4) = 7;
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* We also provide getCol() and getRow() functions witch return a viewer/iterator to rows and
|
||||||
|
* columns of the matrix. In the case of a symmetric matrix instead of a row we return the
|
||||||
|
* equivalent column. This way we gain speed due to CSC format nature.
|
||||||
|
*
|
||||||
|
* @tparam DataType The type for values
|
||||||
|
* @tparam IndexType The type for indexes
|
||||||
|
* @tparam Type The Matrix type (FULL or SYMMETRIC)
|
||||||
|
*/
|
||||||
|
template<typename DataType, typename IndexType,
|
||||||
|
MatrixOrder Order,
|
||||||
|
bool Symmetric>
|
||||||
|
struct Matrix<DataType, IndexType, MatrixType::SPARSE, Order, Symmetric> {
|
||||||
|
|
||||||
|
using dataType = DataType; //!< meta:export of underling data type
|
||||||
|
using indexType = IndexType; //!< meta:export of underling index type
|
||||||
|
static constexpr MatrixOrder matrixOrder = Order; //!< meta:export of array order
|
||||||
|
static constexpr MatrixType matrixType = MatrixType::SPARSE; //!< meta:export of array type
|
||||||
|
static constexpr bool symmetric = Symmetric; //!< meta:export symmetric flag
|
||||||
|
|
||||||
|
friend struct MatCol<Matrix>;
|
||||||
|
friend struct MatRow<Matrix>;
|
||||||
|
friend struct MatVal<Matrix>;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \name Obj lifetime
|
||||||
|
*/
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Default ctor with optional memory allocations
|
||||||
|
Matrix(IndexType n=IndexType{}) noexcept:
|
||||||
|
values{},
|
||||||
|
rows{},
|
||||||
|
col_ptr((n)? n+1:2, IndexType{}),
|
||||||
|
N(n),
|
||||||
|
NNZ(0) { }
|
||||||
|
|
||||||
|
//! A ctor using csc array data
|
||||||
|
Matrix(IndexType n, IndexType nnz, const IndexType* row, const IndexType* col) noexcept:
|
||||||
|
values(nnz, 1),
|
||||||
|
rows(row, row+nnz),
|
||||||
|
col_ptr(col, col+n+1),
|
||||||
|
N(n),
|
||||||
|
NNZ(nnz) { }
|
||||||
|
|
||||||
|
//! ctor using csc array data with value array
|
||||||
|
Matrix(IndexType n, IndexType nnz, const DataType* v, const IndexType* row, const IndexType* col) noexcept:
|
||||||
|
values(v, v+nnz),
|
||||||
|
rows(row, row+nnz),
|
||||||
|
col_ptr(col, col+n+1),
|
||||||
|
N(n),
|
||||||
|
NNZ(nnz) { }
|
||||||
|
|
||||||
|
//! ctor vectors of row/col and default value for values array
|
||||||
|
Matrix(IndexType n, IndexType nnz, const DataType v,
|
||||||
|
const std::vector<IndexType>& row, const std::vector<IndexType>& col) noexcept:
|
||||||
|
values(nnz, v),
|
||||||
|
rows (row),
|
||||||
|
col_ptr(col),
|
||||||
|
N(n),
|
||||||
|
NNZ(nnz) { }
|
||||||
|
|
||||||
|
//! move ctor
|
||||||
|
Matrix(Matrix&& m) noexcept { moves(std::move(m)); }
|
||||||
|
//! move
|
||||||
|
Matrix& operator=(Matrix&& m) noexcept { moves(std::move(m)); return *this; }
|
||||||
|
Matrix(const Matrix& m) = delete; //!< make sure there are no copies
|
||||||
|
Matrix& operator=(const Matrix& m) = delete; //!< make sure there are no copies
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//! \name Data exposure
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! \return the dimension of the matrix
|
||||||
|
IndexType size() noexcept { return N; }
|
||||||
|
//! After construction size configuration tool
|
||||||
|
IndexType resize(IndexType n) {
|
||||||
|
col_ptr.resize(n+1);
|
||||||
|
return N = n;
|
||||||
|
}
|
||||||
|
//! \return the NNZ of the matrix
|
||||||
|
IndexType capacity() noexcept { return NNZ; }
|
||||||
|
//! After construction NNZ size configuration tool
|
||||||
|
IndexType capacity(IndexType nnz) noexcept {
|
||||||
|
values.reserve(nnz);
|
||||||
|
rows.reserve(nnz);
|
||||||
|
return NNZ;
|
||||||
|
}
|
||||||
|
// getters for row arrays of the struct (unused)
|
||||||
|
std::vector<DataType>& getValues() noexcept { return values; }
|
||||||
|
std::vector<IndexType>& getRows() noexcept { return rows; }
|
||||||
|
std::vector<IndexType>& getCols() noexcept { return col_ptr; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Return a proxy MatVal object with read and write capabilities.
|
||||||
|
* @param i The row number
|
||||||
|
* @param j The column number
|
||||||
|
* @return tHE MatVal object
|
||||||
|
*/
|
||||||
|
MatVal<Matrix> operator()(IndexType i, IndexType j) noexcept {
|
||||||
|
return MatVal<Matrix>(this, get(i, j), i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A read item functionality using binary search to find the correct row
|
||||||
|
*
|
||||||
|
* @param i The row number
|
||||||
|
* @param j The column number
|
||||||
|
* @return The value of the item or DataType{} if is not present.
|
||||||
|
*/
|
||||||
|
DataType get(IndexType i, IndexType j) noexcept {
|
||||||
|
IndexType idx; bool found;
|
||||||
|
std::tie(idx, found) =find_idx(rows, col_ptr[j], col_ptr[j+1], i);
|
||||||
|
return (found) ? values[idx] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A write item functionality.
|
||||||
|
*
|
||||||
|
* First we search if the matrix has already a value in (i, j) position.
|
||||||
|
* If so we just change it to a new value. If not we add the item on the matrix.
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* When change a value, we don't increase the NNZ value of the struct. We expect the user has already
|
||||||
|
* change the NNZ value to the right one using @see capacity() function. When adding a value we
|
||||||
|
* increase the NNZ.
|
||||||
|
*
|
||||||
|
* @param i The row number
|
||||||
|
* @param j The column number
|
||||||
|
* @return The new value of the item .
|
||||||
|
*/
|
||||||
|
DataType set(DataType v, IndexType i, IndexType j) {
|
||||||
|
IndexType idx; bool found;
|
||||||
|
std::tie(idx, found) = find_idx(rows, col_ptr[j], col_ptr[j+1], i);
|
||||||
|
if (found)
|
||||||
|
return values[idx] = v; // we don't change NNZ even if we write "0"
|
||||||
|
else {
|
||||||
|
values.insert(values.begin()+idx, v);
|
||||||
|
rows.insert(rows.begin()+idx, i);
|
||||||
|
std::transform(col_ptr.begin()+j+1, col_ptr.end(), col_ptr.begin()+j+1, [](IndexType it) {
|
||||||
|
return ++it;
|
||||||
|
});
|
||||||
|
++NNZ; // we increase the NNZ even if we write "0"
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get a view of a CSC column
|
||||||
|
* @param j The column to get
|
||||||
|
* @return The MatCol object @see MatCol
|
||||||
|
*/
|
||||||
|
MatCol<Matrix> getCol(IndexType j) noexcept {
|
||||||
|
return MatCol<Matrix>(this, col_ptr[j], col_ptr[j+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get a view of a CSC row
|
||||||
|
*
|
||||||
|
* In case of a SYMMETRIC matrix we can return a column instead.
|
||||||
|
*
|
||||||
|
* @param j The row to get
|
||||||
|
* @return On symmetric matrix MatCol otherwise a MatRow
|
||||||
|
*/
|
||||||
|
|
||||||
|
MatCol<Matrix> getRow(IndexType i) noexcept {
|
||||||
|
if constexpr (Symmetric)
|
||||||
|
return getCol(i);
|
||||||
|
else
|
||||||
|
return MatRow<Matrix>(this, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// values only iterator support
|
||||||
|
DataType* begin() noexcept { return values.begin(); }
|
||||||
|
DataType* end() noexcept { return values.end(); }
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//! A small iteration helper
|
||||||
|
template<typename F, typename... Args>
|
||||||
|
void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) {
|
||||||
|
for (IndexType it=begin ; it<end ; ++it) {
|
||||||
|
std::forward<F>(lambda)(std::forward<Args>(args)..., it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
* A small binary search implementation using index for begin-end instead of iterators.
|
||||||
|
*
|
||||||
|
* \param v Reference to vector to search
|
||||||
|
* \param begin The vector's index to begin
|
||||||
|
* \param end The vector's index to end
|
||||||
|
* \param match What to search
|
||||||
|
* \return An <index, status> pair.
|
||||||
|
* index is the index of the item or end if not found
|
||||||
|
* status is true if found, false otherwise
|
||||||
|
*/
|
||||||
|
std::pair<IndexType, bool> find_idx(const std::vector<IndexType>& v, IndexType begin, IndexType end, IndexType match) {
|
||||||
|
if (v.capacity() != 0 && begin < end) {
|
||||||
|
IndexType b = begin, e = end-1;
|
||||||
|
while (b <= e) {
|
||||||
|
IndexType m = (b+e)/2;
|
||||||
|
if (v[m] == match) return std::make_pair(m, true);
|
||||||
|
else if (b >= e) return std::make_pair(end, false);
|
||||||
|
else {
|
||||||
|
if (v[m] < match) b = m +1;
|
||||||
|
else e = m -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::make_pair(end, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// move helper
|
||||||
|
void moves(Matrix&& src) noexcept {
|
||||||
|
values = std::move(src.values);
|
||||||
|
rows = std::move(src.rows);
|
||||||
|
col_ptr = std::move(src.col_ptr);
|
||||||
|
N = std::move(src.N); // redundant for primitives
|
||||||
|
NNZ = std::move(src.NNZ); //
|
||||||
|
}
|
||||||
|
//! \name Data
|
||||||
|
//! @{
|
||||||
|
std::vector<DataType> values {}; //!< vector to store the values of the matrix
|
||||||
|
std::vector<IndexType> rows{}; //!< vector to store the row information
|
||||||
|
std::vector<IndexType> col_ptr{1,0}; //!< vector to store the column pointers
|
||||||
|
IndexType N{0}; //!< The dimension of the matrix (square)
|
||||||
|
IndexType NNZ{0}; //!< The NNZ (capacity of the matrix)
|
||||||
|
//! @}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A view/iterator hybrid object for Matrix columns.
|
||||||
|
*
|
||||||
|
* This object provides access to a column of a Matrix. The public functionalities
|
||||||
|
* allow data access using indexes instead of iterators. We prefer indexes over iterators
|
||||||
|
* because we can apply the same index to different inner vector of Matrix without conversion.
|
||||||
|
*
|
||||||
|
* @tparam DataType
|
||||||
|
* @tparam IndexType
|
||||||
|
*/
|
||||||
|
template<typename MatrixType>
|
||||||
|
struct MatCol {
|
||||||
|
using owner_t = MatrixType;
|
||||||
|
|
||||||
|
using DataType = typename MatrixType::dataType;
|
||||||
|
using IndexType = typename MatrixType::indexType;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* ctor using column pointers for begin-end. own is pointer to Matrix.
|
||||||
|
*/
|
||||||
|
MatCol(owner_t* own, const IndexType begin, const IndexType end) noexcept :
|
||||||
|
owner_(own), index_(begin), begin_(begin), end_(end) {
|
||||||
|
vindex_ = vIndexCalc(index_);
|
||||||
|
}
|
||||||
|
MatCol() = default;
|
||||||
|
MatCol(const MatCol&) = delete; //!< make sure there are no copies
|
||||||
|
MatCol& operator=(const MatCol&)= delete; //!< make sure there are no copies
|
||||||
|
MatCol(MatCol&&) = default;
|
||||||
|
MatCol& operator=(MatCol&&) = default;
|
||||||
|
|
||||||
|
//! a simple dereference operator, like an iterator
|
||||||
|
DataType operator* () {
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
//! Increment operator acts on index(), like an iterator
|
||||||
|
MatCol& operator++ () { advance(); return *this; }
|
||||||
|
MatCol& operator++ (int) { MatCol& p = *this; advance(); return p; }
|
||||||
|
|
||||||
|
//! () operator acts as member access (like a view)
|
||||||
|
DataType operator()(IndexType x) {
|
||||||
|
return (x == index())? get() : DataType{};
|
||||||
|
}
|
||||||
|
//! = operator acts as member assignment (like a view)
|
||||||
|
DataType operator= (DataType v) { return owner_->values[index_] = v; }
|
||||||
|
// iterator like handlers
|
||||||
|
// these return a virtual index value based on the items position on the full matrix
|
||||||
|
// but the move of the index is just a ++ away.
|
||||||
|
IndexType index() noexcept { return vindex_; }
|
||||||
|
const IndexType index() const noexcept { return vindex_; }
|
||||||
|
IndexType begin() noexcept { return vIndexCalc(begin_); }
|
||||||
|
const IndexType begin() const noexcept { return vIndexCalc(begin_); }
|
||||||
|
IndexType end() noexcept { return owner_->N; }
|
||||||
|
const IndexType end() const noexcept { return owner_->N; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Multiplication operator
|
||||||
|
*
|
||||||
|
* We follow only the non-zero values and multiply only the common indexes.
|
||||||
|
*
|
||||||
|
* @tparam C Universal reference for the type right half site column
|
||||||
|
*
|
||||||
|
* @param c The right hand site matrix
|
||||||
|
* @return The value of the inner product of two vectors
|
||||||
|
* @note The time complexity is \$ O(nnz1+nnz2) \$.
|
||||||
|
* Where the nnz is the max NNZ elements of the column of the matrix
|
||||||
|
*/
|
||||||
|
template <typename C>
|
||||||
|
DataType operator* (C&& c) {
|
||||||
|
static_assert(std::is_same<remove_cvref_t<C>, MatCol<MatrixType>>(), "");
|
||||||
|
DataType v{};
|
||||||
|
while (index() != end() && c.index() != c.end()) {
|
||||||
|
if (index() < c.index()) advance(); // advance me
|
||||||
|
else if (index() > c.index()) ++c; // advance other
|
||||||
|
else { //index() == c.index()
|
||||||
|
v += get() * *c; // multiply and advance both
|
||||||
|
++c;
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! small tool to increase the index pointers to Matrix
|
||||||
|
void advance() noexcept {
|
||||||
|
++index_;
|
||||||
|
vindex_ = vIndexCalc(index_);
|
||||||
|
}
|
||||||
|
//! tool to translate between col_ptr indexes and Matrix "virtual" full matrix indexes
|
||||||
|
IndexType vIndexCalc(IndexType idx) {
|
||||||
|
return (idx < end_) ? owner_->rows[idx] : end();
|
||||||
|
}
|
||||||
|
//! small get tool
|
||||||
|
DataType get() { return owner_->values[index_]; }
|
||||||
|
|
||||||
|
owner_t* owner_ {nullptr}; //!< Pointer to owner Matrix. MatCol is just a view
|
||||||
|
IndexType vindex_ {IndexType{}}; //!< Virtual index of full matrix
|
||||||
|
IndexType index_ {IndexType{}}; //!< index to Matrix::rows
|
||||||
|
IndexType begin_ {IndexType{}}; //!< beginning index of the column in Matrix::rows
|
||||||
|
IndexType end_ {IndexType{}}; //!< ending index of the column in Matrix::rows
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A view/iterator hybrid object for Matrix rows.
|
||||||
|
*
|
||||||
|
* This object provides access to a column of a Matrix. The public functionalities
|
||||||
|
* allow data access using indexes instead of iterators. We prefer indexes over iterators
|
||||||
|
* because we can apply the same index to different inner vector of Matrix without conversion.
|
||||||
|
*
|
||||||
|
* @tparam DataType
|
||||||
|
* @tparam IndexType
|
||||||
|
*/
|
||||||
|
template<typename MatrixType>
|
||||||
|
struct MatRow {
|
||||||
|
using owner_t = MatrixType;
|
||||||
|
|
||||||
|
using DataType = typename MatrixType::dataType;
|
||||||
|
using IndexType = typename MatrixType::indexType;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* ctor using virtual full matrix row index. own is pointer to Matrix.
|
||||||
|
*/
|
||||||
|
MatRow(owner_t* own, const IndexType row) noexcept :
|
||||||
|
owner_(own), vindex_(IndexType{}), row_(row), index_(IndexType{}),
|
||||||
|
begin_(IndexType{}), end_(owner_->NNZ) {
|
||||||
|
// place begin
|
||||||
|
while(begin_ != end_ && owner_->rows[begin_] != row_)
|
||||||
|
++begin_;
|
||||||
|
// place index_ and vindex_
|
||||||
|
if (owner_->rows[index_] != row_)
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
MatRow() = default;
|
||||||
|
MatRow(const MatRow&) = delete; //!< make sure there are no copies
|
||||||
|
MatRow& operator=(const MatRow&)= delete; //!< make sure there are no copies
|
||||||
|
MatRow(MatRow&&) = default;
|
||||||
|
MatRow& operator=(MatRow&&) = default;
|
||||||
|
|
||||||
|
//! a simple dereference operator, like an iterator
|
||||||
|
DataType operator* () {
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
//! Increment operator acts on index(), like an iterator
|
||||||
|
//! here the increment is a O(N) process.
|
||||||
|
MatRow& operator++ () { advance(); return *this; }
|
||||||
|
MatRow& operator++ (int) { MatRow& p = *this; advance(); return p; }
|
||||||
|
|
||||||
|
//! () operator acts as member access (like a view)
|
||||||
|
DataType operator()(IndexType x) {
|
||||||
|
return (x == index())? get() : DataType{};
|
||||||
|
}
|
||||||
|
//! = operator acts as member assignment (like a view)
|
||||||
|
DataType operator= (DataType v) { return owner_->values[index_] = v; }
|
||||||
|
// iterator like handlers
|
||||||
|
// these return a virtual index value based on the items position on the full matrix
|
||||||
|
// but the move of the index is just a ++ away.
|
||||||
|
IndexType index() noexcept { return vindex_; }
|
||||||
|
const IndexType index() const noexcept { return vindex_; }
|
||||||
|
IndexType begin() noexcept { return vIndexCalc(begin_); }
|
||||||
|
const IndexType begin() const noexcept { return vIndexCalc(begin_); }
|
||||||
|
IndexType end() noexcept { return owner_->N; }
|
||||||
|
const IndexType end() const noexcept { return owner_->N; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Multiplication operator
|
||||||
|
*
|
||||||
|
* We follow only the non-zero values and multiply only the common indexes.
|
||||||
|
*
|
||||||
|
* @tparam C Universal reference for the type right half site column
|
||||||
|
*
|
||||||
|
* @param c The right hand site matrix
|
||||||
|
* @return The value of the inner product of two vectors
|
||||||
|
* @note The time complexity is \$ O(N+nnz2) \$ and way heavier the ColxCol multiplication.
|
||||||
|
* Where the nnz is the max NNZ elements of the column of the matrix
|
||||||
|
*/
|
||||||
|
template <typename C>
|
||||||
|
DataType operator* (C&& c) {
|
||||||
|
static_assert(std::is_same<remove_cvref_t<C>, MatCol<MatrixType>>(), "");
|
||||||
|
DataType v{};
|
||||||
|
while (index() != end() && c.index() != c.end()) {
|
||||||
|
if (index() < c.index()) advance(); // advance me
|
||||||
|
else if (index() > c.index()) ++c; // advance other
|
||||||
|
else { //index() == c.index()
|
||||||
|
v += get() * *c; // multiply and advance both
|
||||||
|
++c;
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
//! small tool to increase the index pointers to Matrix matrix
|
||||||
|
//! We have to search the entire rows vector in Matrix to find the next
|
||||||
|
//! virtual row position.
|
||||||
|
//! time complexity O(N)
|
||||||
|
void advance() noexcept {
|
||||||
|
do
|
||||||
|
++index_;
|
||||||
|
while(index_ != end_ && owner_->rows[index_] != row_);
|
||||||
|
vindex_ = vIndexCalc(index_);
|
||||||
|
}
|
||||||
|
//! tool to translate between col_ptr indexes and Matrix "virtual" full matrix indexes
|
||||||
|
IndexType vIndexCalc(IndexType idx) {
|
||||||
|
for(IndexType i =0 ; i<(owner_->N+1) ; ++i)
|
||||||
|
if (idx < owner_->col_ptr[i])
|
||||||
|
return i-1;
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
//! small get tool
|
||||||
|
DataType get() { return owner_->values[index_]; }
|
||||||
|
|
||||||
|
owner_t* owner_ {nullptr}; //!< Pointer to owner Matrix. MatCol is just a view
|
||||||
|
IndexType vindex_ {IndexType{}}; //!< Virtual index of full matrix
|
||||||
|
IndexType row_ {IndexType{}}; //!< The virtual full matrix row of the object
|
||||||
|
IndexType index_ {IndexType{}}; //!< index to Matrix::rows
|
||||||
|
IndexType begin_ {IndexType{}}; //!< beginning index of the column in Matrix::rows
|
||||||
|
IndexType end_ {IndexType{}}; //!< ending index of the column in Matrix::rows
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A proxy Matrix value object/view.
|
||||||
|
*
|
||||||
|
* This object acts as proxy to provide read/write access to an Matrix item.
|
||||||
|
*
|
||||||
|
* @tparam DataType The type of the values of the Matrix matrix
|
||||||
|
* @tparam IndexType The type of the indexes of the Matrix matrix
|
||||||
|
*/
|
||||||
|
template<typename MatrixType>
|
||||||
|
struct MatVal {
|
||||||
|
using owner_t = MatrixType;
|
||||||
|
|
||||||
|
using DataType = typename MatrixType::dataType;
|
||||||
|
using IndexType = typename MatrixType::indexType;
|
||||||
|
|
||||||
|
//!< ctor using all value-row-column data, plus a pointer to owner Matrix object
|
||||||
|
MatVal(owner_t* own, DataType v, IndexType i, IndexType j) :
|
||||||
|
owner_(own), v_(v), i_(i), j_(j) { }
|
||||||
|
MatVal() = default;
|
||||||
|
MatVal(const MatVal&) = delete; //!< make sure there are no copies
|
||||||
|
MatVal& operator=(const MatVal&) = delete; //!< make sure there are no copies
|
||||||
|
MatVal(MatVal&&) = default;
|
||||||
|
MatVal& operator=(MatVal&&) = default;
|
||||||
|
|
||||||
|
//! Operator to return the DataType value implicitly
|
||||||
|
operator DataType() { return v_; }
|
||||||
|
//! Operator to write back to owner the assigned value
|
||||||
|
//! for ex: A(2,3) = 5;
|
||||||
|
MatVal& operator=(DataType v) {
|
||||||
|
v_ = v;
|
||||||
|
owner_->set(v_, i_, j_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
owner_t* owner_{nullptr}; //!< Pointer to owner Matrix. MatVal is just a view.
|
||||||
|
DataType v_{DataType{}}; //!< The value of the row-column pair (for speed)
|
||||||
|
IndexType i_{IndexType{}}; //!< The row
|
||||||
|
IndexType j_{IndexType{}}; //!< the column
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace mtx
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* MATRIX_HPP_ */
|
||||||
+51
-385
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* \file
|
* \file utils.hpp
|
||||||
* \brief Utilities header
|
* \brief Utilities header
|
||||||
*
|
*
|
||||||
* \author
|
* \author
|
||||||
@@ -9,353 +9,42 @@
|
|||||||
#ifndef UTILS_HPP_
|
#ifndef UTILS_HPP_
|
||||||
#define UTILS_HPP_
|
#define UTILS_HPP_
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <mpi.h>
|
|
||||||
|
|
||||||
|
#include "matrix.hpp"
|
||||||
#include "config.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.
|
* A Logger for entire program.
|
||||||
*/
|
*/
|
||||||
struct Log {
|
struct Log {
|
||||||
struct Endl {} endl; //!< a tag object to to use it as a new line request.
|
struct Endl {} endl; //!< a tag object to to use it as a new line request.
|
||||||
|
|
||||||
//! We provide logging via << operator
|
|
||||||
template<typename T>
|
|
||||||
Log &operator<<(T &&t) {
|
|
||||||
if (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//! We provide logging via << operator
|
||||||
|
template<typename T>
|
||||||
|
Log& operator<< (T&& t) {
|
||||||
|
if (session.verbose) {
|
||||||
|
if (line_) {
|
||||||
|
std::cout << "[Log]: " << t;
|
||||||
|
line_ = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
std::cout << t;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
// overload for special end line handling
|
||||||
|
Log& operator<< (Endl e) { (void)e;
|
||||||
|
if (session.verbose) {
|
||||||
|
std::cout << '\n';
|
||||||
|
line_ = true;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
bool line_{true};
|
bool line_ {true};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Log logger;
|
extern Log logger;
|
||||||
@@ -363,59 +52,36 @@ extern Log logger;
|
|||||||
/*!
|
/*!
|
||||||
* A small timing utility based on chrono.
|
* A small timing utility based on chrono.
|
||||||
*/
|
*/
|
||||||
struct Timing {
|
struct Timing{
|
||||||
using Tpoint = std::chrono::steady_clock::time_point;
|
using Tpoint = std::chrono::steady_clock::time_point;
|
||||||
using Tduration = std::chrono::microseconds;
|
using microseconds = std::chrono::microseconds;
|
||||||
using microseconds = std::chrono::microseconds;
|
using milliseconds = std::chrono::milliseconds;
|
||||||
using milliseconds = std::chrono::milliseconds;
|
using seconds = std::chrono::seconds;
|
||||||
using seconds = std::chrono::seconds;
|
|
||||||
|
|
||||||
//! tool to mark the starting point
|
//! tool to mark the starting point
|
||||||
Tpoint start() noexcept { return mark_ = std::chrono::steady_clock::now(); }
|
Tpoint start () noexcept { return start_ = std::chrono::steady_clock::now(); }
|
||||||
|
//! tool to mark the ending point
|
||||||
//! tool to mark the ending point
|
Tpoint stop () noexcept { return stop_ = std::chrono::steady_clock::now(); }
|
||||||
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";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
auto dt () noexcept {
|
||||||
|
return std::chrono::duration_cast<std::chrono::microseconds>(stop_ - start_).count();
|
||||||
|
}
|
||||||
|
//! tool to print the time interval
|
||||||
|
void print_dt (const char* what) noexcept {
|
||||||
|
if (session.timing) {
|
||||||
|
auto t = stop_ - start_;
|
||||||
|
if (std::chrono::duration_cast<microseconds>(t).count() < 10000)
|
||||||
|
std::cout << "[Timing]: " << what << ": " << std::to_string(std::chrono::duration_cast<microseconds>(t).count()) << " [usec]\n";
|
||||||
|
else if (std::chrono::duration_cast<milliseconds>(t).count() < 10000)
|
||||||
|
std::cout << "[Timing]: " << what << ": " << std::to_string(std::chrono::duration_cast<milliseconds>(t).count()) << " [msec]\n";
|
||||||
|
else
|
||||||
|
std::cout << "[Timing]: " << what << ": " << std::to_string(std::chrono::duration_cast<seconds>(t).count()) << " [sec]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
Tpoint mark_{};
|
Tpoint start_;
|
||||||
Tduration duration_{};
|
Tpoint stop_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
|
||||||
* 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_ */
|
#endif /* UTILS_HPP_ */
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ function distbubletonic!(p, data)
|
|||||||
for i in 1:p
|
for i in 1:p
|
||||||
sort!(view(data, i, :), rev = !ascending[i])
|
sort!(view(data, i, :), rev = !ascending[i])
|
||||||
end
|
end
|
||||||
for step in 0:p-1
|
for step in 0:p-2
|
||||||
direction = [true for x = 1:p]
|
direction = [true for x = 1:p]
|
||||||
partnerid = partner.(pid, step)
|
partnerid = partner.(pid, step)
|
||||||
activeids = active.(partnerid, p)
|
activeids = active.(partnerid, p)
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
function exchange(localid, remoteid)
|
function exchange(localid, remoteid)
|
||||||
# if verbose
|
if verbose
|
||||||
# println("Exchange local data from $localid with partner $remoteid")
|
println("Exchange local data from $localid with partner $remoteid")
|
||||||
# end
|
end
|
||||||
nothing # We have all data here ;)
|
nothing # We have all data here ;)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -23,89 +23,9 @@ function minmax(data, localid, remoteid, keepsmall)
|
|||||||
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)
|
function sort_network!(data, n, depth)
|
||||||
nodes = 0:n-1
|
nodes = 0:n-1
|
||||||
bitonicFlag = zeros(Int8, size(data, 1))
|
|
||||||
sortFlag = zeros(Int8, size(data, 1))
|
|
||||||
for step = depth-1:-1:0
|
for step = depth-1:-1:0
|
||||||
partnerid = nodes .⊻ (1 << step)
|
partnerid = nodes .⊻ (1 << step)
|
||||||
direction = (nodes .& (1 << depth)) .== 0 .& (nodes .< partnerid)
|
direction = (nodes .& (1 << depth)) .== 0 .& (nodes .< partnerid)
|
||||||
@@ -120,13 +40,6 @@ function sort_network!(data, n, depth)
|
|||||||
minmax(data, i, partnerid[i+1], keepsmall[i+1])
|
minmax(data, i, partnerid[i+1], keepsmall[i+1])
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
+19
-184
@@ -1,28 +1,22 @@
|
|||||||
/*!
|
/*!
|
||||||
* \file
|
* \file main.cpp
|
||||||
* \brief Main application file for PDS HW2 (MPI)
|
* \brief Main application file for PDS HW2 (MPI)
|
||||||
*
|
*
|
||||||
* \author
|
* \author
|
||||||
* Christos Choutouridis AEM:8997
|
* Christos Choutouridis AEM:8997
|
||||||
* <cchoutou@ece.auth.gr>
|
* <cchoutou@ece.auth.gr>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
|
#include <mpi.h>
|
||||||
|
|
||||||
|
#include "matrix.hpp"
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "distsort.hpp"
|
|
||||||
|
|
||||||
|
// Global session data
|
||||||
// Global config data
|
session_t session;
|
||||||
config_t config;
|
|
||||||
MPI_t<> mpi;
|
|
||||||
distBuffer_t Data;
|
|
||||||
Log logger;
|
|
||||||
Timing Ttotal;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* A small command line argument parser
|
* A small command line argument parser
|
||||||
@@ -35,53 +29,29 @@ bool get_options(int argc, char* argv[]){
|
|||||||
for (int i=1 ; i<argc ; ++i) {
|
for (int i=1 ; i<argc ; ++i) {
|
||||||
std::string arg(argv[i]); // get current argument
|
std::string arg(argv[i]); // get current argument
|
||||||
|
|
||||||
if (arg == "-q" || arg == "--array-size") {
|
if (arg == "-x" || arg == "--xxxxx") {
|
||||||
if (i+1 < argc) {
|
if (i+2 < argc) {
|
||||||
config.arraySize = 1 << atoi(argv[++i]);
|
// session.corpusMtxFile = std::string(argv[++i]);
|
||||||
|
// session.corpusDataSet = std::string(argv[++i]);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
status = false;
|
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 == "-v" || arg == "--verbose")
|
||||||
|
session.verbose = true;
|
||||||
else if (arg == "-h" || arg == "--help") {
|
else if (arg == "-h" || arg == "--help") {
|
||||||
std::cout << "distbitonic/distbubbletonic - A distributed bitonic sort\n\n";
|
std::cout << "distbitonic - A distributed bitonic sort\n\n";
|
||||||
std::cout << "distbitonic -q <N> [--validation] [--ndebug] [-v]\n";
|
std::cout << "distbitonic -x <> [-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 << '\n';
|
||||||
std::cout << "Options:\n\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 << " -v | --verbose\n";
|
||||||
std::cout << " Request a more verbose output to stdout.\n\n";
|
std::cout << " Request a more verbose output to stdout.\n\n";
|
||||||
std::cout << " -h | --help\n";
|
std::cout << " -h | --help\n";
|
||||||
std::cout << " Prints this and exit.\n\n";
|
std::cout << " Prints this and exit.\n\n";
|
||||||
std::cout << "Examples:\n\n";
|
std::cout << "Examples:\n\n";
|
||||||
std::cout << " mpirun -np 4 distbitonic -q 24\n";
|
std::cout << " ...Example case...:\n";
|
||||||
std::cout << " Runs distbitonic in 4 MPI processes with 2^24 array points each\n\n";
|
std::cout << " > distbitonic -x <xxxxx> \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);
|
exit(0);
|
||||||
}
|
}
|
||||||
@@ -94,128 +64,11 @@ bool get_options(int argc, char* argv[]){
|
|||||||
return status;
|
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 {
|
int main(int argc, char* argv[]) try {
|
||||||
// Initialize MPI environment
|
// try to read command line
|
||||||
mpi.init(&argc, &argv);
|
|
||||||
|
|
||||||
// try to read command line (after MPI parsing)
|
|
||||||
if (!get_options(argc, argv))
|
if (!get_options(argc, argv))
|
||||||
exit(1);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
catch (std::exception& e) {
|
catch (std::exception& e) {
|
||||||
@@ -223,21 +76,3 @@ catch (std::exception& e) {
|
|||||||
std::cerr << "Error: " << e.what() << '\n';
|
std::cerr << "Error: " << e.what() << '\n';
|
||||||
exit(1);
|
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
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user