diff --git a/tasks/sizov_d_string_mismatch_count/common/include/common.hpp b/tasks/sizov_d_string_mismatch_count/common/include/common.hpp new file mode 100644 index 0000000000..57887faf6b --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/common/include/common.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#include "task/include/task.hpp" + +namespace sizov_d_string_mismatch_count { + +using InType = std::tuple; +using OutType = int; +using TestType = std::string; +using BaseTask = ppc::task::Task; + +} // namespace sizov_d_string_mismatch_count diff --git a/tasks/sizov_d_string_mismatch_count/data/strings.txt b/tasks/sizov_d_string_mismatch_count/data/strings.txt new file mode 100644 index 0000000000..dcdda79dd6 --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/data/strings.txt @@ -0,0 +1,2 @@ +1234567890 +12345X7890 diff --git a/tasks/sizov_d_string_mismatch_count/info.json b/tasks/sizov_d_string_mismatch_count/info.json new file mode 100644 index 0000000000..ec55f60b3e --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Дмитрий", + "last_name": "Сизов", + "middle_name": "Игоревич", + "group_number": "3823Б1ФИ2", + "task_number": "1" + } +} diff --git a/tasks/sizov_d_string_mismatch_count/mpi/include/ops_mpi.hpp b/tasks/sizov_d_string_mismatch_count/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..ab88101e4f --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/mpi/include/ops_mpi.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "sizov_d_string_mismatch_count/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace sizov_d_string_mismatch_count { + +class SizovDStringMismatchCountMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + + explicit SizovDStringMismatchCountMPI(const InType &input); + + private: + std::string str_a_; + std::string str_b_; + int global_result_ = 0; + + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace sizov_d_string_mismatch_count diff --git a/tasks/sizov_d_string_mismatch_count/mpi/src/ops_mpi.cpp b/tasks/sizov_d_string_mismatch_count/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..1f6dae2db5 --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/mpi/src/ops_mpi.cpp @@ -0,0 +1,69 @@ +#include "sizov_d_string_mismatch_count/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include + +#include "sizov_d_string_mismatch_count/common/include/common.hpp" +#include "util/include/util.hpp" + +namespace sizov_d_string_mismatch_count { +SizovDStringMismatchCountMPI::SizovDStringMismatchCountMPI(const InType &input) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = input; + GetOutput() = 0; +} + +bool SizovDStringMismatchCountMPI::ValidationImpl() { + const auto &input = GetInput(); + const auto &a = std::get<0>(input); + const auto &b = std::get<1>(input); + return !a.empty() && a.size() == b.size(); +} + +bool SizovDStringMismatchCountMPI::PreProcessingImpl() { + const auto &input = GetInput(); + str_a_ = std::get<0>(input); + str_b_ = std::get<1>(input); + return true; +} + +bool SizovDStringMismatchCountMPI::RunImpl() { + int rank = 0; + int size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + const int total_size = static_cast(str_a_.size()); + if (total_size == 0) { + return true; + } + + const int base = total_size / size; + const int remainder = total_size % size; + const int start = (rank * base) + std::min(rank, remainder); + const int local_size = base + (rank < remainder ? 1 : 0); + + std::string_view local_a(str_a_.data() + start, local_size); + std::string_view local_b(str_b_.data() + start, local_size); + + int local_result = 0; + for (int i = 0; i < local_size; ++i) { + if (local_a[i] != local_b[i]) { + ++local_result; + } + } + + MPI_Reduce(&local_result, &global_result_, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); + MPI_Bcast(&global_result_, 1, MPI_INT, 0, MPI_COMM_WORLD); + + GetOutput() = global_result_; + return true; +} + +bool SizovDStringMismatchCountMPI::PostProcessingImpl() { + return true; +} +} // namespace sizov_d_string_mismatch_count diff --git a/tasks/sizov_d_string_mismatch_count/report.md b/tasks/sizov_d_string_mismatch_count/report.md new file mode 100644 index 0000000000..28bfa83d84 --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/report.md @@ -0,0 +1,44 @@ +### Отчёт по задаче: "Подсчет числа несовпадающих символов двух строк" +**Вариант:** №27 +**Выполнил студент группы 3823Б1ФИ2:** Сизов Дмитрий Игоревич +**Работу проверили:** преподаватель Нестеров Александр Юрьевич и преподаватель Оболенский Арсений Андреевич + +## Введение +В рамках данной работы реализован алгоритм подсчета несовпадающих символов в двух строках одинаковой длины, а также выполнена его параллелизация с использованием технологии MPI. Целью является ускорение вычислений за счет распределения работы между несколькими процессами. + +## Постановка задачи и формальное описание +Имеются две строки одинаковой длины. Требуется определить количество позиций, на которых символы в строках различаются. +Формально: пусть `A` и `B` — строки длины `n`. Необходимо вычислить: +`count = количество таких i, что A[i] ≠ B[i] для 0 ≤ i < n` + +## Пример +Строка A: `"abcde"` +Строка B: `"abzdf"` +Несовпадения: 2 (позиции 2 и 4) + +## Реализация разделена на модули +- `ops_seq.hpp` и `ops_seq.cpp` — реализация последовательного алгоритма +- `ops_mpi.hpp` и `ops_mpi.cpp` — реализация параллельного алгоритма +- `common.hpp` — общее определение входных/выходных типов + +## Описание последовательной версии +Алгоритм проходит по всем символам строк и сравнивает их попарно. Каждое несовпадение увеличивает счетчик. Итог — общее количество отличий. + +## Описание параллельной версии +Параллельный алгоритм с использованием MPI делит входные строки на блоки, равномерно распределяемые между процессами. Каждый процесс сравнивает символы в своем участке и подсчитывает количество несовпадений. Частичные результаты объединяются с помощью `MPI_Reduce`, а итоговая сумма сохраняется на процессе с рангом 0. +Такой подход позволяет ускорить вычисления за счёт распределённой обработки данных. + +## Тестирования разделены на модули +- `functional/main.cpp` — функциональные тесты +- `performance/main.cpp` — производительные тесты + +## Функциональное тестирование +Функциональные тесты предназначены для проверки корректности работы алгоритма на заранее известных входных данных. +В рамках теста используются строки, считываемые из файла `strings.txt`. Файл содержит две строки, которые сравниваются с помощью обеих реализаций. +Ожидаемый результат рассчитывается в рамках теста: для каждой позиции двух строк выполняется сравнение, и при обнаружении различий счётчик увеличивается. +Результат, возвращаемый алгоритмом, сопоставляется с ожидаемым значением. Это позволяет удостовериться в корректности как последовательной, так и MPI-реализации. + +## Тестирование производительности +В тестировании на производительность формируются две строки по 1 миллиону символов. +Первая строка полностью состоит из символов `'a'`, вторая — копия первой, но с заменой первых 10% символов на `'b'`. +Это гарантирует строго определённое число несовпадений (100 000), что позволяет точно проверить корректность результата. diff --git a/tasks/sizov_d_string_mismatch_count/seq/include/ops_seq.hpp b/tasks/sizov_d_string_mismatch_count/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..0c0931c577 --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/seq/include/ops_seq.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "sizov_d_string_mismatch_count/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace sizov_d_string_mismatch_count { + +class SizovDStringMismatchCountSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + + explicit SizovDStringMismatchCountSEQ(const InType &input); + + private: + std::string str_a_; + std::string str_b_; + int result_ = 0; + + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace sizov_d_string_mismatch_count diff --git a/tasks/sizov_d_string_mismatch_count/seq/src/ops_seq.cpp b/tasks/sizov_d_string_mismatch_count/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..0565fc377d --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/seq/src/ops_seq.cpp @@ -0,0 +1,47 @@ +#include "sizov_d_string_mismatch_count/seq/include/ops_seq.hpp" + +#include +#include + +#include "sizov_d_string_mismatch_count/common/include/common.hpp" +#include "util/include/util.hpp" + +namespace sizov_d_string_mismatch_count { + +SizovDStringMismatchCountSEQ::SizovDStringMismatchCountSEQ(const InType &input) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = input; + GetOutput() = 0; +} + +bool SizovDStringMismatchCountSEQ::ValidationImpl() { + const auto &input = GetInput(); + const auto &a = std::get<0>(input); + const auto &b = std::get<1>(input); + return !a.empty() && a.size() == b.size(); +} + +bool SizovDStringMismatchCountSEQ::PreProcessingImpl() { + const auto &input = GetInput(); + str_a_ = std::get<0>(input); + str_b_ = std::get<1>(input); + result_ = 0; + return true; +} + +bool SizovDStringMismatchCountSEQ::RunImpl() { + result_ = 0; + for (std::size_t i = 0; i < str_a_.size(); ++i) { + if (str_a_[i] != str_b_[i]) { + ++result_; + } + } + return true; +} + +bool SizovDStringMismatchCountSEQ::PostProcessingImpl() { + GetOutput() = result_; + return true; +} + +} // namespace sizov_d_string_mismatch_count diff --git a/tasks/sizov_d_string_mismatch_count/settings.json b/tasks/sizov_d_string_mismatch_count/settings.json new file mode 100644 index 0000000000..0dd4f2b205 --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} \ No newline at end of file diff --git a/tasks/sizov_d_string_mismatch_count/tests/functional/main.cpp b/tasks/sizov_d_string_mismatch_count/tests/functional/main.cpp new file mode 100644 index 0000000000..ff284ba431 --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/tests/functional/main.cpp @@ -0,0 +1,91 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sizov_d_string_mismatch_count/common/include/common.hpp" +#include "sizov_d_string_mismatch_count/mpi/include/ops_mpi.hpp" +#include "sizov_d_string_mismatch_count/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace sizov_d_string_mismatch_count { + +class SizovDRunFuncTestsStringMismatchCount : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return test_param; + } + + protected: + void SetUp() override { + std::string abs_path = ppc::util::GetAbsoluteTaskPath(PPC_ID_sizov_d_string_mismatch_count, "strings.txt"); + + std::ifstream file(abs_path); + if (!file.is_open()) { + throw std::runtime_error("Cannot open strings.txt"); + } + + std::string a; + std::string b; + std::getline(file, a); + std::getline(file, b); + file.close(); + + input_data_ = std::make_tuple(a, b); + is_valid_ = !a.empty() && a.size() == b.size(); + + expected_result_ = 0; + if (is_valid_) { + for (size_t i = 0; i < a.size(); ++i) { + if (a[i] != b[i]) { + ++expected_result_; + } + } + } + } + + InType GetTestInputData() override { + return input_data_; + } + + bool CheckTestOutputData(OutType &output_data) override { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (!is_valid_ || rank != 0) { + return true; + } + return output_data == expected_result_; + } + + private: + InType input_data_; + OutType expected_result_ = 0; + bool is_valid_ = true; +}; + +namespace { + +TEST_P(SizovDRunFuncTestsStringMismatchCount, CompareStringsFromFile) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = {"default"}; + +const auto kTaskList = std::tuple_cat(ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_sizov_d_string_mismatch_count), + ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_sizov_d_string_mismatch_count)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTaskList); +const auto kTestName = SizovDRunFuncTestsStringMismatchCount::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(CompareFromFile, SizovDRunFuncTestsStringMismatchCount, kGtestValues, kTestName); + +} // namespace +} // namespace sizov_d_string_mismatch_count diff --git a/tasks/sizov_d_string_mismatch_count/tests/performance/main.cpp b/tasks/sizov_d_string_mismatch_count/tests/performance/main.cpp new file mode 100644 index 0000000000..72c147ec60 --- /dev/null +++ b/tasks/sizov_d_string_mismatch_count/tests/performance/main.cpp @@ -0,0 +1,53 @@ +#include + +#include +#include +#include + +#include "sizov_d_string_mismatch_count/common/include/common.hpp" +#include "sizov_d_string_mismatch_count/mpi/include/ops_mpi.hpp" +#include "sizov_d_string_mismatch_count/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace sizov_d_string_mismatch_count { + +class SizovDRunPerfTestsStringMismatchCount : public ppc::util::BaseRunPerfTests { + void SetUp() override { + std::string a(1'000'000, 'a'); + std::string b = a; + + for (std::size_t i = 0; i < a.size() / 10; ++i) { + b[i] = 'b'; + } + + expected_result_ = static_cast(a.size() / 10); + input_data_ = std::make_tuple(std::move(a), std::move(b)); + } + + InType GetTestInputData() final { + return input_data_; + } + + bool CheckTestOutputData(OutType &output_data) final { + return output_data == expected_result_; + } + + private: + InType input_data_; + OutType expected_result_ = 0; +}; + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_sizov_d_string_mismatch_count); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); +const auto kPerfTestName = SizovDRunPerfTestsStringMismatchCount::CustomPerfTestName; + +TEST_P(SizovDRunPerfTestsStringMismatchCount, RunPerfModes) { + ExecuteTest(GetParam()); +} + +INSTANTIATE_TEST_SUITE_P(RunPerf, SizovDRunPerfTestsStringMismatchCount, kGtestValues, kPerfTestName); + +} // namespace sizov_d_string_mismatch_count