From 70ac74984fea3e0b39a25580412831df0f26741e Mon Sep 17 00:00:00 2001 From: TemikGOD Date: Sun, 1 Mar 2026 18:15:22 +0000 Subject: [PATCH 1/3] quick_sort_simple_merging added --- .../common/include/common.hpp | 16 ++ .../info.json | 9 + .../mpi/include/ops_mpi.hpp | 27 +++ .../mpi/src/ops_mpi.cpp | 199 ++++++++++++++++++ .../report.md | 128 +++++++++++ .../seq/include/ops_seq.hpp | 27 +++ .../seq/src/ops_seq.cpp | 149 +++++++++++++ .../settings.json | 7 + .../tests/.clang-tidy | 13 ++ .../tests/functional/main.cpp | 107 ++++++++++ .../tests/performance/main.cpp | 55 +++++ 11 files changed, 737 insertions(+) create mode 100644 tasks/denisov_a_quick_sort_simple_merging/common/include/common.hpp create mode 100644 tasks/denisov_a_quick_sort_simple_merging/info.json create mode 100644 tasks/denisov_a_quick_sort_simple_merging/mpi/include/ops_mpi.hpp create mode 100644 tasks/denisov_a_quick_sort_simple_merging/mpi/src/ops_mpi.cpp create mode 100644 tasks/denisov_a_quick_sort_simple_merging/report.md create mode 100644 tasks/denisov_a_quick_sort_simple_merging/seq/include/ops_seq.hpp create mode 100644 tasks/denisov_a_quick_sort_simple_merging/seq/src/ops_seq.cpp create mode 100644 tasks/denisov_a_quick_sort_simple_merging/settings.json create mode 100644 tasks/denisov_a_quick_sort_simple_merging/tests/.clang-tidy create mode 100644 tasks/denisov_a_quick_sort_simple_merging/tests/functional/main.cpp create mode 100644 tasks/denisov_a_quick_sort_simple_merging/tests/performance/main.cpp diff --git a/tasks/denisov_a_quick_sort_simple_merging/common/include/common.hpp b/tasks/denisov_a_quick_sort_simple_merging/common/include/common.hpp new file mode 100644 index 00000000..3884ff11 --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/common/include/common.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +#include "task/include/task.hpp" + +namespace denisov_a_quick_sort_simple_merging { + +using InType = std::vector; +using OutType = std::vector; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace denisov_a_quick_sort_simple_merging diff --git a/tasks/denisov_a_quick_sort_simple_merging/info.json b/tasks/denisov_a_quick_sort_simple_merging/info.json new file mode 100644 index 00000000..5aadc92d --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Артём", + "group_number": "3823Б1ПР4", + "last_name": "Денисов", + "middle_name": "Андреевич", + "task_number": "3" + } +} diff --git a/tasks/denisov_a_quick_sort_simple_merging/mpi/include/ops_mpi.hpp b/tasks/denisov_a_quick_sort_simple_merging/mpi/include/ops_mpi.hpp new file mode 100644 index 00000000..3fd54634 --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/mpi/include/ops_mpi.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "denisov_a_quick_sort_simple_merging/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace denisov_a_quick_sort_simple_merging { + +class DenisovAQuickSortMergeMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit DenisovAQuickSortMergeMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + static void QuickSort(std::vector &data, int begin, int end); + static std::vector Merge(const std::vector &left_block, const std::vector &right_block); +}; + +} // namespace denisov_a_quick_sort_simple_merging diff --git a/tasks/denisov_a_quick_sort_simple_merging/mpi/src/ops_mpi.cpp b/tasks/denisov_a_quick_sort_simple_merging/mpi/src/ops_mpi.cpp new file mode 100644 index 00000000..3ae912d7 --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/mpi/src/ops_mpi.cpp @@ -0,0 +1,199 @@ +#include "denisov_a_quick_sort_simple_merging/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include + +#include "denisov_a_quick_sort_simple_merging/common/include/common.hpp" + +namespace denisov_a_quick_sort_simple_merging { + +DenisovAQuickSortMergeMPI::DenisovAQuickSortMergeMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput().clear(); +} + +bool DenisovAQuickSortMergeMPI::ValidationImpl() { + return GetOutput().empty(); +} + +bool DenisovAQuickSortMergeMPI::PreProcessingImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + auto &out = GetOutput(); + const auto &in = GetInput(); + + out = in; + + if (rank == 0) { + if (out.size() != in.size()) { + return false; + } + for (size_t i = 0; i < out.size(); i++) { + if (out[i] != in[i]) { + return false; + } + } + } + + return true; +} + +bool DenisovAQuickSortMergeMPI::RunImpl() { + int rank = 0; + int world = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &world); + + auto &global = GetOutput(); + + if (global.empty()) { + MPI_Barrier(MPI_COMM_WORLD); + return true; + } + + int total = static_cast(global.size()); + std::vector parts(world); + std::vector shifts(world); + + int chunk = total / world; + int extra = total % world; + + for (int i = 0; i < world; i++) { + parts[i] = chunk + (i < extra ? 1 : 0); + shifts[i] = (i == 0 ? 0 : shifts[i - 1] + parts[i - 1]); + } + + std::vector local(parts[rank]); + + MPI_Scatterv(rank == 0 ? global.data() : nullptr, parts.data(), shifts.data(), MPI_INT, local.data(), parts[rank], + MPI_INT, 0, MPI_COMM_WORLD); + + if (!local.empty()) { + QuickSort(local, 0, static_cast(local.size()) - 1); + } + + if (rank == 0) { + global = local; + + for (int i = 1; i < world; i++) { + std::vector recv(parts[i]); + MPI_Recv(recv.data(), parts[i], MPI_INT, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + global = Merge(global, recv); + } + + for (int i = 1; i < world; i++) { + MPI_Send(global.data(), static_cast(global.size()), MPI_INT, i, 1, MPI_COMM_WORLD); + } + + } else { + MPI_Send(local.data(), static_cast(local.size()), MPI_INT, 0, 0, MPI_COMM_WORLD); + + MPI_Status st; + int recv_len = 0; + MPI_Probe(0, 1, MPI_COMM_WORLD, &st); + MPI_Get_count(&st, MPI_INT, &recv_len); + + global.resize(recv_len); + MPI_Recv(global.data(), recv_len, MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + } + + MPI_Barrier(MPI_COMM_WORLD); + return true; +} + +bool DenisovAQuickSortMergeMPI::PostProcessingImpl() { + const auto &out = GetOutput(); + const auto &in = GetInput(); + + if (out.empty()) { + return in.empty(); + } + + if (!std::ranges::is_sorted(out)) { + return false; + } + + if (out.size() != in.size()) { + return false; + } + + int64_t sum_in = 0; + int64_t sum_out = 0; + + for (int v : in) { + sum_in += v; + } + for (int v : out) { + sum_out += v; + } + + return sum_in == sum_out; +} + +// NOLINTNEXTLINE(misc-no-recursion) +void DenisovAQuickSortMergeMPI::QuickSort(std::vector &data, int begin, int end) { + if (begin >= end) { + return; + } + + int pivot = data[(begin + end) / 2]; + int i = begin; + int j = end; + + while (i <= j) { + while (data[i] < pivot) { + i++; + } + while (data[j] > pivot) { + j--; + } + + if (i <= j) { + std::swap(data[i], data[j]); + i++; + j--; + } + } + + QuickSort(data, begin, j); + QuickSort(data, i, end); +} + +std::vector DenisovAQuickSortMergeMPI::Merge(const std::vector &left_block, + const std::vector &right_block) { + std::vector res; + res.reserve(left_block.size() + right_block.size()); + + size_t i = 0; + size_t j = 0; + + while (i < left_block.size() && j < right_block.size()) { + if (left_block[i] <= right_block[j]) { + res.push_back(left_block[i]); + i++; + } else { + res.push_back(right_block[j]); + j++; + } + } + + while (i < left_block.size()) { + res.push_back(left_block[i]); + i++; + } + + while (j < right_block.size()) { + res.push_back(right_block[j]); + j++; + } + + return res; +} + +} // namespace denisov_a_quick_sort_simple_merging diff --git a/tasks/denisov_a_quick_sort_simple_merging/report.md b/tasks/denisov_a_quick_sort_simple_merging/report.md new file mode 100644 index 00000000..6cb1abe4 --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/report.md @@ -0,0 +1,128 @@ +# Быстрая сортировка с простым слиянием + +- Студент: Денисов Артём Андреевич, группа 3823Б1ПР4 +- Технология: SEQ + MPI +- Вариант: 14 + +## 1. Введение + +Быстрая сортировка — один из наиболее эффективных алгоритмов сортировки с временной сложностью O(N logN) в среднем случае. + +Цель работы: реализация параллельной версии быстрой сортировки с использованием технологии MPI и сравнение ее производительности с последовательной реализацией. + +## 2. Постановка задачи + +Требуется реализовать алгоритм быстрой сортировки с простым слиянием для сортировки массива целых чисел. Алгоритм должен быть реализован в двух вариантах: последовательном (SEQ) и параллельном (MPI). + +**Входные данные:** Массив целых чисел произвольного размера. + +**Выходные данные:** Отсортированный по возрастанию массив целых чисел. + +## 3. Базовый алгоритм (последовательный) + +1. Разделение массива на две части примерно равного размера +2. Рекурсивная сортировка каждой части с помощью алгоритма быстрой сортировки +3. Слияние двух отсортированных частей в один отсортированный массив + +**Сложность:** O(N logN) + +## 4. Схема распараллеливания + +Параллельная реализация использует технологию MPI для распределения вычислений между несколькими процессами. Алгоритм состоит из следующих этапов: + +1. Распределение данных: процесс с рангом 0 получает исходный массив и распределяет его между всеми процессами с помощью MPI_Scatterv. Размер массива делится на количество процессов, остаток распределяется между первыми процессами. + +2. Локальная сортировка: каждый процесс получает свою часть массива и сортирует ее локально с помощью алгоритма быстрой сортировки. + +3. Сбор результатов: процессы с рангом больше 0 отправляют отсортированные части процессу с рангом 0 с помощью MPI_Send. + +4. Слияние: процесс 0 последовательно сливает полученные отсортированные части в один массив с помощью функции Merge. + +5. Распространение результата: процесс 0 отправляет финальный отсортированный массив всем остальным процессам с помощью MPI_Send, а остальные процессы получают его с помощью MPI_Recv. + +Использование MPI_Scatterv позволяет равномерно распределить данные между процессами, даже если размер массива не делится нацело на количество процессов. Этап слияния выполняется последовательно на процессе 0, что является узким местом при большом количестве процессов. + +Схема распараллеливания включает следующие шаги коммуникации: + +1. Распределение данных (MPI_Scatterv): процесс 0 распределяет исходный массив между всеми процессами +2. Локальная обработка: каждый процесс сортирует свою часть независимо +3. Сбор данных (MPI_Send/MPI_Recv): процессы отправляют отсортированные части процессу 0 +4. Слияние: процесс 0 последовательно объединяет части +5. Распространение результата (MPI_Send/MPI_Recv): процесс 0 отправляет финальный массив всем процессам + +Топология коммуникации представляет собой звезду с процессом 0 в центре. Все процессы обмениваются данными только с процессом 0, что упрощает реализацию, но создает узкое место при большом количестве процессов. + +## 5. Детали реализации + +### Структура кода +- common/include/common.hpp - общие типы данных (InType, OutType) +- seq/include/ops_seq.hpp, seq/src/ops_seq.cpp - последовательная реализация +- mpi/include/ops_mpi.hpp, mpi/src/ops_mpi.cpp - параллельная реализация MPI +- tests/functional/main.cpp - функциональные тесты +- tests/performance/main.cpp - тесты производительности + +### Главные функции: +- QuickSort - рекурсивная функция быстрой сортировки +- Merge - функция слияния двух отсортированных массивов +- ValidationImpl - проверка корректности входных данных +- PreProcessingImpl - подготовка данных +- RunImpl - основная логика сортировки +- PostProcessingImpl - проверка результата + + +## 6. Экспериментальная установка + +**Hardware/OS/Toolchain** + +| Параметр | Значение | +| ---------- | ------------------------------------------------------- | +| CPU | AMD Ryzen 7 5800H (8 ядер / 16 потоков) @ 3.201 GHz | +| RAM | 16 ГБ | +| ОС | Windows 11 + WSL2 (Ubuntu 24.04.3 LTS) | +| Ядро | 6.6.87.2-microsoft-standard-WSL2 | +| Компилятор | GCC (g++) | +| MPI | Open MPI | +| Тип сборки | Release | + +**Окружение:** + +- Переменные, влияющие на запуск: + - `PPC_NUM_PROC` задаёт число MPI‑процессов при тестах (например, 1,2,4,8) +## 7. Результаты + +### 7.1 Корректность + +Все функциональные тесты проходят успешно. + +### 7.2 Производительность + +| Mode | Proc count | Time, s | Speedup | Efficiency | +|-----------------|------------|----------|---------|------------| +| seq (pipeline) | 1 | 0.127 | 1.00 | — | +| seq (task_run) | 1 | 0.120 | 1.00 | — | +| mpi (pipeline) | 2 | 0.083 | 1.53 | 77% | +| mpi (task_run) | 2 | 0.077 | 1.56 | 78% | +| mpi (pipeline) | 4 | 0.121 | 1.05 | 26% | +| mpi (task_run) | 4 | 0.091 | 1.32 | 33% | +| mpi (pipeline) | 8 | 0.176 | 0.72 | 9% | +| mpi (task_run) | 8 | 0.153 | 0.78 | 10% | + +- Параллельная реализация в данном случае показывает эффективность только на малом количестве процессов, лучше всего при 2 +- Накладные расходы на коммуникацию (через MPI_Scatterv, MPI_Send, MPI_Recv) становятся преобладающими при большем количестве процессов + +## 8. Выводы + +В ходе работы была создана параллельная версия алгоритма быстрой сортировки с простым слиянием, основанная на использовании MPI, а также реализован её последовательный аналог. Оба варианта корректно функционируют при различном числе процессов, а параллельная реализация демонстрирует заметное ускорение по сравнению с последовательной. +### Основные результаты: +- разработаны и протестированы последовательная и параллельная версии алгоритма; +- функциональные тесты охватывают ключевые сценарии и обеспечивают проверку корректности; +- подготовлены производительные тесты, позволяющие оценить работу алгоритма. +### Возникшие трудности: +- при увеличении числа процессов возрастает стоимость коммуникаций, что снижает масштабируемость; +- при обработке очень больших массивов может потребоваться дополнительная оптимизация этапа слияния, чтобы избежать потерь в производительности. + + +## 9. Ссылки + +1. Сысоев А. В. Лекции курса «Параллельное программирование для кластерных систем» +2. Документация лабораторных работ — \ No newline at end of file diff --git a/tasks/denisov_a_quick_sort_simple_merging/seq/include/ops_seq.hpp b/tasks/denisov_a_quick_sort_simple_merging/seq/include/ops_seq.hpp new file mode 100644 index 00000000..612551ac --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/seq/include/ops_seq.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "denisov_a_quick_sort_simple_merging/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace denisov_a_quick_sort_simple_merging { + +class DenisovAQuickSortMergeSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit DenisovAQuickSortMergeSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + static void QuickSort(std::vector &data, int begin, int end); + static std::vector Merge(const std::vector &left_block, const std::vector &right_block); +}; + +} // namespace denisov_a_quick_sort_simple_merging diff --git a/tasks/denisov_a_quick_sort_simple_merging/seq/src/ops_seq.cpp b/tasks/denisov_a_quick_sort_simple_merging/seq/src/ops_seq.cpp new file mode 100644 index 00000000..dab7625c --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/seq/src/ops_seq.cpp @@ -0,0 +1,149 @@ +#include "denisov_a_quick_sort_simple_merging/seq/include/ops_seq.hpp" + +#include +#include +#include +#include + +#include "denisov_a_quick_sort_simple_merging/common/include/common.hpp" + +namespace denisov_a_quick_sort_simple_merging { + +DenisovAQuickSortMergeSEQ::DenisovAQuickSortMergeSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = {}; +} + +bool DenisovAQuickSortMergeSEQ::ValidationImpl() { + return GetOutput().empty(); +} + +bool DenisovAQuickSortMergeSEQ::PreProcessingImpl() { + const auto &src = GetInput(); + auto &dst = GetOutput(); + + dst = src; + + if (dst.size() != src.size()) { + return false; + } + + for (size_t idx = 0; idx < dst.size(); idx++) { + if (dst[idx] != src[idx]) { + return false; + } + } + + return true; +} + +bool DenisovAQuickSortMergeSEQ::RunImpl() { + auto &arr = GetOutput(); + + if (arr.empty() || arr.size() == 1) { + return true; + } + + size_t half = arr.size() / 2; + + std::vector left_part(arr.begin(), arr.begin() + static_cast(half)); + std::vector right_part(arr.begin() + static_cast(half), arr.end()); + + QuickSort(left_part, 0, static_cast(left_part.size()) - 1); + QuickSort(right_part, 0, static_cast(right_part.size()) - 1); + + arr = Merge(left_part, right_part); + return true; +} + +bool DenisovAQuickSortMergeSEQ::PostProcessingImpl() { + const auto &out = GetOutput(); + const auto &in = GetInput(); + + if (out.empty()) { + return in.empty(); + } + + if (!std::ranges::is_sorted(out)) { + return false; + } + + if (out.size() != in.size()) { + return false; + } + + int64_t sum_in = 0; + int64_t sum_out = 0; + + for (int v : in) { + sum_in += v; + } + for (int v : out) { + sum_out += v; + } + + return sum_in == sum_out; +} + +// NOLINTNEXTLINE(misc-no-recursion) +void DenisovAQuickSortMergeSEQ::QuickSort(std::vector &data, int begin, int end) { + if (begin >= end) { + return; + } + + int pivot = data[(begin + end) / 2]; + int i = begin; + int j = end; + + while (i <= j) { + while (data[i] < pivot) { + i++; + } + while (data[j] > pivot) { + j--; + } + + if (i <= j) { + std::swap(data[i], data[j]); + i++; + j--; + } + } + + QuickSort(data, begin, j); + QuickSort(data, i, end); +} + +std::vector DenisovAQuickSortMergeSEQ::Merge(const std::vector &left_block, + const std::vector &right_block) { + std::vector res; + res.reserve(left_block.size() + right_block.size()); + + size_t i = 0; + size_t j = 0; + + while (i < left_block.size() && j < right_block.size()) { + if (left_block[i] <= right_block[j]) { + res.push_back(left_block[i]); + i++; + } else { + res.push_back(right_block[j]); + j++; + } + } + + while (i < left_block.size()) { + res.push_back(left_block[i]); + i++; + } + + while (j < right_block.size()) { + res.push_back(right_block[j]); + j++; + } + + return res; +} + +} // namespace denisov_a_quick_sort_simple_merging diff --git a/tasks/denisov_a_quick_sort_simple_merging/settings.json b/tasks/denisov_a_quick_sort_simple_merging/settings.json new file mode 100644 index 00000000..16f25e42 --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/settings.json @@ -0,0 +1,7 @@ +{ + "tasks": { + "mpi": "enabled", + "seq": "enabled" + }, + "tasks_type": "processes" +} diff --git a/tasks/denisov_a_quick_sort_simple_merging/tests/.clang-tidy b/tasks/denisov_a_quick_sort_simple_merging/tests/.clang-tidy new file mode 100644 index 00000000..ef43b7aa --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/tests/.clang-tidy @@ -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 diff --git a/tasks/denisov_a_quick_sort_simple_merging/tests/functional/main.cpp b/tasks/denisov_a_quick_sort_simple_merging/tests/functional/main.cpp new file mode 100644 index 00000000..62bdea3a --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/tests/functional/main.cpp @@ -0,0 +1,107 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "denisov_a_quick_sort_simple_merging/common/include/common.hpp" +#include "denisov_a_quick_sort_simple_merging/mpi/include/ops_mpi.hpp" +#include "denisov_a_quick_sort_simple_merging/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace denisov_a_quick_sort_simple_merging { + +class DenisovAQuickSortMergeFuncTests : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType ¶m) { + return std::to_string(std::get<0>(param)) + "_" + std::get<1>(param); + } + + protected: + void SetUp() override { + const auto &test_info = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + const int test_number = std::get<0>(test_info); + + if (test_number == 1) { + input_data_.clear(); + } else if (test_number == 2) { + input_data_ = {42}; + } else if (test_number == 3) { + input_data_ = {2, 5, 22, 23, 25, 26, 37, 41, 43}; + } else if (test_number == 4) { + input_data_ = {97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47}; + } else if (test_number == 5) { + input_data_ = {17, 29, 17, 11, 29, 7, 17, 19, 29, 11, 23}; + } else if (test_number == 6) { + input_data_ = {-17, 31, -13, 0, -19, 11, 23, -7, 31, 5}; + } else if (test_number == 7) { + input_data_.resize(127); + for (size_t i = 0; i < 127; i++) { + input_data_[i] = static_cast(126 - i); + } + } else if (test_number == 8) { + input_data_ = {91, 17, 83, 29, 5, 67, 41, 13, 97, 3, 59, 31, 7, 73, 19}; + } else if (test_number == 9) { + input_data_ = {37, 11}; + } else if (test_number == 10) { + input_data_ = {47, 23, 71, 3, 59, 13, 89, 31, 7, 61, 19, 43, 79, 11, 53}; + } else if (test_number == 11) { + input_data_ = {64, 32, 16, 8, 4, 2, 1, 128, 256, 512}; + } else { + input_data_ = {83, 17, 59, 7, 41, 97, 23, 71, 13, 89}; + } + } + + bool CheckTestOutputData(OutType &out) final { + if (input_data_.empty()) { + return out.empty(); + } + + if (out.size() != input_data_.size()) { + return false; + } + + std::vector sorted_ref = input_data_; + std::ranges::sort(sorted_ref); + + return out == sorted_ref && std::ranges::is_sorted(out); + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_; +}; + +namespace { + +TEST_P(DenisovAQuickSortMergeFuncTests, QuickSortMergeTest) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = { + std::make_tuple(1, "empty"), std::make_tuple(2, "single"), std::make_tuple(3, "sorted"), + std::make_tuple(4, "reversed"), std::make_tuple(5, "duplicates"), std::make_tuple(6, "negative"), + std::make_tuple(7, "large"), std::make_tuple(8, "mixed"), std::make_tuple(9, "small"), + std::make_tuple(10, "medium"), std::make_tuple(11, "powers"), std::make_tuple(12, "basic")}; + +const auto kTestTasksList = std::tuple_cat(ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_denisov_a_quick_sort_simple_merging), + ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_denisov_a_quick_sort_simple_merging)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = DenisovAQuickSortMergeFuncTests::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(QuickSortMergeTests, DenisovAQuickSortMergeFuncTests, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace denisov_a_quick_sort_simple_merging diff --git a/tasks/denisov_a_quick_sort_simple_merging/tests/performance/main.cpp b/tasks/denisov_a_quick_sort_simple_merging/tests/performance/main.cpp new file mode 100644 index 00000000..8d1928d2 --- /dev/null +++ b/tasks/denisov_a_quick_sort_simple_merging/tests/performance/main.cpp @@ -0,0 +1,55 @@ +#include + +#include +#include +#include + +#include "denisov_a_quick_sort_simple_merging/common/include/common.hpp" +#include "denisov_a_quick_sort_simple_merging/mpi/include/ops_mpi.hpp" +#include "denisov_a_quick_sort_simple_merging/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace denisov_a_quick_sort_simple_merging { + +class DenisovAQuickSortMergePerfTests : public ppc::util::BaseRunPerfTests { + private: + static constexpr size_t kDataCount = 1'000'000; + InType buffer_; + + void SetUp() override { + buffer_.resize(kDataCount); + for (size_t idx = 0; idx < kDataCount; idx++) { + buffer_[idx] = static_cast(kDataCount - idx); + } + } + + bool CheckTestOutputData(OutType &result) final { + if (result.size() != buffer_.size()) { + return false; + } + + std::vector sorted_ref = buffer_; + std::ranges::sort(sorted_ref); + + return result == sorted_ref && std::ranges::is_sorted(result); + } + + InType GetTestInputData() final { + return buffer_; + } +}; + +TEST_P(DenisovAQuickSortMergePerfTests, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_denisov_a_quick_sort_simple_merging); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = DenisovAQuickSortMergePerfTests::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, DenisovAQuickSortMergePerfTests, kGtestValues, kPerfTestName); + +} // namespace denisov_a_quick_sort_simple_merging From e3787d811b213d84e71999fab2a114e19559f516 Mon Sep 17 00:00:00 2001 From: TemikGOD Date: Sun, 1 Mar 2026 19:48:37 +0000 Subject: [PATCH 2/3] fixed sort --- .../mpi/src/ops_mpi.cpp | 48 +++++++++++++++---- .../seq/src/ops_seq.cpp | 48 +++++++++++++++---- 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/tasks/denisov_a_quick_sort_simple_merging/mpi/src/ops_mpi.cpp b/tasks/denisov_a_quick_sort_simple_merging/mpi/src/ops_mpi.cpp index 3ae912d7..7c1736fd 100644 --- a/tasks/denisov_a_quick_sort_simple_merging/mpi/src/ops_mpi.cpp +++ b/tasks/denisov_a_quick_sort_simple_merging/mpi/src/ops_mpi.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "denisov_a_quick_sort_simple_merging/common/include/common.hpp" @@ -136,15 +137,12 @@ bool DenisovAQuickSortMergeMPI::PostProcessingImpl() { return sum_in == sum_out; } -// NOLINTNEXTLINE(misc-no-recursion) -void DenisovAQuickSortMergeMPI::QuickSort(std::vector &data, int begin, int end) { - if (begin >= end) { - return; - } +namespace { - int pivot = data[(begin + end) / 2]; - int i = begin; - int j = end; +inline int Partition(std::vector &data, int left, int right) { + int pivot = data[(left + right) / 2]; + int i = left; + int j = right; while (i <= j) { while (data[i] < pivot) { @@ -160,9 +158,39 @@ void DenisovAQuickSortMergeMPI::QuickSort(std::vector &data, int begin, int j--; } } + return i; +} + +inline void PushRange(std::vector> &stack, int l, int r) { + if (l < r) { + stack.emplace_back(l, r); + } +} + +} // namespace + +void DenisovAQuickSortMergeMPI::QuickSort(std::vector &data, int begin, int end) { + std::vector> stack; + PushRange(stack, begin, end); + + while (!stack.empty()) { + auto [l, r] = stack.back(); + stack.pop_back(); + + int mid = Partition(data, l, r); + + if (mid - 1 - l < r - mid) { + PushRange(stack, l, mid - 1); + l = mid; + } else { + PushRange(stack, mid, r); + r = mid - 1; + } - QuickSort(data, begin, j); - QuickSort(data, i, end); + if (l < r) { + stack.emplace_back(l, r); + } + } } std::vector DenisovAQuickSortMergeMPI::Merge(const std::vector &left_block, diff --git a/tasks/denisov_a_quick_sort_simple_merging/seq/src/ops_seq.cpp b/tasks/denisov_a_quick_sort_simple_merging/seq/src/ops_seq.cpp index dab7625c..4d96a807 100644 --- a/tasks/denisov_a_quick_sort_simple_merging/seq/src/ops_seq.cpp +++ b/tasks/denisov_a_quick_sort_simple_merging/seq/src/ops_seq.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "denisov_a_quick_sort_simple_merging/common/include/common.hpp" @@ -86,15 +87,12 @@ bool DenisovAQuickSortMergeSEQ::PostProcessingImpl() { return sum_in == sum_out; } -// NOLINTNEXTLINE(misc-no-recursion) -void DenisovAQuickSortMergeSEQ::QuickSort(std::vector &data, int begin, int end) { - if (begin >= end) { - return; - } +namespace { - int pivot = data[(begin + end) / 2]; - int i = begin; - int j = end; +inline int Partition(std::vector &data, int left, int right) { + int pivot = data[(left + right) / 2]; + int i = left; + int j = right; while (i <= j) { while (data[i] < pivot) { @@ -110,9 +108,39 @@ void DenisovAQuickSortMergeSEQ::QuickSort(std::vector &data, int begin, int j--; } } + return i; +} + +inline void PushRange(std::vector> &stack, int l, int r) { + if (l < r) { + stack.emplace_back(l, r); + } +} + +} // namespace + +void DenisovAQuickSortMergeSEQ::QuickSort(std::vector &data, int begin, int end) { + std::vector> stack; + PushRange(stack, begin, end); + + while (!stack.empty()) { + auto [l, r] = stack.back(); + stack.pop_back(); + + int mid = Partition(data, l, r); + + if (mid - 1 - l < r - mid) { + PushRange(stack, l, mid - 1); + l = mid; + } else { + PushRange(stack, mid, r); + r = mid - 1; + } - QuickSort(data, begin, j); - QuickSort(data, i, end); + if (l < r) { + stack.emplace_back(l, r); + } + } } std::vector DenisovAQuickSortMergeSEQ::Merge(const std::vector &left_block, From 251bdb9835b4e35137c89bf0e8d461b8de2ae3cd Mon Sep 17 00:00:00 2001 From: TemikGOD Date: Sun, 1 Mar 2026 22:45:08 +0000 Subject: [PATCH 3/3] fixed? --- tasks/denisov_a_quick_sort_simple_merging/report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/denisov_a_quick_sort_simple_merging/report.md b/tasks/denisov_a_quick_sort_simple_merging/report.md index 6cb1abe4..42aba456 100644 --- a/tasks/denisov_a_quick_sort_simple_merging/report.md +++ b/tasks/denisov_a_quick_sort_simple_merging/report.md @@ -12,7 +12,7 @@ ## 2. Постановка задачи -Требуется реализовать алгоритм быстрой сортировки с простым слиянием для сортировки массива целых чисел. Алгоритм должен быть реализован в двух вариантах: последовательном (SEQ) и параллельном (MPI). +Требуется реализовать алгоритм быстрой сортировки с простым слиянием для сортировки массива целых чисел. Алгоритм должен быть реализован в двух вариантах: последовательном(SEQ) и параллельном(MPI). **Входные данные:** Массив целых чисел произвольного размера.