From ed25153613e4a6cfed117c0b34aeb6b4b3ce6193 Mon Sep 17 00:00:00 2001 From: WickedJack99 Date: Sun, 29 Jun 2025 17:50:13 +0200 Subject: [PATCH] some stuff --- lab12/exc4/.$architecture.drawio.bkp | 299 ++++++++++++++++++++++++ lab12/exc4/Makefile | 2 +- lab12/exc4/architecture.drawio | 335 +++++++++++++++++++++++++++ lab12/exc4/jacobi | Bin 0 -> 117832 bytes lab12/exc4/jacobi.cpp | 130 ++++++++--- lab12/exc4/jacobi.h | 21 ++ lab12/exc4/jacobi2.cpp | 169 ++++++++++++++ lab12/exc4/run.sh | 10 +- 8 files changed, 935 insertions(+), 31 deletions(-) create mode 100644 lab12/exc4/.$architecture.drawio.bkp create mode 100644 lab12/exc4/architecture.drawio create mode 100755 lab12/exc4/jacobi create mode 100644 lab12/exc4/jacobi2.cpp diff --git a/lab12/exc4/.$architecture.drawio.bkp b/lab12/exc4/.$architecture.drawio.bkp new file mode 100644 index 0000000..673f591 --- /dev/null +++ b/lab12/exc4/.$architecture.drawio.bkp @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lab12/exc4/Makefile b/lab12/exc4/Makefile index 0b9574d..4ad4826 100644 --- a/lab12/exc4/Makefile +++ b/lab12/exc4/Makefile @@ -12,7 +12,7 @@ debug: $(SOURCE_FILES) mpicxx $(SOURCE_FILES) -g -o $(PROGRAM_NAME) ${COMPILER_FLAGS} run: release - mpirun -np 4 ./$(PROGRAM_NAME) 3072 3e-6 100000 + mpirun -np 1 ./$(PROGRAM_NAME) 3072 3e-6 100000 plot: plot.py python3 plot.py \ No newline at end of file diff --git a/lab12/exc4/architecture.drawio b/lab12/exc4/architecture.drawio new file mode 100644 index 0000000..1ad5982 --- /dev/null +++ b/lab12/exc4/architecture.drawio @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lab12/exc4/jacobi b/lab12/exc4/jacobi new file mode 100755 index 0000000000000000000000000000000000000000..76c3f9a542ea5b740095ed763d9ed7fa31c7580d GIT binary patch literal 117832 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 req; // Communication with upper partner - if (!isLastRank()) { - int upper = rank_ + 1; + if (!isLastRank()) + { + int upper = rank_ - 1; MPI_Isend(&phi(n - 2, 0), sendSize, MPI_DOUBLE, upper, tag, MPI_COMM_WORLD, &req.emplace_back()); MPI_Irecv(&phi(n - 1, 0), sendSize, MPI_DOUBLE, upper, tag, MPI_COMM_WORLD, @@ -60,7 +90,43 @@ void Jacobi::exchangeHaloLayers(Matrix& phi) { } // Communication with lower partner - if (!isFirstRank()) { + if (!isFirstRank()) + { + // communication with second rank on same node via shared memory + // We write our send row to shared memory row 0 + for (int j = 0; j < sendSize; ++j) + shared_rows_[0 * sendSize + j] = phi(n - 2, j); // our last inner row + + MPI_Win_sync(win_); // ensure memory visibility + + // Wait for second proc to write its row back to shared memory row 1 + MPI_Win_sync(win_); + for (int j = 0; j < sendSize; ++j) + phi(n - 1, j) = shared_rows_[1 * sendSize + j]; // halo from second proc + } + + // Wait for communication to finish + std::vector stat(req.size()); + MPI_Waitall(req.size(), req.data(), stat.data()); +} + +void Jacobi::exchangeHaloLayersNodeMPIProcSecond(Matrix &phi) +{ + int n = phi.rows(); + int sendSize = phi.cols(); + int tag = 0; + + std::vector req; + + // Communication with upper partner + if (!isLastRank()) + { + // communication with first rank on same node via shared memory + } + + // Communication with lower partner + if (!isFirstRank()) + { int lower = rank_ - 1; MPI_Isend(&phi(1, 0), sendSize, MPI_DOUBLE, lower, tag, MPI_COMM_WORLD, &req.emplace_back()); @@ -73,35 +139,42 @@ void Jacobi::exchangeHaloLayers(Matrix& phi) { MPI_Waitall(req.size(), req.data(), stat.data()); } -Matrix Jacobi::addHaloLayers(const Matrix& withoutHalos) { +Matrix Jacobi::addHaloLayers(const Matrix &withoutHalos) +{ const int numRows = withoutHalos.rows(); const int numCols = withoutHalos.cols(); Matrix withHalos = Matrix::zeros(numRowsWithHalos(numRows), numCols); - for (int i = 0; i < numRows; ++i) { - for (int j = 0; j < numCols; ++j) { + for (int i = 0; i < numRows; ++i) + { + for (int j = 0; j < numCols; ++j) + { withHalos(i + lowerOffset(), j) = withoutHalos(i, j); } } return withHalos; } -Matrix Jacobi::removeHaloLayers(const Matrix& withHalos) { +Matrix Jacobi::removeHaloLayers(const Matrix &withHalos) +{ const int numRows = numRowsWithoutHalos(withHalos.rows()); const int numCols = withHalos.cols(); Matrix withoutHalos = Matrix::zeros(numRows, numCols); - for (int i = 0; i < numRows; ++i) { - for (int j = 0; j < numCols; ++j) { + for (int i = 0; i < numRows; ++i) + { + for (int j = 0; j < numCols; ++j) + { withoutHalos(i, j) = withHalos(i + lowerOffset(), j); } } return withoutHalos; } -Jacobi::Result Jacobi::run(const Matrix& init, double eps, int maxNumIter) { +Jacobi::Result Jacobi::run(const Matrix &init, double eps, int maxNumIter) +{ std::vector phi(2, addHaloLayers(init)); const int numRows = phi[0].rows(); const int numCols = phi[0].cols(); @@ -109,15 +182,18 @@ Jacobi::Result Jacobi::run(const Matrix& init, double eps, int maxNumIter) { int nIter = 0; double dist = std::numeric_limits::max(); - int t0 = 0; // array index of current timestep - int t1 = 1; // array index of next timestep - while (dist > eps && nIter < maxNumIter) { + int t0 = 0; // array index of current timestep + int t1 = 1; // array index of next timestep + while (dist > eps && nIter < maxNumIter) + { dist = 0; exchangeHaloLayers(phi[t0]); - for (int i = 1; i < numRows - 1; ++i) { - for (int j = 1; j < numCols - 1; ++j) { + for (int i = 1; i < numRows - 1; ++i) + { + for (int j = 1; j < numCols - 1; ++j) + { phi[t1](i, j) = .25 * (phi[t0](i + 1, j) + phi[t0](i - 1, j) + phi[t0](i, j + 1) + phi[t0](i, j - 1)); diff --git a/lab12/exc4/jacobi.h b/lab12/exc4/jacobi.h index e552131..f3d8479 100644 --- a/lab12/exc4/jacobi.h +++ b/lab12/exc4/jacobi.h @@ -31,6 +31,9 @@ class Jacobi { private: void exchangeHaloLayers(Matrix& phi); + void exchangeHaloLayersNodeMPIProcFirst(Matrix& phi); + void exchangeHaloLayersNodeMPIProcSecond(Matrix& phi); + Matrix addHaloLayers(const Matrix& phi); Matrix removeHaloLayers(const Matrix& phi); @@ -43,8 +46,26 @@ class Jacobi { int numRowsWithHalos(int numRowsWithoutHalos) const; int numRowsWithoutHalos(int numRowsWithHalos) const; + // Global rank on cluster int rank_ = MPI_PROC_NULL; + + // Local rank on node + int shm_rank_ = MPI_PROC_NULL; + + // Communicator for local domain of node + MPI_Comm shm_comm_; + + // True if this MPI proc is first on node + bool is_first_on_node_; + + // count of MPI procs int numProc_ = 1; }; +// 4 times horizontal split | with mpi + +// 12 times vertical split - with openmp + + + #endif // JACOBI_H \ No newline at end of file diff --git a/lab12/exc4/jacobi2.cpp b/lab12/exc4/jacobi2.cpp new file mode 100644 index 0000000..b404a08 --- /dev/null +++ b/lab12/exc4/jacobi2.cpp @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "matrix.h" +#include "jacobi.h" +#include "matrix_io.h" + +Jacobi::Jacobi() +{ + MPI_Comm_rank(MPI_COMM_WORLD, &rank_); + MPI_Comm_size(MPI_COMM_WORLD, &numProc_); +} + +bool Jacobi::isFirstRank() const +{ + return rank_ == 0; +} + +bool Jacobi::isLastRank() const +{ + return rank_ == numProc_ - 1; +} + +int Jacobi::numRowsWithHalos(int numRowsWithoutHalos) const +{ + if (isFirstRank() || isLastRank()) + { + return numRowsWithoutHalos + 1; + } + else + { + return numRowsWithoutHalos + 2; + } +} + +int Jacobi::numRowsWithoutHalos(int numRowsWithHalos) const +{ + if (isFirstRank() || isLastRank()) + { + return numRowsWithHalos - 1; + } + else + { + return numRowsWithHalos - 2; + } +} + +int Jacobi::lowerOffset() const +{ + return isFirstRank() ? 0 : 1; +} + +int Jacobi::upperOffset() const +{ + return isLastRank() ? 0 : 1; +} + +void Jacobi::exchangeHaloLayers(Matrix &phi) +{ + int n = phi.rows(); + int sendSize = phi.cols(); + int tag = 0; + + std::vector req; + + // Communication with upper partner + if (!isLastRank()) + { + int upper = rank_ + 1; + MPI_Isend(&phi(n - 2, 0), sendSize, MPI_DOUBLE, upper, tag, MPI_COMM_WORLD, + &req.emplace_back()); + MPI_Irecv(&phi(n - 1, 0), sendSize, MPI_DOUBLE, upper, tag, MPI_COMM_WORLD, + &req.emplace_back()); + } + + // Communication with lower partner + if (!isFirstRank()) + { + int lower = rank_ - 1; + MPI_Isend(&phi(1, 0), sendSize, MPI_DOUBLE, lower, tag, MPI_COMM_WORLD, + &req.emplace_back()); + MPI_Irecv(&phi(0, 0), sendSize, MPI_DOUBLE, lower, tag, MPI_COMM_WORLD, + &req.emplace_back()); + } + + // Wait for communication to finish + std::vector stat(req.size()); + MPI_Waitall(req.size(), req.data(), stat.data()); +} + +Matrix Jacobi::addHaloLayers(const Matrix &withoutHalos) +{ + const int numRows = withoutHalos.rows(); + const int numCols = withoutHalos.cols(); + + Matrix withHalos = Matrix::zeros(numRowsWithHalos(numRows), numCols); + + for (int i = 0; i < numRows; ++i) + { + for (int j = 0; j < numCols; ++j) + { + withHalos(i + lowerOffset(), j) = withoutHalos(i, j); + } + } + return withHalos; +} + +Matrix Jacobi::removeHaloLayers(const Matrix &withHalos) +{ + const int numRows = numRowsWithoutHalos(withHalos.rows()); + const int numCols = withHalos.cols(); + + Matrix withoutHalos = Matrix::zeros(numRows, numCols); + + for (int i = 0; i < numRows; ++i) + { + for (int j = 0; j < numCols; ++j) + { + withoutHalos(i, j) = withHalos(i + lowerOffset(), j); + } + } + return withoutHalos; +} + +Jacobi::Result Jacobi::run(const Matrix &init, double eps, int maxNumIter) +{ + std::vector phi(2, addHaloLayers(init)); + const int numRows = phi[0].rows(); + const int numCols = phi[0].cols(); + + int nIter = 0; + double dist = std::numeric_limits::max(); + + int t0 = 0; // array index of current timestep + int t1 = 1; // array index of next timestep + +#pragma omp parallel num_threads(6) firstprivate(nIter, t0, t1) + while (dist > eps && nIter < maxNumIter) + { + dist = 0; + +#pragma omp single + exchangeHaloLayers(phi[t0]); + + for (int i = 1; i < numRows - 1; ++i) + { + +#pragma omp for schedule(static, numCols / 12) reduction(max : dist) + for (int j = 1; j < numCols - 1; ++j) + { + phi[t1](i, j) = .25 * (phi[t0](i + 1, j) + phi[t0](i - 1, j) + + phi[t0](i, j + 1) + phi[t0](i, j - 1)); + + const double diff = phi[t1](i, j) - phi[t0](i, j); + dist = std::max(dist, std::abs(diff)); + } + } + +#pragma omp barrier +#pragma omp single + MPI_Allreduce(MPI_IN_PLACE, &dist, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + + nIter++; + std::swap(t0, t1); + } + + return {removeHaloLayers(phi[t0]), dist, nIter}; +} diff --git a/lab12/exc4/run.sh b/lab12/exc4/run.sh index 0d1a3eb..04b41a1 100644 --- a/lab12/exc4/run.sh +++ b/lab12/exc4/run.sh @@ -2,8 +2,12 @@ #SBATCH --job-name=jacobi #SBATCH --output=jacobi.out #SBATCH --error=jacobi.err -#SBATCH --ntasks=48 -#SBATCH --cpus-per-task=1 +#SBATCH --ntasks=8 # 8 MPI processes (1 per socket) +#SBATCH --cpus-per-task=6 # 6 threads per MPI process #SBATCH --time=01:00:00 -srun --mpi=pmix ./jacobi \ No newline at end of file +export OMP_NUM_THREADS=6 +export OMP_PROC_BIND=true +export OMP_PLACES=cores + +srun --mpi=pmix ./jacobi