Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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/akimov_i_words_string_count/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 <vector>

#include "task/include/task.hpp"

namespace akimov_i_words_string_count {

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

} // namespace akimov_i_words_string_count
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions tasks/akimov_i_words_string_count/data/words.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
This is a sample line with leading and trailing spaces.
Multiple spaces here
Tabs and spaces mixed
Newlines
are
also
handled
Punctuation! Isn't it counted? Yes.
Русские слова тоже считаются

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

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

namespace akimov_i_words_string_count {

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

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

InType input_buffer_;
InType local_buffer_;
int local_space_count_ = 0;
int global_space_count_ = 0;
int word_count_ = 0;
};
} // namespace akimov_i_words_string_count
124 changes: 124 additions & 0 deletions tasks/akimov_i_words_string_count/mpi/src/ops_mpi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "akimov_i_words_string_count/mpi/include/ops_mpi.hpp"

#include <mpi.h>

#include <algorithm>
#include <cstring>
#include <numeric>
#include <vector>

#include "akimov_i_words_string_count/common/include/common.hpp"
#include "util/include/util.hpp"

namespace akimov_i_words_string_count {

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

bool AkimovIWordsStringCountMPI::ValidationImpl() {
int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
return (!GetInput().empty()) && (GetOutput() == 0);
}
return true;
}

bool AkimovIWordsStringCountMPI::PreProcessingImpl() {
int rank = 0;
int size = 1;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);

local_space_count_ = 0;
global_space_count_ = 0;
word_count_ = 0;
local_buffer_.clear();
input_buffer_.clear();

std::size_t total = 0;
if (rank == 0) {
input_buffer_ = GetInput();
total = input_buffer_.size();
}

int base = 0;
int remainder = 0;
if (rank == 0) {
base = static_cast<int>(total / static_cast<std::size_t>(size));
remainder = static_cast<int>(total % static_cast<std::size_t>(size));
}
MPI_Bcast(&base, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Bcast(&remainder, 1, MPI_INT, 0, MPI_COMM_WORLD);

int my_count = base + ((rank < remainder) ? 1 : 0);
local_buffer_.resize(my_count);

if (rank == 0) {
std::size_t offset = 0;
for (int proc = 0; proc < size; ++proc) {
int count = base + ((proc < remainder) ? 1 : 0);
if (count == 0) {
} else if (proc == 0) {
std::memcpy(local_buffer_.data(), input_buffer_.data() + offset, static_cast<std::size_t>(count));
} else {
MPI_Send(input_buffer_.data() + offset, count, MPI_CHAR, proc, 0, MPI_COMM_WORLD);
}
offset += static_cast<std::size_t>(count);
}
} else {
if (my_count > 0) {
MPI_Recv(local_buffer_.data(), my_count, MPI_CHAR, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
} else {
}
}

return true;
}

bool AkimovIWordsStringCountMPI::RunImpl() {
local_space_count_ = 0;
for (char c : local_buffer_) {
if (c == ' ') {
++local_space_count_;
}
}

MPI_Reduce(&local_space_count_, &global_space_count_, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);

int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
if (GetInput().empty()) {
word_count_ = 0;
} else {
const InType &buf = GetInput();
bool in_word = false;
word_count_ = 0;
for (char c : buf) {
if (c != ' ' && !in_word) {
in_word = true;
++word_count_;
} else if (c == ' ' && in_word) {
in_word = false;
}
}
}
}

return true;
}

bool AkimovIWordsStringCountMPI::PostProcessingImpl() {
int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
GetOutput() = word_count_;
}
return true;
}

} // namespace akimov_i_words_string_count
50 changes: 50 additions & 0 deletions tasks/akimov_i_words_string_count/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
### Отчёт по задаче: "Подсчёт числа слов в строке"
**Вариант:** №24
**Выполнил студент группы 3823Б1ФИ2:** Акимов Илья Александрович
**Работу проверили:** преподаватель Нестеров Александр Юрьевич и преподаватель Оболенский Арсений Андреевич

## Введение
В данной работе реализован алгоритм подсчёта количества слов в строке. Алгоритм представлен в двух вариантах: последовательная реализация (SEQ) и параллельная реализация с использованием технологии MPI. Цель работы — корректно подсчитать количество слов в строке и ускорить вычисления для больших объемов данных за счёт параллельной обработки.

## Постановка задачи и формальное описание
Необходимо определить количество слов в строке. Слово — последовательность непробельных символов, разделённая пробелами, табуляциями или переводами строк.

Формально: пусть `S` — входная строка длины `n`. Необходимо вычислить:
`word_count = количество таких последовательностей символов, которые отделены пробелами и другими разделителями`

## Пример
Строка: `" This is an example "`
Слова: `"This"`, `"is"`, `"an"`, `"example"`
Количество слов: 4

## Реализация разделена на модули
- `ops_seq.hpp` и `ops_seq.cpp` — последовательная реализация подсчёта слов
- `ops_mpi.hpp` и `ops_mpi.cpp` — параллельная реализация с использованием MPI
- `common.hpp` — определения типов данных и базовый класс задачи
- `functional/main.cpp` — функциональные тесты
- `performance/main.cpp` — тесты производительности

## Описание последовательной версии
Последовательная версия (`AkimovIWordsStringCountSEQ`) сохраняет строку во внутренний буфер и проходит по всем символам. Алгоритм подсчёта слов работает следующим образом:

1. Для каждого символа проверяется, является ли он пробелом.
2. Если текущий символ — начало слова (не пробел, предыдущий был пробел или начало строки), счётчик слов увеличивается.
3. Итоговый результат сохраняется в `GetOutput()`.

## Описание параллельной версии
Параллельная версия (`AkimovIWordsStringCountMPI`) распределяет входную строку между процессами MPI:

1. Процесс с рангом 0 делит строку на равные блоки для всех процессов.
2. Каждый процесс получает свой блок и подсчитывает количество пробелов в нём.
3. Частичные результаты объединяются с помощью `MPI_Reduce`.
4. На процессе с рангом 0 выполняется финальный подсчёт слов по всей строке, корректно учитывая границы блоков.

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

## Тестирования

### Функциональное тестирование
Функциональные тесты проверяют корректность работы алгоритмов на заранее известных данных. Тесты считывают строки из файла `words.txt`, заменяя табуляции и переводы строк на пробелы. Для каждой позиции строки алгоритм определяет, начинается ли слово, и увеличивает счётчик.

### Тестирование производительности
Для тестирования производительности формируется строка из 1 миллиона слов `"word"`, разделённых пробелами. Это гарантирует предсказуемый результат (1 000 000 слов), позволяя точно проверять корректность работы как последовательной, так и параллельной версий.
26 changes: 26 additions & 0 deletions tasks/akimov_i_words_string_count/seq/include/ops_seq.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

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

namespace akimov_i_words_string_count {

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

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

InType input_buffer_;
int word_count_ = 0;
int space_count_ = 0;
};

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

#include <algorithm>
#include <numeric>

#include "akimov_i_words_string_count/common/include/common.hpp"
#include "util/include/util.hpp"

namespace akimov_i_words_string_count {

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

bool AkimovIWordsStringCountSEQ::ValidationImpl() {
return (!GetInput().empty()) && (GetOutput() == 0);
}

bool AkimovIWordsStringCountSEQ::PreProcessingImpl() {
input_buffer_ = GetInput();
word_count_ = 0;
space_count_ = 0;
return true;
}

bool AkimovIWordsStringCountSEQ::RunImpl() {
if (input_buffer_.empty()) {
return false;
}

for (char c : input_buffer_) {
if (c == ' ') {
++space_count_;
}
}

bool in_word = false;
word_count_ = 0;
for (char c : input_buffer_) {
if (c != ' ' && !in_word) {
in_word = true;
++word_count_;
} else if (c == ' ' && in_word) {
in_word = false;
}
}

return true;
}

bool AkimovIWordsStringCountSEQ::PostProcessingImpl() {
GetOutput() = word_count_;
return true;
}

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