Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cc707ca
add str_order_check_task
Allor-maker Oct 24, 2025
c07fdf5
Merge branch 'master' of https://github.com/learning-process/ppc-2025…
Allor-maker Nov 6, 2025
04f5956
mpi&seq
Allor-maker Nov 6, 2025
13e5e53
add tests
Allor-maker Nov 6, 2025
b0a85da
fix test_type
Allor-maker Nov 6, 2025
71195d6
report
Allor-maker Nov 6, 2025
a12eb15
fix type error
Allor-maker Nov 10, 2025
f2ca181
fix type error
Allor-maker Nov 10, 2025
1e9ae7c
Merge branch 'master' of https://github.com/learning-process/ppc-2025…
Allor-maker Nov 10, 2025
b44e79e
fix size_t error
Allor-maker Nov 10, 2025
fcb902f
clang
Allor-maker Nov 10, 2025
6af32f5
-
Allor-maker Nov 10, 2025
a959011
fix test name
Allor-maker Nov 10, 2025
2e44bda
fix test name 2
Allor-maker Nov 10, 2025
86dd3ba
fix mpi logic
Allor-maker Nov 10, 2025
3ce3eb7
fix mpi
Allor-maker Nov 10, 2025
1fc95a4
add allgather
Allor-maker Nov 10, 2025
dd2e5a3
add perf test
Allor-maker Nov 10, 2025
3846a05
improve perf test
Allor-maker Nov 10, 2025
c96b8d3
clang-tidy
Allor-maker Nov 10, 2025
3ed5676
clang
Allor-maker Nov 10, 2025
12bf865
fix_func_tests
Allor-maker Nov 16, 2025
1a1cc5e
add report
Allor-maker Nov 16, 2025
c163ad2
fix cov
Allor-maker Nov 17, 2025
7b88eee
del unused picture
Allor-maker Nov 26, 2025
2223ddc
add data scatter
Allor-maker Nov 26, 2025
e30a39f
fix min len calc
Allor-maker Nov 26, 2025
258df7b
fix cognitive complexity clang
Allor-maker Dec 1, 2025
bfea5ab
fix clang error with static funcs
Allor-maker Dec 1, 2025
30f887f
fix report
Allor-maker Dec 1, 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
16 changes: 16 additions & 0 deletions tasks/smyshlaev_a_str_order_check/common/include/common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <string>
#include <tuple>
#include <utility>

#include "task/include/task.hpp"

namespace smyshlaev_a_str_order_check {

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

} // namespace smyshlaev_a_str_order_check
9 changes: 9 additions & 0 deletions tasks/smyshlaev_a_str_order_check/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ФИ2",
"task_number": "1"
}
}
24 changes: 24 additions & 0 deletions tasks/smyshlaev_a_str_order_check/mpi/include/ops_mpi.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

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

namespace smyshlaev_a_str_order_check {

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

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

bool RunSequential(int min_len, int len1, int len2);
};

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

#include <mpi.h>

#include <algorithm>
#include <string>
#include <vector>

#include "smyshlaev_a_str_order_check/common/include/common.hpp"

namespace smyshlaev_a_str_order_check {

namespace {

int CompareBuffers(const char *s1, const char *s2, int len) {
for (int i = 0; i < len; ++i) {
if (s1[i] < s2[i]) {
return -1;
}
if (s1[i] > s2[i]) {
return 1;
}
}
return 0;
}

int ResolveResult(int diff_res, int len1, int len2) {
if (diff_res != 0) {
return diff_res;
}
if (len1 < len2) {
return -1;
}
if (len1 > len2) {
return 1;
}
return 0;
}

void CalculateDistribution(int total_len, int proc_count, std::vector<int> &counts, std::vector<int> &offsets) {
const int chunk = total_len / proc_count;
const int remainder = total_len % proc_count;
int offset = 0;
for (int i = 0; i < proc_count; i++) {
counts[i] = chunk + (i < remainder ? 1 : 0);
offsets[i] = offset;
offset += counts[i];
}
}

} // namespace

SmyshlaevAStrOrderCheckMPI::SmyshlaevAStrOrderCheckMPI(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());

int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
GetInput() = in;
}

GetOutput() = 0;
}

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

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

bool SmyshlaevAStrOrderCheckMPI::RunSequential(int min_len, int len1, int len2) {
int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

int res = 0;
if (rank == 0) {
const auto &str1 = GetInput().first;
const auto &str2 = GetInput().second;
int cmp = CompareBuffers(str1.data(), str2.data(), min_len);
res = ResolveResult(cmp, len1, len2);
}

MPI_Bcast(&res, 1, MPI_INT, 0, MPI_COMM_WORLD);
GetOutput() = res;
return true;
}

bool SmyshlaevAStrOrderCheckMPI::RunImpl() {
int proc_count = 0;
int rank = 0;
MPI_Comm_size(MPI_COMM_WORLD, &proc_count);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

int len1 = 0;
int len2 = 0;

if (rank == 0) {
const auto &input_data = GetInput();
len1 = static_cast<int>(input_data.first.length());
len2 = static_cast<int>(input_data.second.length());
}

MPI_Bcast(&len1, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Bcast(&len2, 1, MPI_INT, 0, MPI_COMM_WORLD);

const int min_len = static_cast<int>(std::min(len1, len2));

if (proc_count > min_len) {
return RunSequential(min_len, len1, len2);
}

std::vector<int> sendcounts(proc_count);
std::vector<int> offsets(proc_count);
CalculateDistribution(min_len, proc_count, sendcounts, offsets);

int local_size = sendcounts[rank];
std::vector<char> local_str1(local_size);
std::vector<char> local_str2(local_size);

const char *s1_ptr = (rank == 0) ? GetInput().first.data() : nullptr;
const char *s2_ptr = (rank == 0) ? GetInput().second.data() : nullptr;

MPI_Scatterv(s1_ptr, sendcounts.data(), offsets.data(), MPI_CHAR, local_str1.data(), local_size, MPI_CHAR, 0,
MPI_COMM_WORLD);

MPI_Scatterv(s2_ptr, sendcounts.data(), offsets.data(), MPI_CHAR, local_str2.data(), local_size, MPI_CHAR, 0,
MPI_COMM_WORLD);

int local_result = CompareBuffers(local_str1.data(), local_str2.data(), local_size);

std::vector<int> all_results(proc_count);
MPI_Allgather(&local_result, 1, MPI_INT, all_results.data(), 1, MPI_INT, MPI_COMM_WORLD);

int global_result = 0;
for (int res : all_results) {
if (res != 0) {
global_result = res;
break;
}
}

GetOutput() = ResolveResult(global_result, len1, len2);

return true;
}

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

} // namespace smyshlaev_a_str_order_check
55 changes: 55 additions & 0 deletions tasks/smyshlaev_a_str_order_check/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# <Проверка лексикографической упорядоченности двух строк>

- Student: Смышляев Александр Павлович, group 3823Б1ФИ2
- Technology: SEQ | MPI
- Variant: 26

## 1. Introduction
**Мотивация:** Исследовать эффективность распараллеливания задачи сравнения строк через MPI, которая, в отличие от простых арифметических операций, имеет переменную вычислительную сложность.

**Проблема:** Производительность алгоритма сильно зависит от входных данных. В "лучшем случае" (различие в начале строк) затраты на коммуникацию могут превысить выгоду, в то время как в "худшем" (различие в конце) распараллеливание должно быть эффективным.

**Ожидаемый результат:** MPI-версия покажет значительное ускорение по сравнению с последовательной в "худшем случае", но эффективность будет снижаться с ростом числа процессов из-за накладных расходов.
## 2. Problem Statement
На вход поступают две строки произвольного размера. Задача — определить их лексикографический порядок.

## 3. Baseline Algorithm (Sequential)
Базовый (последовательный) алгоритм посимвольно сравнивает строки до первого различия. Если общая часть идентична (одна строка является префиксом другой), результат определяется сравнением длин строк.

## 4. Parallelization Scheme
1. **Распределение работы:** 0-й процесс получает входные данные и рассылает длины строк всем процессам через MPI_Bcast. Затем вычисляются смещения (offsets) и размеры порций (counts). С помощью операции MPI_Scatterv части строк рассылаются по процессам. Таким образом, память расходуется экономно, и каждый процесс хранит только свою часть данных.
2. **Локальное сравнение:** Каждый процесс выполняет сравнение символов только в своем диапазоне. Если различие найдено, он сохраняет локальный результат (`-1` или `1`), иначе — `0`.
3. **Сбор результатов:** С помощью операции `MPI_Allgather` каждый процесс отправляет свой локальный результат всем остальным. В итоге каждый процесс получает полный массив результатов от всех участников.
4. **Финализация:** Каждый процесс анализирует полученный массив. Итоговый результат — это первый ненулевой элемент в этом массиве (так как массив упорядочен по рангам, это гарантирует нахождение самого первого различия в строках). Если все результаты нулевые, итоговый ответ определяется сравнением длин строк.

## 5. Experimental Setup
- **Hardware/OS:** `Intel Core i7-1255U` (10 ядер, 12 потоков), `16GB RAM`, `Windows 11`
- **Toolchain:** `MSVC v19.38.33130 (Visual Studio 2022)`, `MS-MPI`, `Release`
- **Environment:** `PPC_NUM_PROC`
- **Data:** Две строки на 20'000'000 элементов, различающиеся в последнем символе ("худший случай" для вычислений).

## 6. Results and Discussion

### 6.1 Correctness
Корректность реализаций проверена функциональными тестами (GTest). Тесты покрывают все основные сценарии: равные строки, пустые строки, строки-префиксы и строки с различиями в разных позициях.

### 6.2 Performance
Результаты исследования производительности для различного числа процессов:

| Mode | Count | Time, s | Speedup | Efficiency |
|-------------|-------|---------|---------|------------|
| seq | 1 | 0.0243 | 1.00 | N/A |
| mpi | 2 | 0.0149 | 1.64 | 82.0% |
| mpi | 4 | 0.014 | 1.74 | 44.0% |
| mpi | 8 | 0.0153 | 1.60 | 20.0% |
Результаты показывают, что алгоритм эффективно масштабируется при переходе от 1 к 2 процессам (ускорение 1.64, эффективность 82%).

Однако при дальнейшем увеличении числа процессов (N=4) прирост производительности становится незначительным (ускорение выросло лишь до 1.74), а эффективность резко падает.

На 8 процессах наблюдается деградация производительности: время выполнения (0.01527 с) оказалось больше, чем на 4 процессах (0.01401 с). Это объясняется тем, что абсолютное время решения задачи очень мало (порядка 15-20 мс). В таких условиях накладные расходы на инициализацию коммуникаций, пересылку данных (MPI_Scatterv) и синхронизацию потоков начинают превышать выгоду от распараллеливания вычислений.
## 7. Conclusions
Реализация распределенного сравнения строк с использованием MPI показала свою высокую эффективность для сценариев с большой вычислительной нагрузкой. Использование MPI_Scatterv позволило выполнить эффективную декомпозицию данных, передав каждому процессу только необходимый фрагмент строки для независимой обработки. Однако, накладные расходы на пересылку данных и синхронизацию (Scatterv + Allgather) при большом числе процессов (8) начинают превышать выигрыш от параллельных вычислений, что видно по падению эффективности. В целом, MPI-подход является оправданным и эффективным решением для сравнения очень больших, преимущественно схожих строк.

## 8. References
1. Лекции по параллельному программированию ННГУ
2. Стандарт MPI
22 changes: 22 additions & 0 deletions tasks/smyshlaev_a_str_order_check/seq/include/ops_seq.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

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

namespace smyshlaev_a_str_order_check {

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

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

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

#include <algorithm>
#include <string>
#include <utility>

#include "smyshlaev_a_str_order_check/common/include/common.hpp"

namespace smyshlaev_a_str_order_check {

SmyshlaevAStrOrderCheckSEQ::SmyshlaevAStrOrderCheckSEQ(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput() = 0;
}

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

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

bool SmyshlaevAStrOrderCheckSEQ::RunImpl() {
const auto &input_data = GetInput();

const std::string &str1 = input_data.first;
const std::string &str2 = input_data.second;

int min_len = static_cast<int>(std::min(str1.length(), str2.length()));

for (int i = 0; i < min_len; ++i) {
if (str1[i] < str2[i]) {
GetOutput() = -1;
return true;
}
if (str1[i] > str2[i]) {
GetOutput() = 1;
return true;
}
}

if (str1.length() < str2.length()) {
GetOutput() = -1;
} else if (str1.length() > str2.length()) {
GetOutput() = 1;
} else {
GetOutput() = 0;
}

return true;
}

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

} // namespace smyshlaev_a_str_order_check
7 changes: 7 additions & 0 deletions tasks/smyshlaev_a_str_order_check/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/smyshlaev_a_str_order_check/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