From df3994f2b0fd886d8d4bde2dd561acc28505b18c Mon Sep 17 00:00:00 2001 From: EkaterinaVin Date: Sat, 28 Feb 2026 20:26:58 +0300 Subject: [PATCH] add solution from the old branch --- .../common/include/common.hpp | 16 + tasks/vinyaikina_e_quicksort_simple/info.json | 9 + .../mpi/include/ops_mpi.hpp | 26 ++ .../mpi/src/ops_mpi.cpp | 153 ++++++++++ tasks/vinyaikina_e_quicksort_simple/report.md | 226 ++++++++++++++ .../seq/include/ops_seq.hpp | 26 ++ .../seq/src/ops_seq.cpp | 80 +++++ .../settings.json | 7 + .../tests/.clang-tidy | 13 + .../tests/functional/main.cpp | 287 ++++++++++++++++++ .../tests/performance/main.cpp | 47 +++ 11 files changed, 890 insertions(+) create mode 100644 tasks/vinyaikina_e_quicksort_simple/common/include/common.hpp create mode 100644 tasks/vinyaikina_e_quicksort_simple/info.json create mode 100644 tasks/vinyaikina_e_quicksort_simple/mpi/include/ops_mpi.hpp create mode 100644 tasks/vinyaikina_e_quicksort_simple/mpi/src/ops_mpi.cpp create mode 100644 tasks/vinyaikina_e_quicksort_simple/report.md create mode 100644 tasks/vinyaikina_e_quicksort_simple/seq/include/ops_seq.hpp create mode 100644 tasks/vinyaikina_e_quicksort_simple/seq/src/ops_seq.cpp create mode 100644 tasks/vinyaikina_e_quicksort_simple/settings.json create mode 100644 tasks/vinyaikina_e_quicksort_simple/tests/.clang-tidy create mode 100644 tasks/vinyaikina_e_quicksort_simple/tests/functional/main.cpp create mode 100644 tasks/vinyaikina_e_quicksort_simple/tests/performance/main.cpp diff --git a/tasks/vinyaikina_e_quicksort_simple/common/include/common.hpp b/tasks/vinyaikina_e_quicksort_simple/common/include/common.hpp new file mode 100644 index 00000000..191271db --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/common/include/common.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +#include "task/include/task.hpp" + +namespace vinyaikina_e_quicksort_simple { + +using InType = std::vector; +using OutType = std::vector; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace vinyaikina_e_quicksort_simple diff --git a/tasks/vinyaikina_e_quicksort_simple/info.json b/tasks/vinyaikina_e_quicksort_simple/info.json new file mode 100644 index 00000000..695c9bc0 --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Екатерина", + "group_number": "3823Б1ПР3", + "last_name": "Виняйкина", + "middle_name": "Александровна", + "task_number": "3" + } +} diff --git a/tasks/vinyaikina_e_quicksort_simple/mpi/include/ops_mpi.hpp b/tasks/vinyaikina_e_quicksort_simple/mpi/include/ops_mpi.hpp new file mode 100644 index 00000000..40d57ef9 --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/mpi/include/ops_mpi.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "task/include/task.hpp" +#include "vinyaikina_e_quicksort_simple/common/include/common.hpp" + +namespace vinyaikina_e_quicksort_simple { + +class VinyaikinaEQuicksortSimpleMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit VinyaikinaEQuicksortSimpleMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + std::vector data_; +}; + +} // namespace vinyaikina_e_quicksort_simple diff --git a/tasks/vinyaikina_e_quicksort_simple/mpi/src/ops_mpi.cpp b/tasks/vinyaikina_e_quicksort_simple/mpi/src/ops_mpi.cpp new file mode 100644 index 00000000..03623731 --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/mpi/src/ops_mpi.cpp @@ -0,0 +1,153 @@ +#include "vinyaikina_e_quicksort_simple/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include + +#include "vinyaikina_e_quicksort_simple/common/include/common.hpp" + +namespace vinyaikina_e_quicksort_simple { + +namespace { + +std::pair Partition(std::vector &arr, int lo, int hi) { + int pivot = arr[lo + ((hi - lo) / 2)]; + int i = lo; + int j = hi; + while (i <= j) { + while (arr[i] < pivot) { + i++; + } + while (arr[j] > pivot) { + j--; + } + if (i <= j) { + std::swap(arr[i], arr[j]); + i++; + j--; + } + } + return {i, j}; +} + +void QuickSort(std::vector &arr, int left, int right) { + std::vector> stack; + stack.emplace_back(left, right); + while (!stack.empty()) { + auto [lo, hi] = stack.back(); + stack.pop_back(); + if (lo >= hi) { + continue; + } + auto [i, j] = Partition(arr, lo, hi); + if (lo < j) { + stack.emplace_back(lo, j); + } + if (i < hi) { + stack.emplace_back(i, hi); + } + } +} + +std::vector MergeSorted(const std::vector &a, const std::vector &b) { + std::vector result; + result.reserve(a.size() + b.size()); + int i = 0; + int j = 0; + int a_size = static_cast(a.size()); + int b_size = static_cast(b.size()); + while (i < a_size && j < b_size) { + if (a[i] <= b[j]) { + result.push_back(a[i]); + i++; + } else { + result.push_back(b[j]); + j++; + } + } + while (i < a_size) { + result.push_back(a[i]); + i++; + } + while (j < b_size) { + result.push_back(b[j]); + j++; + } + return result; +} + +} // namespace + +VinyaikinaEQuicksortSimpleMPI::VinyaikinaEQuicksortSimpleMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; +} + +bool VinyaikinaEQuicksortSimpleMPI::ValidationImpl() { + return true; +} + +bool VinyaikinaEQuicksortSimpleMPI::PreProcessingImpl() { + data_ = GetInput(); + return true; +} + +bool VinyaikinaEQuicksortSimpleMPI::RunImpl() { + int rank = 0; + int proc_count = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &proc_count); + + int n = static_cast(data_.size()); + MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); + + if (n == 0) { + return true; + } + + std::vector send_counts(proc_count); + std::vector offsets(proc_count); + int base_chunk = n / proc_count; + int extra = n % proc_count; + for (int i = 0; i < proc_count; i++) { + send_counts[i] = base_chunk + (i < extra ? 1 : 0); + offsets[i] = (i > 0) ? (offsets[i - 1] + send_counts[i - 1]) : 0; + } + + std::vector local_data(send_counts[rank]); + MPI_Scatterv(data_.data(), send_counts.data(), offsets.data(), MPI_INT, local_data.data(), send_counts[rank], MPI_INT, + 0, MPI_COMM_WORLD); + + if (local_data.size() > 1) { + QuickSort(local_data, 0, static_cast(local_data.size()) - 1); + } + + std::vector gathered; + if (rank == 0) { + gathered.resize(n); + } + MPI_Gatherv(local_data.data(), send_counts[rank], MPI_INT, gathered.data(), send_counts.data(), offsets.data(), + MPI_INT, 0, MPI_COMM_WORLD); + + if (rank == 0) { + std::vector merged(gathered.begin(), gathered.begin() + send_counts[0]); + for (int i = 1; i < proc_count; i++) { + std::vector chunk(gathered.begin() + offsets[i], gathered.begin() + offsets[i] + send_counts[i]); + merged = MergeSorted(merged, chunk); + } + data_ = std::move(merged); + } + + MPI_Bcast(data_.data(), n, MPI_INT, 0, MPI_COMM_WORLD); + + return true; +} + +bool VinyaikinaEQuicksortSimpleMPI::PostProcessingImpl() { + GetOutput() = data_; + return true; +} + +} // namespace vinyaikina_e_quicksort_simple diff --git a/tasks/vinyaikina_e_quicksort_simple/report.md b/tasks/vinyaikina_e_quicksort_simple/report.md new file mode 100644 index 00000000..ff869370 --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/report.md @@ -0,0 +1,226 @@ +# Быстрая сортировка с простым слиянием + +- Студент: Виняйкина Екатерина Александровна +- Группа: 3823Б1ПР3 +- Технология: SEQ, MPI +- Вариант: 14 + +## 1. Введение + +Быстрая сортировка (quicksort) относится к алгоритмам сортировки + сравнением и в среднем имеет сложность O(n log n). + Цель работы - реализовать последовательный и параллельный варианты + быстрой сортировки с простым слиянием: последовательный вариант + для одного процесса и параллельный с использованием технологии MPI, + при котором данные распределяются по процессам, каждый процесс + сортирует свою часть, после чего результаты собираются и сливаются + на корневом процессе. + +## 2. Постановка задачи + +**Входные данные:** вектор целых чисел произвольной длины + +**Выходные данные:** тот же набор чисел, отсортированный по неубыванию. + +**Ограничения:** реализация параллельного алгоритма должна использовать + MPI; в методе RunImpl необходимо применять MPI_Send либо MPI_Scatterv + для распределения данных. Алгоритм должен быть детерминированным + и корректно обрабатывать граничные случаи (пустой массив, один элемент, + уже отсортированный массив, дубликаты, отрицательные числа). + +## 3. Последовательный алгоритм + +В качестве базового алгоритма используется итеративная быстрая + сортировка с разбиением Хоара и выбором опорного элемента + по середине отрезка. + +**Шаги алгоритма:** + +1. Инициализируется стек пар границ (left, right), + в который помещается (0, n-1). +2. Пока стек не пуст: извлекается пара (lo, hi). + Если lo >= hi, переход к следующей итерации. +3. Выбирается опорный элемент pivot = arr[lo + (hi - lo) / 2]. +4. Выполняется разбиение Хоара: два индекса i и j движутся + с концов к центру; элементы, меньшие pivot, остаются слева, + большие - справа; при пересечении индексов выполняется обмен + и сдвиг. В результате получаются границы (i, j), + разделяющие массив на две части. +5. Если lo < j, в стек добавляется (lo, j); + если i < hi, в стек добавляется (i, hi). +6. Шаги 2-5 повторяются до опустошения стека. + +Рекурсия не используется (реализация полностью итеративная со стеком), + что устраняет риск переполнения стека вызовов. + Сложность по времени в среднем O(n log n), + по памяти O(log n) для стека границ. + +## 4. Схема распараллеливания (MPI) + +**Распределение данных:** корневой процесс (rank 0) хранит + исходный массив. Размер массива n рассылается всем процессам + через MPI_Bcast. Каждому процессу назначается непрерывный кусок + массива: размеры кусков вычисляются так, чтобы разница в размерах + не превышала 1 (base_chunk = n / P, extra = n % P; + первые extra процессов получают base_chunk + 1 элемент, + остальные - base_chunk). Распределение выполняется с помощью + MPI_Scatterv (sendcounts и displs задают размер и смещение + для каждого ранга). + +**Локальная сортировка:** каждый процесс получает свой подмассив + и сортирует его той же итеративной быстрой сортировкой с разбиением + Хоара (без обмена данными между процессами на этом этапе). + +**Сбор и слияние:** отсортированные куски собираются на ранге 0 + через MPI_Gatherv. На ранге 0 выполняется последовательное слияние + упорядоченных отрезков: из собранного массива берутся куски + в порядке рангов и попарно сливаются (merge двух отсортированных + массивов в один) до получения одного отсортированного массива. + +**Рассылка результата:** итоговый отсортированный массив рассылается + всем процессам через MPI_Bcast, чтобы каждый процесс мог вернуть + его в выходных данных задачи (требование тестового каркаса). + +**Роли процессов:** все процессы участвуют в Bcast, Scatterv, + локальной сортировке, Gatherv и финальном Bcast; только rank 0 + выполняет слияние и хранит полный результат до Bcast. + +## 5. Детали реализации + +**Структура кода:** общие типы InType/OutType (`std::vector`) + и BaseTask в common/include/common.hpp. + Последовательная реализация: seq/include/ops_seq.hpp, + seq/src/ops_seq.cpp (класс VinyaikinaEQuicksortSimpleSEQ). + Параллельная: mpi/include/ops_mpi.hpp, mpi/src/ops_mpi.cpp + (класс VinyaikinaEQuicksortSimpleMPI). В обоих вариантах используются + локальные функции Partition (разбиение Хоара) и QuickSort + (итеративная с явным стеком); в MPI дополнительно - MergeSorted + для слияния двух отсортированных векторов. + +**Граничные случаи:** пустой массив и массив из одного элемента + не передаются в QuickSort; при n = 0 MPI-версия после Bcast + выходит из RunImpl без Scatterv/Gatherv. Один процесс (P = 1) + обрабатывается корректно: Scatterv и Gatherv передают весь массив + на единственный ранг, слияние одного куска тривиально. + +**Память:** на каждом процессе хранится локальный кусок размера + порядка n/P; на ранге 0 дополнительно - полный массив для сбора + и результат слияния. Общий объем O(n) по всем процессам. + +## 6. Экспериментальная установка + +**Оборудование и программное обеспечение:** + +- Процессор: Intel Core i7-14700KF +- Количество ядер/потоков: 20 ядер / 28 потоков +- Оперативная память: 16 гб +- Операционная система: Windows 11 + +**Инструментарий:** + +- Компилятор: MSVC 14.44 +- Тип сборки: Release +- Версия MPI: Microsoft MPI 10.1 + +**Данные:** тесты производительности используют массив фиксированного + размера, заполненный в обратном порядке для гарантии нетривиальной + работы сортировки + +## 7. Результаты и обсуждение + +### 7.1 Проверка корректности + +Корректность проверяется функциональными тестами: параметризованные + тесты для разных размеров ввода (3, 5, 7 элементов) для SEQ и MPI, + а также отдельные тесты на граничные случаи - пустой массив, + один элемент, уже отсортированный массив, массив в обратном порядке, + все элементы одинаковые, отрицательные числа, смешанные знаки, + дубликаты, два элемента (сортированный и нет), массив из 500 + элементов. Ожидаемый результат - вектор, совпадающий с эталонным + (отсортированный ввод). Для эталона используется std::ranges::sort + над копией ввода. Все тесты выполняются в едином пайплайне задачи + (Validation, PreProcessing, Run, PostProcessing) + +### 7.2 Производительность + +#### Режим task_run + +| Процессов | Время, с | Ускорение | Эффективность | +|-----------|----------|-----------|---------------| +| 1 | 0.223 | 1.00 | - | +| 2 | 0.181 | 1.23 | 61.5% | +| 4 | 0.206 | 1.08 | 27.1% | +| 6 | 0.257 | 0.87 | 14.4% | +| 8 | 0.332 | 0.67 | 8.4% | + +#### Режим task_pipeline + +| Процессов | Время, с | Ускорение | Эффективность | +|-----------|----------|-----------|---------------| +| 1 | 0.226 | 1.00 | - | +| 2 | 0.185 | 1.22 | 61.1% | +| 4 | 0.209 | 1.08 | 27.0% | +| 6 | 0.258 | 0.87 | 14.6% | +| 8 | 0.325 | 0.70 | 8.7% | + +Ускорение считается относительно времени SEQ на одном процессе. + Эффективность = (время SEQ / P) / время MPI, в процентах. + На двух процессах наблюдается ускорение около 1.22-1.23 + и эффективность около 61%. При увеличении числа процессов время MPI + растет из-за накладных расходов на коммуникации и последовательного + слияния на ранге 0, поэтому ускорение падает, а при 6 и 8 процессах + параллельный вариант оказывается медленнее последовательного. + +## 8. Выводы + +Реализованы последовательная и параллельная (MPI) версии быстрой + сортировки с простым слиянием. Последовательный алгоритм использует + итеративную схему с разбиением Хоара и стеком границ. Параллельный + распределяет данные через MPI_Scatterv, выполняет локальную сортировку + на каждом процессе и собирает результат с последующим последовательным + слиянием на корневом процессе и рассылкой через MPI_Bcast. + Корректность подтверждена функциональными тестами, + включая граничные случаи. + +Ограничения: слияние выполняется только на одном процессе + и может становиться узким местом при большом числе процессов. + +## 9. Источники + +1. Кормен Т., Лейзерсон Ч., Ривест Р., Штайн К. Алгоритмы: + построение и анализ. 3-е изд. М.: Вильямс, 2013. +2. Воеводин В.В., Воеводин Вл.В. Параллельные вычисления. + СПб.: БХВ-Петербург, 2002. +3. Антонов А.С. Параллельное программирование с использованием + технологии MPI. М.: Изд-во МГУ, 2004. +4. MPI Forum. MPI: A Message-Passing Interface Standard. Version 4.0. +2021. +5. Сысоев А.В., лекции по курсу "Параллельное программирование" - + ННГУ, 2025 год. + +## Приложение (фрагменты кода) + +Итеративное разбиение Хоара: + +```cpp +std::pair Partition(std::vector &arr, int lo, int hi) { + int pivot = arr[lo + ((hi - lo) / 2)]; + int i = lo, j = hi; + while (i <= j) { + while (arr[i] < pivot) i++; + while (arr[j] > pivot) j--; + if (i <= j) { std::swap(arr[i], arr[j]); i++; j--; } + } + return {i, j}; +} +``` + +Распределение и сбор в MPI (фрагмент RunImpl): + +```cpp +MPI_Scatterv(data_.data(), send_counts.data(), offsets.data(), MPI_INT, + local_data.data(), send_counts[rank], MPI_INT, 0, MPI_COMM_WORLD); +// ... локальная QuickSort(local_data, ...) ... +MPI_Gatherv(local_data.data(), send_counts[rank], MPI_INT, gathered.data(), + send_counts.data(), offsets.data(), MPI_INT, 0, MPI_COMM_WORLD); +``` diff --git a/tasks/vinyaikina_e_quicksort_simple/seq/include/ops_seq.hpp b/tasks/vinyaikina_e_quicksort_simple/seq/include/ops_seq.hpp new file mode 100644 index 00000000..77212675 --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/seq/include/ops_seq.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "task/include/task.hpp" +#include "vinyaikina_e_quicksort_simple/common/include/common.hpp" + +namespace vinyaikina_e_quicksort_simple { + +class VinyaikinaEQuicksortSimpleSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit VinyaikinaEQuicksortSimpleSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + std::vector data_; +}; + +} // namespace vinyaikina_e_quicksort_simple diff --git a/tasks/vinyaikina_e_quicksort_simple/seq/src/ops_seq.cpp b/tasks/vinyaikina_e_quicksort_simple/seq/src/ops_seq.cpp new file mode 100644 index 00000000..664789c9 --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/seq/src/ops_seq.cpp @@ -0,0 +1,80 @@ +#include "vinyaikina_e_quicksort_simple/seq/include/ops_seq.hpp" + +#include +#include +#include + +#include "vinyaikina_e_quicksort_simple/common/include/common.hpp" + +namespace vinyaikina_e_quicksort_simple { + +namespace { + +std::pair Partition(std::vector &arr, int lo, int hi) { + int pivot = arr[lo + ((hi - lo) / 2)]; + int i = lo; + int j = hi; + while (i <= j) { + while (arr[i] < pivot) { + i++; + } + while (arr[j] > pivot) { + j--; + } + if (i <= j) { + std::swap(arr[i], arr[j]); + i++; + j--; + } + } + return {i, j}; +} + +void QuickSort(std::vector &arr, int left, int right) { + std::vector> stack; + stack.emplace_back(left, right); + while (!stack.empty()) { + auto [lo, hi] = stack.back(); + stack.pop_back(); + if (lo >= hi) { + continue; + } + auto [i, j] = Partition(arr, lo, hi); + if (lo < j) { + stack.emplace_back(lo, j); + } + if (i < hi) { + stack.emplace_back(i, hi); + } + } +} + +} // namespace + +VinyaikinaEQuicksortSimpleSEQ::VinyaikinaEQuicksortSimpleSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; +} + +bool VinyaikinaEQuicksortSimpleSEQ::ValidationImpl() { + return true; +} + +bool VinyaikinaEQuicksortSimpleSEQ::PreProcessingImpl() { + data_ = GetInput(); + return true; +} + +bool VinyaikinaEQuicksortSimpleSEQ::RunImpl() { + if (data_.size() > 1) { + QuickSort(data_, 0, static_cast(data_.size()) - 1); + } + return true; +} + +bool VinyaikinaEQuicksortSimpleSEQ::PostProcessingImpl() { + GetOutput() = data_; + return true; +} + +} // namespace vinyaikina_e_quicksort_simple diff --git a/tasks/vinyaikina_e_quicksort_simple/settings.json b/tasks/vinyaikina_e_quicksort_simple/settings.json new file mode 100644 index 00000000..16f25e42 --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/settings.json @@ -0,0 +1,7 @@ +{ + "tasks": { + "mpi": "enabled", + "seq": "enabled" + }, + "tasks_type": "processes" +} diff --git a/tasks/vinyaikina_e_quicksort_simple/tests/.clang-tidy b/tasks/vinyaikina_e_quicksort_simple/tests/.clang-tidy new file mode 100644 index 00000000..ef43b7aa --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/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/vinyaikina_e_quicksort_simple/tests/functional/main.cpp b/tasks/vinyaikina_e_quicksort_simple/tests/functional/main.cpp new file mode 100644 index 00000000..eefbff4d --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/tests/functional/main.cpp @@ -0,0 +1,287 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" +#include "vinyaikina_e_quicksort_simple/common/include/common.hpp" +#include "vinyaikina_e_quicksort_simple/mpi/include/ops_mpi.hpp" +#include "vinyaikina_e_quicksort_simple/seq/include/ops_seq.hpp" + +namespace vinyaikina_e_quicksort_simple { + +class VinyaikinaEQuicksortSimpleFuncTests : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + int size = std::get<0>(params); + input_data_.resize(size); + for (int i = 0; i < size; i++) { + input_data_[i] = size - i; + } + expected_output_ = input_data_; + std::ranges::sort(expected_output_); + } + + bool CheckTestOutputData(OutType &output_data) final { + return expected_output_ == output_data; + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_; + OutType expected_output_; +}; + +namespace { + +TEST_P(VinyaikinaEQuicksortSimpleFuncTests, MatmulFromPic) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = {std::make_tuple(3, "3"), std::make_tuple(5, "5"), std::make_tuple(7, "7")}; + +const auto kTestTasksList = std::tuple_cat(ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_vinyaikina_e_quicksort_simple), + ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_vinyaikina_e_quicksort_simple)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = VinyaikinaEQuicksortSimpleFuncTests::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(PicMatrixTests, VinyaikinaEQuicksortSimpleFuncTests, kGtestValues, kPerfTestName); + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, EmptyArray) { + std::vector input; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + ASSERT_TRUE(task->GetOutput().empty()); +} + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, SingleElement) { + std::vector input = {42}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected = {42}; + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, AlreadySorted) { + std::vector input = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + ASSERT_EQ(task->GetOutput(), input); +} + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, ReverseSorted) { + std::vector input = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, AllSameElements) { + std::vector input = {5, 5, 5, 5, 5}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + ASSERT_EQ(task->GetOutput(), input); +} + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, NegativeNumbers) { + std::vector input = {-3, -1, -7, -5, -2}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected = {-7, -5, -3, -2, -1}; + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, MixedPositiveNegative) { + std::vector input = {3, -1, 4, -1, 5, -9, 2, -6}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected = {-9, -6, -1, -1, 2, 3, 4, 5}; + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, WithDuplicates) { + std::vector input = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected = {1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9}; + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, TwoElements) { + std::vector input = {2, 1}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected = {1, 2}; + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, TwoElementsSorted) { + std::vector input = {1, 2}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + ASSERT_EQ(task->GetOutput(), input); +} + +TEST(VinyaikinaEQuicksortSimpleSEQEdge, LargerArray) { + const int sz = 500; + std::vector input(sz); + for (int i = 0; i < sz; i++) { + input[i] = sz - i; + } + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected(sz); + for (int i = 0; i < sz; i++) { + expected[i] = i + 1; + } + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleMPIEdge, EmptyArray) { + std::vector input; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + ASSERT_TRUE(task->GetOutput().empty()); +} + +TEST(VinyaikinaEQuicksortSimpleMPIEdge, SingleElement) { + std::vector input = {42}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected = {42}; + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleMPIEdge, AlreadySorted) { + std::vector input = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + ASSERT_EQ(task->GetOutput(), input); +} + +TEST(VinyaikinaEQuicksortSimpleMPIEdge, NegativeNumbers) { + std::vector input = {-3, -1, -7, -5, -2}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected = {-7, -5, -3, -2, -1}; + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleMPIEdge, WithDuplicates) { + std::vector input = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected = {1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9}; + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleMPIEdge, AllSameElements) { + std::vector input = {7, 7, 7, 7, 7, 7}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + ASSERT_EQ(task->GetOutput(), input); +} + +TEST(VinyaikinaEQuicksortSimpleMPIEdge, TwoElements) { + std::vector input = {2, 1}; + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected = {1, 2}; + ASSERT_EQ(task->GetOutput(), expected); +} + +TEST(VinyaikinaEQuicksortSimpleMPIEdge, LargerArray) { + const int sz = 500; + std::vector input(sz); + for (int i = 0; i < sz; i++) { + input[i] = sz - i; + } + auto task = std::make_shared(input); + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + std::vector expected(sz); + for (int i = 0; i < sz; i++) { + expected[i] = i + 1; + } + ASSERT_EQ(task->GetOutput(), expected); +} + +} // namespace + +} // namespace vinyaikina_e_quicksort_simple diff --git a/tasks/vinyaikina_e_quicksort_simple/tests/performance/main.cpp b/tasks/vinyaikina_e_quicksort_simple/tests/performance/main.cpp new file mode 100644 index 00000000..731afed0 --- /dev/null +++ b/tasks/vinyaikina_e_quicksort_simple/tests/performance/main.cpp @@ -0,0 +1,47 @@ +#include + +#include +#include + +#include "util/include/perf_test_util.hpp" +#include "vinyaikina_e_quicksort_simple/common/include/common.hpp" +#include "vinyaikina_e_quicksort_simple/mpi/include/ops_mpi.hpp" +#include "vinyaikina_e_quicksort_simple/seq/include/ops_seq.hpp" + +namespace vinyaikina_e_quicksort_simple { + +class VinyaikinaEQuicksortSimplePerfTests : public ppc::util::BaseRunPerfTests { + const int kCount_ = 3000000; + InType input_data_; + + void SetUp() override { + input_data_.resize(kCount_); + for (int i = 0; i < kCount_; i++) { + input_data_[i] = kCount_ - i; + } + } + + bool CheckTestOutputData(OutType &output_data) final { + return std::ranges::is_sorted(output_data) && std::cmp_equal(output_data.size(), kCount_); + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(VinyaikinaEQuicksortSimplePerfTests, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_vinyaikina_e_quicksort_simple); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = VinyaikinaEQuicksortSimplePerfTests::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, VinyaikinaEQuicksortSimplePerfTests, kGtestValues, kPerfTestName); + +} // namespace vinyaikina_e_quicksort_simple