Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
5ad06c3
[OTHER] .gitignore update
Akarfire Nov 4, 2025
3352029
[INITIAL] Renamed namespaces and includes
Akarfire Nov 4, 2025
d733756
[INITIAL] Finished renaming and filled out info
Akarfire Nov 4, 2025
15b87e2
[INITIAL] tests fix
Akarfire Nov 4, 2025
1eaf8e4
[DEV] First task seq, mpi & tests first iteratation
Akarfire Nov 17, 2025
b700265
[DEV] Task1 Fixed mpi version
Akarfire Nov 17, 2025
a36101b
[FORMAT] Formatting fixes
Akarfire Nov 17, 2025
7b7d39c
[DEV] Task1 mpi version fixes
Akarfire Nov 17, 2025
f0043eb
[FORMAT] formatting fixes
Akarfire Nov 17, 2025
8ef5be2
[DEV] Task1 comparison type fix
Akarfire Nov 17, 2025
da7bd35
[DEV] CLang-Tidy fixes
Akarfire Nov 17, 2025
d54783a
[FORMAT] formatting
Akarfire Nov 17, 2025
1d79b87
[DEV] Broadcasting batch_size attempted fix
Akarfire Nov 18, 2025
33ebff5
[FORMAT] Fix attempt 1
Akarfire Nov 18, 2025
8e076f6
[DEV] More slight fixes
Akarfire Nov 18, 2025
7e4a8dc
[DEV] More fixes
Akarfire Nov 18, 2025
82bb23d
[FORMAT] Fix attempt 3
Akarfire Nov 18, 2025
3543bc3
[DEV] More explicit conversions
Akarfire Nov 18, 2025
202dc6f
[FORMAT] Fix attempt 4
Akarfire Nov 18, 2025
0d47034
[DEV] removed scatter & tweaked func tests
Akarfire Nov 19, 2025
ec3aa5c
[TEST] Set all to 1
Akarfire Nov 19, 2025
93d09fa
[TEST] Changed answer calculation
Akarfire Nov 19, 2025
15ae7a1
[DEV] Debuging
Akarfire Nov 19, 2025
5827a36
[FORMAT] Debuging
Akarfire Nov 19, 2025
c150269
[OTHER] .gitignore update
Akarfire Nov 4, 2025
220638d
[INITIAL] Renamed namespaces and includes
Akarfire Nov 4, 2025
b313cef
[INITIAL] Finished renaming and filled out info
Akarfire Nov 4, 2025
0eb4ab8
[INITIAL] tests fix
Akarfire Nov 4, 2025
6f803b2
[DEV] First task seq, mpi & tests first iteratation
Akarfire Nov 17, 2025
636c52f
[DEV] Task1 Fixed mpi version
Akarfire Nov 17, 2025
02ef337
[FORMAT] Formatting fixes
Akarfire Nov 17, 2025
ed1577e
[DEV] Task1 mpi version fixes
Akarfire Nov 17, 2025
3c18bf3
[FORMAT] formatting fixes
Akarfire Nov 17, 2025
61440d8
[DEV] Task1 comparison type fix
Akarfire Nov 17, 2025
0db04bf
[DEV] CLang-Tidy fixes
Akarfire Nov 17, 2025
1609eaf
[FORMAT] formatting
Akarfire Nov 17, 2025
c2731ab
[DEV] Broadcasting batch_size attempted fix
Akarfire Nov 18, 2025
6aac03c
[FORMAT] Fix attempt 1
Akarfire Nov 18, 2025
73e814d
[DEV] More slight fixes
Akarfire Nov 18, 2025
f9d2362
[DEV] More fixes
Akarfire Nov 18, 2025
fae0121
[FORMAT] Fix attempt 3
Akarfire Nov 18, 2025
de2f9ad
[DEV] More explicit conversions
Akarfire Nov 18, 2025
bc9a305
[FORMAT] Fix attempt 4
Akarfire Nov 18, 2025
815cc19
[DEV] removed scatter & tweaked func tests
Akarfire Nov 19, 2025
d90af75
[TEST] Set all to 1
Akarfire Nov 19, 2025
20e53b5
[TEST] Changed answer calculation
Akarfire Nov 19, 2025
f9d4acf
[DEV] Debuging
Akarfire Nov 19, 2025
56653a4
[FORMAT] Debuging
Akarfire Nov 19, 2025
f8c5064
Merge branch 'kutuzov_i_elem_vec_average' of https://github.com/Akarf…
Akarfire Nov 19, 2025
6a4a7d9
[TEST] Perf test fixes
Akarfire Nov 19, 2025
9a39a06
[DEV] MPI brought scatter back
Akarfire Nov 22, 2025
7350c28
[FORMAT] MPI brought scatter back
Akarfire Nov 22, 2025
c8679a8
[DEV] MPI fixed wrong buffer usage
Akarfire Nov 22, 2025
ad28a86
[TEST] Larger performance test data
Akarfire Nov 22, 2025
9fbb80d
[REPORT] Report commit
Akarfire Nov 22, 2025
e3e5120
[OTHER] Revert .gitignore
Akarfire Nov 23, 2025
1c7f40f
[STYLE] Sudden style errors fix
Akarfire Nov 23, 2025
8bc7ff6
[STYLE] Changed auto to auto *
Akarfire Nov 23, 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
15 changes: 15 additions & 0 deletions tasks/kutuzov_i_elem_vec_average/common/include/common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <cstddef>
#include <vector>

#include "task/include/task.hpp"

namespace kutuzov_i_elem_vec_average {

using InType = std::vector<double>;
using OutType = double;
using TestType = size_t;
using BaseTask = ppc::task::Task<InType, OutType>;

} // namespace kutuzov_i_elem_vec_average
Binary file added tasks/kutuzov_i_elem_vec_average/data/pic.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions tasks/kutuzov_i_elem_vec_average/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ФИ3",
"task_number": "1"
}
}
22 changes: 22 additions & 0 deletions tasks/kutuzov_i_elem_vec_average/mpi/include/ops_mpi.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

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

namespace kutuzov_i_elem_vec_average {

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

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

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

#include <mpi.h>

#include <cmath>
#include <vector>

#include "kutuzov_i_elem_vec_average/common/include/common.hpp"

namespace kutuzov_i_elem_vec_average {

KutuzovIElemVecAverageMPI::KutuzovIElemVecAverageMPI(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput() = 0.0;
}

bool KutuzovIElemVecAverageMPI::ValidationImpl() {
return !GetInput().empty();
}

bool KutuzovIElemVecAverageMPI::PreProcessingImpl() {
return true;
}

bool KutuzovIElemVecAverageMPI::RunImpl() {
GetOutput() = 0.0;

double result = 0.0;
double global_sum = 0.0;

// MPI Data
int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

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

// Syncing input size data
int total_elements_num = 0;
if (rank == 0) {
total_elements_num = static_cast<int>(GetInput().size());
}
MPI_Bcast(&total_elements_num, 1, MPI_INT, 0, MPI_COMM_WORLD);

// Calculating batch size
int batch_size = 0;
batch_size = total_elements_num / num_processes;

// If batch size isn't negative: Scatter the data among processes,
// sum it and reduce back to the process-0
if (batch_size > 0) {
auto *local_buffer = new double[batch_size];
MPI_Scatter(GetInput().data(), batch_size, MPI_DOUBLE, local_buffer, batch_size, MPI_DOUBLE, 0, MPI_COMM_WORLD);

double sum = 0.0;
for (int i = 0; i < batch_size; i++) {
sum += local_buffer[i];
}

MPI_Reduce(&sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
delete[] local_buffer;
}

if (rank == 0) {
// Add remaining elements on process-0
if (num_processes * batch_size < total_elements_num) {
for (int i = num_processes * batch_size; i < total_elements_num; i++) {
global_sum += GetInput()[i];
}
}
// Get the average
result = global_sum / static_cast<double>(total_elements_num);
}

// Broadcast the result to all the processes
MPI_Bcast(&result, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
GetOutput() = result;

// Wait for all processes to finish working
MPI_Barrier(MPI_COMM_WORLD);
return true;
}

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

} // namespace kutuzov_i_elem_vec_average
163 changes: 163 additions & 0 deletions tasks/kutuzov_i_elem_vec_average/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Вычисление среднего значения элементов вектора

- Student: Кутузов Иван Арсеньевич, group 3823Б1ФИ3
- Technology: SEQ | MPI
- Variant: 2

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

Вычисление среднего значения элементов вектора - типовая задача, которая легко адаптируется к параллельному исполнению. Идеальное ускорение соответствует количеству задействованных процессоров.

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

### Входные данные:

Дан вектор длины `N`, состоящий из элементов типа `double`.

### Выходные данные:

Значение типа `double`, равное среднему значению всех `N` элементов вектора.

### 3. Последовательный алгоритм

Решение "в лоб": суммируем элементы вектора, затем делим полученную сумму на число элементов в изначальном векторе.

```c++
  GetOutput() = 0.0;

  for (size_t i = 0; i < GetInput().size(); i++) {
    GetOutput() += GetInput()[i];
  }

  GetOutput() /= GetInput().size();
```

### 4. Параллельный алгоритм

Объявлеются две переменные типа `double`:
* `result` - на конец вычислений будет содержать среднее значение элеметов вектора на (всех процессах) - ответ на поставленную задачу;
* `global_sum` - будет содержать значение суммы всех элеметов вектора, частично вычисленное на отдельных процессах и собранное в итогувую сумму на процессе с рангом 0;

```c++
double result = 0.0;
double global_sum = 0.0;
```

Через MPI вызовы получаются значения ранга исполняемого процесса и числа всех процессов;

```c++
int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

int num_processes = 0;
MPI_Comm_size(MPI_COMM_WORLD, &num_processes);
```

На процессе с рангом 0 определяется количество элементов в векторе. С помощью вызова `MPI_Bcast` это значение рассылается по всем остальным процессам.

```c++
int total_elements_num = 0;
if (rank == 0) {
total_elements_num = static_cast<int>(GetInput().size());
}
MPI_Bcast(&total_elements_num, 1, MPI_INT, 0, MPI_COMM_WORLD);
```

Определяется размер "пачки" - число элеметов вектора, отдаваемых на обработку каждому процессу. Заметим, что `batch_size * num_processes` может быть меньше числа элеметов в векторе.

```c++
int batch_size = 0;
batch_size = total_elements_num / num_processes;
```

Если размер "пачки" больше нуля, то:
* Выделяем память под локальный буффер данных, который будет содержать данные, выданные процессу для обработки;
* При помощи вызова `MPI_Scatter` распределяем элементы изначального вектора по процессам поровну (по `batch_size` штук);
* Суммируем данные, лежащие в локальном буфере;
* При помощи вызова `MPI_Reduce` собираем и складываем частичные суммы со всех процессов на процессе с рангом 0 (значение помещается в `global_sum`);
* Здесь же освобождаем память, выделенную под локальный буфер.

```c++
if (batch_size > 0) {
double *local_buffer = new double[batch_size];
MPI_Scatter(GetInput().data(), batch_size, MPI_DOUBLE, local_buffer, batch_size, MPI_DOUBLE, 0, MPI_COMM_WORLD);

double sum = 0.0;
for (int i = 0; i < batch_size; i++) {
sum += local_buffer[i];
}

MPI_Reduce(&sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
delete[] local_buffer;
}
```

На процессе с рангом ноль обрабатываем элементы, не попавшие в рассылку, просто добавляя их к значению `global_sum`. Затем делим итоговую сумму на число элеметов в изначельном векторе, т.е. получаем ответ задачи - среднее значение элементов вектора. Полученный ответ записываем в `result`.

```c++
if (rank == 0) {
if (num_processes * batch_size < total_elements_num) {
for (int i = num_processes * batch_size; i < total_elements_num; i++) {
global_sum += GetInput()[i];
}
}

result = global_sum / static_cast<double>(total_elements_num);
}
```

Рассылаем значение `result` на все остальные процессы (чтобы проходили тесты корректности ответа).

```c++
MPI_Bcast(&result, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
GetOutput() = result;
```

Вызываем `MPI_Barrier`, чтобы убедится, что все процессы завершили свою работу.

```c++
MPI_Barrier(MPI_COMM_WORLD);
```

### 5. Среда экспериментов

- Hardware/OS: 14th gen Intel(R) Core(TM) i5-14600KF, 14 ядер (6P+8E ядер), 32 GB RAM, Windows 11 x64
- Toolchain: compiler, version, build type (Release/RelWithDebInfo)
- Cmake 3.28.3
- Компилятор: g++ (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
- Использовался Docker-контейнер.
- Режим сборки: Release.
- Data: Вектор длиной 50 000 000.

## 6. Результаты

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

Корректность работы проверена с помощью тестов Google Test на векторах размером: 1, 10, 1000 и 10000.

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

| Mode | Count | Time, s | Speedup | Efficiency |
| ---- | ----- | ------- | ------- | ---------- |
| seq | 1 | 0.0325 | 1.00 | N/A |
| mpi | 2 | 0.1479 | 0.22 | 11.0% |
| mpi | 4 | 0.1289 | 0.25 | 6.25% |
| mpi | 6 | 0.1329 | 0.24 | 4.0% |
| mpi | 8 | 0.1646 | 0.20 | 2.5% |
| mpi | 10 | 0.1629 | 0.20 | 2.0% |

График времени исполнения (в секундах) в зависомости от кол-ва процессов:

![](report_img/PerfTime.png)

График эффективности параллелизма в зависомости от кол-ва процессов:

![](report_img/PerfEfficiency.png)

## 7. Заключение

Операции рассылки данных по процессам и их последующий сбор требуют значительно больше времени, чем решение данной задачи последовательно, что привело к очень низкой эффективности параллелизма.

## 8. Ссылки

1. "Параллельное программирование для кластерных систем" ННГУ им. Лобачевского, ИИТММ
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions tasks/kutuzov_i_elem_vec_average/seq/include/ops_seq.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

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

namespace kutuzov_i_elem_vec_average {

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

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

} // namespace kutuzov_i_elem_vec_average
38 changes: 38 additions & 0 deletions tasks/kutuzov_i_elem_vec_average/seq/src/ops_seq.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "kutuzov_i_elem_vec_average/seq/include/ops_seq.hpp"

#include <vector>

#include "kutuzov_i_elem_vec_average/common/include/common.hpp"

namespace kutuzov_i_elem_vec_average {

KutuzovIElemVecAverageSEQ::KutuzovIElemVecAverageSEQ(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput() = 0.0;
}

bool KutuzovIElemVecAverageSEQ::ValidationImpl() {
return !GetInput().empty();
}

bool KutuzovIElemVecAverageSEQ::PreProcessingImpl() {
return true;
}

bool KutuzovIElemVecAverageSEQ::RunImpl() {
GetOutput() = 0.0;
for (double x : GetInput()) {
GetOutput() += x;
}

GetOutput() /= static_cast<double>(GetInput().size());

return true;
}

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

} // namespace kutuzov_i_elem_vec_average
7 changes: 7 additions & 0 deletions tasks/kutuzov_i_elem_vec_average/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tasks_type": "processes",
"tasks": {
"mpi": "enabled",
"seq": "enabled"
}
}
13 changes: 13 additions & 0 deletions tasks/kutuzov_i_elem_vec_average/tests/.clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
InheritParentConfig: true

Checks: >
-modernize-loop-convert,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-non-const-global-variables,
-misc-use-anonymous-namespace,
-modernize-use-std-print,
-modernize-type-traits
CheckOptions:
- key: readability-function-cognitive-complexity.Threshold
value: 50 # Relaxed for tests
Loading
Loading