Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
9ac1ff2
Add papulina_y_count_of_letters task implementation
ulillli Oct 29, 2025
2422cff
Trigger CI rebuild
ulillli Oct 29, 2025
2fa34c6
Trigger CI rebuild
ulillli Oct 29, 2025
0006422
some fixes for clang-format
ulillli Oct 30, 2025
651c68e
some fixes for clang-format
ulillli Oct 30, 2025
d86ba06
function countofletters is changed
ulillli Oct 30, 2025
ecb46f8
clang-format fixes
ulillli Oct 30, 2025
ef4a179
fixed some bugs in the mpi version
ulillli Oct 30, 2025
c765324
some fixes for clang-format
ulillli Oct 30, 2025
fa4d6d7
fixed some bugs in the mpi version of task
ulillli Oct 30, 2025
faa6c48
Merge remote-tracking branch 'upstream/master' into papulina_y_count_…
ulillli Nov 2, 2025
2569936
added cout for mpi
ulillli Nov 2, 2025
d0285a2
changed names of tests
ulillli Nov 2, 2025
150e316
fixed clang-format
ulillli Nov 2, 2025
eb56fe5
added some tests for checking results
ulillli Nov 2, 2025
36cea32
Merge remote-tracking branch 'upstream/master' into papulina_y_count_…
ulillli Nov 4, 2025
fba47d9
Merge remote-tracking branch 'upstream/master' into papulina_y_count_…
ulillli Nov 5, 2025
d35e56c
added some tests
ulillli Nov 5, 2025
0452e06
added output info
ulillli Nov 5, 2025
8e118e6
clang-format is fixed
ulillli Nov 5, 2025
a4a1309
added some fixes in MPI version
ulillli Nov 5, 2025
b37727c
fixed some bugs in MPI
ulillli Nov 5, 2025
2a9a2b9
new MPI version checking
ulillli Nov 5, 2025
77ba775
trying to use Barrier
ulillli Nov 5, 2025
1de2e37
some fixes for coverage
ulillli Nov 6, 2025
8f3ff92
[pre-commit] Update hooks versions
github-actions[bot] Nov 7, 2025
8a7dcc4
added fixes in function countofletters
ulillli Nov 7, 2025
e82dd8d
Merge remote-tracking branch 'upstream/master' into papulina_y_count_…
ulillli Nov 7, 2025
9f850a5
fixes for clang-tidy and romeved output info
ulillli Nov 7, 2025
57a9d79
merge with update pre-commit-hooks
ulillli Nov 7, 2025
07fe6f5
clang-format.......
ulillli Nov 7, 2025
f7559df
Merge remote-tracking branch 'origin/update-pre-commit-hooks' into pa…
ulillli Nov 7, 2025
b1f96a6
empty commit
ulillli Nov 7, 2025
68c267b
deleted strange file
ulillli Nov 8, 2025
b91dc5f
Merge remote-tracking branch 'upstream/master' into branch_for_fixes
ulillli Nov 17, 2025
6b2ca94
some fixes in mpi version and added generating of string
ulillli Nov 17, 2025
d683313
fixed clang-tidy
ulillli Nov 17, 2025
ad05246
deleted output info for perf tests
ulillli Nov 17, 2025
c5a188a
report is added
ulillli Nov 17, 2025
fdbdfe6
report is improved
ulillli Nov 17, 2025
e2bdd82
fixes in report
ulillli Nov 17, 2025
9475696
last fixes in report
ulillli Nov 17, 2025
9cb7e17
some fixes for clang-tidy
ulillli Nov 18, 2025
6436e84
comments deleted
ulillli Nov 23, 2025
30a8a32
checking on the empty string before resize
ulillli Nov 24, 2025
845442d
accidental file pre-commit-config.yaml corrections are deleted
ulillli Nov 24, 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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ repos:

# Ruff Python linter
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.14.0
rev: v0.14.4
hooks:
- id: ruff
args: [--fix]
Expand Down
15 changes: 15 additions & 0 deletions tasks/papulina_y_count_of_letters/common/include/common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <string>
#include <tuple>

#include "task/include/task.hpp"

namespace papulina_y_count_of_letters {

using InType = std::string;
using OutType = int;
using TestType = std::tuple<std::tuple<std::string, int>, std::string>; // патерн + длина строки
using BaseTask = ppc::task::Task<InType, OutType>;

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

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

namespace papulina_y_count_of_letters {

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

private:
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;
static int CountOfLetters(const char *s, const int &n);

int procNum_ = 0;
};

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

#include <mpi.h>

#include <algorithm>
#include <cctype>
#include <string>

#include "papulina_y_count_of_letters/common/include/common.hpp"

namespace papulina_y_count_of_letters {

PapulinaYCountOfLettersMPI::PapulinaYCountOfLettersMPI(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput() = 0;
MPI_Comm_size(MPI_COMM_WORLD, &procNum_);
}
int PapulinaYCountOfLettersMPI::CountOfLetters(const char *s, const int &n) {
int k = 0;
if (n <= 0) {
return 0;
}
for (int i = 0; i < n; i++) {
char c = s[i];
if (isalpha(c) != 0) {
k++;
}
}
return k;
}
bool PapulinaYCountOfLettersMPI::ValidationImpl() {
return procNum_ > 0;
}

Comment thread
allnes marked this conversation as resolved.
bool PapulinaYCountOfLettersMPI::PreProcessingImpl() {
return true;
}

bool PapulinaYCountOfLettersMPI::RunImpl() {
int proc_rank = 0;
int result = 0;
std::string part_of_string; // части строки, которая будет обрабатываться потоком
unsigned int len = 0; // предполагаемая длина обрабатываемой части
unsigned int remainder = 0; // остаток, если длина строки не кратна числу потоков
unsigned int true_len = 0; // реальная длина обрабатываемой части
MPI_Comm_rank(MPI_COMM_WORLD, &proc_rank);

if (proc_rank == 0) {
std::string s = GetInput();

Comment thread
allnes marked this conversation as resolved.
len = s.size() / procNum_;
remainder = s.size() % procNum_;
MPI_Bcast(&len, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD);
MPI_Bcast(&remainder, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD);

unsigned int begin_0 = (0 * len) + std::min(static_cast<unsigned int>(0), remainder);
unsigned int end_0 = ((0 + 1) * len) + std::min(static_cast<unsigned int>(0 + 1), remainder);
true_len = end_0 - begin_0;
part_of_string = (true_len > 0) ? s.substr(begin_0, true_len) : "";
Comment thread
allnes marked this conversation as resolved.

for (int i = 1; i < procNum_; i++) {
unsigned int begin = (i * len) + std::min(static_cast<unsigned int>(i), remainder);
unsigned int end = ((i + 1) * len) + std::min(static_cast<unsigned int>(i + 1), remainder);
unsigned int pre_true_len = end - begin; // предварительная длина обрабатываемой части

MPI_Send(&pre_true_len, 1, MPI_UNSIGNED, i, 0, MPI_COMM_WORLD);
if (end - begin != 0) {
MPI_Send(s.substr(begin, pre_true_len).data(), static_cast<int>(pre_true_len), MPI_CHAR, i, 1, MPI_COMM_WORLD);
} else {
MPI_Send("", 0, MPI_CHAR, i, 1, MPI_COMM_WORLD);
}
}
} else {
MPI_Bcast(&len, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD);
MPI_Bcast(&remainder, 1, MPI_UNSIGNED, 0, MPI_COMM_WORLD);
MPI_Recv(&true_len, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
if (true_len > 0) {
part_of_string.resize(true_len);
}
MPI_Recv(part_of_string.data(), static_cast<int>(true_len), MPI_CHAR, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
int local_result = CountOfLetters(part_of_string.data(), static_cast<int>(part_of_string.size()));
MPI_Reduce(&local_result, &result, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Bcast(&result, 1, MPI_INT, 0, MPI_COMM_WORLD);
GetOutput() = result;
MPI_Barrier(MPI_COMM_WORLD);
return true;
}

bool PapulinaYCountOfLettersMPI::PostProcessingImpl() {
return GetOutput() >= 0;
}

} // namespace papulina_y_count_of_letters
188 changes: 188 additions & 0 deletions tasks/papulina_y_count_of_letters/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# Подсчет числа буквенных символов в строке

- Student: Папулина Юлия Андреевна, group 3823Б1ФИ3
- Technology: SEQ | MPI
- Variant: 22

## 1. Введение
Задача подсчета буквенных символов в строке является одной из базовых операций в обработке текстовых данных и представляет собой идеальный пример для освоения принципов параллельного программирования. Сейчас, когда объемы обрабатываемой текстовой информации постоянно растут, эффективная реализация таких операций становится особенно важной. В рамках данной работы реализован MPI-алгоритм, демонстрирующий ключевые аспекты параллельного программирования: декомпозицию задачи, балансировку нагрузки и коллективные операции.

## 2. Постановка задачи
**Формальная постановка:** для заданной строки S длиной N подсчитать количество символов, являющихся буквами латинского алфавита.

**Входные данные:** строка S произвольной длины

**Выходные данные:** целое число - количество буквенных символов

**Ограничения:**
- Решение должно быть масштабируемым для различного числа процессов

## 3. Последовательная версия(Baseline)
Алгоритм последовательно проверяет каждый символ строки, определяя принадлежность к латинскому алфавиту с помощью функции int isalpha( int ch )
```cpp
int result = 0;
if (n <= 0) {
return 0;
}
for (int i = 0; i < n; i++) {
unsigned char c = s[i];
if (isalpha(c) != 0) {
result++;
}
}
return result;
```

## 4. Параллельная версия

### 4.1. Разделение данных
Используется блочное распределение данных с равномерным распределением остатка:
- базовый размер блока: len = length / num_processes
- остаток: remainder = length % num_processes
- процесс i получает: len + (i < remainder ? 1 : 0) символов (таким образом, все ramainder процессов получат + дополнительный символ к обработке)

### 4.2. Взаимодействие процессов
#### Процесс 0 (Координатор)
- читает входную строку
- рассылает базовый размер блока(len) и остаток(reminder) остальным потокам(MPI_Bcast)
- инициализирует свою часть обработки строки
- отправка данных(часть строки) для обработки другим процессам через for (MPI_Send)
- cобирает результаты (MPI_Reduce)

#### Процессы 1..N-1 (Рабочие процессы)
- получают параметры: размер блока, остаток(MPI_Bcast)
- получают свою часть строки для обработки(MPI_Recv)
- выполняют подсчет на своей части
- отправляют результат (MPI_Reduce)

### 4.3. Псевдокод алгоритма
```pseudocode
FUNCTION:
├─ INITIALIZATION
│ • Get process rank
│ • Initialize variables
├─ DATA DISTRIBUTION PHASE
│ │
│ ├─ IF rank = 0 (MASTER):
│ │ • Read input string
│ │ • Calculate base_len = length / procNum
│ │ • Calculate remainder = length % procNum
│ │ • Broadcast base_len, remainder
│ │ • Counting the processed part for master
│ │ • For each worker i:
│ │ - Calculate chunk boundaries
│ │ - Send chunk_size
│ │ - Send chunk_data
│ │
│ └─ ELSE (WORKER):
│ • Receive broadcast base_len, remainder
│ • Receive chunk_size
│ • Receive chunk_data
├─ COMPUTATION PHASE
│ • Each process: Count letters in local_data
├─ RESULT COLLECTION PHASE
│ • MPI_Reduce: SUM all local_count → process 0
│ • Broadcast result
│ • Set result
└─ RETURN success
```
## 5. Детали реализации

### 5.1. Файловая структура проекта
papulina_y_count_of_letters/
├── common/include/common.hpp
├── seq/include/ops_seq.hpp
├── seq/src/ops_seq.cpp
├── mpi/include/ops_mpi.hpp
├── mpi/src/ops_mpi.cpp
├── tests/functional/main.cpp
├── tests/performance/main.cpp
└── data/

### 5.2. Ключевые классы и функции

- `InType = std::string` - тип входных данных (строка)
- `OutType = int` - тип выходных данных (количество букв)
- `CountOfLetters(const char* s, const int& n)` - функция подсчета символов
- `RunImpl()` - основная логика последовательного выполнения
- `ValidationImpl()` - проверка валидности входных данных
- `PreProcessingImpl()` - подготовительные операции
- `PostProcessingImpl()` - завершающие операции
- `class PapulinaYRunFuncTestsProcesses` - параметрические функциональные тесты
- `class PapulinaYRunPerfTestsProcesses` - параметрические тесты производительности

### 5.3. Использование памяти

**SEQ версия:**
- хранит всю входную строку в памяти
- O(N) память, где N - длина строки
- минимальные накладные расходы(отсутствуют затраты на синхронизацию между потоками/процессами)

**MPI версия:**
- процесс 0: хранит всю строку + буферы для коммуникации
- рабочие процессы: хранят только свою часть строки
- дополнительная память для MPI буферов сообщений
- эффективное распределение памяти при больших N

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

**Hardware/OS:**
- **CPU:** Intel Core i5-11400H (6 cores, 12 threads, 2.70 GHz base frequency)
- **RAM:** 16.0 GB DDR4
- **Storage:** SSD 512 GB
- **OS:** Windows 10

**Toolchain:**
- **Compiler:** Microsoft Visual C++ 2019 (MSVC 19.29.30153)
- **MPI Implementation:** Microsoft MPI Version10.1.12498.52
- **Build System:** CMake 3.30.3
- **Build Type:** Release

**Environment:**
- **PPC_NUM_PROC:** 1, 2, 4, 6

**Data:** строка из 40 000 000 латинских символов

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

### 7.1 Корректность
Корректность реализации была проверена через комплексную систему тестирования, включающую:
- 20 функциональных тестов(пустая строка, строка из одного символа, строка с генерированными символами)
- тест на производительность на стабильных данных

**Структура параметров теста:**
- строка (generate - если тест на генерацию)
- ожидаемый результат
- название теста(его номер)

### 7.2 Производительность
Время выполнения для строки длиной 40 000 000 символов:

| Mode | Count | Time, s | Speedup | Efficiency |
|-------------|-------|---------|---------|------------|
| seq | 1 | 0.0951 | 1.00 | N/A |
| mpi | 2 | 0.0937 | 1.01 | 50.5% |
| mpi | 3 | 0.0718 | 1.32 | 44.0% |
| mpi | 4 | 0.0737 | 1.29 | 32.3% |
| mpi | 6 | 0.0778 | 1.22 | 20.3% |

**Анализ результатов:**
- минимальное ускорение: наблюдается незначительное ускорение (1.01-1.32x) даже при использовании нескольких процессов
- низкая эффективность: эффективность варьируется от 20.3% до 50.5%, что указывает на значительные накладные расходы
- оптимальная конфигурация: максимальное ускорение достигается при 3 процессах (1.32x)

**Анализ узких мест:**
- коммуникационные затраты: время передачи данных между процессами превышает время вычислений для данного объема данных
- неидеальное распределение: алгоритм распределения данных создает дополнительную нагрузку

## 8. Заключение
В результате работы в учебных целях разработаны последовательная (SEQ) и параллельная (MPI) версии программы, подсчитывающей число латинских символов в строке. Несмотря на ограниченную практическую эффективность для конкретной задачи подсчета символов, реализация успешно демонстрирует принципы распределенных вычислений и может служить основой для более сложных алгоритмов обработки текста.

## 9. Источники
1. Microsoft MPI : документация [Электронный ресурс] // Microsoft Learn. – URL: https://learn.microsoft.com/ru-ru/message-passing-interface/microsoft-mpi (дата обращения: 03.11.2025).
2. Сысоев А. В. Курс лекций по параллельному программированию
23 changes: 23 additions & 0 deletions tasks/papulina_y_count_of_letters/seq/include/ops_seq.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

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

namespace papulina_y_count_of_letters {

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

private:
static int CountOfLetters(const char *s, const int &n);
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;
};

} // namespace papulina_y_count_of_letters
Loading
Loading