Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
f9d0ac4
Project setup and verification
NiTro005 Oct 30, 2025
5b350ee
fix: Mpi version fixed
NiTro005 Nov 1, 2025
cf8af92
fetch remote upstream
NiTro005 Nov 15, 2025
f25d835
Merge branch 'master' of https://github.com/learning-process/ppc-2025…
NiTro005 Nov 15, 2025
5f1ede7
sync with upstream
NiTro005 Nov 15, 2025
db199ad
update
NiTro005 Nov 16, 2025
cae727f
clang format fix
NiTro005 Nov 16, 2025
67f9116
fix output in seq and mpi
NiTro005 Nov 16, 2025
982284c
clang tidy try fix
NiTro005 Nov 16, 2025
13cbe7f
...
NiTro005 Nov 16, 2025
a0c9150
...
NiTro005 Nov 16, 2025
f1356bd
...
NiTro005 Nov 16, 2025
6ed8a25
clang format
NiTro005 Nov 16, 2025
7d7e532
...
NiTro005 Nov 16, 2025
e00ad3b
...
NiTro005 Nov 16, 2025
a3de07f
fixed tests
NiTro005 Nov 16, 2025
78cbd71
...
NiTro005 Nov 16, 2025
92cbb2b
Merge branch 'trofimov_n_max_val_matrix'
NiTro005 Nov 16, 2025
049a973
clang format update
NiTro005 Nov 16, 2025
937bcf4
...
NiTro005 Nov 16, 2025
cfa03bc
modify perfomance test
NiTro005 Nov 16, 2025
8495178
Revert "modify perfomance test"
NiTro005 Nov 16, 2025
abaeb78
try....
NiTro005 Nov 16, 2025
3cb44ce
try..
NiTro005 Nov 16, 2025
5f338a5
...
NiTro005 Nov 16, 2025
ce38a5c
try...
NiTro005 Nov 16, 2025
07362cc
try..
NiTro005 Nov 16, 2025
f1ed5c5
try again...
NiTro005 Nov 16, 2025
61ce0f4
try again.......
NiTro005 Nov 16, 2025
abc0dba
try 394879...
NiTro005 Nov 16, 2025
565d5db
finish try..
NiTro005 Nov 16, 2025
b52ef6f
...
NiTro005 Nov 16, 2025
8ffe1ae
modify matrix test size
NiTro005 Nov 17, 2025
5a75afa
added report
NiTro005 Nov 18, 2025
06de90f
added report
NiTro005 Nov 18, 2025
95a237f
...
NiTro005 Nov 18, 2025
50c55a2
added new tests
NiTro005 Nov 19, 2025
34e39a7
fixed errors for pul request
NiTro005 Nov 26, 2025
7a41c8a
Added data splitting and sending from rank 0 to other ranks
NiTro005 Nov 30, 2025
15d2566
changed the distribution of data across processes in the MPI version
NiTro005 Dec 1, 2025
d009a17
fic clang tidy errors
NiTro005 Dec 3, 2025
f724f94
...
NiTro005 Dec 3, 2025
12edf9b
...
NiTro005 Dec 3, 2025
355f831
...
NiTro005 Dec 3, 2025
a124837
...
NiTro005 Dec 3, 2025
b203b04
...
NiTro005 Dec 3, 2025
a089fea
Initial commit
NiTro005 Dec 23, 2025
33ae3a2
fix errors in func tests
NiTro005 Dec 24, 2025
027a56c
...
NiTro005 Dec 24, 2025
634b9e8
...
NiTro005 Dec 24, 2025
64ef5a5
...
NiTro005 Dec 24, 2025
64a81e6
fix clang_tidy
NiTro005 Dec 24, 2025
760b2d3
...
NiTro005 Dec 24, 2025
fb6b3a9
fix ops_mpi clang tidy errors
NiTro005 Dec 24, 2025
822fa2c
fix errors clang tidy in tests
NiTro005 Dec 24, 2025
ade8c2b
...
NiTro005 Dec 24, 2025
98a3fef
edit tests
NiTro005 Dec 24, 2025
2eb578d
...
NiTro005 Dec 24, 2025
4e4e5df
...
NiTro005 Dec 25, 2025
62fb5a4
...
NiTro005 Dec 25, 2025
eeb7445
...
NiTro005 Dec 25, 2025
f8cb786
added report
NiTro005 Dec 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions tasks/trofimov_n_mult_matrix_cannon/common/include/common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <string>
#include <tuple>
#include <vector>

#include "task/include/task.hpp"

namespace trofimov_n_mult_matrix_cannon {

using Matrix = std::vector<double>;

using InType = std::tuple<Matrix, Matrix, int>;
using OutType = Matrix;

using TestType = std::tuple<InType, std::string>;
using BaseTask = ppc::task::Task<InType, OutType>;

} // namespace trofimov_n_mult_matrix_cannon
9 changes: 9 additions & 0 deletions tasks/trofimov_n_mult_matrix_cannon/info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"student": {
"first_name": "Никита",
"last_name": "Трофимов",
"middle_name": "Сергеевич",
"group_number": "3823Б1ПР4",
"task_number": "1"
}
}
22 changes: 22 additions & 0 deletions tasks/trofimov_n_mult_matrix_cannon/mpi/include/ops_mpi.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include "task/include/task.hpp"
#include "trofimov_n_mult_matrix_cannon/common/include/common.hpp"

namespace trofimov_n_mult_matrix_cannon {

class TrofimovNMultMatrixCanonMPI : public BaseTask {
public:
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
return ppc::task::TypeOfTask::kMPI;
}
explicit TrofimovNMultMatrixCanonMPI(const InType &in);

private:
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;
};

} // namespace trofimov_n_mult_matrix_cannon
214 changes: 214 additions & 0 deletions tasks/trofimov_n_mult_matrix_cannon/mpi/src/ops_mpi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
#include "trofimov_n_mult_matrix_cannon/mpi/include/ops_mpi.hpp"

#include <mpi.h>

#include <array>
#include <cmath>
#include <cstddef>
#include <utility>
#include <vector>

#include "trofimov_n_mult_matrix_cannon/common/include/common.hpp"

namespace trofimov_n_mult_matrix_cannon {

namespace {

void MultiplySequential(const std::vector<double> &a, const std::vector<double> &b, int n,
std::vector<double> &result) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
result[(i * n) + j] += a[(i * n) + k] * b[(k * n) + j];
}
}
}
}

MPI_Comm CreateCartesianComm(int q) {
const std::array<int, 2> dims = {q, q};
const std::array<int, 2> periods = {1, 1};

MPI_Comm cart = MPI_COMM_NULL;
MPI_Cart_create(MPI_COMM_WORLD, 2, dims.data(), periods.data(), 1, &cart);
return cart;
}

void ScatterBlocks(MPI_Comm cart, int block, int world_size, const std::vector<double> &matrix_a,
const std::vector<double> &matrix_b, int matrix_size, std::vector<double> &block_a,
std::vector<double> &block_b) {
const std::size_t block_size = static_cast<std::size_t>(block) * static_cast<std::size_t>(block);

int rank = 0;
MPI_Comm_rank(cart, &rank);

if (rank == 0) {
for (int process = 0; process < world_size; process++) {
std::array<int, 2> coords{};
MPI_Cart_coords(cart, process, 2, coords.data());

std::vector<double> local_a(block_size);
std::vector<double> local_b(block_size);

for (int i = 0; i < block; i++) {
for (int j = 0; j < block; j++) {
const int gi = (coords[0] * block) + i;
const int gj = (coords[1] * block) + j;
local_a[(i * block) + j] = matrix_a[(gi * matrix_size) + gj];
local_b[(i * block) + j] = matrix_b[(gi * matrix_size) + gj];
}
}

if (process == 0) {
block_a = std::move(local_a);
block_b = std::move(local_b);
} else {
MPI_Send(local_a.data(), static_cast<int>(block_size), MPI_DOUBLE, process, 0, cart);
MPI_Send(local_b.data(), static_cast<int>(block_size), MPI_DOUBLE, process, 1, cart);
}
}
} else {
MPI_Recv(block_a.data(), static_cast<int>(block_size), MPI_DOUBLE, 0, 0, cart, MPI_STATUS_IGNORE);
MPI_Recv(block_b.data(), static_cast<int>(block_size), MPI_DOUBLE, 0, 1, cart, MPI_STATUS_IGNORE);
}
}

void InitialShift(MPI_Comm cart, int row, int col, std::vector<double> &block_a, std::vector<double> &block_b,
std::size_t block_size) {
int left = 0;
int right = 0;
int up = 0;
int down = 0;

for (int i = 0; i < row; i++) {
MPI_Cart_shift(cart, 1, -1, &right, &left);
MPI_Sendrecv_replace(block_a.data(), static_cast<int>(block_size), MPI_DOUBLE, left, 0, right, 0, cart,
MPI_STATUS_IGNORE);
}

for (int i = 0; i < col; i++) {
MPI_Cart_shift(cart, 0, -1, &down, &up);
MPI_Sendrecv_replace(block_b.data(), static_cast<int>(block_size), MPI_DOUBLE, up, 1, down, 1, cart,
MPI_STATUS_IGNORE);
}
}

void CannonMultiply(MPI_Comm cart, int q, int block, std::vector<double> &block_a, std::vector<double> &block_b,
std::vector<double> &block_c) {
const std::size_t block_size = static_cast<std::size_t>(block) * static_cast<std::size_t>(block);

int left = 0;
int right = 0;
int up = 0;
int down = 0;

for (int step = 0; step < q; step++) {
for (int i = 0; i < block; i++) {
for (int j = 0; j < block; j++) {
for (int k = 0; k < block; k++) {
block_c[(i * block) + j] += block_a[(i * block) + k] * block_b[(k * block) + j];
}
}
}

MPI_Cart_shift(cart, 1, -1, &right, &left);
MPI_Sendrecv_replace(block_a.data(), static_cast<int>(block_size), MPI_DOUBLE, left, 0, right, 0, cart,
MPI_STATUS_IGNORE);

MPI_Cart_shift(cart, 0, -1, &down, &up);
MPI_Sendrecv_replace(block_b.data(), static_cast<int>(block_size), MPI_DOUBLE, up, 1, down, 1, cart,
MPI_STATUS_IGNORE);
}
}

void GatherResult(MPI_Comm cart, int world_size, int block, int matrix_size, const std::vector<double> &block_c,
std::vector<double> &result) {
const std::size_t block_size = static_cast<std::size_t>(block) * static_cast<std::size_t>(block);

std::vector<double> all_blocks(static_cast<std::size_t>(world_size) * block_size);

MPI_Allgather(block_c.data(), static_cast<int>(block_size), MPI_DOUBLE, all_blocks.data(),
static_cast<int>(block_size), MPI_DOUBLE, cart);

for (int process = 0; process < world_size; process++) {
std::array<int, 2> coords{};
MPI_Cart_coords(cart, process, 2, coords.data());

const double *src = &all_blocks[static_cast<std::size_t>(process) * block_size];

for (int i = 0; i < block; i++) {
for (int j = 0; j < block; j++) {
const int gi = (coords[0] * block) + i;
const int gj = (coords[1] * block) + j;
result[(gi * matrix_size) + gj] = src[(i * block) + j];
}
}
}
}

} // namespace

TrofimovNMultMatrixCanonMPI::TrofimovNMultMatrixCanonMPI(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
}

bool TrofimovNMultMatrixCanonMPI::ValidationImpl() {
return true;
}

bool TrofimovNMultMatrixCanonMPI::PreProcessingImpl() {
const auto &[a_vector, b_vector, matrix_size] = GetInput();
if (matrix_size > 0) {
GetOutput().assign(static_cast<std::size_t>(matrix_size) * static_cast<std::size_t>(matrix_size), 0.0);
}
return true;
}

bool TrofimovNMultMatrixCanonMPI::RunImpl() {
const auto &[matrix_a, matrix_b, matrix_size] = GetInput();
auto &result_matrix = GetOutput();

if (matrix_size <= 0) {
return true;
}

int world_size = 0;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);

const int q = static_cast<int>(std::sqrt(world_size));
if ((q * q != world_size) || (matrix_size % q != 0)) {
MultiplySequential(matrix_a, matrix_b, matrix_size, result_matrix);
return true;
}

const int block = matrix_size / q;
MPI_Comm cart = CreateCartesianComm(q);

int rank = 0;
MPI_Comm_rank(cart, &rank);

std::array<int, 2> coords{};
MPI_Cart_coords(cart, rank, 2, coords.data());

const std::size_t block_size = static_cast<std::size_t>(block) * static_cast<std::size_t>(block);

std::vector<double> block_a(block_size);
std::vector<double> block_b(block_size);
std::vector<double> block_c(block_size, 0.0);

ScatterBlocks(cart, block, world_size, matrix_a, matrix_b, matrix_size, block_a, block_b);

InitialShift(cart, coords[0], coords[1], block_a, block_b, block_size);
CannonMultiply(cart, q, block, block_a, block_b, block_c);
GatherResult(cart, world_size, block, matrix_size, block_c, result_matrix);

MPI_Comm_free(&cart);
return true;
}

bool TrofimovNMultMatrixCanonMPI::PostProcessingImpl() {
return true;
}

} // namespace trofimov_n_mult_matrix_cannon
99 changes: 99 additions & 0 deletions tasks/trofimov_n_mult_matrix_cannon/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Умножение матриц с использованием алгоритма Кэннона

* Студент: Трофимов Никита, группа 3823Б1ПР4
* Технология: ALL (SEQ + MPI)
* Вариант: 1

## 1. Введение

В отчёте представлена реализация и анализ производительности умножения плотных матриц с использованием алгоритма Кэннона для параллельного исполнения с MPI, а также последовательная реализация для сравнения. Цель – оценить масштабируемость и эффективность параллельного подхода по сравнению с последовательным.

## 2. Постановка задачи

Необходимо перемножить две квадратные плотные матрицы типа `double`. Входные данные: матрицы A и B размером N x N. Выходные данные: матрица C = A * B. Ограничения: N должно делиться на квадратный корень из числа MPI процессов.

## 3. Базовый алгоритм (последовательный)

Последовательный алгоритм исaпользует стандартные тройные вложенные циклы по индексам i, j, k:

```
for i in 0..N-1:
for j in 0..N-1:
C[i][j] = 0
for k in 0..N-1:
C[i][j] += A[i][k] * B[k][j]
```

## 4. Схема параллелизации

### MPI (алгоритм Кэннона)

* **Распределение данных:** Матрицы делятся на блоки размера (N/q) x (N/q), где q = sqrt(число процессов). Каждый процесс получает один блок A и один блок B.
* **Схема коммуникаций:** Двумерная декартова топология с периодическими граничными условиями. Изначально блоки A сдвигаются влево на номер строки, блоки B — вверх на номер столбца.
* **Вычисления:** Каждый процесс перемножает локальные блоки и выполняет q шагов циклических сдвигов A влево и B вверх.
* **Сбор результатов:** Используется `MPI_Allgather` для сбора частичных результатов.

Пример схемы для сетки процессов 2x2:

```
Сетка процессов:
[0,0] [0,1]
[1,0] [1,1]
```

## 5. Детали реализации

* **Структура кода:**

* `ops_seq.hpp/cpp` — последовательное умножение
* `ops_mpi.hpp/cpp` — MPI реализация алгоритма Кэннона
* **Предположения:** N делится на sqrt(world_size)
* **Использование памяти:** Блоки выделяются на каждый процесс; общий объём памяти на процесс = 3 * block_size (A, B, C)
* **Важные моменты кода:**

* Функция `ScatterBlocks` распределяет блоки между процессами.
* `InitialShift` выполняет начальные сдвиги блоков перед умножением.
* `CannonMultiply` содержит основной цикл умножения блоков с циклической перестановкой A и B.
* `GatherResult` собирает все блоки в результирующую матрицу.
* В последовательной версии `MultiplySequential` используется тройной вложенный цикл.

## 6. Экспериментальная установка

* **Аппаратное/ПО:** CPU AMD Ryzen 7700, RAM > 8GB, Windows OS
* **Инструменты:** g++/MSVC, сборка Release
* **Окружение:** PPC_NUM_PROC задаётся через `mpiexec -n X`
* **Данные:** Генерируются последовательно, значения матриц возрастают/убывают для тестирования

## 7. Результаты и обсуждение

### 7.1 Корректность

Корректность проверялась сравнением с последовательной реализацией для маленьких матриц (1x1, 2x2, 4x4). Все MPI-прогоны дали правильный результат.

### 7.2 Производительность

Время выполнения для матрицы N=128:

| Режим | Кол-во процессов | Время, с | Ускорение | Эффективность |
| ----- | ---------------- | -------- | --------- | ------------- |
| SEQ | 1 | 0.00275 | 1.00 | N/A |
| MPI | 1 | 0.00398 | 0.69 | 69% |
| MPI | 2 | 0.00344 | 0.80 | 40% |
| MPI | 4 | 0.00122 | 2.25 | 56% |
| MPI | 8 | 0.00342 | 0.80 | 10% |

**Обсуждение:** MPI-реализация хорошо масштабируется до 4 процессов для маленькой матрицы. При увеличении числа процессов свыше 4, накладные расходы на коммуникацию начинают доминировать. Последовательная реализация быстрее на одном потоке для маленьких матриц, но для больших N будет крайне медленной.

## 8. Выводы

* Алгоритм Кэннона эффективно параллелизует умножение матриц.
* MPI демонстрирует ускорение при умеренном количестве процессов.
* Накладные расходы на коммуникацию ограничивают масштабируемость для маленьких матриц.
* Последовательная реализация практична только для небольших матриц.
* Важные моменты кода, такие как распределение блоков, начальные сдвиги и сбор результата, критичны для корректной работы алгоритма.

## 9. Ссылки

1. H. T. Kung, C. E. Leiserson. Algorithms for Matrix Multiplication on a Mesh-connected Multiprocessor. 1978.
2. Документация MPI. [https://www.mpi-forum.org/docs/](https://www.mpi-forum.org/docs/)
3. Сысоев А. В. Лекции по параллельному программированию.
22 changes: 22 additions & 0 deletions tasks/trofimov_n_mult_matrix_cannon/seq/include/ops_seq.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include "task/include/task.hpp"
#include "trofimov_n_mult_matrix_cannon/common/include/common.hpp"

namespace trofimov_n_mult_matrix_cannon {

class TrofimovNMultMatrixCanonSEQ : public BaseTask {
public:
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
return ppc::task::TypeOfTask::kSEQ;
}
explicit TrofimovNMultMatrixCanonSEQ(const InType &in);

private:
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;
};

} // namespace trofimov_n_mult_matrix_cannon
Loading
Loading