From 87b0e5118091e9e46b584a6bfea5014eb36c18dc Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 14:10:26 +0000 Subject: [PATCH 01/12] add reports --- .../all/report.md | 377 ++++++++++++++++++ .../omp/report.md | 224 +++++++++++ .../report.md | 94 +++++ .../seq/report.md | 174 ++++++++ .../stl/report.md | 359 +++++++++++++++++ .../tbb/report.md | 347 ++++++++++++++++ 6 files changed, 1575 insertions(+) create mode 100644 tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md create mode 100644 tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md create mode 100644 tasks/krasnopevtseva_v_hoare_batcher_sort/report.md create mode 100644 tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md create mode 100644 tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md create mode 100644 tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md new file mode 100644 index 0000000000..3640b89625 --- /dev/null +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md @@ -0,0 +1,377 @@ +# Гибридная сортировка Хоара-Бэтчера (Quick Batcher Sort) + +- Студент: Краснопевцева Вероника Дмитриевна, группа 3823Б1ПМоп3 +- Технология: ALL (STL + OMP) +- Вариант: 14 + +## 1. Введение + +Сортировка массивов данных — одна из фундаментальных задач в области программирования. Быстрая сортировка Хоара известна своей эффективностью в среднем случае, однако её производительность может деградировать при неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой алгоритм сортировки слиянием с хорошими свойствами параллелизации, но может быть избыточна для небольших массивов. + +Целью данной работы является реализация комбинированного параллельного гибридного алгоритма, использующего возможности как OpenMP, так и STL, сочетающего адаптивный итеративный QuickSort с последующим слиянием Бэтчера для улучшения общей производительности. Ожидаемый результат — устойчивая работа алгоритма на массивах различных размеров с использованием оптимизаций, таких как сортировка вставками для малых подмассивов и эффективное параллельное слияние, а также адаптивный выбор стратегии в зависимости от размера данных. + +## 2. Постановка задачи + +### Формальное определение + +Необходимо реализовать комбинированный параллельный алгоритм сортировки массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием технологий OpenMP и STL. + +### Входные данные + +- Вектор целых чисел `std::vector` произвольной длины. + +### Выходные данные + +- Отсортированный по неубыванию вектор целых чисел. + +### Ограничения + +- Алгоритм должен корректно обрабатывать массивы любого размера. +- Для малых подмассивов используется сортировка вставками. +- Параллельная обработка выполняется на этапе начальной сортировки подмассивов и на этапе слияния. +- Количество потоков определяется через `omp_get_max_threads()`. +- Реализована адаптивная стратегия: для малых массивов (n < 1000) используется последовательная сортировка. + +## 3. Базовый алгоритм (последовательная версия) + +### 3.1 Быстрая сортировка (итеративная версия) + +Используется нерекурсивная реализация через стек: + +1. В стек помещается пара `(left, right)` для всего массива. +2. Пока стек не пуст: + - Извлекается диапазон `[l, r]`. + - Если `l >= r` — пропуск. + - Если размер диапазона небольшой - вызывается сортировка вставками. + - Выбирается опорный элемент (последний элемент массива). + - Выполняется разбиение Хоара. + - Меньший поддиапазон помещается в стек первым (для балансировки). + +### 3.2 Сортировка вставками + +Для малых подмассивов (размер < 16) применяется классическая сортировка вставками, так как она показывает хорошие результаты на почти отсортированных данных и малых размерах. + +### 3.3 Слияние Бэтчера + +После завершения быстрой сортировки выполняется слияние отсортированных блоков с использованием алгоритма Бэтчера с последовательным применением `std::inplace_merge`. + +## 4. Схема параллелизации + +### 4.1 Общая стратегия + +Параллелизация реализована по принципу "разделяй и властвуй" с комбинированным использованием OpenMP и STL: + +1. **Адаптивный выбор стратегии**: Если размер массива менее 1000 элементов, используется последовательная сортировка (QuickSort), иначе — параллельная. +2. **Определение количества потоков**: Количество потоков определяется через `omp_get_max_threads()`. +3. **Разбиение данных**: Исходный массив разделяется на `N` блоков, где `N` — количество доступных потоков. +4. **Параллельная сортировка**: Каждый блок сортируется с использованием директивы `#pragma omp parallel for`. +5. **Параллельное слияние**: Отсортированные блоки попарно сливаются с использованием иерархической схемы Бэтчера, где каждая пара сливается параллельно через OpenMP. + +### 4.2 Распределение данных + +- Размер блока для каждого потока: `base_size = n / numthreads` +- Остаток от деления добавляется к последнему блоку: `last_size = base_size + remainder` +- Каждый поток получает указатель на начало своего блока и его размер +- OpenMP автоматически распределяет итерации по доступным потокам + +### 4.3 Коммуникационная схема + +- На этапе сортировки: блоки сортируются независимо, обмен данными отсутствует. +- На этапе слияния: выполняется попарное слияние блоков с использованием `std::inplace_merge`. +- Слияние организовано в виде иерархического дерева (парное слияние на каждом уровне). +- OpenMP обеспечивает параллельное выполнение операций слияния на каждом уровне. + +### 4.4 Синхронизация + +- Используется `#pragma omp parallel for` с явным указанием shared-переменных. +- Отсутствие гонок данных гарантируется тем, что каждый поток работает со своим выделенным диапазоном памяти. +- На этапе слияния используется условное распараллеливание через параметр `par_if_greater` (порог 32). +- Адаптивная стратегия позволяет избежать накладных расходов на малых массивах. + +## 5. Детали реализации + +### 5.1 Структура кода + +- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/all/include/ops_all.hpp`, `krasnopevtseva_v_hoare_batcher_sort/all/ops_all.cpp` +- **Пространство имён**: `krasnopevtseva_v_hoare_batcher_sort` +- **Класс**: `KrasnopevtsevaVHoareBatcherSortALL` + +### 5.2 Основные методы + +| Метод | Назначение | +|-------|-------------| +| `ValidationImpl()` | Проверка входных данных (массив не пуст) | +| `PreProcessingImpl()` | Инициализация выходного вектора | +| `RunImpl()` | Запуск комбинированной сортировки | +| `SortLocalData()` | Адаптивный выбор стратегии сортировки | +| `ParallelSortChunks()` | Параллельная сортировка блоков с использованием OpenMP | +| `QuickSort()` | Итеративная быстрая сортировка | +| `Partition()` | Разбиение Хоара (с последним опорным элементом) | +| `InsertionSort()` | Сортировка вставками для малых диапазонов | +| `BatcherMergeBlocksStep()` | Слияние двух блоков | +| `BatcherMerge()` | Параллельное слияние блоков с использованием OpenMP | + +### 5.3 Важные особенности + +- **Адаптивная стратегия**: Для массивов размером менее 1000 элементов используется последовательная сортировка, что позволяет избежать накладных расходов на создание потоков. +- **Выбор опорного элемента**: Используется последний элемент массива, что упрощает реализацию. +- **Порог сортировки вставками**: 16 элементов. +- **Условное распараллеливание слияния**: Слияние выполняется параллельно только если `(thread_input_size / step) > 32`. +- **Итеративный QuickSort**: Избегает переполнения стека вызовов. +- **Комбинированный подход**: Сочетает директивы OpenMP для параллельных регионов и стандартную библиотеку для базовых операций. + +### 5.4 Использование памяти + +- Исходный массив сортируется на месте. +- Для отслеживания границ блоков используются массивы `pointers` и `sizes`. +- OpenMP управляет созданием и распределением потоков. +- Дополнительная память: O(numthreads) для хранения указателей на блоки. + +## 6. Окружение и тестирование + +### 6.1 Аппаратное и программное обеспечение + +- **Процессор**: AMD Ryzen 7 5700X 8-Core Processor +- **ОЗУ**: 32.0 ГБ +- **ОС**: Windows 11 Pro 25H2 +- **Набор инструментов**: DevContainer с компилятором GCC (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 +- **Тип сборки**: Release +- **Фреймворк тестирования**: Google Test (gtest) +- **Технологии**: OpenMP + STL + +### 6.2 Переменные окружения + +- `OMP_NUM_THREADS` — количество потоков OpenMP + +### 6.3 Тестовые данные и верификация + +#### Функциональное тестирование + +Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: + +| № | Тип массива | Описание | Размер | +|---|-------------|----------|--------| +| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | +| 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | +| 3 | `short_array` | Короткий массив минимального размера | 4 | +| 4 | `long_array` | Длинный массив с повторяющимися и случайными значениями | 50 | +| 5 | `two_number_array` | Массив, содержащий только два различных значения (10 и 20) | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию (наихудший случай) | 24 | + +#### Верификация результата + +Для каждого тестового набора проверялось выполнение основного инварианта сортировки: + +- Для всех индексов `i` от 0 до `size-2` выполняется условие `output[i] <= output[i+1]` + +Дополнительно проводилось сравнение с эталонной реализацией `std::sort` на тех же входных данных для всех тестовых случаев. + +#### Тестирование производительности + +Для оценки производительности был реализован отдельный тестовый набор, генерирующий массив из **100 000** случайных целых чисел в диапазоне `[0, 100 000 000]`. Генерация выполняется с использованием генератора псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости результатов. + +**Результаты корректности:** Все 6 функциональных тестов успешно пройдены для ALL-версии. Перфоманс-тесты также подтверждают корректную сортировку массива из 100 000 элементов. + +## 7. Результаты и обсуждение + +### 7.1 Корректность + +Корректность комбинированной реализации на ALL (OpenMP + STL) подтверждена: + +- Сравнением результатов с последовательной версией на идентичных входных данных. +- Проверкой инварианта отсортированности для всех выходных массивов. +- Успешным прохождением всех функциональных тестов. +- Проверкой адаптивной стратегии на массивах различных размеров. + +### 7.2 Производительность + +Результаты для массива из **100 000** случайных целых чисел: + +| Реализация | Количество потоков | Время, мс | Ускорение | Эффективность | +|------------|--------------------|-----------|-----------|----------------| +| SEQ | 1 | 32.0 | 1.00 | N/A | +| ALL | 8 | 22.0 | 1.45 | 18.2% | + + +### 7.3 Анализ производительности + +#### Наблюдаемые эффекты + +- **Ускорение ALL версии около 1.45x** при использовании 8 потоков. Это соответствует эффективности около 18.2%. + + +#### Факторы, влияющие на производительность ALL версии + +| Фактор | Влияние на ALL версию | +|--------|----------------------| +| Адаптивная стратегия (n < 1000) | Положительное (избегает накладных расходов) | +| Накладные расходы OpenMP | Умеренные (fork-join модель) | +| Параллельная сортировка блоков | Хорошее ускорение (блоки независимы) | +| Параллельное слияние | Умеренное ускорение (зависимость по данным) | +| Балансировка нагрузки | Средняя (последний блок может быть больше) | + + +## 8. Заключения + +### Основные выводы + +1. Разработан комбинированный параллельный гибридный алгоритм сортировки с использованием OpenMP и STL, сочетающий QuickSort, InsertionSort и BatcherMerge. +2. Алгоритм корректен и демонстрирует ускорение до 1.45x на 8 потоках для массива из 100 000 элементов. +3. Реализована адаптивная стратегия: для массивов размером менее 1000 элементов используется последовательная сортировка, что позволяет избежать накладных расходов на создание потоков. +4. Сортировка вставками для подмассивов размера < 16 даёт выигрыш по сравнению с рекурсивным QuickSort. +5. Условное распараллеливание слияния позволяет адаптироваться под размер обрабатываемых данных. + + +## Приложение + +```cpp +bool KrasnopevtsevaVHoareBatcherSortALL::RunImpl() { + const auto &input = GetInput(); + std::vector result = input; + + if (result.size() <= 1) { + GetOutput() = result; + return true; + } + + SortLocalData(result); + GetOutput() = std::move(result); + return true; +} + +void KrasnopevtsevaVHoareBatcherSortALL::SortLocalData(std::vector &data) { + int n = static_cast(data.size()); + if (n <= 0) { + return; + } + + int numthreads = omp_get_max_threads(); + numthreads = std::min(n, numthreads); + + if (n < 1000) { + QuickSort(data, 0, n - 1); + } else { + ParallelSortChunks(data, n, numthreads); + } +} + +void KrasnopevtsevaVHoareBatcherSortALL::ParallelSortChunks(std::vector &arr, int n, int numthreads) { + if (n <= 0) { + return; + } + + numthreads = std::min(n, numthreads); + if (numthreads <= 0) { + numthreads = 1; + } + + int thread_input_size = n / numthreads; + int thread_input_remainder_size = n % numthreads; + + std::vector pointers(numthreads); + std::vector sizes(numthreads); + + for (int i = 0; i < numthreads; ++i) { + std::ptrdiff_t offset = static_cast(i) * static_cast(thread_input_size); + pointers[i] = arr.data() + offset; + sizes[i] = thread_input_size; + } + sizes.back() += thread_input_remainder_size; + +#pragma omp parallel for default(none) shared(arr, pointers, sizes, numthreads) + for (int i = 0; i < numthreads; ++i) { + int left = static_cast(pointers[i] - arr.data()); + int right = left + sizes[i] - 1; + if (left < right) { + QuickSort(arr, left, right); + } + } + + BatcherMerge(thread_input_size, pointers, sizes, 32); +} + +void KrasnopevtsevaVHoareBatcherSortALL::BatcherMerge(int thread_input_size, std::vector &pointers, + std::vector &sizes, int par_if_greater) { + int pack = static_cast(pointers.size()); + for (int step = 1; pack > 1; step *= 2, pack /= 2) { + bool do_parallel = (thread_input_size / step) > par_if_greater; + + if (do_parallel) { +#pragma omp parallel for default(none) shared(pointers, sizes, pack, step) + for (int off = 0; off < pack / 2; ++off) { + auto idx1 = static_cast(2 * step) * static_cast(off); + auto idx2 = idx1 + static_cast(step); + BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); + } + } else { + for (int off = 0; off < pack / 2; ++off) { + auto idx1 = static_cast(2 * step) * static_cast(off); + auto idx2 = idx1 + static_cast(step); + BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); + } + } + + if ((pack / 2) - 1 == 0) { + BatcherMergeBlocksStep(pointers[0], sizes[sizes.size() - 1], pointers[pointers.size() - 1], + sizes[sizes.size() - 1]); + } else if ((pack / 2) % 2 != 0) { + auto idx1 = static_cast(2 * step) * static_cast((pack / 2) - 2); + auto idx2 = static_cast(2 * step) * static_cast((pack / 2) - 1); + BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); + } + } +} + +void KrasnopevtsevaVHoareBatcherSortALL::QuickSort(std::vector &arr, int first, int last) { + std::stack> stack; + stack.emplace(first, last); + + while (!stack.empty()) { + auto [l, r] = stack.top(); + stack.pop(); + + if (l >= r) { + continue; + } + + if (r - l < 16) { + InsertionSort(arr, l, r); + continue; + } + + int iter = Partition(arr, l, r); + + if (iter - l < r - iter) { + stack.emplace(iter + 1, r); + stack.emplace(l, iter - 1); + } else { + stack.emplace(l, iter - 1); + stack.emplace(iter + 1, r); + } + } +} + +int KrasnopevtsevaVHoareBatcherSortALL::Partition(std::vector &arr, int first, int last) { + int i = first - 1; + int value = arr[last]; + + for (int j = first; j <= last - 1; ++j) { + if (arr[j] <= value) { + ++i; + std::swap(arr[i], arr[j]); + } + } + std::swap(arr[i + 1], arr[last]); + return i + 1; +} +void KrasnopevtsevaVHoareBatcherSortALL::InsertionSort(std::vector &arr, int first, int last) { + for (int i = first + 1; i <= last; ++i) { + int key = arr[i]; + int j = i - 1; + while (j >= first && arr[j] > key) { + arr[j + 1] = arr[j]; + --j; + } + arr[j + 1] = key; + } +} \ No newline at end of file diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md new file mode 100644 index 0000000000..65e110d279 --- /dev/null +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md @@ -0,0 +1,224 @@ +# Гибридная сортировка Хоара-Бэтчера (Quick Batcher Sort) + +- Студент: Краснопевцева Вероника Дмитриевна, группа 3823Б1ПМоп3 +- Технология: OMP +- Вариант: 14 + +## 1. Введение + +Сортировка массивов данных — одна из фундаментальных задач в области программирования. Быстрая сортировка Хоара известна своей эффективностью в среднем случае, однако её производительность может деградировать при неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой алгоритм сортировки слиянием с хорошими свойствами параллелизации, но может быть избыточна для небольших массивов. + +Целью данной работы является реализация параллельного гибридного алгоритма с использованием технологии OpenMP, сочетающего адаптивный итеративный QuickSort с последующим слиянием Бэтчера для улучшения общей производительности. Ожидаемый результат — устойчивая работа алгоритма на массивах различных размеров с использованием оптимизаций, таких как сортировка вставками для малых подмассивов и эффективное параллельное слияние. + +## 2. Постановка задачи + +### Формальное определение +Необходимо реализовать параллельный алгоритм сортировки массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием технологии OpenMP. + +### Входные данные +- Вектор целых чисел `std::vector` произвольной длины. + +### Выходные данные +- Отсортированный по неубыванию вектор целых чисел. + +### Ограничения +- Алгоритм должен корректно обрабатывать массивы любого размера. +- Для малых подмассивов используется сортировка вставками. +- Параллельная обработка выполняется на этапе начальной сортировки подмассивов и на этапе слияния. + +## 3. Базовый алгоритм (последовательная версия) + +### 3.1 Быстрая сортировка (итеративная версия) +Используется нерекурсивная реализация через стек: + +1. В стек помещается пара `(left, right)` для всего массива. +2. Пока стек не пуст: + - Извлекается диапазон `[l, r]`. + - Если `l >= r` — пропуск. + - Если размер диапазона небольшой - вызывается сортировка вставками. + - Выбирается опорный элемент (последний элемент массива). + - Выполняется разбиение Хоара. + - Меньший поддиапазон помещается в стек первым (для балансировки). + +### 3.2 Сортировка вставками +Для малых подмассивов (размер < 16) применяется классическая сортировка вставками, так как она показывает хорошие результаты на почти отсортированных данных и малых размерах. + +### 3.3 Слияние Бэтчера +После завершения быстрой сортировки выполняется слияние отсортированных блоков с использованием алгоритма Бэтчера с последовательным применением `std::inplace_merge`. + +## 4. Схема параллелизации + +### 4.1 Общая стратегия +Параллелизация реализована по принципу "разделяй и властвуй" с использованием OpenMP: + +1. **Разбиение данных**: Исходный массив разделяется на `N` блоков, где `N` — количество доступных потоков. +2. **Параллельная сортировка**: Каждый поток независимо сортирует свой блок с использованием итеративного QuickSort. +3. **Параллельное слияние**: Отсортированные блоки попарно сливаются с использованием параллельного алгоритма Бэтчера. + +### 4.2 Распределение данных +- Размер блока для каждого потока: `base_size = n / numthreads` +- Остаток от деления добавляется к последнему блоку: `last_size = base_size + remainder` +- Каждый поток получает указатель на начало своего блока и его размер + +### 4.3 Коммуникационная схема +- На этапе сортировки: потоки работают независимо, обмен данными отсутствует. +- На этапе слияния: выполняется попарное слияние блоков с использованием `std::inplace_merge`. +- Слияние организовано в виде иерархического дерева (парное слияние на каждом уровне). + +### 4.4 Синхронизация +- Используется `#pragma omp parallel for` с явным указанием shared-переменных. +- Отсутствие гонок данных гарантируется тем, что каждый поток работает со своим выделенным диапазоном памяти. +- На этапе слияния используется `#pragma omp parallel for` с порогом `par_if_greater` для распараллеливания. + +## 5. Детали реализации + +### 5.1 Структура кода +- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/omp/include/ops_omp.hpp`, `krasnopevtseva_v_hoare_batcher_sort/omp/ops_omp.cpp` +- **Пространство имён**: `krasnopevtseva_v_hoare_batcher_sort` +- **Класс**: `KrasnopevtsevaVHoareBatcherSortOMP` + +### 5.2 Основные методы +| Метод | Назначение | +|-------|-------------| +| `ValidationImpl()` | Проверка входных данных (массив не пуст) | +| `PreProcessingImpl()` | Инициализация выходного вектора | +| `RunImpl()` | Запуск параллельной сортировки | +| `QuickSort()` | Итеративная быстрая сортировка | +| `Partition()` | Разбиение Хоара (с последним опорным элементом) | +| `InsertionSort()` | Сортировка вставками для малых диапазонов | +| `BatcherMergeBlocksStep()` | Слияние двух блоков | +| `BatcherMerge()` | Параллельное слияние блоков | + +### 5.3 Важные особенности +- **Выбор опорного элемента**: В параллельной версии используется последний элемент массива, что упрощает реализацию. +- **Порог сортировки вставками**: 16 элементов (эмпирически оптимальное значение). +- **Условное распараллеливание слияния**: Слияние выполняется параллельно только если `thread_input_size / step > par_if_greater` (параметр 32). +- **Итеративный QuickSort**: Избегает переполнения стека вызовов. +- **Особая обработка граничных случаев**: Учитываются ситуации с нечётным количеством блоков. + +### 5.4 Использование памяти +- Исходный массив сортируется на месте. +- Для отслеживания границ блоков используются массивы `pointers` и `sizes`. +- Дополнительная память: O(numthreads) для хранения указателей на блоки. + +## 6. Окружение и тестирование + +### 6.1 Аппаратное и программное обеспечение +- **Процессор**: AMD Ryzen 7 5700X 8-Core Processor +- **ОЗУ**: 32.0 ГБ +- **ОС**: Windows 11 Pro 25H2 +- **Набор инструментов**: DevContainer с компилятором GCC (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 +- **Тип сборки**: Release +- **Фреймворк тестирования**: Google Test (gtest) +- **Технология**: OpenMP + +### 6.2 Переменные окружения +- `OMP_NUM_THREADS` — количество потоков OpenMP + +### 6.3 Тестовые данные и верификация + +#### Функциональное тестирование + +Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: + +| № | Тип массива | Описание | Размер | +|---|-------------|----------|--------| +| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | +| 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | +| 3 | `short_array` | Короткий массив минимального размера | 4 | +| 4 | `long_array` | Длинный массив с повторяющимися и случайными значениями | 50 | +| 5 | `two_number_array` | Массив, содержащий только два различных значения (10 и 20) | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию (наихудший случай) | 24 | + +#### Верификация результата + +Для каждого тестового набора проверялось выполнение основного инварианта сортировки: +- Для всех индексов `i` от 0 до `size-2` выполняется условие `output[i] <= output[i+1]` + +Дополнительно проводилось сравнение с эталонной реализацией `std::sort` на тех же входных данных для всех тестовых случаев. + +#### Тестирование производительности (перфоманс-тесты) + +Для оценки производительности был реализован отдельный тестовый набор, генерирующий массив из **100 000** случайных целых чисел в диапазоне `[0, 100 000 000]`. Генерация выполняется с использованием генератора псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости результатов. + +**Результаты корректности:** Все 6 функциональных тестов успешно пройдены для всех конфигураций количества потоков (1, 2, 4, 8). Перфоманс-тесты также подтверждают корректную сортировку массива из 100 000 элементов. + +## 7. Результаты и обсуждение + +### 7.1 Корректность +Корректность параллельной реализации подтверждена: +- Сравнением результатов с последовательной версией на идентичных входных данных. +- Проверкой инварианта отсортированности для всех выходных массивов. +- Успешным прохождением всех функциональных тестов для различных конфигураций потоков. + +### 7.2 Производительность + +Результаты для массива из **100 000** случайных целых чисел: + +| Режим | Количество потоков | Время, мс | Ускорение | Эффективность | +|-------|--------------------|-----------|-----------|----------------| +| seq | 1 | 32 | 1.00 | N/A | +| omp | 8 | 19 | 1.94 | 24.3% | + + + +### 7.3 Анализ производительности + +#### Масштабирование + +- При использовании 8 потоков ускорение достигает 1.94x (эффективность 24.3%). + +## 8. Заключения + +### Основные выводы +1. Разработан параллельный гибридный алгоритм сортировки с использованием OpenMP, сочетающий QuickSort, InsertionSort и BatcherMerge. +2. При использовании 8 потоков ускорение достигает 1.94x, что подтверждает эффективность параллелизации для массивов большого размера. + +### Ограничения +- Снижение эффективности при использовании большого количества потоков из-за накладных расходов на слияние. +- Зависимость от равномерности начального разбиения данных. + +## Приложение + +### Основной цикл параллельной сортировки + +```cpp +bool KrasnopevtsevaVHoareBatcherSortOMP::RunImpl() { + const auto &input = GetInput(); + std::size_t size = input.size(); + + if (size <= 1) { + GetOutput() = input; + return true; + } + + std::vector res = input; + + int n = static_cast(size); + int numthreads = omp_get_max_threads(); + numthreads = std::min(n, numthreads); + + int thread_input_size = n / numthreads; + int thread_input_remainder_size = n % numthreads; + + std::vector pointers(numthreads); + std::vector sizes(numthreads); + for (int i = 0; i < numthreads; ++i) { + std::ptrdiff_t offset = static_cast(i) * static_cast(thread_input_size); + pointers[i] = res.data() + offset; + sizes[i] = thread_input_size; + } + sizes[sizes.size() - 1] += thread_input_remainder_size; + +#pragma omp parallel for default(none) shared(res, pointers, sizes, numthreads) + for (int i = 0; i < numthreads; ++i) { + int left = static_cast(pointers[i] - res.data()); + int right = left + sizes[i] - 1; + QuickSort(res, left, right); + } + + BatcherMerge(thread_input_size, pointers, sizes, 32); + + GetOutput() = std::move(res); + return true; +} \ No newline at end of file diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md new file mode 100644 index 0000000000..f7ca77215d --- /dev/null +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md @@ -0,0 +1,94 @@ +# Итоговый отчет по реализации гибридной сортировки Хоара-Бэтчера + +- Студент: Краснопевцева Вероника Дмитриевна, группа 3823Б1ПМоп3 +- Вариант: 14 + +## 1. Цель работы + +Реализовать и исследовать гибридный алгоритм сортировки, сочетающий итеративную быструю сортировку Хоара с сортировкой вставками для малых подмассивов и последующим слиянием Бэтчера. Алгоритм реализован в пяти вариантах: последовательная версия (SEQ) и параллельные версии с использованием OpenMP, STL, Intel TBB и комбинированной версии ALL (OpenMP + адаптивная стратегия). Провести сравнительный анализ производительности различных технологий параллелизации. + +## 2. Реализованные версии + +### 2.1 Общая структура алгоритма + +Все версии реализуют единую логику сортировки: + +1. Итеративная быстрая сортировка с использованием стека +2. Сортировка вставками для подмассивов размером менее 16 элементов +3. Слияние отсортированных блоков по алгоритму Бэтчера с использованием `std::inplace_merge` + +### 2.2 Особенности реализаций + +| Версия | Технология | Ключевые особенности | +|--------|------------|----------------------| +| **SEQ** | Последовательная | Базовый алгоритм без параллелизации | +| **OMP** | OpenMP | `#pragma omp parallel for`, параллельная сортировка блоков и слияние | +| **STL** | std::async + future | Асинхронные задачи, `std::future`, аппаратное определение потоков | +| **TBB** | Intel TBB | `tbb::parallel_for`, `tbb::global_control`, `simple_partitioner` | +| **ALL** | OpenMP + STL | Комбинированный подход, адаптивная стратегия (n < 1000 — последовательно) | + +## 3. Экспериментальная среда + +- **Процессор**: AMD Ryzen 7 5700X 8-Core Processor +- **ОЗУ**: 32.0 ГБ +- **ОС**: Windows 11 Pro 25H2 +- **Компилятор**: GCC 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04) +- **Тип сборки**: Release +- **Размер тестовых данных**: 100 000 случайных целых чисел + +## 4. Результаты производительности + +### 4.1 Сводная таблица + +| Версия | Технология | Время, мс | Ускорение (относительно SEQ) | Эффективность | +|--------|------------|-----------|------------------------------|---------------| +| SEQ | Последовательная | 32.0 | 1.00x | N/A | +| OMP | OpenMP | 19.0 | 1.68x | 21.0% | +| STL | std::async | 18.0 | 1.78x | 22.3% | +| TBB | Intel TBB | 22.0 | 1.45x | 18.2% | +| ALL | OpenMP + адаптивная | 22.0 | 1.45x | 18.2% | + + +## 5. Сравнительный анализ + +### 5.1 Лидер производительности + +**STL (std::async)** показывает наилучший результат — 18 мс (ускорение 1.78x). Это объясняется: +- Минимальными накладными расходами при создании асинхронных задач для данного размера данных +- Отсутствием дополнительных абстракций (в отличие от TBB) +- Эффективным распределением блоков между потоками + +### 5.2 Анализ эффективности + +| Версия | Эффективность | Объяснение | +|--------|---------------|------------| +| STL | 22.3% | Наиболее эффективная реализация для данного размера данных | +| OMP | 21.0% | Небольшое отставание от STL, хорошее масштабирование | +| TBB | 18.2% | Планировщик задач вносит дополнительные накладные расходы | +| ALL | 18.2% | Накладные расходы OpenMP + адаптивная стратегия | + + + +## 6. Выводы + +### 6.1 Основные результаты + +1. **Все реализации корректны** и успешно проходят функциональные тесты на массивах различных типов (отсортированные, случайные, с повторениями, обратные). + +2. **Наилучшую производительность** для массива из 100 000 элементов показала версия на STL — 18 мс (ускорение 1.78x относительно SEQ). + +3. **OpenMP** показал результат 19 мс (ускорение 1.68x), что лишь на 1 мс хуже лидера. + +4. **TBB и ALL** показали одинаковый результат 22 мс (ускорение 1.45x). + +5. **Эффективность параллелизации** составляет от 18% до 22%, что объясняется: + - Относительно небольшим размером данных (100 000 элементов) + - Наличием последовательной части алгоритма (слияние Бэтчера) + - Накладными расходами на управление потоками + +## 7. Заключение + +В данной работе была успешно реализована и исследована гибридная сортировка Хоара-Бэтчера в пяти различных вариантах. Наилучшую производительность для массива из 100 000 элементов показала реализация на STL (std::async) с временем 18 мс, что в 1.78 раза быстрее последовательной версии. Реализация на OpenMP оказалась лишь на 1 мс медленнее, демонстрируя хороший баланс между производительностью и простотой реализации. TBB и комбинированная версия показали одинаковый результат 22 мс. + +Все реализации корректно работают на различных типах входных данных и могут быть рекомендованы для использования в зависимости от конкретных требований к производительности, переносимости и сложности реализации. + diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md new file mode 100644 index 0000000000..ee9a3cc49f --- /dev/null +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md @@ -0,0 +1,174 @@ +# Гибридная сортировка Хоара-Бэтчера (Quick Batcher Sort) + +- Студент: Краснопевцева Вероника Дмитриевна, группа 3823Б1ПМоп3 +- Технология: SEQ +- Вариант: 14 + +## 1. Введение + +Сортировка массивов данных — одна из фундаментальных задач в области программирования. Быстрая сортировка Хоара известна своей эффективностью в среднем случае, однако её производительность может деградировать при неудачном выборе опорного элемента. Сортировка Бэтчера (Batcher's odd-even merge sort) представляет собой алгоритм сортировки слиянием с хорошими свойствами параллелизации, но может быть избыточна для небольших массивов. + +Целью данной работы является реализация гибридного алгоритма, сочетающего адаптивный итеративный QuickSort с последующим слиянием Бэтчера для улучшения общей производительности. Ожидаемый результат — устойчивая работа алгоритма на массивах различных размеров с использованием оптимизаций, таких как сортировка вставками для малых подмассивов и медиана из трёх для выбора опорного элемента. + +## 2. Постановка задачи + +### Формальное определение +Необходимо реализовать последовательный алгоритм сортировки массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера. + +### Входные данные +- Вектор целых чисел `std::vector` произвольной длины. + +### Выходные данные +- Отсортированный по неубыванию вектор целых чисел. + +### Ограничения +- Алгоритм должен корректно обрабатывать массивы любого размера. +- Для малых подмассивов используется сортировка вставками. +- Для больших массивов после завершения быстрой сортировки выполняется слияние Бэтчера. + +## 3. Базовый алгоритм (последовательная версия) + +### 3.1 Быстрая сортировка (итеративная версия) +Используется нерекурсивная реализация через стек: + +1. В стек помещается пара `(left, right)` для всего массива. +2. Пока стек не пуст: + - Извлекается диапазон `[l, r]`. + - Если `l >= r` — пропуск. + - Если размер диапазона небольшой - вызывается сортировка вставками. + - Выбирается опорный элемент (медиана из трёх: `arr[l]`, `arr[mid]`, `arr[r]`). + - Выполняется разбиение Хоара. + - Меньший поддиапазон помещается в стек первым (для балансировки). + +### 3.2 Сортировка вставками +Для малых подмассивов применяется классическая сортировка вставками, так как она показывает хорошие результаты на почти отсортированных данных и малых размерах. + +### 3.3 Слияние Бэтчера +После завершения быстрой сортировки, если размер исходного диапазона превышает 32, вызывается `BatcherMerge`: + +- Создаётся временная копия диапазона `[left, right]`. +- Рекурсивно выполняется нечётно-чётное слияние. +- Выполняется постобработка для соседних элементов с шагом 2. + +## 4. Схема параллелизации + +Данная реализация является последовательной и не использует параллельные технологии. В рамках задания SEQ не предполагается параллелизация. + +## 5. Детали реализации + +### 5.1 Структура кода +- **Файл** `krasnopevtseva_v_hoare_batcher_sort/seq/include/ops_seq.hpp`, `krasnopevtseva_v_hoare_batcher_sort/seq/ops_seq.cpp` +- **Пространство имён** `krasnopevtseva_v_hoare_batcher_sort` +- **Класс** `KrasnopevtsevaVHoareBatcherSortSEQ` + +### 5.2 Основные методы +| Метод | Назначение | +|-------|-------------| +| `ValidationImpl()` | Проверка входных данных (массив не пуст) | +| `PreProcessingImpl()` | Инициализация выходного вектора | +| `RunImpl()` | Запуск сортировки | +| `QuickBatcherSort()` | Итеративная быстрая сортировка со стеком | +| `Partition()` | Разбиение Хоара с медианой из трёх | +| `InsertionSort()` | Сортировка вставками | +| `BatcherMerge()` | Слияние Бэтчера | +| `CompareAndSwap()` | Вспомогательная функция сравнения и обмена | + + +## 6. Окружение и тестирование + +### 6.1 Аппаратное и программное обеспечение +- **Процессор**: AMD Ryzen 7 5700X 8-Core Processor +- **ОЗУ**: 32.0 ГБ +- **ОС**: Windows 11 Pro 25H2 +- **Набор инструментов**: DevContainer с компилятором GCC (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 +- **Тип сборки**: Release +- **Фреймворк тестирования**: Google Test (gtest) +- **Режим**: SEQ + +### 6.2 Тестовые данные и верификация + +#### Функциональное тестирование + +Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: + +| № | Тип массива | Описание | Размер | +|---|-------------|----------|--------| +| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | +| 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | +| 3 | `short_array` | Короткий массив минимального размера | 4 | +| 4 | `long_array` | Длинный массив с повторяющимися и случайными значениями | 50 | +| 5 | `two_number_array` | Массив, содержащий только два различных значения (10 и 20) | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию (наихудший случай) | 24 | + + +#### Верификация результата + +Для каждого тестового набора проверялось выполнение основного инварианта сортировки: +- Для всех индексов `i` от 0 до `size-2` выполняется условие `output[i] <= output[i+1]` + +Дополнительно проводилось сравнение с эталонной реализацией `std::sort` на тех же входных данных для всех тестовых случаев. + +#### Тестирование производительности (перфоманс-тесты) + +Для оценки производительности был реализован отдельный тестовый набор, генерирующий массив из **100 000** случайных целых чисел в диапазоне `[0, 100 000 000]`. Генерация выполняется с использованием генератора псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости результатов. + +**Результаты корректности:** Все 6 функциональных тестов успешно пройдены для SEQ. Перфоманс-тесты также подтверждают корректную сортировку массива из 100 000 элементов. + +### 7 Производительность + +### 7.2 Производительность + +Результаты для массива из **100 000** случайных целых чисел: + +| Режим | Размер | Время,ms | Ускорение | Эффективность | +|-------|--------|----------|-----------|----------------| +| seq |100 000 | 32 ms | 1.00 | N/A | + +*Сравнение с параллельными версиями не предусмотрено для SEQ.* + +### 7.3 Анализ производительности +- Основной вклад вносят сравнения и перемещения во время разбиения и слияния. + + +## 8. Заключения + +### Основные выводы +1. Разработан гибридный последовательный алгоритм сортировки, сочетающий QuickSort, InsertionSort и BatcherMerge. +2. Алгоритм корректен и показывает приемлемую производительность для массивов до 10^6 элементов. +3. Использование сортировки вставками для подмассивов размера < 16 даёт выигрыш по сравнению с QuickSort. +4. Слияние Бэтчера улучшает порядок элементов после завершения QuickSort, но ценой дополнительной памяти. + + +## Приложение + +```cpp +void KrasnopevtsevaVHoareBatcherSortSEQ::QuickBatcherSort( + std::vector &arr, int left, int right) { + std::stack> stack; + stack.emplace(left, right); + + while (!stack.empty()) { + auto [l, r] = stack.top(); + stack.pop(); + + if (l >= r) continue; + if (r - l < 16) { + InsertionSort(arr, l, r); + continue; + } + + int pivot_index = Partition(arr, l, r); + + if (pivot_index - l < r - pivot_index) { + stack.emplace(pivot_index + 1, r); + stack.emplace(l, pivot_index - 1); + } else { + stack.emplace(l, pivot_index - 1); + stack.emplace(pivot_index + 1, r); + } + } + + if (right - left > 32) { + BatcherMerge(arr, left, right); + } +} \ No newline at end of file diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md new file mode 100644 index 0000000000..87606e1498 --- /dev/null +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md @@ -0,0 +1,359 @@ +# Гибридная сортировка Хоара-Бэтчера (Quick Batcher Sort) + +- Студент: Краснопевцева Вероника Дмитриевна, группа 3823Б1ПМоп3 +- Технология: STL +- Вариант: 14 + +## 1. Введение + +Сортировка массивов данных — одна из фундаментальных задач в области программирования. Быстрая сортировка Хоара известна своей эффективностью в среднем случае, однако её производительность может деградировать при неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой алгоритм сортировки слиянием с хорошими свойствами параллелизации, но может быть избыточна для небольших массивов. + +Целью данной работы является реализация параллельного гибридного алгоритма с использованием стандартной библиотеки шаблонов C++ (STL), в частности средств асинхронного выполнения `std::async` и `std::future`, сочетающего адаптивный итеративный QuickSort с последующим слиянием Бэтчера для улучшения общей производительности. Ожидаемый результат — устойчивая работа алгоритма на массивах различных размеров с использованием оптимизаций, таких как сортировка вставками для малых подмассивов и эффективное параллельное слияние. + +## 2. Постановка задачи + +### Формальное определение +Необходимо реализовать параллельный алгоритм сортировки массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием средств параллелизации из стандартной библиотеки C++ (STL). + +### Входные данные +- Вектор целых чисел `std::vector` произвольной длины. + +### Выходные данные +- Отсортированный по неубыванию вектор целых чисел. + +### Ограничения +- Алгоритм должен корректно обрабатывать массивы любого размера. +- Для малых подмассивов используется сортировка вставками. +- Параллельная обработка выполняется на этапе начальной сортировки подмассивов и на этапе слияния. +- Количество потоков определяется аппаратными возможностями системы. + +## 3. Базовый алгоритм (последовательная версия) + +### 3.1 Быстрая сортировка (итеративная версия) +Используется нерекурсивная реализация через стек: + +1. В стек помещается пара `(left, right)` для всего массива. +2. Пока стек не пуст: + - Извлекается диапазон `[l, r]`. + - Если `l >= r` — пропуск. + - Если размер диапазона небольшой - вызывается сортировка вставками. + - Выбирается опорный элемент (последний элемент массива). + - Выполняется разбиение Хоара. + - Меньший поддиапазон помещается в стек первым (для балансировки). + +### 3.2 Сортировка вставками +Для малых подмассивов (размер < 16) применяется классическая сортировка вставками, так как она показывает хорошие результаты на почти отсортированных данных и малых размерах. + +### 3.3 Слияние Бэтчера +После завершения быстрой сортировки выполняется слияние отсортированных блоков с использованием алгоритма Бэтчера с последовательным применением `std::inplace_merge`. + +## 4. Схема параллелизации + +### 4.1 Общая стратегия +Параллелизация реализована по принципу "разделяй и властвуй" с использованием средств STL: + +1. **Определение количества потоков**: Автоматическое определение аппаратной конкуренции через `std::thread::hardware_concurrency()`. +2. **Разбиение данных**: Исходный массив разделяется на `N` блоков, где `N` — количество доступных потоков. +3. **Параллельная сортировка**: Каждый блок сортируется асинхронно с использованием `std::async(std::launch::async, ...)`. +4. **Параллельное слияние**: Отсортированные блоки попарно сливаются с использованием иерархической схемы Бэтчера, где каждая пара сливается асинхронно. + +### 4.2 Распределение данных +- Размер блока для каждого потока: `base_size = n / numthreads` +- Остаток от деления добавляется к последнему блоку: `last_size = base_size + remainder` +- Каждый блок описывается структурой `Chunk`, содержащей указатель на данные, размер и границы (left, right) + +### 4.3 Коммуникационная схема +- На этапе сортировки: блоки сортируются независимо, обмен данными отсутствует. +- На этапе слияния: выполняется попарное слияние блоков с использованием `std::inplace_merge`. +- Слияние организовано в виде иерархического дерева (парное слияние на каждом уровне). +- После слияния блоков на уровне структура `chunks` обновляется (удаляются слитые блоки). + +### 4.4 Синхронизация +- Используется `std::future` и `std::async` для асинхронного выполнения задач. +- Синхронизация выполняется через вызов `future.wait()` для каждого асинхронного задания. +- Отсутствие гонок данных гарантируется тем, что каждый поток работает со своим выделенным диапазоном памяти. +- На этапе слияния используется условное распараллеливание через параметр `par_if_greater` (порог 32). + +## 5. Детали реализации + +### 5.1 Структура кода +- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/stl/include/ops_stl.hpp`, `krasnopevtseva_v_hoare_batcher_sort/stl/ops_stl.cpp` +- **Пространство имён**: `krasnopevtseva_v_hoare_batcher_sort` +- **Класс**: `KrasnopevtsevaVHoareBatcherSortSTL` + +### 5.2 Основные методы +| Метод | Назначение | +|-------|-------------| +| `ValidationImpl()` | Проверка входных данных (массив не пуст) | +| `PreProcessingImpl()` | Инициализация выходного вектора | +| `RunImpl()` | Запуск параллельной сортировки | +| `GetNumThreads()` | Определение оптимального количества потоков | +| `SetupChunks()` | Разбиение массива на блоки для параллельной обработки | +| `ParallelSortChunks()` | Асинхронная сортировка блоков | +| `QuickSort()` | Итеративная быстрая сортировка | +| `Partition()` | Разбиение Хоара (с последним опорным элементом) | +| `InsertionSort()` | Сортировка вставками для малых диапазонов | +| `BatcherMergeBlocksStep()` | Слияние двух блоков | +| `BatcherMergeLevel()` | Параллельное слияние на уровне иерархии | + +### 5.3 Структура Chunk +```cpp +struct Chunk { + int* ptr; // указатель на начало блока в массиве + int size; // размер блока + int left; // левая граница в исходном массиве + int right; // правая граница в исходном массиве +}; +``` +### 5.4 Важные особенности + +- **Автоматическое определение потоков**: Количество потоков определяется через `std::thread::hardware_concurrency()` с fallback на 1. +- **Выбор опорного элемента**: Используется последний элемент массива, что упрощает реализацию. +- **Порог сортировки вставками**: 16 элементов. +- **Условное распараллеливание слияния**: Слияние выполняется параллельно только если `(thread_input_size / step) > 32`. +- **Динамическое управление блоками**: Вектор `chunks` изменяет свой размер по мере слияния блоков. +- **Итеративный QuickSort**: Избегает переполнения стека вызовов. + +### 5.5 Использование памяти + +- Для отслеживания границ блоков используется вектор `chunks` размера O(numthreads). +- Асинхронные задачи создаются динамически на этапах сортировки и слияния. + +## 6. Окружение и тестирование + +### 6.1 Аппаратное и программное обеспечение + +- **Процессор**: AMD Ryzen 7 5700X 8-Core Processor +- **ОЗУ**: 32.0 ГБ +- **ОС**: Windows 11 Pro 25H2 +- **Набор инструментов**: DevContainer с компилятором GCC (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 +- **Тип сборки**: Release +- **Фреймворк тестирования**: Google Test (gtest) +- **Технология**: STL + +### 6.2 Тестовые данные и верификация + +#### Функциональное тестирование + +Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: + +| № | Тип массива | Описание | Размер | +|---|-------------|----------|--------| +| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | +| 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | +| 3 | `short_array` | Короткий массив минимального размера | 4 | +| 4 | `long_array` | Длинный массив с повторяющимися и случайными значениями | 50 | +| 5 | `two_number_array` | Массив, содержащий только два различных значения (10 и 20) | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию (наихудший случай) | 24 | + +#### Верификация результата + +Для каждого тестового набора проверялось выполнение основного инварианта сортировки: + +- Для всех индексов `i` от 0 до `size-2` выполняется условие `output[i] <= output[i+1]` + +Дополнительно проводилось сравнение с эталонной реализацией `std::sort` на тех же входных данных для всех тестовых случаев. + +#### Тестирование производительности + +Для оценки производительности был реализован отдельный тестовый набор, генерирующий массив из **100 000** случайных целых чисел в диапазоне `[0, 100 000 000]`. Генерация выполняется с использованием генератора псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости результатов. + +**Результаты корректности:** Все 6 функциональных тестов успешно пройдены для STL-версии. Перфоманс-тесты также подтверждают корректную сортировку массива из 100 000 элементов. + +## 7. Результаты и обсуждение + +### 7.1 Корректность + +Корректность параллельной реализации на STL подтверждена: + +- Сравнением результатов с последовательной версией на идентичных входных данных. +- Проверкой инварианта отсортированности для всех выходных массивов. +- Успешным прохождением всех функциональных тестов. + +### 7.2 Производительность + +Результаты для массива из **100 000** случайных целых чисел: + +| Реализация | Количество потоков | Время, мс | Ускорение | Эффективность | +|------------|--------------------|-----------|-----------|----------------| +| SEQ | 1 | 32.0 | 1.00 | N/A | +| STL | 8 | 18.0 | 1.78 | 22.3% | + +*Примечание: Количество потоков определяется автоматически через `std::thread::hardware_concurrency()`.* + +### 7.3 Анализ производительности + +#### Наблюдаемые эффекты + +- **Ускорение STL версии около 1.78x** при использовании аппаратно-определённого количества потоков (8). Это соответствует эффективности около 22.3%. +- Для массива из 100 000 элементов время выполнения составляет 18 мс, что в 1.78 раза быстрее последовательной версии (32 мс). + +#### Факторы, влияющие на производительность + +| Фактор | Влияние на STL версию | +|--------|----------------------| +| Накладные расходы std::async | Высокие (создание и управление future) | +| Параллельная сортировка блоков | Хорошее ускорение (блоки независимы) | +| Параллельное слияние | Умеренное ускорение (зависимость по данным) | +| Балансировка нагрузки | Средняя (последний блок может быть больше) | +| Условное распараллеливание | Положительное (избегает избыточности на малых данных) | + + +## 8. Заключения + +### Основные выводы + +1. Разработан параллельный гибридный алгоритм сортировки с использованием STL (std::async, std::future), сочетающий QuickSort, InsertionSort и BatcherMerge. +2. Алгоритм корректен и демонстрирует ускорение до 1.78x на 8 аппаратных потоках для массива из 100 000 элементов. +3. Средства STL обеспечивают хорошую переносимость, но уступают OpenMP в эффективности из-за более высоких накладных расходов на управление асинхронными задачами. +4. Сортировка вставками для подмассивов размера < 16 даёт выигрыш по сравнению с рекурсивным QuickSort. +5. Условное распараллеливание слияния позволяет адаптироваться под размер обрабатываемых данных. + + + + +## Приложение + +```cpp +bool KrasnopevtsevaVHoareBatcherSortSTL::RunImpl() { + const auto &input = GetInput(); + std::size_t size = input.size(); + + if (size <= 1) { + GetOutput() = input; + return true; + } + + std::vector res = input; + int n = static_cast(size); + int numthreads = GetNumThreads(n); + + std::vector chunks; + SetupChunks(chunks, n, numthreads); + + for (auto &chunk : chunks) { + chunk.ptr = res.data() + chunk.left; + } + + ParallelSortChunks(res, chunks); + + for (int step = 1; static_cast(chunks.size()) > 1; step *= 2) { + int pack = static_cast(chunks.size()); + BatcherMergeLevel(step, chunks, n / numthreads, 32); + + if ((pack / 2) - 1 == 0) { + BatcherMergeBlocksStep(chunks[0].ptr, chunks[0].size, chunks.back().ptr, chunks.back().size); + chunks[0].right = chunks.back().right; + chunks.resize(1); + } else if ((pack / 2) % 2 != 0) { + size_t idx1 = static_cast(2 * step) * static_cast((pack / 2) - 2); + size_t idx2 = idx1 + static_cast(step); + BatcherMergeBlocksStep(chunks[idx1].ptr, chunks[idx1].size, chunks[idx2].ptr, chunks[idx2].size); + chunks.erase(chunks.begin() + static_cast(idx2)); + } else { + int new_size = pack / 2; + chunks.resize(new_size); + } + } + + GetOutput() = std::move(res); + return true; +} + +void KrasnopevtsevaVHoareBatcherSortSTL::ParallelSortChunks(std::vector &arr, std::vector &chunks) { + std::vector> futures; + futures.reserve(chunks.size()); + + for (auto &chunk : chunks) { + futures.push_back(std::async(std::launch::async, [&arr, &chunk]() { + QuickSort(arr, chunk.left, chunk.right); + })); + } + + for (auto &f : futures) { + f.wait(); + } +} + +void KrasnopevtsevaVHoareBatcherSortSTL::BatcherMergeLevel(int step, std::vector &chunks, int thread_input_size, + int par_if_greater) { + int pack = static_cast(chunks.size()); + bool do_parallel = (thread_input_size / step) > par_if_greater; + + auto merge_pair = [&](int off) { + size_t idx1 = static_cast(2 * step) * static_cast(off); + size_t idx2 = idx1 + static_cast(step); + BatcherMergeBlocksStep(chunks[idx1].ptr, chunks[idx1].size, chunks[idx2].ptr, chunks[idx2].size); + chunks[idx1].right = chunks[idx2].right; + }; + + if (do_parallel) { + std::vector> futures; + futures.reserve(pack / 2); + for (int off = 0; off < pack / 2; ++off) { + futures.push_back(std::async(std::launch::async, merge_pair, off)); + } + for (auto &f : futures) { + f.wait(); + } + } else { + for (int off = 0; off < pack / 2; ++off) { + merge_pair(off); + } + } +} + +void KrasnopevtsevaVHoareBatcherSortSTL::QuickSort(std::vector &arr, int first, int last) { + std::stack> stack; + stack.emplace(first, last); + + while (!stack.empty()) { + auto [l, r] = stack.top(); + stack.pop(); + + if (l >= r) { + continue; + } + + if (r - l < 16) { + InsertionSort(arr, l, r); + continue; + } + + int iter = Partition(arr, l, r); + + if (iter - l < r - iter) { + stack.emplace(iter + 1, r); + stack.emplace(l, iter - 1); + } else { + stack.emplace(l, iter - 1); + stack.emplace(iter + 1, r); + } + } +} + +int KrasnopevtsevaVHoareBatcherSortSTL::Partition(std::vector &arr, int first, int last) { + int i = first - 1; + int value = arr[last]; + + for (int j = first; j <= last - 1; ++j) { + if (arr[j] <= value) { + ++i; + std::swap(arr[i], arr[j]); + } + } + std::swap(arr[i + 1], arr[last]); + return i + 1; +} + +void KrasnopevtsevaVHoareBatcherSortSTL::InsertionSort(std::vector &arr, int first, int last) { + for (int i = first + 1; i <= last; ++i) { + int key = arr[i]; + int j = i - 1; + while (j >= first && arr[j] > key) { + arr[j + 1] = arr[j]; + --j; + } + arr[j + 1] = key; + } +} \ No newline at end of file diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md new file mode 100644 index 0000000000..39cbf7eb91 --- /dev/null +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md @@ -0,0 +1,347 @@ +# Гибридная сортировка Хоара-Бэтчера (Quick Batcher Sort) + +- Студент: Краснопевцева Вероника Дмитриевна, группа 3823Б1ПМоп3 +- Технология: TBB +- Вариант: 14 + +## 1. Введение + +Сортировка массивов данных — одна из фундаментальных задач в области программирования. Быстрая сортировка Хоара известна своей эффективностью в среднем случае, однако её производительность может деградировать при неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой алгоритм сортировки слиянием с хорошими свойствами параллелизации, но может быть избыточна для небольших массивов. + +Целью данной работы является реализация параллельного гибридного алгоритма с использованием библиотеки Intel Threading Building Blocks (TBB), сочетающего адаптивный итеративный QuickSort с последующим слиянием Бэтчера для улучшения общей производительности. Ожидаемый результат — устойчивая работа алгоритма на массивах различных размеров с использованием оптимизаций, таких как сортировка вставками для малых подмассивов и эффективное параллельное слияние. + +## 2. Постановка задачи + +### Формальное определение + +Необходимо реализовать параллельный алгоритм сортировки массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием библиотеки Intel TBB. + +### Входные данные + +- Вектор целых чисел `std::vector` произвольной длины. + +### Выходные данные + +- Отсортированный по неубыванию вектор целых чисел. + +### Ограничения + +- Алгоритм должен корректно обрабатывать массивы любого размера. +- Для малых подмассивов используется сортировка вставками. +- Параллельная обработка выполняется на этапе начальной сортировки подмассивов и на этапе слияния. +- Количество потоков определяется аппаратными возможностями системы и контролируется через `tbb::global_control`. + +## 3. Базовый алгоритм (последовательная версия) + +### 3.1 Быстрая сортировка (итеративная версия) + +Используется нерекурсивная реализация через стек: + +1. В стек помещается пара `(left, right)` для всего массива. +2. Пока стек не пуст: + - Извлекается диапазон `[l, r]`. + - Если `l >= r` — пропуск. + - Если размер диапазона небольшой - вызывается сортировка вставками. + - Выбирается опорный элемент (последний элемент массива). + - Выполняется разбиение Хоара. + - Меньший поддиапазон помещается в стек первым (для балансировки). + +### 3.2 Сортировка вставками + +Для малых подмассивов (размер < 16) применяется классическая сортировка вставками, так как она показывает хорошие результаты на почти отсортированных данных и малых размерах. + +### 3.3 Слияние Бэтчера + +После завершения быстрой сортировки выполняется слияние отсортированных блоков с использованием алгоритма Бэтчера с последовательным применением `std::inplace_merge`. + +## 4. Схема параллелизации + +### 4.1 Общая стратегия + +Параллелизация реализована по принципу "разделяй и властвуй" с использованием библиотеки Intel TBB: + +1. **Определение количества потоков**: Автоматическое определение аппаратной конкуренции через `std::thread::hardware_concurrency()` с настройкой глобального контроля TBB через `tbb::global_control`. +2. **Разбиение данных**: Исходный массив разделяется на `N` блоков, где `N` — количество доступных потоков. +3. **Параллельная сортировка**: Каждый блок сортируется с использованием `tbb::parallel_for` с `simple_partitioner`. +4. **Параллельное слияние**: Отсортированные блоки попарно сливаются с использованием иерархической схемы Бэтчера, где каждая пара сливается параллельно через `tbb::parallel_for`. + +### 4.2 Распределение данных + +- Размер блока для каждого потока: `base_size = n / numthreads` +- Остаток от деления добавляется к последнему блоку: `last_size = base_size + remainder` +- Каждый поток получает указатель на начало своего блока и его размер +- TBB-планировщик автоматически распределяет итерации по доступным потокам + +### 4.3 Коммуникационная схема + +- На этапе сортировки: блоки сортируются независимо, обмен данными отсутствует. +- На этапе слияния: выполняется попарное слияние блоков с использованием `std::inplace_merge`. +- Слияние организовано в виде иерархического дерева (парное слияние на каждом уровне). +- TBB обеспечивает автоматическую балансировку нагрузки на этапе слияния. + +### 4.4 Синхронизация + +- Используется `tbb::parallel_for` с явным указанием `simple_partitioner`. +- TBB управляет созданием потоков и распределением работы. +- Отсутствие гонок данных гарантируется тем, что каждый поток работает со своим выделенным диапазоном памяти. +- На этапе слияния используется условное распараллеливание через параметр `par_if_greater` (порог 32). +- Глобальный контроль параллелизма через `tbb::global_control` ограничивает максимальное количество потоков. + +## 5. Детали реализации + +### 5.1 Структура кода + +- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/tbb/include/ops_tbb.hpp`, `krasnopevtseva_v_hoare_batcher_sort/tbb/ops_tbb.cpp` +- **Пространство имён**: `krasnopevtseva_v_hoare_batcher_sort` +- **Класс**: `KrasnopevtsevaVHoareBatcherSortTBB` + +### 5.2 Основные методы + +| Метод | Назначение | +|-------|-------------| +| `ValidationImpl()` | Проверка входных данных (массив не пуст) | +| `PreProcessingImpl()` | Инициализация выходного вектора | +| `RunImpl()` | Запуск параллельной сортировки | +| `QuickSort()` | Итеративная быстрая сортировка | +| `Partition()` | Разбиение Хоара (с последним опорным элементом) | +| `InsertionSort()` | Сортировка вставками для малых диапазонов | +| `BatcherMergeBlocksStep()` | Слияние двух блоков | +| `BatcherMerge()` | Параллельное слияние блоков с использованием TBB | + +### 5.3 Важные особенности + +- **Глобальный контроль параллелизма**: Используется `tbb::global_control` для установки максимального количества потоков на основе аппаратной конкуренции. +- **Планировщик TBB**: Применяется `tbb::simple_partitioner()` для минимальных накладных расходов при распределении итераций. +- **Выбор опорного элемента**: Используется последний элемент массива, что упрощает реализацию. +- **Порог сортировки вставками**: 16 элементов. +- **Условное распараллеливание слияния**: Слияние выполняется параллельно только если `(thread_input_size / step) > 32`. +- **Итеративный QuickSort**: Избегает переполнения стека вызовов. + +### 5.4 Использование памяти + +- Исходный массив сортируется на месте. +- Для отслеживания границ блоков используются массивы `pointers` и `sizes`. +- TBB управляет созданием и распределением задач с минимальными накладными расходами. +- Дополнительная память: O(numthreads) для хранения указателей на блоки. + +## 6. Окружение и тестирование + +### 6.1 Аппаратное и программное обеспечение + +- **Процессор**: AMD Ryzen 7 5700X 8-Core Processor +- **ОЗУ**: 32.0 ГБ +- **ОС**: Windows 11 Pro 25H2 +- **Набор инструментов**: DevContainer с компилятором GCC (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 +- **Тип сборки**: Release +- **Фреймворк тестирования**: Google Test (gtest) +- **Технология**: TBB + +### 6.2 Тестовые данные и верификация + +#### Функциональное тестирование + +Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: + +| № | Тип массива | Описание | Размер | +|---|-------------|----------|--------| +| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | +| 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | +| 3 | `short_array` | Короткий массив минимального размера | 4 | +| 4 | `long_array` | Длинный массив с повторяющимися и случайными значениями | 50 | +| 5 | `two_number_array` | Массив, содержащий только два различных значения (10 и 20) | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию (наихудший случай) | 24 | + +#### Верификация результата + +Для каждого тестового набора проверялось выполнение основного инварианта сортировки: + +- Для всех индексов `i` от 0 до `size-2` выполняется условие `output[i] <= output[i+1]` + +Дополнительно проводилось сравнение с эталонной реализацией `std::sort` на тех же входных данных для всех тестовых случаев. + +#### Тестирование производительности + +Для оценки производительности был реализован отдельный тестовый набор, генерирующий массив из **100 000** случайных целых чисел в диапазоне `[0, 100 000 000]`. Генерация выполняется с использованием генератора псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости результатов. + +**Результаты корректности:** Все 6 функциональных тестов успешно пройдены для TBB-версии. Перфоманс-тесты также подтверждают корректную сортировку массива из 100 000 элементов. + +## 7. Результаты и обсуждение + +### 7.1 Корректность + +Корректность параллельной реализации на TBB подтверждена: + +- Сравнением результатов с последовательной версией на идентичных входных данных. +- Проверкой инварианта отсортированности для всех выходных массивов. +- Успешным прохождением всех функциональных тестов. + +### 7.2 Производительность + +Результаты для массива из **100 000** случайных целых чисел: + +| Реализация | Количество потоков | Время, мс | Ускорение | Эффективность | +|------------|--------------------|-----------|-----------|----------------| +| SEQ | 1 | 32.0 | 1.00 | N/A | +| TBB | 8 | 22.0 | 1.45 | 18.2% | + +*Примечание: Количество потоков определяется автоматически через `std::thread::hardware_concurrency()` и контролируется через `tbb::global_control`.* + +### 7.3 Анализ производительности + +#### Наблюдаемые эффекты + +- **Ускорение TBB версии около 1.45x** при использовании аппаратно-определённого количества потоков (8). Это соответствует эффективности около 18.2%. +- Для массива из 100 000 элементов время выполнения составляет 22 мс, что в 1.45 раза быстрее последовательной версии (32 мс). + +#### Факторы, влияющие на производительность TBB + +| Фактор | Влияние на TBB версию | +|--------|----------------------| +| Планировщик задач | Умеренные накладные расходы | +| simple_partitioner | Минимизирует накладные расходы на разбиение | +| Глобальный контроль потоков | Позволяет ограничить параллелизм | +| Параллельная сортировка блоков | Хорошее ускорение (блоки независимы) | +| Параллельное слияние | Умеренное ускорение (зависимость по данным) | + + +## 8. Заключения + +### Основные выводы + +1. Разработан параллельный гибридный алгоритм сортировки с использованием Intel TBB, сочетающий QuickSort, InsertionSort и BatcherMerge. +2. Алгоритм корректен и демонстрирует ускорение до 1.45x на 8 аппаратных потоках для массива из 100 000 элементов. +3. Сортировка вставками для подмассивов размера < 16 даёт выигрыш по сравнению с рекурсивным QuickSort. +4. Условное распараллеливание слияния с использованием TBB позволяет адаптироваться под размер обрабатываемых данных. + + +## Приложение + +```cpp +bool KrasnopevtsevaVHoareBatcherSortTBB::RunImpl() { + const auto &input = GetInput(); + std::size_t size = input.size(); + + if (size <= 1) { + GetOutput() = input; + return true; + } + + int numthreads = static_cast(std::thread::hardware_concurrency()); + static tbb::global_control control(tbb::global_control::max_allowed_parallelism, numthreads); + + std::vector res = input; + + int n = static_cast(size); + numthreads = std::min(n, numthreads); + + int thread_input_size = n / numthreads; + int thread_input_remainder_size = n % numthreads; + + std::vector pointers(numthreads); + std::vector sizes(numthreads); + for (int i = 0; i < numthreads; ++i) { + auto offset = static_cast(i) * static_cast(thread_input_size); + pointers[i] = res.data() + offset; + sizes[i] = thread_input_size; + } + sizes[sizes.size() - 1] += thread_input_remainder_size; + + tbb::parallel_for(tbb::blocked_range(0, numthreads, 1), [&](const tbb::blocked_range &r) { + for (int i = r.begin(); i < r.end(); ++i) { + int left = static_cast(pointers[i] - res.data()); + int right = left + sizes[i] - 1; + QuickSort(res, left, right); + } + }, tbb::simple_partitioner()); + + BatcherMerge(thread_input_size, pointers, sizes, 32); + + GetOutput() = std::move(res); + return true; +} + +void KrasnopevtsevaVHoareBatcherSortTBB::BatcherMerge(int thread_input_size, std::vector &pointers, + std::vector &sizes, int par_if_greater) { + int pack = static_cast(pointers.size()); + for (int step = 1; pack > 1; step *= 2, pack /= 2) { + if ((thread_input_size / step) > par_if_greater) { + tbb::parallel_for(tbb::blocked_range(0, pack / 2, 1), [&](const tbb::blocked_range &r) { + for (int off = r.begin(); off < r.end(); ++off) { + auto idx1 = static_cast(2 * step) * static_cast(off); + auto idx2 = idx1 + static_cast(step); + BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); + } + }, tbb::simple_partitioner()); + } else { + for (int off = 0; off < pack / 2; ++off) { + auto idx1 = static_cast(2 * step) * static_cast(off); + auto idx2 = idx1 + static_cast(step); + BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); + } + } + + if ((pack / 2) - 1 == 0) { + BatcherMergeBlocksStep(pointers[0], sizes[sizes.size() - 1], pointers[pointers.size() - 1], + sizes[sizes.size() - 1]); + } else if ((pack / 2) % 2 != 0) { + auto idx1 = static_cast(2 * step) * static_cast((pack / 2) - 2); + auto idx2 = static_cast(2 * step) * static_cast((pack / 2) - 1); + BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); + } + } +} + +void KrasnopevtsevaVHoareBatcherSortTBB::QuickSort(std::vector &arr, int first, int last) { + std::stack> stack; + stack.emplace(first, last); + + while (!stack.empty()) { + auto [l, r] = stack.top(); + stack.pop(); + + if (l >= r) { + continue; + } + + if (r - l < 16) { + InsertionSort(arr, l, r); + continue; + } + + int iter = Partition(arr, l, r); + + if (iter - l < r - iter) { + stack.emplace(iter + 1, r); + stack.emplace(l, iter - 1); + } else { + stack.emplace(l, iter - 1); + stack.emplace(iter + 1, r); + } + } +} + +int KrasnopevtsevaVHoareBatcherSortTBB::Partition(std::vector &arr, int first, int last) { + int i = first - 1; + int value = arr[last]; + + for (int j = first; j <= last - 1; ++j) { + if (arr[j] <= value) { + ++i; + std::swap(arr[i], arr[j]); + } + } + std::swap(arr[i + 1], arr[last]); + return i + 1; +} + +void KrasnopevtsevaVHoareBatcherSortTBB::InsertionSort(std::vector &arr, int first, int last) { + for (int i = first + 1; i <= last; ++i) { + int key = arr[i]; + int j = i - 1; + while (j >= first && arr[j] > key) { + arr[j + 1] = arr[j]; + --j; + } + arr[j + 1] = key; + } +} \ No newline at end of file From 315ed7d76f6cc274d34f79e0197e659ef0037168 Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 14:28:48 +0000 Subject: [PATCH 02/12] fix reports --- .../all/report.md | 287 +++++++-------- .../omp/report.md | 175 +++++++--- .../report.md | 73 ++-- .../seq/report.md | 124 ++++--- .../stl/report.md | 330 ++++++++---------- .../tbb/report.md | 289 +++++++-------- 6 files changed, 667 insertions(+), 611 deletions(-) diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md index 3640b89625..c036500521 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md @@ -6,15 +6,29 @@ ## 1. Введение -Сортировка массивов данных — одна из фундаментальных задач в области программирования. Быстрая сортировка Хоара известна своей эффективностью в среднем случае, однако её производительность может деградировать при неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой алгоритм сортировки слиянием с хорошими свойствами параллелизации, но может быть избыточна для небольших массивов. - -Целью данной работы является реализация комбинированного параллельного гибридного алгоритма, использующего возможности как OpenMP, так и STL, сочетающего адаптивный итеративный QuickSort с последующим слиянием Бэтчера для улучшения общей производительности. Ожидаемый результат — устойчивая работа алгоритма на массивах различных размеров с использованием оптимизаций, таких как сортировка вставками для малых подмассивов и эффективное параллельное слияние, а также адаптивный выбор стратегии в зависимости от размера данных. +Сортировка массивов данных — одна из фундаментальных задач в области +программирования. Быстрая сортировка Хоара известна своей эффективностью +в среднем случае, однако её производительность может деградировать при +неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой +алгоритм сортировки слиянием с хорошими свойствами параллелизации, но +может быть избыточна для небольших массивов. + +Целью данной работы является реализация комбинированного параллельного +гибридного алгоритма, использующего возможности как OpenMP, так и STL, +сочетающего адаптивный итеративный QuickSort с последующим слиянием +Бэтчера для улучшения общей производительности. Ожидаемый результат — +устойчивая работа алгоритма на массивах различных размеров с +использованием оптимизаций, таких как сортировка вставками для малых +подмассивов и эффективное параллельное слияние, а также адаптивный выбор +стратегии в зависимости от размера данных. ## 2. Постановка задачи ### Формальное определение -Необходимо реализовать комбинированный параллельный алгоритм сортировки массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием технологий OpenMP и STL. +Необходимо реализовать комбинированный параллельный алгоритм сортировки +массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера с +использованием технологий OpenMP и STL. ### Входные данные @@ -28,9 +42,11 @@ - Алгоритм должен корректно обрабатывать массивы любого размера. - Для малых подмассивов используется сортировка вставками. -- Параллельная обработка выполняется на этапе начальной сортировки подмассивов и на этапе слияния. +- Параллельная обработка выполняется на этапе начальной сортировки + подмассивов и на этапе слияния. - Количество потоков определяется через `omp_get_max_threads()`. -- Реализована адаптивная стратегия: для малых массивов (n < 1000) используется последовательная сортировка. +- Реализована адаптивная стратегия: для малых массивов (n < 1000) + используется последовательная сортировка. ## 3. Базовый алгоритм (последовательная версия) @@ -49,50 +65,72 @@ ### 3.2 Сортировка вставками -Для малых подмассивов (размер < 16) применяется классическая сортировка вставками, так как она показывает хорошие результаты на почти отсортированных данных и малых размерах. +Для малых подмассивов (размер < 16) применяется классическая сортировка +вставками, так как она показывает хорошие результаты на почти +отсортированных данных и малых размерах. ### 3.3 Слияние Бэтчера -После завершения быстрой сортировки выполняется слияние отсортированных блоков с использованием алгоритма Бэтчера с последовательным применением `std::inplace_merge`. +После завершения быстрой сортировки выполняется слияние отсортированных +блоков с использованием алгоритма Бэтчера с последовательным применением +`std::inplace_merge`. ## 4. Схема параллелизации ### 4.1 Общая стратегия -Параллелизация реализована по принципу "разделяй и властвуй" с комбинированным использованием OpenMP и STL: - -1. **Адаптивный выбор стратегии**: Если размер массива менее 1000 элементов, используется последовательная сортировка (QuickSort), иначе — параллельная. -2. **Определение количества потоков**: Количество потоков определяется через `omp_get_max_threads()`. -3. **Разбиение данных**: Исходный массив разделяется на `N` блоков, где `N` — количество доступных потоков. -4. **Параллельная сортировка**: Каждый блок сортируется с использованием директивы `#pragma omp parallel for`. -5. **Параллельное слияние**: Отсортированные блоки попарно сливаются с использованием иерархической схемы Бэтчера, где каждая пара сливается параллельно через OpenMP. +Параллелизация реализована по принципу "разделяй и властвуй" с +комбинированным использованием OpenMP и STL: + +1. **Адаптивный выбор стратегии**: Если размер массива менее 1000 + элементов, используется последовательная сортировка (QuickSort), + иначе — параллельная. +2. **Определение количества потоков**: Количество потоков определяется + через `omp_get_max_threads()`. +3. **Разбиение данных**: Исходный массив разделяется на `N` блоков, + где `N` — количество доступных потоков. +4. **Параллельная сортировка**: Каждый блок сортируется с + использованием директивы `#pragma omp parallel for`. +5. **Параллельное слияние**: Отсортированные блоки попарно сливаются + с использованием иерархической схемы Бэтчера, где каждая пара + сливается параллельно через OpenMP. ### 4.2 Распределение данных - Размер блока для каждого потока: `base_size = n / numthreads` -- Остаток от деления добавляется к последнему блоку: `last_size = base_size + remainder` +- Остаток от деления добавляется к последнему блоку: + `last_size = base_size + remainder` - Каждый поток получает указатель на начало своего блока и его размер - OpenMP автоматически распределяет итерации по доступным потокам ### 4.3 Коммуникационная схема -- На этапе сортировки: блоки сортируются независимо, обмен данными отсутствует. -- На этапе слияния: выполняется попарное слияние блоков с использованием `std::inplace_merge`. -- Слияние организовано в виде иерархического дерева (парное слияние на каждом уровне). -- OpenMP обеспечивает параллельное выполнение операций слияния на каждом уровне. +- На этапе сортировки: блоки сортируются независимо, обмен данными + отсутствует. +- На этапе слияния: выполняется попарное слияние блоков с + использованием `std::inplace_merge`. +- Слияние организовано в виде иерархического дерева (парное слияние + на каждом уровне). +- OpenMP обеспечивает параллельное выполнение операций слияния на + каждом уровне. ### 4.4 Синхронизация -- Используется `#pragma omp parallel for` с явным указанием shared-переменных. -- Отсутствие гонок данных гарантируется тем, что каждый поток работает со своим выделенным диапазоном памяти. -- На этапе слияния используется условное распараллеливание через параметр `par_if_greater` (порог 32). -- Адаптивная стратегия позволяет избежать накладных расходов на малых массивах. +- Используется `#pragma omp parallel for` с явным указанием + shared-переменных. +- Отсутствие гонок данных гарантируется тем, что каждый поток работает + со своим выделенным диапазоном памяти. +- На этапе слияния используется условное распараллеливание через + параметр `par_if_greater` (порог 32). +- Адаптивная стратегия позволяет избежать накладных расходов на малых + массивах. ## 5. Детали реализации ### 5.1 Структура кода -- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/all/include/ops_all.hpp`, `krasnopevtseva_v_hoare_batcher_sort/all/ops_all.cpp` +- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/all/include/ops_all.hpp`, + `krasnopevtseva_v_hoare_batcher_sort/all/ops_all.cpp` - **Пространство имён**: `krasnopevtseva_v_hoare_batcher_sort` - **Класс**: `KrasnopevtsevaVHoareBatcherSortALL` @@ -104,28 +142,35 @@ | `PreProcessingImpl()` | Инициализация выходного вектора | | `RunImpl()` | Запуск комбинированной сортировки | | `SortLocalData()` | Адаптивный выбор стратегии сортировки | -| `ParallelSortChunks()` | Параллельная сортировка блоков с использованием OpenMP | +| `ParallelSortChunks()` | Параллельная сортировка блоков | | `QuickSort()` | Итеративная быстрая сортировка | -| `Partition()` | Разбиение Хоара (с последним опорным элементом) | -| `InsertionSort()` | Сортировка вставками для малых диапазонов | +| `Partition()` | Разбиение Хоара | +| `InsertionSort()` | Сортировка вставками | | `BatcherMergeBlocksStep()` | Слияние двух блоков | -| `BatcherMerge()` | Параллельное слияние блоков с использованием OpenMP | +| `BatcherMerge()` | Параллельное слияние блоков | ### 5.3 Важные особенности -- **Адаптивная стратегия**: Для массивов размером менее 1000 элементов используется последовательная сортировка, что позволяет избежать накладных расходов на создание потоков. -- **Выбор опорного элемента**: Используется последний элемент массива, что упрощает реализацию. +- **Адаптивная стратегия**: Для массивов размером менее 1000 элементов + используется последовательная сортировка, что позволяет избежать + накладных расходов на создание потоков. +- **Выбор опорного элемента**: Используется последний элемент массива, + что упрощает реализацию. - **Порог сортировки вставками**: 16 элементов. -- **Условное распараллеливание слияния**: Слияние выполняется параллельно только если `(thread_input_size / step) > 32`. +- **Условное распараллеливание слияния**: Слияние выполняется параллельно + только если `(thread_input_size / step) > 32`. - **Итеративный QuickSort**: Избегает переполнения стека вызовов. -- **Комбинированный подход**: Сочетает директивы OpenMP для параллельных регионов и стандартную библиотеку для базовых операций. +- **Комбинированный подход**: Сочетает директивы OpenMP для параллельных + регионов и стандартную библиотеку для базовых операций. ### 5.4 Использование памяти - Исходный массив сортируется на месте. -- Для отслеживания границ блоков используются массивы `pointers` и `sizes`. +- Для отслеживания границ блоков используются массивы `pointers` и + `sizes`. - OpenMP управляет созданием и распределением потоков. -- Дополнительная память: O(numthreads) для хранения указателей на блоки. +- Дополнительная память: O(numthreads) для хранения указателей на + блоки. ## 6. Окружение и тестирование @@ -134,10 +179,11 @@ - **Процессор**: AMD Ryzen 7 5700X 8-Core Processor - **ОЗУ**: 32.0 ГБ - **ОС**: Windows 11 Pro 25H2 -- **Набор инструментов**: DevContainer с компилятором GCC (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 +- **Набор инструментов**: DevContainer с компилятором GCC + (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 - **Тип сборки**: Release - **Фреймворк тестирования**: Google Test (gtest) -- **Технологии**: OpenMP + STL +- **Технологии**: OpenMP + STL ### 6.2 Переменные окружения @@ -147,30 +193,40 @@ #### Функциональное тестирование -Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: +Для проверки базовой корректности алгоритма был разработан набор из 6 +тестовых сценариев, покрывающих различные типы входных данных: | № | Тип массива | Описание | Размер | |---|-------------|----------|--------| | 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | +| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторяющимися и случайными значениями | 50 | -| 5 | `two_number_array` | Массив, содержащий только два различных значения (10 и 20) | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию (наихудший случай) | 24 | +| 4 | `long_array` | Длинный массив с повторениями | 50 | +| 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | #### Верификация результата -Для каждого тестового набора проверялось выполнение основного инварианта сортировки: +Для каждого тестового набора проверялось выполнение основного инварианта +сортировки: -- Для всех индексов `i` от 0 до `size-2` выполняется условие `output[i] <= output[i+1]` +- Для всех индексов `i` от 0 до `size-2` выполняется условие + `output[i] <= output[i+1]` -Дополнительно проводилось сравнение с эталонной реализацией `std::sort` на тех же входных данных для всех тестовых случаев. +Дополнительно проводилось сравнение с эталонной реализацией `std::sort` +на тех же входных данных для всех тестовых случаев. #### Тестирование производительности -Для оценки производительности был реализован отдельный тестовый набор, генерирующий массив из **100 000** случайных целых чисел в диапазоне `[0, 100 000 000]`. Генерация выполняется с использованием генератора псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости результатов. +Для оценки производительности был реализован отдельный тестовый набор, +генерирующий массив из **100 000** случайных целых чисел в диапазоне +`[0, 100 000 000]`. Генерация выполняется с использованием генератора +псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости +результатов. -**Результаты корректности:** Все 6 функциональных тестов успешно пройдены для ALL-версии. Перфоманс-тесты также подтверждают корректную сортировку массива из 100 000 элементов. +**Результаты корректности:** Все 6 функциональных тестов успешно пройдены +для ALL-версии. Перфоманс-тесты также подтверждают корректную сортировку +массива из 100 000 элементов. ## 7. Результаты и обсуждение @@ -178,7 +234,8 @@ Корректность комбинированной реализации на ALL (OpenMP + STL) подтверждена: -- Сравнением результатов с последовательной версией на идентичных входных данных. +- Сравнением результатов с последовательной версией на идентичных + входных данных. - Проверкой инварианта отсортированности для всех выходных массивов. - Успешным прохождением всех функциональных тестов. - Проверкой адаптивной стратегии на массивах различных размеров. @@ -187,40 +244,44 @@ Результаты для массива из **100 000** случайных целых чисел: -| Реализация | Количество потоков | Время, мс | Ускорение | Эффективность | -|------------|--------------------|-----------|-----------|----------------| -| SEQ | 1 | 32.0 | 1.00 | N/A | -| ALL | 8 | 22.0 | 1.45 | 18.2% | - +| Реализация | Потоков | Время, мс | Ускорение | Эффективность | +|------------|---------|-----------|-----------|---------------| +| SEQ | 1 | 32.0 | 1.00 | N/A | +| ALL | 8 | 22.0 | 1.45 | 18.2% | ### 7.3 Анализ производительности #### Наблюдаемые эффекты -- **Ускорение ALL версии около 1.45x** при использовании 8 потоков. Это соответствует эффективности около 18.2%. - +- **Ускорение ALL версии около 1.45x** при использовании 8 потоков. + Это соответствует эффективности около 18.2%. #### Факторы, влияющие на производительность ALL версии | Фактор | Влияние на ALL версию | |--------|----------------------| -| Адаптивная стратегия (n < 1000) | Положительное (избегает накладных расходов) | -| Накладные расходы OpenMP | Умеренные (fork-join модель) | -| Параллельная сортировка блоков | Хорошее ускорение (блоки независимы) | -| Параллельное слияние | Умеренное ускорение (зависимость по данным) | -| Балансировка нагрузки | Средняя (последний блок может быть больше) | - +| Адаптивная стратегия (n < 1000) | Положительное | +| Накладные расходы OpenMP | Умеренные | +| Параллельная сортировка блоков | Хорошее ускорение | +| Параллельное слияние | Умеренное ускорение | +| Балансировка нагрузки | Средняя | ## 8. Заключения ### Основные выводы -1. Разработан комбинированный параллельный гибридный алгоритм сортировки с использованием OpenMP и STL, сочетающий QuickSort, InsertionSort и BatcherMerge. -2. Алгоритм корректен и демонстрирует ускорение до 1.45x на 8 потоках для массива из 100 000 элементов. -3. Реализована адаптивная стратегия: для массивов размером менее 1000 элементов используется последовательная сортировка, что позволяет избежать накладных расходов на создание потоков. -4. Сортировка вставками для подмассивов размера < 16 даёт выигрыш по сравнению с рекурсивным QuickSort. -5. Условное распараллеливание слияния позволяет адаптироваться под размер обрабатываемых данных. - +1. Разработан комбинированный параллельный гибридный алгоритм сортировки + с использованием OpenMP и STL, сочетающий QuickSort, InsertionSort + и BatcherMerge. +2. Алгоритм корректен и демонстрирует ускорение до 1.45x на 8 потоках + для массива из 100 000 элементов. +3. Реализована адаптивная стратегия: для массивов размером менее 1000 + элементов используется последовательная сортировка, что позволяет + избежать накладных расходов на создание потоков. +4. Сортировка вставками для подмассивов размера < 16 даёт выигрыш по + сравнению с рекурсивным QuickSort. +5. Условное распараллеливание слияния позволяет адаптироваться под + размер обрабатываемых данных. ## Приложение @@ -255,7 +316,8 @@ void KrasnopevtsevaVHoareBatcherSortALL::SortLocalData(std::vector &data) { } } -void KrasnopevtsevaVHoareBatcherSortALL::ParallelSortChunks(std::vector &arr, int n, int numthreads) { +void KrasnopevtsevaVHoareBatcherSortALL::ParallelSortChunks( + std::vector &arr, int n, int numthreads) { if (n <= 0) { return; } @@ -272,7 +334,8 @@ void KrasnopevtsevaVHoareBatcherSortALL::ParallelSortChunks(std::vector &ar std::vector sizes(numthreads); for (int i = 0; i < numthreads; ++i) { - std::ptrdiff_t offset = static_cast(i) * static_cast(thread_input_size); + std::ptrdiff_t offset = static_cast(i) * + static_cast(thread_input_size); pointers[i] = arr.data() + offset; sizes[i] = thread_input_size; } @@ -288,90 +351,4 @@ void KrasnopevtsevaVHoareBatcherSortALL::ParallelSortChunks(std::vector &ar } BatcherMerge(thread_input_size, pointers, sizes, 32); -} - -void KrasnopevtsevaVHoareBatcherSortALL::BatcherMerge(int thread_input_size, std::vector &pointers, - std::vector &sizes, int par_if_greater) { - int pack = static_cast(pointers.size()); - for (int step = 1; pack > 1; step *= 2, pack /= 2) { - bool do_parallel = (thread_input_size / step) > par_if_greater; - - if (do_parallel) { -#pragma omp parallel for default(none) shared(pointers, sizes, pack, step) - for (int off = 0; off < pack / 2; ++off) { - auto idx1 = static_cast(2 * step) * static_cast(off); - auto idx2 = idx1 + static_cast(step); - BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); - } - } else { - for (int off = 0; off < pack / 2; ++off) { - auto idx1 = static_cast(2 * step) * static_cast(off); - auto idx2 = idx1 + static_cast(step); - BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); - } - } - - if ((pack / 2) - 1 == 0) { - BatcherMergeBlocksStep(pointers[0], sizes[sizes.size() - 1], pointers[pointers.size() - 1], - sizes[sizes.size() - 1]); - } else if ((pack / 2) % 2 != 0) { - auto idx1 = static_cast(2 * step) * static_cast((pack / 2) - 2); - auto idx2 = static_cast(2 * step) * static_cast((pack / 2) - 1); - BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); - } - } -} - -void KrasnopevtsevaVHoareBatcherSortALL::QuickSort(std::vector &arr, int first, int last) { - std::stack> stack; - stack.emplace(first, last); - - while (!stack.empty()) { - auto [l, r] = stack.top(); - stack.pop(); - - if (l >= r) { - continue; - } - - if (r - l < 16) { - InsertionSort(arr, l, r); - continue; - } - - int iter = Partition(arr, l, r); - - if (iter - l < r - iter) { - stack.emplace(iter + 1, r); - stack.emplace(l, iter - 1); - } else { - stack.emplace(l, iter - 1); - stack.emplace(iter + 1, r); - } - } -} - -int KrasnopevtsevaVHoareBatcherSortALL::Partition(std::vector &arr, int first, int last) { - int i = first - 1; - int value = arr[last]; - - for (int j = first; j <= last - 1; ++j) { - if (arr[j] <= value) { - ++i; - std::swap(arr[i], arr[j]); - } - } - std::swap(arr[i + 1], arr[last]); - return i + 1; -} -void KrasnopevtsevaVHoareBatcherSortALL::InsertionSort(std::vector &arr, int first, int last) { - for (int i = first + 1; i <= last; ++i) { - int key = arr[i]; - int j = i - 1; - while (j >= first && arr[j] > key) { - arr[j + 1] = arr[j]; - --j; - } - arr[j + 1] = key; - } } \ No newline at end of file diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md index 65e110d279..9dd0fbebc1 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md @@ -6,29 +6,48 @@ ## 1. Введение -Сортировка массивов данных — одна из фундаментальных задач в области программирования. Быстрая сортировка Хоара известна своей эффективностью в среднем случае, однако её производительность может деградировать при неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой алгоритм сортировки слиянием с хорошими свойствами параллелизации, но может быть избыточна для небольших массивов. - -Целью данной работы является реализация параллельного гибридного алгоритма с использованием технологии OpenMP, сочетающего адаптивный итеративный QuickSort с последующим слиянием Бэтчера для улучшения общей производительности. Ожидаемый результат — устойчивая работа алгоритма на массивах различных размеров с использованием оптимизаций, таких как сортировка вставками для малых подмассивов и эффективное параллельное слияние. +Сортировка массивов данных — одна из фундаментальных задач в области +программирования. Быстрая сортировка Хоара известна своей эффективностью +в среднем случае, однако её производительность может деградировать при +неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой +алгоритм сортировки слиянием с хорошими свойствами параллелизации, но +может быть избыточна для небольших массивов. + +Целью данной работы является реализация параллельного гибридного +алгоритма с использованием технологии OpenMP, сочетающего адаптивный +итеративный QuickSort с последующим слиянием Бэтчера для улучшения +общей производительности. Ожидаемый результат — устойчивая работа +алгоритма на массивах различных размеров с использованием оптимизаций, +таких как сортировка вставками для малых подмассивов и эффективное +параллельное слияние. ## 2. Постановка задачи ### Формальное определение -Необходимо реализовать параллельный алгоритм сортировки массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием технологии OpenMP. + +Необходимо реализовать параллельный алгоритм сортировки массива целых +чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием +технологии OpenMP. ### Входные данные + - Вектор целых чисел `std::vector` произвольной длины. ### Выходные данные + - Отсортированный по неубыванию вектор целых чисел. ### Ограничения + - Алгоритм должен корректно обрабатывать массивы любого размера. - Для малых подмассивов используется сортировка вставками. -- Параллельная обработка выполняется на этапе начальной сортировки подмассивов и на этапе слияния. +- Параллельная обработка выполняется на этапе начальной сортировки + подмассивов и на этапе слияния. ## 3. Базовый алгоритм (последовательная версия) ### 3.1 Быстрая сортировка (итеративная версия) + Используется нерекурсивная реализация через стек: 1. В стек помещается пара `(left, right)` для всего массива. @@ -41,43 +60,67 @@ - Меньший поддиапазон помещается в стек первым (для балансировки). ### 3.2 Сортировка вставками -Для малых подмассивов (размер < 16) применяется классическая сортировка вставками, так как она показывает хорошие результаты на почти отсортированных данных и малых размерах. + +Для малых подмассивов (размер < 16) применяется классическая сортировка +вставками, так как она показывает хорошие результаты на почти +отсортированных данных и малых размерах. ### 3.3 Слияние Бэтчера -После завершения быстрой сортировки выполняется слияние отсортированных блоков с использованием алгоритма Бэтчера с последовательным применением `std::inplace_merge`. + +После завершения быстрой сортировки выполняется слияние отсортированных +блоков с использованием алгоритма Бэтчера с последовательным применением +`std::inplace_merge`. ## 4. Схема параллелизации ### 4.1 Общая стратегия -Параллелизация реализована по принципу "разделяй и властвуй" с использованием OpenMP: -1. **Разбиение данных**: Исходный массив разделяется на `N` блоков, где `N` — количество доступных потоков. -2. **Параллельная сортировка**: Каждый поток независимо сортирует свой блок с использованием итеративного QuickSort. -3. **Параллельное слияние**: Отсортированные блоки попарно сливаются с использованием параллельного алгоритма Бэтчера. +Параллелизация реализована по принципу "разделяй и властвуй" с +использованием OpenMP: + +1. **Разбиение данных**: Исходный массив разделяется на `N` блоков, + где `N` — количество доступных потоков. +2. **Параллельная сортировка**: Каждый поток независимо сортирует свой + блок с использованием итеративного QuickSort. +3. **Параллельное слияние**: Отсортированные блоки попарно сливаются + с использованием параллельного алгоритма Бэтчера. ### 4.2 Распределение данных + - Размер блока для каждого потока: `base_size = n / numthreads` -- Остаток от деления добавляется к последнему блоку: `last_size = base_size + remainder` +- Остаток от деления добавляется к последнему блоку: + `last_size = base_size + remainder` - Каждый поток получает указатель на начало своего блока и его размер ### 4.3 Коммуникационная схема -- На этапе сортировки: потоки работают независимо, обмен данными отсутствует. -- На этапе слияния: выполняется попарное слияние блоков с использованием `std::inplace_merge`. -- Слияние организовано в виде иерархического дерева (парное слияние на каждом уровне). + +- На этапе сортировки: потоки работают независимо, обмен данными + отсутствует. +- На этапе слияния: выполняется попарное слияние блоков с + использованием `std::inplace_merge`. +- Слияние организовано в виде иерархического дерева (парное слияние + на каждом уровне). ### 4.4 Синхронизация -- Используется `#pragma omp parallel for` с явным указанием shared-переменных. -- Отсутствие гонок данных гарантируется тем, что каждый поток работает со своим выделенным диапазоном памяти. -- На этапе слияния используется `#pragma omp parallel for` с порогом `par_if_greater` для распараллеливания. + +- Используется `#pragma omp parallel for` с явным указанием + shared-переменных. +- Отсутствие гонок данных гарантируется тем, что каждый поток работает + со своим выделенным диапазоном памяти. +- На этапе слияния используется `#pragma omp parallel for` с порогом + `par_if_greater` для распараллеливания. ## 5. Детали реализации ### 5.1 Структура кода -- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/omp/include/ops_omp.hpp`, `krasnopevtseva_v_hoare_batcher_sort/omp/ops_omp.cpp` + +- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/omp/include/ops_omp.hpp`, + `krasnopevtseva_v_hoare_batcher_sort/omp/ops_omp.cpp` - **Пространство имён**: `krasnopevtseva_v_hoare_batcher_sort` - **Класс**: `KrasnopevtsevaVHoareBatcherSortOMP` ### 5.2 Основные методы + | Метод | Назначение | |-------|-------------| | `ValidationImpl()` | Проверка входных данных (массив не пуст) | @@ -90,92 +133,121 @@ | `BatcherMerge()` | Параллельное слияние блоков | ### 5.3 Важные особенности -- **Выбор опорного элемента**: В параллельной версии используется последний элемент массива, что упрощает реализацию. -- **Порог сортировки вставками**: 16 элементов (эмпирически оптимальное значение). -- **Условное распараллеливание слияния**: Слияние выполняется параллельно только если `thread_input_size / step > par_if_greater` (параметр 32). + +- **Выбор опорного элемента**: В параллельной версии используется + последний элемент массива, что упрощает реализацию. +- **Порог сортировки вставками**: 16 элементов. +- **Условное распараллеливание слияния**: Слияние выполняется параллельно + только если `thread_input_size / step > par_if_greater` (параметр 32). - **Итеративный QuickSort**: Избегает переполнения стека вызовов. -- **Особая обработка граничных случаев**: Учитываются ситуации с нечётным количеством блоков. +- **Особая обработка граничных случаев**: Учитываются ситуации с + нечётным количеством блоков. ### 5.4 Использование памяти + - Исходный массив сортируется на месте. -- Для отслеживания границ блоков используются массивы `pointers` и `sizes`. -- Дополнительная память: O(numthreads) для хранения указателей на блоки. +- Для отслеживания границ блоков используются массивы `pointers` и + `sizes`. +- Дополнительная память: O(numthreads) для хранения указателей на + блоки. ## 6. Окружение и тестирование ### 6.1 Аппаратное и программное обеспечение + - **Процессор**: AMD Ryzen 7 5700X 8-Core Processor - **ОЗУ**: 32.0 ГБ - **ОС**: Windows 11 Pro 25H2 -- **Набор инструментов**: DevContainer с компилятором GCC (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 +- **Набор инструментов**: DevContainer с компилятором GCC + (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 - **Тип сборки**: Release - **Фреймворк тестирования**: Google Test (gtest) - **Технология**: OpenMP ### 6.2 Переменные окружения -- `OMP_NUM_THREADS` — количество потоков OpenMP + +- `OMP_NUM_THREADS` — количество потоков OpenMP ### 6.3 Тестовые данные и верификация #### Функциональное тестирование -Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: +Для проверки базовой корректности алгоритма был разработан набор из 6 +тестовых сценариев, покрывающих различные типы входных данных: | № | Тип массива | Описание | Размер | |---|-------------|----------|--------| | 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | +| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторяющимися и случайными значениями | 50 | -| 5 | `two_number_array` | Массив, содержащий только два различных значения (10 и 20) | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию (наихудший случай) | 24 | +| 4 | `long_array` | Длинный массив с повторениями | 50 | +| 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | #### Верификация результата -Для каждого тестового набора проверялось выполнение основного инварианта сортировки: -- Для всех индексов `i` от 0 до `size-2` выполняется условие `output[i] <= output[i+1]` +Для каждого тестового набора проверялось выполнение основного инварианта +сортировки: + +- Для всех индексов `i` от 0 до `size-2` выполняется условие + `output[i] <= output[i+1]` -Дополнительно проводилось сравнение с эталонной реализацией `std::sort` на тех же входных данных для всех тестовых случаев. +Дополнительно проводилось сравнение с эталонной реализацией `std::sort` +на тех же входных данных для всех тестовых случаев. -#### Тестирование производительности (перфоманс-тесты) +#### Тестирование производительности -Для оценки производительности был реализован отдельный тестовый набор, генерирующий массив из **100 000** случайных целых чисел в диапазоне `[0, 100 000 000]`. Генерация выполняется с использованием генератора псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости результатов. +Для оценки производительности был реализован отдельный тестовый набор, +генерирующий массив из **100 000** случайных целых чисел в диапазоне +`[0, 100 000 000]`. Генерация выполняется с использованием генератора +псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости +результатов. -**Результаты корректности:** Все 6 функциональных тестов успешно пройдены для всех конфигураций количества потоков (1, 2, 4, 8). Перфоманс-тесты также подтверждают корректную сортировку массива из 100 000 элементов. +**Результаты корректности:** Все 6 функциональных тестов успешно пройдены +для всех конфигураций количества потоков (1, 2, 4, 8). Перфоманс-тесты +также подтверждают корректную сортировку массива из 100 000 элементов. ## 7. Результаты и обсуждение ### 7.1 Корректность + Корректность параллельной реализации подтверждена: -- Сравнением результатов с последовательной версией на идентичных входных данных. + +- Сравнением результатов с последовательной версией на идентичных + входных данных. - Проверкой инварианта отсортированности для всех выходных массивов. -- Успешным прохождением всех функциональных тестов для различных конфигураций потоков. +- Успешным прохождением всех функциональных тестов для различных + конфигураций потоков. ### 7.2 Производительность Результаты для массива из **100 000** случайных целых чисел: -| Режим | Количество потоков | Время, мс | Ускорение | Эффективность | -|-------|--------------------|-----------|-----------|----------------| -| seq | 1 | 32 | 1.00 | N/A | -| omp | 8 | 19 | 1.94 | 24.3% | - - +| Режим | Потоков | Время, мс | Ускорение | Эффективность | +|-------|---------|-----------|-----------|---------------| +| seq | 1 | 32 | 1.00 | N/A | +| omp | 8 | 19 | 1.94 | 24.3% | ### 7.3 Анализ производительности #### Масштабирование -- При использовании 8 потоков ускорение достигает 1.94x (эффективность 24.3%). +- При использовании 8 потоков ускорение достигает 1.94x + (эффективность 24.3%). ## 8. Заключения ### Основные выводы -1. Разработан параллельный гибридный алгоритм сортировки с использованием OpenMP, сочетающий QuickSort, InsertionSort и BatcherMerge. -2. При использовании 8 потоков ускорение достигает 1.94x, что подтверждает эффективность параллелизации для массивов большого размера. + +1. Разработан параллельный гибридный алгоритм сортировки с использованием + OpenMP, сочетающий QuickSort, InsertionSort и BatcherMerge. +2. При использовании 8 потоков ускорение достигает 1.94x, что подтверждает + эффективность параллелизации для массивов большого размера. ### Ограничения -- Снижение эффективности при использовании большого количества потоков из-за накладных расходов на слияние. + +- Снижение эффективности при использовании большого количества потоков + из-за накладных расходов на слияние. - Зависимость от равномерности начального разбиения данных. ## Приложение @@ -204,7 +276,8 @@ bool KrasnopevtsevaVHoareBatcherSortOMP::RunImpl() { std::vector pointers(numthreads); std::vector sizes(numthreads); for (int i = 0; i < numthreads; ++i) { - std::ptrdiff_t offset = static_cast(i) * static_cast(thread_input_size); + std::ptrdiff_t offset = static_cast(i) * + static_cast(thread_input_size); pointers[i] = res.data() + offset; sizes[i] = thread_input_size; } diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md index f7ca77215d..b7721eec3e 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md @@ -5,7 +5,13 @@ ## 1. Цель работы -Реализовать и исследовать гибридный алгоритм сортировки, сочетающий итеративную быструю сортировку Хоара с сортировкой вставками для малых подмассивов и последующим слиянием Бэтчера. Алгоритм реализован в пяти вариантах: последовательная версия (SEQ) и параллельные версии с использованием OpenMP, STL, Intel TBB и комбинированной версии ALL (OpenMP + адаптивная стратегия). Провести сравнительный анализ производительности различных технологий параллелизации. +Реализовать и исследовать гибридный алгоритм сортировки, сочетающий +итеративную быструю сортировку Хоара с сортировкой вставками для малых +подмассивов и последующим слиянием Бэтчера. Алгоритм реализован в пяти +вариантах: последовательная версия (SEQ) и параллельные версии с +использованием OpenMP, STL, Intel TBB и комбинированной версии ALL +(OpenMP + адаптивная стратегия). Провести сравнительный анализ +производительности различных технологий параллелизации. ## 2. Реализованные версии @@ -13,19 +19,20 @@ Все версии реализуют единую логику сортировки: -1. Итеративная быстрая сортировка с использованием стека +1. Итеративная быстрая сортировка с использованием стека 2. Сортировка вставками для подмассивов размером менее 16 элементов -3. Слияние отсортированных блоков по алгоритму Бэтчера с использованием `std::inplace_merge` +3. Слияние отсортированных блоков по алгоритму Бэтчера с + использованием `std::inplace_merge` ### 2.2 Особенности реализаций | Версия | Технология | Ключевые особенности | |--------|------------|----------------------| -| **SEQ** | Последовательная | Базовый алгоритм без параллелизации | -| **OMP** | OpenMP | `#pragma omp parallel for`, параллельная сортировка блоков и слияние | -| **STL** | std::async + future | Асинхронные задачи, `std::future`, аппаратное определение потоков | -| **TBB** | Intel TBB | `tbb::parallel_for`, `tbb::global_control`, `simple_partitioner` | -| **ALL** | OpenMP + STL | Комбинированный подход, адаптивная стратегия (n < 1000 — последовательно) | +| SEQ | Последовательная | Базовый алгоритм без параллелизации | +| OMP | OpenMP | `#pragma omp parallel for`, параллельная сортировка | +| STL | std::async + future | Асинхронные задачи, аппаратное определение потоков | +| TBB | Intel TBB | `tbb::parallel_for`, `tbb::global_control` | +| ALL | OpenMP + STL | Комбинированный подход, адаптивная стратегия | ## 3. Экспериментальная среда @@ -40,21 +47,22 @@ ### 4.1 Сводная таблица -| Версия | Технология | Время, мс | Ускорение (относительно SEQ) | Эффективность | -|--------|------------|-----------|------------------------------|---------------| +| Версия | Технология | Время, мс | Ускорение | Эффективность | +|--------|------------|-----------|-----------|---------------| | SEQ | Последовательная | 32.0 | 1.00x | N/A | | OMP | OpenMP | 19.0 | 1.68x | 21.0% | | STL | std::async | 18.0 | 1.78x | 22.3% | | TBB | Intel TBB | 22.0 | 1.45x | 18.2% | | ALL | OpenMP + адаптивная | 22.0 | 1.45x | 18.2% | - ## 5. Сравнительный анализ ### 5.1 Лидер производительности -**STL (std::async)** показывает наилучший результат — 18 мс (ускорение 1.78x). Это объясняется: -- Минимальными накладными расходами при создании асинхронных задач для данного размера данных +**STL (std::async)** показывает наилучший результат — 18 мс +(ускорение 1.78x). Это объясняется: + +- Минимальными накладными расходами при создании асинхронных задач - Отсутствием дополнительных абстракций (в отличие от TBB) - Эффективным распределением блоков между потоками @@ -62,33 +70,44 @@ | Версия | Эффективность | Объяснение | |--------|---------------|------------| -| STL | 22.3% | Наиболее эффективная реализация для данного размера данных | -| OMP | 21.0% | Небольшое отставание от STL, хорошее масштабирование | -| TBB | 18.2% | Планировщик задач вносит дополнительные накладные расходы | +| STL | 22.3% | Наиболее эффективная реализация | +| OMP | 21.0% | Небольшое отставание от STL | +| TBB | 18.2% | Планировщик задач вносит накладные расходы | | ALL | 18.2% | Накладные расходы OpenMP + адаптивная стратегия | - - ## 6. Выводы ### 6.1 Основные результаты -1. **Все реализации корректны** и успешно проходят функциональные тесты на массивах различных типов (отсортированные, случайные, с повторениями, обратные). +1. **Все реализации корректны** и успешно проходят функциональные тесты + на массивах различных типов. -2. **Наилучшую производительность** для массива из 100 000 элементов показала версия на STL — 18 мс (ускорение 1.78x относительно SEQ). +2. **Наилучшую производительность** для массива из 100 000 элементов + показала версия на STL — 18 мс (ускорение 1.78x относительно SEQ). -3. **OpenMP** показал результат 19 мс (ускорение 1.68x), что лишь на 1 мс хуже лидера. +3. **OpenMP** показал результат 19 мс (ускорение 1.68x), что лишь на + 1 мс хуже лидера. -4. **TBB и ALL** показали одинаковый результат 22 мс (ускорение 1.45x). +4. **TBB и ALL** показали одинаковый результат 22 мс + (ускорение 1.45x). -5. **Эффективность параллелизации** составляет от 18% до 22%, что объясняется: +5. **Эффективность параллелизации** составляет от 18% до 22%, что + объясняется: - Относительно небольшим размером данных (100 000 элементов) - Наличием последовательной части алгоритма (слияние Бэтчера) - Накладными расходами на управление потоками ## 7. Заключение -В данной работе была успешно реализована и исследована гибридная сортировка Хоара-Бэтчера в пяти различных вариантах. Наилучшую производительность для массива из 100 000 элементов показала реализация на STL (std::async) с временем 18 мс, что в 1.78 раза быстрее последовательной версии. Реализация на OpenMP оказалась лишь на 1 мс медленнее, демонстрируя хороший баланс между производительностью и простотой реализации. TBB и комбинированная версия показали одинаковый результат 22 мс. - -Все реализации корректно работают на различных типах входных данных и могут быть рекомендованы для использования в зависимости от конкретных требований к производительности, переносимости и сложности реализации. - +В данной работе была успешно реализована и исследована гибридная +сортировка Хоара-Бэтчера в пяти различных вариантах. Наилучшую +производительность для массива из 100 000 элементов показала +реализация на STL (std::async) с временем 18 мс, что в 1.78 раза +быстрее последовательной версии. Реализация на OpenMP оказалась лишь на +1 мс медленнее, демонстрируя хороший баланс между производительностью и +простотой реализации. TBB и комбинированная версия показали одинаковый +результат 22 мс. + +Все реализации корректно работают на различных типах входных данных и +могут быть рекомендованы для использования в зависимости от конкретных +требований к производительности, переносимости и сложности реализации. \ No newline at end of file diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md index ee9a3cc49f..49202a01c1 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md @@ -6,29 +6,46 @@ ## 1. Введение -Сортировка массивов данных — одна из фундаментальных задач в области программирования. Быстрая сортировка Хоара известна своей эффективностью в среднем случае, однако её производительность может деградировать при неудачном выборе опорного элемента. Сортировка Бэтчера (Batcher's odd-even merge sort) представляет собой алгоритм сортировки слиянием с хорошими свойствами параллелизации, но может быть избыточна для небольших массивов. - -Целью данной работы является реализация гибридного алгоритма, сочетающего адаптивный итеративный QuickSort с последующим слиянием Бэтчера для улучшения общей производительности. Ожидаемый результат — устойчивая работа алгоритма на массивах различных размеров с использованием оптимизаций, таких как сортировка вставками для малых подмассивов и медиана из трёх для выбора опорного элемента. +Сортировка массивов данных — одна из фундаментальных задач в области +программирования. Быстрая сортировка Хоара известна своей эффективностью +в среднем случае, однако её производительность может деградировать при +неудачном выборе опорного элемента. Сортировка Бэтчера (Batcher's odd-even +merge sort) представляет собой алгоритм сортировки слиянием с хорошими +свойствами параллелизации, но может быть избыточна для небольших массивов. + +Целью данной работы является реализация гибридного алгоритма, сочетающего +адаптивный итеративный QuickSort с последующим слиянием Бэтчера для +улучшения общей производительности. Ожидаемый результат — устойчивая +работа алгоритма на массивах различных размеров с использованием +оптимизаций, таких как сортировка вставками для малых подмассивов и +медиана из трёх для выбора опорного элемента. ## 2. Постановка задачи ### Формальное определение -Необходимо реализовать последовательный алгоритм сортировки массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера. + +Необходимо реализовать последовательный алгоритм сортировки массива целых +чисел на основе комбинации алгоритмов Хоара и Бэтчера. ### Входные данные + - Вектор целых чисел `std::vector` произвольной длины. ### Выходные данные + - Отсортированный по неубыванию вектор целых чисел. ### Ограничения + - Алгоритм должен корректно обрабатывать массивы любого размера. - Для малых подмассивов используется сортировка вставками. -- Для больших массивов после завершения быстрой сортировки выполняется слияние Бэтчера. +- Для больших массивов после завершения быстрой сортировки выполняется + слияние Бэтчера. ## 3. Базовый алгоритм (последовательная версия) ### 3.1 Быстрая сортировка (итеративная версия) + Используется нерекурсивная реализация через стек: 1. В стек помещается пара `(left, right)` для всего массива. @@ -36,15 +53,21 @@ - Извлекается диапазон `[l, r]`. - Если `l >= r` — пропуск. - Если размер диапазона небольшой - вызывается сортировка вставками. - - Выбирается опорный элемент (медиана из трёх: `arr[l]`, `arr[mid]`, `arr[r]`). + - Выбирается опорный элемент (медиана из трёх: `arr[l]`, `arr[mid]`, + `arr[r]`). - Выполняется разбиение Хоара. - Меньший поддиапазон помещается в стек первым (для балансировки). ### 3.2 Сортировка вставками -Для малых подмассивов применяется классическая сортировка вставками, так как она показывает хорошие результаты на почти отсортированных данных и малых размерах. -### 3.3 Слияние Бэтчера -После завершения быстрой сортировки, если размер исходного диапазона превышает 32, вызывается `BatcherMerge`: +Для малых подмассивов применяется классическая сортировка вставками, так +как она показывает хорошие результаты на почти отсортированных данных и +малых размерах. + +### 3.3 Слияние Бэтчера + +После завершения быстрой сортировки, если размер исходного диапазона +превышает 32, вызывается `BatcherMerge`: - Создаётся временная копия диапазона `[left, right]`. - Рекурсивно выполняется нечётно-чётное слияние. @@ -52,16 +75,20 @@ ## 4. Схема параллелизации -Данная реализация является последовательной и не использует параллельные технологии. В рамках задания SEQ не предполагается параллелизация. +Данная реализация является последовательной и не использует параллельные +технологии. В рамках задания SEQ не предполагается параллелизация. ## 5. Детали реализации ### 5.1 Структура кода -- **Файл** `krasnopevtseva_v_hoare_batcher_sort/seq/include/ops_seq.hpp`, `krasnopevtseva_v_hoare_batcher_sort/seq/ops_seq.cpp` -- **Пространство имён** `krasnopevtseva_v_hoare_batcher_sort` -- **Класс** `KrasnopevtsevaVHoareBatcherSortSEQ` + +- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/seq/include/ops_seq.hpp`, + `krasnopevtseva_v_hoare_batcher_sort/seq/ops_seq.cpp` +- **Пространство имён**: `krasnopevtseva_v_hoare_batcher_sort` +- **Класс**: `KrasnopevtsevaVHoareBatcherSortSEQ` ### 5.2 Основные методы + | Метод | Назначение | |-------|-------------| | `ValidationImpl()` | Проверка входных данных (массив не пуст) | @@ -73,73 +100,90 @@ | `BatcherMerge()` | Слияние Бэтчера | | `CompareAndSwap()` | Вспомогательная функция сравнения и обмена | - ## 6. Окружение и тестирование ### 6.1 Аппаратное и программное обеспечение + - **Процессор**: AMD Ryzen 7 5700X 8-Core Processor - **ОЗУ**: 32.0 ГБ - **ОС**: Windows 11 Pro 25H2 -- **Набор инструментов**: DevContainer с компилятором GCC (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 +- **Набор инструментов**: DevContainer с компилятором GCC + (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 - **Тип сборки**: Release - **Фреймворк тестирования**: Google Test (gtest) -- **Режим**: SEQ +- **Режим**: SEQ ### 6.2 Тестовые данные и верификация #### Функциональное тестирование -Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: +Для проверки базовой корректности алгоритма был разработан набор из 6 +тестовых сценариев, покрывающих различные типы входных данных: | № | Тип массива | Описание | Размер | |---|-------------|----------|--------| | 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | +| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторяющимися и случайными значениями | 50 | -| 5 | `two_number_array` | Массив, содержащий только два различных значения (10 и 20) | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию (наихудший случай) | 24 | - +| 4 | `long_array` | Длинный массив с повторениями | 50 | +| 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | #### Верификация результата -Для каждого тестового набора проверялось выполнение основного инварианта сортировки: -- Для всех индексов `i` от 0 до `size-2` выполняется условие `output[i] <= output[i+1]` +Для каждого тестового набора проверялось выполнение основного инварианта +сортировки: + +- Для всех индексов `i` от 0 до `size-2` выполняется условие + `output[i] <= output[i+1]` -Дополнительно проводилось сравнение с эталонной реализацией `std::sort` на тех же входных данных для всех тестовых случаев. +Дополнительно проводилось сравнение с эталонной реализацией `std::sort` +на тех же входных данных для всех тестовых случаев. -#### Тестирование производительности (перфоманс-тесты) +#### Тестирование производительности -Для оценки производительности был реализован отдельный тестовый набор, генерирующий массив из **100 000** случайных целых чисел в диапазоне `[0, 100 000 000]`. Генерация выполняется с использованием генератора псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости результатов. +Для оценки производительности был реализован отдельный тестовый набор, +генерирующий массив из **100 000** случайных целых чисел в диапазоне +`[0, 100 000 000]`. Генерация выполняется с использованием генератора +псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости +результатов. -**Результаты корректности:** Все 6 функциональных тестов успешно пройдены для SEQ. Перфоманс-тесты также подтверждают корректную сортировку массива из 100 000 элементов. +**Результаты корректности:** Все 6 функциональных тестов успешно пройдены +для SEQ. Перфоманс-тесты также подтверждают корректную сортировку массива +из 100 000 элементов. -### 7 Производительность +## 7. Производительность -### 7.2 Производительность +### 7.1 Результаты Результаты для массива из **100 000** случайных целых чисел: -| Режим | Размер | Время,ms | Ускорение | Эффективность | -|-------|--------|----------|-----------|----------------| -| seq |100 000 | 32 ms | 1.00 | N/A | +| Режим | Размер | Время, мс | Ускорение | Эффективность | +|-------|--------|-----------|-----------|---------------| +| seq | 100000 | 32 | 1.00 | N/A | *Сравнение с параллельными версиями не предусмотрено для SEQ.* -### 7.3 Анализ производительности -- Основной вклад вносят сравнения и перемещения во время разбиения и слияния. +### 7.2 Анализ производительности +- Основной вклад вносят сравнения и перемещения во время разбиения и + слияния. ## 8. Заключения ### Основные выводы -1. Разработан гибридный последовательный алгоритм сортировки, сочетающий QuickSort, InsertionSort и BatcherMerge. -2. Алгоритм корректен и показывает приемлемую производительность для массивов до 10^6 элементов. -3. Использование сортировки вставками для подмассивов размера < 16 даёт выигрыш по сравнению с QuickSort. -4. Слияние Бэтчера улучшает порядок элементов после завершения QuickSort, но ценой дополнительной памяти. + +1. Разработан гибридный последовательный алгоритм сортировки, сочетающий + QuickSort, InsertionSort и BatcherMerge. +2. Алгоритм корректен и показывает приемлемую производительность для + массивов до 10^6 элементов. +3. Использование сортировки вставками для подмассивов размера < 16 даёт + выигрыш по сравнению с QuickSort. +4. Слияние Бэтчера улучшает порядок элементов после завершения QuickSort, + но ценой дополнительной памяти. -## Приложение +## Приложение ```cpp void KrasnopevtsevaVHoareBatcherSortSEQ::QuickBatcherSort( diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md index 87606e1498..1da372aeb1 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md @@ -6,30 +6,50 @@ ## 1. Введение -Сортировка массивов данных — одна из фундаментальных задач в области программирования. Быстрая сортировка Хоара известна своей эффективностью в среднем случае, однако её производительность может деградировать при неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой алгоритм сортировки слиянием с хорошими свойствами параллелизации, но может быть избыточна для небольших массивов. - -Целью данной работы является реализация параллельного гибридного алгоритма с использованием стандартной библиотеки шаблонов C++ (STL), в частности средств асинхронного выполнения `std::async` и `std::future`, сочетающего адаптивный итеративный QuickSort с последующим слиянием Бэтчера для улучшения общей производительности. Ожидаемый результат — устойчивая работа алгоритма на массивах различных размеров с использованием оптимизаций, таких как сортировка вставками для малых подмассивов и эффективное параллельное слияние. +Сортировка массивов данных — одна из фундаментальных задач в области +программирования. Быстрая сортировка Хоара известна своей эффективностью +в среднем случае, однако её производительность может деградировать при +неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой +алгоритм сортировки слиянием с хорошими свойствами параллелизации, но +может быть избыточна для небольших массивов. + +Целью данной работы является реализация параллельного гибридного +алгоритма с использованием стандартной библиотеки шаблонов C++ (STL), +в частности средств асинхронного выполнения `std::async` и `std::future`, +сочетающего адаптивный итеративный QuickSort с последующим слиянием +Бэтчера для улучшения общей производительности. Ожидаемый результат — +устойчивая работа алгоритма на массивах различных размеров с +использованием оптимизаций, таких как сортировка вставками для малых +подмассивов и эффективное параллельное слияние. ## 2. Постановка задачи ### Формальное определение -Необходимо реализовать параллельный алгоритм сортировки массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием средств параллелизации из стандартной библиотеки C++ (STL). + +Необходимо реализовать параллельный алгоритм сортировки массива целых +чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием +средств параллелизации из стандартной библиотеки C++ (STL). ### Входные данные + - Вектор целых чисел `std::vector` произвольной длины. ### Выходные данные + - Отсортированный по неубыванию вектор целых чисел. ### Ограничения + - Алгоритм должен корректно обрабатывать массивы любого размера. - Для малых подмассивов используется сортировка вставками. -- Параллельная обработка выполняется на этапе начальной сортировки подмассивов и на этапе слияния. +- Параллельная обработка выполняется на этапе начальной сортировки + подмассивов и на этапе слияния. - Количество потоков определяется аппаратными возможностями системы. ## 3. Базовый алгоритм (последовательная версия) ### 3.1 Быстрая сортировка (итеративная версия) + Используется нерекурсивная реализация через стек: 1. В стек помещается пара `(left, right)` для всего массива. @@ -42,123 +62,159 @@ - Меньший поддиапазон помещается в стек первым (для балансировки). ### 3.2 Сортировка вставками -Для малых подмассивов (размер < 16) применяется классическая сортировка вставками, так как она показывает хорошие результаты на почти отсортированных данных и малых размерах. + +Для малых подмассивов (размер < 16) применяется классическая сортировка +вставками, так как она показывает хорошие результаты на почти +отсортированных данных и малых размерах. ### 3.3 Слияние Бэтчера -После завершения быстрой сортировки выполняется слияние отсортированных блоков с использованием алгоритма Бэтчера с последовательным применением `std::inplace_merge`. + +После завершения быстрой сортировки выполняется слияние отсортированных +блоков с использованием алгоритма Бэтчера с последовательным применением +`std::inplace_merge`. ## 4. Схема параллелизации ### 4.1 Общая стратегия -Параллелизация реализована по принципу "разделяй и властвуй" с использованием средств STL: -1. **Определение количества потоков**: Автоматическое определение аппаратной конкуренции через `std::thread::hardware_concurrency()`. -2. **Разбиение данных**: Исходный массив разделяется на `N` блоков, где `N` — количество доступных потоков. -3. **Параллельная сортировка**: Каждый блок сортируется асинхронно с использованием `std::async(std::launch::async, ...)`. -4. **Параллельное слияние**: Отсортированные блоки попарно сливаются с использованием иерархической схемы Бэтчера, где каждая пара сливается асинхронно. +Параллелизация реализована по принципу "разделяй и властвуй" с +использованием средств STL: + +1. **Определение количества потоков**: Автоматическое определение + аппаратной конкуренции через `std::thread::hardware_concurrency()`. +2. **Разбиение данных**: Исходный массив разделяется на `N` блоков, + где `N` — количество доступных потоков. +3. **Параллельная сортировка**: Каждый блок сортируется асинхронно с + использованием `std::async(std::launch::async, ...)`. +4. **Параллельное слияние**: Отсортированные блоки попарно сливаются + с использованием иерархической схемы Бэтчера, где каждая пара + сливается асинхронно. ### 4.2 Распределение данных + - Размер блока для каждого потока: `base_size = n / numthreads` -- Остаток от деления добавляется к последнему блоку: `last_size = base_size + remainder` -- Каждый блок описывается структурой `Chunk`, содержащей указатель на данные, размер и границы (left, right) +- Остаток от деления добавляется к последнему блоку: + `last_size = base_size + remainder` +- Каждый блок описывается структурой `Chunk`, содержащей указатель + на данные, размер и границы (left, right) ### 4.3 Коммуникационная схема -- На этапе сортировки: блоки сортируются независимо, обмен данными отсутствует. -- На этапе слияния: выполняется попарное слияние блоков с использованием `std::inplace_merge`. -- Слияние организовано в виде иерархического дерева (парное слияние на каждом уровне). -- После слияния блоков на уровне структура `chunks` обновляется (удаляются слитые блоки). + +- На этапе сортировки: блоки сортируются независимо, обмен данными + отсутствует. +- На этапе слияния: выполняется попарное слияние блоков с + использованием `std::inplace_merge`. +- Слияние организовано в виде иерархического дерева (парное слияние + на каждом уровне). +- После слияния блоков на уровне структура `chunks` обновляется + (удаляются слитые блоки). ### 4.4 Синхронизация -- Используется `std::future` и `std::async` для асинхронного выполнения задач. -- Синхронизация выполняется через вызов `future.wait()` для каждого асинхронного задания. -- Отсутствие гонок данных гарантируется тем, что каждый поток работает со своим выделенным диапазоном памяти. -- На этапе слияния используется условное распараллеливание через параметр `par_if_greater` (порог 32). + +- Используется `std::future` и `std::async` для асинхронного + выполнения задач. +- Синхронизация выполняется через вызов `future.wait()` для каждого + асинхронного задания. +- Отсутствие гонок данных гарантируется тем, что каждый поток работает + со своим выделенным диапазоном памяти. +- На этапе слияния используется условное распараллеливание через + параметр `par_if_greater` (порог 32). ## 5. Детали реализации ### 5.1 Структура кода -- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/stl/include/ops_stl.hpp`, `krasnopevtseva_v_hoare_batcher_sort/stl/ops_stl.cpp` + +- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/stl/include/ops_stl.hpp`, + `krasnopevtseva_v_hoare_batcher_sort/stl/ops_stl.cpp` - **Пространство имён**: `krasnopevtseva_v_hoare_batcher_sort` - **Класс**: `KrasnopevtsevaVHoareBatcherSortSTL` ### 5.2 Основные методы + | Метод | Назначение | |-------|-------------| | `ValidationImpl()` | Проверка входных данных (массив не пуст) | | `PreProcessingImpl()` | Инициализация выходного вектора | | `RunImpl()` | Запуск параллельной сортировки | | `GetNumThreads()` | Определение оптимального количества потоков | -| `SetupChunks()` | Разбиение массива на блоки для параллельной обработки | +| `SetupChunks()` | Разбиение массива на блоки | | `ParallelSortChunks()` | Асинхронная сортировка блоков | | `QuickSort()` | Итеративная быстрая сортировка | -| `Partition()` | Разбиение Хоара (с последним опорным элементом) | -| `InsertionSort()` | Сортировка вставками для малых диапазонов | +| `Partition()` | Разбиение Хоара | +| `InsertionSort()` | Сортировка вставками | | `BatcherMergeBlocksStep()` | Слияние двух блоков | -| `BatcherMergeLevel()` | Параллельное слияние на уровне иерархии | +| `BatcherMergeLevel()` | Параллельное слияние на уровне | -### 5.3 Структура Chunk -```cpp -struct Chunk { - int* ptr; // указатель на начало блока в массиве - int size; // размер блока - int left; // левая граница в исходном массиве - int right; // правая граница в исходном массиве -}; -``` -### 5.4 Важные особенности - -- **Автоматическое определение потоков**: Количество потоков определяется через `std::thread::hardware_concurrency()` с fallback на 1. -- **Выбор опорного элемента**: Используется последний элемент массива, что упрощает реализацию. +### 5.3 Важные особенности + +- **Автоматическое определение потоков**: Количество потоков определяется + через `std::thread::hardware_concurrency()` с fallback на 1. +- **Выбор опорного элемента**: Используется последний элемент массива, + что упрощает реализацию. - **Порог сортировки вставками**: 16 элементов. -- **Условное распараллеливание слияния**: Слияние выполняется параллельно только если `(thread_input_size / step) > 32`. -- **Динамическое управление блоками**: Вектор `chunks` изменяет свой размер по мере слияния блоков. +- **Условное распараллеливание слияния**: Слияние выполняется параллельно + только если `(thread_input_size / step) > 32`. +- **Динамическое управление блоками**: Вектор `chunks` изменяет свой + размер по мере слияния блоков. - **Итеративный QuickSort**: Избегает переполнения стека вызовов. -### 5.5 Использование памяти +### 5.4 Использование памяти -- Для отслеживания границ блоков используется вектор `chunks` размера O(numthreads). -- Асинхронные задачи создаются динамически на этапах сортировки и слияния. +- Для отслеживания границ блоков используется вектор `chunks` размера + O(numthreads). +- Асинхронные задачи создаются динамически на этапах сортировки и + слияния. ## 6. Окружение и тестирование ### 6.1 Аппаратное и программное обеспечение -- **Процессор**: AMD Ryzen 7 5700X 8-Core Processor +- **Процессор**: AMD Ryzen 7 5700X 8-Core Processor - **ОЗУ**: 32.0 ГБ - **ОС**: Windows 11 Pro 25H2 -- **Набор инструментов**: DevContainer с компилятором GCC (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 +- **Набор инструментов**: DevContainer с компилятором GCC + (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 - **Тип сборки**: Release - **Фреймворк тестирования**: Google Test (gtest) -- **Технология**: STL +- **Технология**: STL ### 6.2 Тестовые данные и верификация #### Функциональное тестирование -Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: +Для проверки базовой корректности алгоритма был разработан набор из 6 +тестовых сценариев, покрывающих различные типы входных данных: | № | Тип массива | Описание | Размер | |---|-------------|----------|--------| | 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | +| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторяющимися и случайными значениями | 50 | -| 5 | `two_number_array` | Массив, содержащий только два различных значения (10 и 20) | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию (наихудший случай) | 24 | +| 4 | `long_array` | Длинный массив с повторениями | 50 | +| 5 | `two_number_array` | Массив только из двух значений | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | #### Верификация результата -Для каждого тестового набора проверялось выполнение основного инварианта сортировки: +Для каждого тестового набора проверялось выполнение основного инварианта +сортировки: -- Для всех индексов `i` от 0 до `size-2` выполняется условие `output[i] <= output[i+1]` +- Для всех индексов `i` от 0 до `size-2` выполняется условие + `output[i] <= output[i+1]` -Дополнительно проводилось сравнение с эталонной реализацией `std::sort` на тех же входных данных для всех тестовых случаев. +Дополнительно проводилось сравнение с эталонной реализацией `std::sort` +на тех же входных данных для всех тестовых случаев. #### Тестирование производительности -Для оценки производительности был реализован отдельный тестовый набор, генерирующий массив из **100 000** случайных целых чисел в диапазоне `[0, 100 000 000]`. Генерация выполняется с использованием генератора псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости результатов. +Для оценки производительности был реализован отдельный тестовый набор, +генерирующий массив из **100 000** случайных целых чисел в диапазоне +`[0, 100 000 000]`. Генерация выполняется с использованием генератора +псевдослучайных чисел `std::mt19937`. -**Результаты корректности:** Все 6 функциональных тестов успешно пройдены для STL-версии. Перфоманс-тесты также подтверждают корректную сортировку массива из 100 000 элементов. +**Результаты корректности:** Все 6 функциональных тестов успешно пройдены +для STL-версии. Перфоманс-тесты также подтверждают корректную сортировку +массива из 100 000 элементов. ## 7. Результаты и обсуждение @@ -166,50 +222,54 @@ struct Chunk { Корректность параллельной реализации на STL подтверждена: -- Сравнением результатов с последовательной версией на идентичных входных данных. -- Проверкой инварианта отсортированности для всех выходных массивов. +- Сравнением результатов с последовательной версией. +- Проверкой инварианта отсортированности. - Успешным прохождением всех функциональных тестов. ### 7.2 Производительность Результаты для массива из **100 000** случайных целых чисел: -| Реализация | Количество потоков | Время, мс | Ускорение | Эффективность | -|------------|--------------------|-----------|-----------|----------------| -| SEQ | 1 | 32.0 | 1.00 | N/A | -| STL | 8 | 18.0 | 1.78 | 22.3% | +| Реализация | Потоков | Время, мс | Ускорение | Эффективность | +|------------|---------|-----------|-----------|---------------| +| SEQ | 1 | 32.0 | 1.00 | N/A | +| STL | 8 | 18.0 | 1.78 | 22.3% | -*Примечание: Количество потоков определяется автоматически через `std::thread::hardware_concurrency()`.* +*Примечание: Количество потоков определяется автоматически через +`std::thread::hardware_concurrency()`.* ### 7.3 Анализ производительности #### Наблюдаемые эффекты -- **Ускорение STL версии около 1.78x** при использовании аппаратно-определённого количества потоков (8). Это соответствует эффективности около 22.3%. -- Для массива из 100 000 элементов время выполнения составляет 18 мс, что в 1.78 раза быстрее последовательной версии (32 мс). +- **Ускорение STL версии около 1.78x** при использовании 8 потоков. + Это соответствует эффективности около 22.3%. +- Для массива из 100 000 элементов время выполнения составляет 18 мс, + что в 1.78 раза быстрее последовательной версии (32 мс). #### Факторы, влияющие на производительность | Фактор | Влияние на STL версию | |--------|----------------------| -| Накладные расходы std::async | Высокие (создание и управление future) | -| Параллельная сортировка блоков | Хорошее ускорение (блоки независимы) | -| Параллельное слияние | Умеренное ускорение (зависимость по данным) | -| Балансировка нагрузки | Средняя (последний блок может быть больше) | -| Условное распараллеливание | Положительное (избегает избыточности на малых данных) | - +| Накладные расходы std::async | Высокие | +| Параллельная сортировка блоков | Хорошее ускорение | +| Параллельное слияние | Умеренное ускорение | +| Балансировка нагрузки | Средняя | +| Условное распараллеливание | Положительное | ## 8. Заключения ### Основные выводы -1. Разработан параллельный гибридный алгоритм сортировки с использованием STL (std::async, std::future), сочетающий QuickSort, InsertionSort и BatcherMerge. -2. Алгоритм корректен и демонстрирует ускорение до 1.78x на 8 аппаратных потоках для массива из 100 000 элементов. -3. Средства STL обеспечивают хорошую переносимость, но уступают OpenMP в эффективности из-за более высоких накладных расходов на управление асинхронными задачами. -4. Сортировка вставками для подмассивов размера < 16 даёт выигрыш по сравнению с рекурсивным QuickSort. -5. Условное распараллеливание слияния позволяет адаптироваться под размер обрабатываемых данных. - - +1. Разработан параллельный гибридный алгоритм сортировки с использованием + STL (std::async, std::future), сочетающий QuickSort, InsertionSort и + BatcherMerge. +2. Алгоритм корректен и демонстрирует ускорение до 1.78x на 8 потоках + для массива из 100 000 элементов. +3. Средства STL обеспечивают хорошую переносимость. +4. Сортировка вставками для подмассивов размера < 16 даёт выигрыш. +5. Условное распараллеливание слияния позволяет адаптироваться под + размер обрабатываемых данных. ## Приложение @@ -242,118 +302,22 @@ bool KrasnopevtsevaVHoareBatcherSortSTL::RunImpl() { BatcherMergeLevel(step, chunks, n / numthreads, 32); if ((pack / 2) - 1 == 0) { - BatcherMergeBlocksStep(chunks[0].ptr, chunks[0].size, chunks.back().ptr, chunks.back().size); + BatcherMergeBlocksStep(chunks[0].ptr, chunks[0].size, + chunks.back().ptr, chunks.back().size); chunks[0].right = chunks.back().right; chunks.resize(1); } else if ((pack / 2) % 2 != 0) { - size_t idx1 = static_cast(2 * step) * static_cast((pack / 2) - 2); + size_t idx1 = static_cast(2 * step) * + static_cast((pack / 2) - 2); size_t idx2 = idx1 + static_cast(step); - BatcherMergeBlocksStep(chunks[idx1].ptr, chunks[idx1].size, chunks[idx2].ptr, chunks[idx2].size); + BatcherMergeBlocksStep(chunks[idx1].ptr, chunks[idx1].size, + chunks[idx2].ptr, chunks[idx2].size); chunks.erase(chunks.begin() + static_cast(idx2)); } else { - int new_size = pack / 2; - chunks.resize(new_size); + chunks.resize(pack / 2); } } GetOutput() = std::move(res); return true; -} - -void KrasnopevtsevaVHoareBatcherSortSTL::ParallelSortChunks(std::vector &arr, std::vector &chunks) { - std::vector> futures; - futures.reserve(chunks.size()); - - for (auto &chunk : chunks) { - futures.push_back(std::async(std::launch::async, [&arr, &chunk]() { - QuickSort(arr, chunk.left, chunk.right); - })); - } - - for (auto &f : futures) { - f.wait(); - } -} - -void KrasnopevtsevaVHoareBatcherSortSTL::BatcherMergeLevel(int step, std::vector &chunks, int thread_input_size, - int par_if_greater) { - int pack = static_cast(chunks.size()); - bool do_parallel = (thread_input_size / step) > par_if_greater; - - auto merge_pair = [&](int off) { - size_t idx1 = static_cast(2 * step) * static_cast(off); - size_t idx2 = idx1 + static_cast(step); - BatcherMergeBlocksStep(chunks[idx1].ptr, chunks[idx1].size, chunks[idx2].ptr, chunks[idx2].size); - chunks[idx1].right = chunks[idx2].right; - }; - - if (do_parallel) { - std::vector> futures; - futures.reserve(pack / 2); - for (int off = 0; off < pack / 2; ++off) { - futures.push_back(std::async(std::launch::async, merge_pair, off)); - } - for (auto &f : futures) { - f.wait(); - } - } else { - for (int off = 0; off < pack / 2; ++off) { - merge_pair(off); - } - } -} - -void KrasnopevtsevaVHoareBatcherSortSTL::QuickSort(std::vector &arr, int first, int last) { - std::stack> stack; - stack.emplace(first, last); - - while (!stack.empty()) { - auto [l, r] = stack.top(); - stack.pop(); - - if (l >= r) { - continue; - } - - if (r - l < 16) { - InsertionSort(arr, l, r); - continue; - } - - int iter = Partition(arr, l, r); - - if (iter - l < r - iter) { - stack.emplace(iter + 1, r); - stack.emplace(l, iter - 1); - } else { - stack.emplace(l, iter - 1); - stack.emplace(iter + 1, r); - } - } -} - -int KrasnopevtsevaVHoareBatcherSortSTL::Partition(std::vector &arr, int first, int last) { - int i = first - 1; - int value = arr[last]; - - for (int j = first; j <= last - 1; ++j) { - if (arr[j] <= value) { - ++i; - std::swap(arr[i], arr[j]); - } - } - std::swap(arr[i + 1], arr[last]); - return i + 1; -} - -void KrasnopevtsevaVHoareBatcherSortSTL::InsertionSort(std::vector &arr, int first, int last) { - for (int i = first + 1; i <= last; ++i) { - int key = arr[i]; - int j = i - 1; - while (j >= first && arr[j] > key) { - arr[j + 1] = arr[j]; - --j; - } - arr[j + 1] = key; - } } \ No newline at end of file diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md index 39cbf7eb91..c04261fc94 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md @@ -6,15 +6,28 @@ ## 1. Введение -Сортировка массивов данных — одна из фундаментальных задач в области программирования. Быстрая сортировка Хоара известна своей эффективностью в среднем случае, однако её производительность может деградировать при неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой алгоритм сортировки слиянием с хорошими свойствами параллелизации, но может быть избыточна для небольших массивов. - -Целью данной работы является реализация параллельного гибридного алгоритма с использованием библиотеки Intel Threading Building Blocks (TBB), сочетающего адаптивный итеративный QuickSort с последующим слиянием Бэтчера для улучшения общей производительности. Ожидаемый результат — устойчивая работа алгоритма на массивах различных размеров с использованием оптимизаций, таких как сортировка вставками для малых подмассивов и эффективное параллельное слияние. +Сортировка массивов данных — одна из фундаментальных задач в области +программирования. Быстрая сортировка Хоара известна своей эффективностью +в среднем случае, однако её производительность может деградировать при +неудачном выборе опорного элемента. Сортировка Бэтчера представляет собой +алгоритм сортировки слиянием с хорошими свойствами параллелизации, но +может быть избыточна для небольших массивов. + +Целью данной работы является реализация параллельного гибридного +алгоритма с использованием библиотеки Intel Threading Building Blocks +(TBB), сочетающего адаптивный итеративный QuickSort с последующим +слиянием Бэтчера для улучшения общей производительности. Ожидаемый +результат — устойчивая работа алгоритма на массивах различных размеров с +использованием оптимизаций, таких как сортировка вставками для малых +подмассивов и эффективное параллельное слияние. ## 2. Постановка задачи ### Формальное определение -Необходимо реализовать параллельный алгоритм сортировки массива целых чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием библиотеки Intel TBB. +Необходимо реализовать параллельный алгоритм сортировки массива целых +чисел на основе комбинации алгоритмов Хоара и Бэтчера с использованием +библиотеки Intel TBB. ### Входные данные @@ -28,8 +41,10 @@ - Алгоритм должен корректно обрабатывать массивы любого размера. - Для малых подмассивов используется сортировка вставками. -- Параллельная обработка выполняется на этапе начальной сортировки подмассивов и на этапе слияния. -- Количество потоков определяется аппаратными возможностями системы и контролируется через `tbb::global_control`. +- Параллельная обработка выполняется на этапе начальной сортировки + подмассивов и на этапе слияния. +- Количество потоков определяется аппаратными возможностями системы и + контролируется через `tbb::global_control`. ## 3. Базовый алгоритм (последовательная версия) @@ -48,50 +63,72 @@ ### 3.2 Сортировка вставками -Для малых подмассивов (размер < 16) применяется классическая сортировка вставками, так как она показывает хорошие результаты на почти отсортированных данных и малых размерах. +Для малых подмассивов (размер < 16) применяется классическая сортировка +вставками, так как она показывает хорошие результаты на почти +отсортированных данных и малых размерах. ### 3.3 Слияние Бэтчера -После завершения быстрой сортировки выполняется слияние отсортированных блоков с использованием алгоритма Бэтчера с последовательным применением `std::inplace_merge`. +После завершения быстрой сортировки выполняется слияние отсортированных +блоков с использованием алгоритма Бэтчера с последовательным применением +`std::inplace_merge`. ## 4. Схема параллелизации ### 4.1 Общая стратегия -Параллелизация реализована по принципу "разделяй и властвуй" с использованием библиотеки Intel TBB: +Параллелизация реализована по принципу "разделяй и властвуй" с +использованием библиотеки Intel TBB: -1. **Определение количества потоков**: Автоматическое определение аппаратной конкуренции через `std::thread::hardware_concurrency()` с настройкой глобального контроля TBB через `tbb::global_control`. -2. **Разбиение данных**: Исходный массив разделяется на `N` блоков, где `N` — количество доступных потоков. -3. **Параллельная сортировка**: Каждый блок сортируется с использованием `tbb::parallel_for` с `simple_partitioner`. -4. **Параллельное слияние**: Отсортированные блоки попарно сливаются с использованием иерархической схемы Бэтчера, где каждая пара сливается параллельно через `tbb::parallel_for`. +1. **Определение количества потоков**: Автоматическое определение + аппаратной конкуренции через `std::thread::hardware_concurrency()` с + настройкой глобального контроля TBB через `tbb::global_control`. +2. **Разбиение данных**: Исходный массив разделяется на `N` блоков, + где `N` — количество доступных потоков. +3. **Параллельная сортировка**: Каждый блок сортируется с + использованием `tbb::parallel_for` с `simple_partitioner`. +4. **Параллельное слияние**: Отсортированные блоки попарно сливаются + с использованием иерархической схемы Бэтчера, где каждая пара + сливается параллельно через `tbb::parallel_for`. ### 4.2 Распределение данных - Размер блока для каждого потока: `base_size = n / numthreads` -- Остаток от деления добавляется к последнему блоку: `last_size = base_size + remainder` +- Остаток от деления добавляется к последнему блоку: + `last_size = base_size + remainder` - Каждый поток получает указатель на начало своего блока и его размер -- TBB-планировщик автоматически распределяет итерации по доступным потокам +- TBB-планировщик автоматически распределяет итерации по доступным + потокам ### 4.3 Коммуникационная схема -- На этапе сортировки: блоки сортируются независимо, обмен данными отсутствует. -- На этапе слияния: выполняется попарное слияние блоков с использованием `std::inplace_merge`. -- Слияние организовано в виде иерархического дерева (парное слияние на каждом уровне). -- TBB обеспечивает автоматическую балансировку нагрузки на этапе слияния. +- На этапе сортировки: блоки сортируются независимо, обмен данными + отсутствует. +- На этапе слияния: выполняется попарное слияние блоков с + использованием `std::inplace_merge`. +- Слияние организовано в виде иерархического дерева (парное слияние + на каждом уровне). +- TBB обеспечивает автоматическую балансировку нагрузки на этапе + слияния. ### 4.4 Синхронизация -- Используется `tbb::parallel_for` с явным указанием `simple_partitioner`. +- Используется `tbb::parallel_for` с явным указанием + `simple_partitioner`. - TBB управляет созданием потоков и распределением работы. -- Отсутствие гонок данных гарантируется тем, что каждый поток работает со своим выделенным диапазоном памяти. -- На этапе слияния используется условное распараллеливание через параметр `par_if_greater` (порог 32). -- Глобальный контроль параллелизма через `tbb::global_control` ограничивает максимальное количество потоков. +- Отсутствие гонок данных гарантируется тем, что каждый поток + работает со своим выделенным диапазоном памяти. +- На этапе слияния используется условное распараллеливание через + параметр `par_if_greater` (порог 32). +- Глобальный контроль параллелизма через `tbb::global_control` + ограничивает максимальное количество потоков. ## 5. Детали реализации ### 5.1 Структура кода -- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/tbb/include/ops_tbb.hpp`, `krasnopevtseva_v_hoare_batcher_sort/tbb/ops_tbb.cpp` +- **Файл**: `krasnopevtseva_v_hoare_batcher_sort/tbb/include/ops_tbb.hpp`, + `krasnopevtseva_v_hoare_batcher_sort/tbb/ops_tbb.cpp` - **Пространство имён**: `krasnopevtseva_v_hoare_batcher_sort` - **Класс**: `KrasnopevtsevaVHoareBatcherSortTBB` @@ -110,19 +147,27 @@ ### 5.3 Важные особенности -- **Глобальный контроль параллелизма**: Используется `tbb::global_control` для установки максимального количества потоков на основе аппаратной конкуренции. -- **Планировщик TBB**: Применяется `tbb::simple_partitioner()` для минимальных накладных расходов при распределении итераций. -- **Выбор опорного элемента**: Используется последний элемент массива, что упрощает реализацию. +- **Глобальный контроль параллелизма**: Используется `tbb::global_control` + для установки максимального количества потоков на основе аппаратной + конкуренции. +- **Планировщик TBB**: Применяется `tbb::simple_partitioner()` для + минимальных накладных расходов при распределении итераций. +- **Выбор опорного элемента**: Используется последний элемент массива, + что упрощает реализацию. - **Порог сортировки вставками**: 16 элементов. -- **Условное распараллеливание слияния**: Слияние выполняется параллельно только если `(thread_input_size / step) > 32`. +- **Условное распараллеливание слияния**: Слияние выполняется параллельно + только если `(thread_input_size / step) > 32`. - **Итеративный QuickSort**: Избегает переполнения стека вызовов. ### 5.4 Использование памяти - Исходный массив сортируется на месте. -- Для отслеживания границ блоков используются массивы `pointers` и `sizes`. -- TBB управляет созданием и распределением задач с минимальными накладными расходами. -- Дополнительная память: O(numthreads) для хранения указателей на блоки. +- Для отслеживания границ блоков используются массивы `pointers` и + `sizes`. +- TBB управляет созданием и распределением задач с минимальными + накладными расходами. +- Дополнительная память: O(numthreads) для хранения указателей на + блоки. ## 6. Окружение и тестирование @@ -131,7 +176,8 @@ - **Процессор**: AMD Ryzen 7 5700X 8-Core Processor - **ОЗУ**: 32.0 ГБ - **ОС**: Windows 11 Pro 25H2 -- **Набор инструментов**: DevContainer с компилятором GCC (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 +- **Набор инструментов**: DevContainer с компилятором GCC + (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 - **Тип сборки**: Release - **Фреймворк тестирования**: Google Test (gtest) - **Технология**: TBB @@ -140,30 +186,40 @@ #### Функциональное тестирование -Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: +Для проверки базовой корректности алгоритма был разработан набор из 6 +тестовых сценариев, покрывающих различные типы входных данных: | № | Тип массива | Описание | Размер | |---|-------------|----------|--------| | 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | | 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторяющимися и случайными значениями | 50 | -| 5 | `two_number_array` | Массив, содержащий только два различных значения (10 и 20) | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию (наихудший случай) | 24 | +| 4 | `long_array` | Длинный массив с повторениями | 50 | +| 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | #### Верификация результата -Для каждого тестового набора проверялось выполнение основного инварианта сортировки: +Для каждого тестового набора проверялось выполнение основного инварианта +сортировки: -- Для всех индексов `i` от 0 до `size-2` выполняется условие `output[i] <= output[i+1]` +- Для всех индексов `i` от 0 до `size-2` выполняется условие + `output[i] <= output[i+1]` -Дополнительно проводилось сравнение с эталонной реализацией `std::sort` на тех же входных данных для всех тестовых случаев. +Дополнительно проводилось сравнение с эталонной реализацией `std::sort` +на тех же входных данных для всех тестовых случаев. #### Тестирование производительности -Для оценки производительности был реализован отдельный тестовый набор, генерирующий массив из **100 000** случайных целых чисел в диапазоне `[0, 100 000 000]`. Генерация выполняется с использованием генератора псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости результатов. +Для оценки производительности был реализован отдельный тестовый набор, +генерирующий массив из **100 000** случайных целых чисел в диапазоне +`[0, 100 000 000]`. Генерация выполняется с использованием генератора +псевдослучайных чисел `std::mt19937` для обеспечения воспроизводимости +результатов. -**Результаты корректности:** Все 6 функциональных тестов успешно пройдены для TBB-версии. Перфоманс-тесты также подтверждают корректную сортировку массива из 100 000 элементов. +**Результаты корректности:** Все 6 функциональных тестов успешно пройдены +для TBB-версии. Перфоманс-тесты также подтверждают корректную сортировку +массива из 100 000 элементов. ## 7. Результаты и обсуждение @@ -171,7 +227,8 @@ Корректность параллельной реализации на TBB подтверждена: -- Сравнением результатов с последовательной версией на идентичных входных данных. +- Сравнением результатов с последовательной версией на идентичных + входных данных. - Проверкой инварианта отсортированности для всех выходных массивов. - Успешным прохождением всех функциональных тестов. @@ -179,40 +236,45 @@ Результаты для массива из **100 000** случайных целых чисел: -| Реализация | Количество потоков | Время, мс | Ускорение | Эффективность | -|------------|--------------------|-----------|-----------|----------------| -| SEQ | 1 | 32.0 | 1.00 | N/A | -| TBB | 8 | 22.0 | 1.45 | 18.2% | +| Реализация | Потоков | Время, мс | Ускорение | Эффективность | +|------------|---------|-----------|-----------|---------------| +| SEQ | 1 | 32.0 | 1.00 | N/A | +| TBB | 8 | 22.0 | 1.45 | 18.2% | -*Примечание: Количество потоков определяется автоматически через `std::thread::hardware_concurrency()` и контролируется через `tbb::global_control`.* +*Примечание: Количество потоков определяется автоматически через +`std::thread::hardware_concurrency()`.* ### 7.3 Анализ производительности #### Наблюдаемые эффекты -- **Ускорение TBB версии около 1.45x** при использовании аппаратно-определённого количества потоков (8). Это соответствует эффективности около 18.2%. -- Для массива из 100 000 элементов время выполнения составляет 22 мс, что в 1.45 раза быстрее последовательной версии (32 мс). +- **Ускорение TBB версии около 1.45x** при использовании 8 потоков. + Это соответствует эффективности около 18.2%. +- Для массива из 100 000 элементов время выполнения составляет 22 мс, + что в 1.45 раза быстрее последовательной версии (32 мс). #### Факторы, влияющие на производительность TBB | Фактор | Влияние на TBB версию | |--------|----------------------| | Планировщик задач | Умеренные накладные расходы | -| simple_partitioner | Минимизирует накладные расходы на разбиение | +| simple_partitioner | Минимизирует накладные расходы | | Глобальный контроль потоков | Позволяет ограничить параллелизм | -| Параллельная сортировка блоков | Хорошее ускорение (блоки независимы) | -| Параллельное слияние | Умеренное ускорение (зависимость по данным) | - +| Параллельная сортировка | Хорошее ускорение | +| Параллельное слияние | Умеренное ускорение | ## 8. Заключения ### Основные выводы -1. Разработан параллельный гибридный алгоритм сортировки с использованием Intel TBB, сочетающий QuickSort, InsertionSort и BatcherMerge. -2. Алгоритм корректен и демонстрирует ускорение до 1.45x на 8 аппаратных потоках для массива из 100 000 элементов. -3. Сортировка вставками для подмассивов размера < 16 даёт выигрыш по сравнению с рекурсивным QuickSort. -4. Условное распараллеливание слияния с использованием TBB позволяет адаптироваться под размер обрабатываемых данных. - +1. Разработан параллельный гибридный алгоритм сортировки с использованием + Intel TBB, сочетающий QuickSort, InsertionSort и BatcherMerge. +2. Алгоритм корректен и демонстрирует ускорение до 1.45x на 8 потоках + для массива из 100 000 элементов. +3. Сортировка вставками для подмассивов размера < 16 даёт выигрыш по + сравнению с рекурсивным QuickSort. +4. Условное распараллеливание слияния с использованием TBB позволяет + адаптироваться под размер обрабатываемых данных. ## Приложение @@ -227,10 +289,10 @@ bool KrasnopevtsevaVHoareBatcherSortTBB::RunImpl() { } int numthreads = static_cast(std::thread::hardware_concurrency()); - static tbb::global_control control(tbb::global_control::max_allowed_parallelism, numthreads); + static tbb::global_control control( + tbb::global_control::max_allowed_parallelism, numthreads); std::vector res = input; - int n = static_cast(size); numthreads = std::min(n, numthreads); @@ -240,108 +302,25 @@ bool KrasnopevtsevaVHoareBatcherSortTBB::RunImpl() { std::vector pointers(numthreads); std::vector sizes(numthreads); for (int i = 0; i < numthreads; ++i) { - auto offset = static_cast(i) * static_cast(thread_input_size); + auto offset = static_cast(i) * + static_cast(thread_input_size); pointers[i] = res.data() + offset; sizes[i] = thread_input_size; } sizes[sizes.size() - 1] += thread_input_remainder_size; - tbb::parallel_for(tbb::blocked_range(0, numthreads, 1), [&](const tbb::blocked_range &r) { - for (int i = r.begin(); i < r.end(); ++i) { - int left = static_cast(pointers[i] - res.data()); - int right = left + sizes[i] - 1; - QuickSort(res, left, right); - } - }, tbb::simple_partitioner()); + tbb::parallel_for( + tbb::blocked_range(0, numthreads, 1), + [&](const tbb::blocked_range &r) { + for (int i = r.begin(); i < r.end(); ++i) { + int left = static_cast(pointers[i] - res.data()); + int right = left + sizes[i] - 1; + QuickSort(res, left, right); + } + }, + tbb::simple_partitioner()); BatcherMerge(thread_input_size, pointers, sizes, 32); - GetOutput() = std::move(res); return true; -} - -void KrasnopevtsevaVHoareBatcherSortTBB::BatcherMerge(int thread_input_size, std::vector &pointers, - std::vector &sizes, int par_if_greater) { - int pack = static_cast(pointers.size()); - for (int step = 1; pack > 1; step *= 2, pack /= 2) { - if ((thread_input_size / step) > par_if_greater) { - tbb::parallel_for(tbb::blocked_range(0, pack / 2, 1), [&](const tbb::blocked_range &r) { - for (int off = r.begin(); off < r.end(); ++off) { - auto idx1 = static_cast(2 * step) * static_cast(off); - auto idx2 = idx1 + static_cast(step); - BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); - } - }, tbb::simple_partitioner()); - } else { - for (int off = 0; off < pack / 2; ++off) { - auto idx1 = static_cast(2 * step) * static_cast(off); - auto idx2 = idx1 + static_cast(step); - BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); - } - } - - if ((pack / 2) - 1 == 0) { - BatcherMergeBlocksStep(pointers[0], sizes[sizes.size() - 1], pointers[pointers.size() - 1], - sizes[sizes.size() - 1]); - } else if ((pack / 2) % 2 != 0) { - auto idx1 = static_cast(2 * step) * static_cast((pack / 2) - 2); - auto idx2 = static_cast(2 * step) * static_cast((pack / 2) - 1); - BatcherMergeBlocksStep(pointers[idx1], sizes[idx1], pointers[idx2], sizes[idx2]); - } - } -} - -void KrasnopevtsevaVHoareBatcherSortTBB::QuickSort(std::vector &arr, int first, int last) { - std::stack> stack; - stack.emplace(first, last); - - while (!stack.empty()) { - auto [l, r] = stack.top(); - stack.pop(); - - if (l >= r) { - continue; - } - - if (r - l < 16) { - InsertionSort(arr, l, r); - continue; - } - - int iter = Partition(arr, l, r); - - if (iter - l < r - iter) { - stack.emplace(iter + 1, r); - stack.emplace(l, iter - 1); - } else { - stack.emplace(l, iter - 1); - stack.emplace(iter + 1, r); - } - } -} - -int KrasnopevtsevaVHoareBatcherSortTBB::Partition(std::vector &arr, int first, int last) { - int i = first - 1; - int value = arr[last]; - - for (int j = first; j <= last - 1; ++j) { - if (arr[j] <= value) { - ++i; - std::swap(arr[i], arr[j]); - } - } - std::swap(arr[i + 1], arr[last]); - return i + 1; -} - -void KrasnopevtsevaVHoareBatcherSortTBB::InsertionSort(std::vector &arr, int first, int last) { - for (int i = first + 1; i <= last; ++i) { - int key = arr[i]; - int j = i - 1; - while (j >= first && arr[j] > key) { - arr[j + 1] = arr[j]; - --j; - } - arr[j + 1] = key; - } } \ No newline at end of file From b2680450773f59e3107104c46b0d2803cf3da8f2 Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 14:39:56 +0000 Subject: [PATCH 03/12] fix reports again --- .../all/report.md | 12 +++++----- .../omp/report.md | 14 ++++-------- .../report.md | 22 +++++++++---------- .../seq/report.md | 6 ++--- .../stl/report.md | 18 +++++++-------- .../tbb/report.md | 22 +++++++++---------- 6 files changed, 44 insertions(+), 50 deletions(-) diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md index c036500521..0ccbbc1211 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md @@ -137,7 +137,7 @@ ### 5.2 Основные методы | Метод | Назначение | -|-------|-------------| +| ----- | ---------- | | `ValidationImpl()` | Проверка входных данных (массив не пуст) | | `PreProcessingImpl()` | Инициализация выходного вектора | | `RunImpl()` | Запуск комбинированной сортировки | @@ -197,7 +197,7 @@ тестовых сценариев, покрывающих различные типы входных данных: | № | Тип массива | Описание | Размер | -|---|-------------|----------|--------| +| - | ----------- | -------- | -----: | | 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | | 2 | `default_array` | Случайный массив с произвольным порядком | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | @@ -245,9 +245,9 @@ Результаты для массива из **100 000** случайных целых чисел: | Реализация | Потоков | Время, мс | Ускорение | Эффективность | -|------------|---------|-----------|-----------|---------------| -| SEQ | 1 | 32.0 | 1.00 | N/A | -| ALL | 8 | 22.0 | 1.45 | 18.2% | +| ---------- | ------: | --------: | --------: | ------------: | +| SEQ | 1 | 32.0 | 1.00 | N/A | +| ALL | 8 | 22.0 | 1.45 | 18.2% | ### 7.3 Анализ производительности @@ -259,7 +259,7 @@ #### Факторы, влияющие на производительность ALL версии | Фактор | Влияние на ALL версию | -|--------|----------------------| +| ------ | --------------------- | | Адаптивная стратегия (n < 1000) | Положительное | | Накладные расходы OpenMP | Умеренные | | Параллельная сортировка блоков | Хорошее ускорение | diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md index 9dd0fbebc1..c0bd9b1179 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md @@ -176,7 +176,7 @@ тестовых сценариев, покрывающих различные типы входных данных: | № | Тип массива | Описание | Размер | -|---|-------------|----------|--------| +| - | ----------- | -------- | -----: | | 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | | 2 | `default_array` | Случайный массив с произвольным порядком | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | @@ -224,9 +224,9 @@ Результаты для массива из **100 000** случайных целых чисел: | Режим | Потоков | Время, мс | Ускорение | Эффективность | -|-------|---------|-----------|-----------|---------------| -| seq | 1 | 32 | 1.00 | N/A | -| omp | 8 | 19 | 1.94 | 24.3% | +| ----- | ------: | --------: | --------: | ------------: | +| seq | 1 | 32 | 1.00 | N/A | +| omp | 8 | 19 | 1.94 | 24.3% | ### 7.3 Анализ производительности @@ -244,12 +244,6 @@ 2. При использовании 8 потоков ускорение достигает 1.94x, что подтверждает эффективность параллелизации для массивов большого размера. -### Ограничения - -- Снижение эффективности при использовании большого количества потоков - из-за накладных расходов на слияние. -- Зависимость от равномерности начального разбиения данных. - ## Приложение ### Основной цикл параллельной сортировки diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md index b7721eec3e..0dad216b36 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md @@ -48,12 +48,12 @@ ### 4.1 Сводная таблица | Версия | Технология | Время, мс | Ускорение | Эффективность | -|--------|------------|-----------|-----------|---------------| -| SEQ | Последовательная | 32.0 | 1.00x | N/A | -| OMP | OpenMP | 19.0 | 1.68x | 21.0% | -| STL | std::async | 18.0 | 1.78x | 22.3% | -| TBB | Intel TBB | 22.0 | 1.45x | 18.2% | -| ALL | OpenMP + адаптивная | 22.0 | 1.45x | 18.2% | +| ------ | ---------- | --------: | --------: | ------------: | +| SEQ | Последовательная | 32.0 | 1.00x | N/A | +| OMP | OpenMP | 19.0 | 1.68x | 21.0% | +| STL | std::async | 18.0 | 1.78x | 22.3% | +| TBB | Intel TBB | 22.0 | 1.45x | 18.2% | +| ALL | OpenMP + адаптивная | 22.0 | 1.45x | 18.2% | ## 5. Сравнительный анализ @@ -69,11 +69,11 @@ ### 5.2 Анализ эффективности | Версия | Эффективность | Объяснение | -|--------|---------------|------------| -| STL | 22.3% | Наиболее эффективная реализация | -| OMP | 21.0% | Небольшое отставание от STL | -| TBB | 18.2% | Планировщик задач вносит накладные расходы | -| ALL | 18.2% | Накладные расходы OpenMP + адаптивная стратегия | +| ------ | ------------- | ---------- | +| STL | 22.3% | Наиболее эффективная реализация | +| OMP | 21.0% | Небольшое отставание от STL | +| TBB | 18.2% | Планировщик задач вносит накладные расходы | +| ALL | 18.2% | Накладные расходы OpenMP + адаптивная стратегия | ## 6. Выводы diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md index 49202a01c1..d4ead72eed 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md @@ -121,7 +121,7 @@ merge sort) представляет собой алгоритм сортиро тестовых сценариев, покрывающих различные типы входных данных: | № | Тип массива | Описание | Размер | -|---|-------------|----------|--------| +| - | ----------- | -------- | -----: | | 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | | 2 | `default_array` | Случайный массив с произвольным порядком | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | @@ -159,8 +159,8 @@ merge sort) представляет собой алгоритм сортиро Результаты для массива из **100 000** случайных целых чисел: | Режим | Размер | Время, мс | Ускорение | Эффективность | -|-------|--------|-----------|-----------|---------------| -| seq | 100000 | 32 | 1.00 | N/A | +| ----- | -----: | --------: | --------: | ------------: | +| seq | 100000 | 32 | 1.00 | N/A | *Сравнение с параллельными версиями не предусмотрено для SEQ.* diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md index 1da372aeb1..bc8e7e24ce 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md @@ -132,11 +132,11 @@ ### 5.2 Основные методы | Метод | Назначение | -|-------|-------------| -| `ValidationImpl()` | Проверка входных данных (массив не пуст) | +| ----- | ---------- | +| `ValidationImpl()` | Проверка входных данных | | `PreProcessingImpl()` | Инициализация выходного вектора | | `RunImpl()` | Запуск параллельной сортировки | -| `GetNumThreads()` | Определение оптимального количества потоков | +| `GetNumThreads()` | Определение количества потоков | | `SetupChunks()` | Разбиение массива на блоки | | `ParallelSortChunks()` | Асинхронная сортировка блоков | | `QuickSort()` | Итеративная быстрая сортировка | @@ -186,12 +186,12 @@ тестовых сценариев, покрывающих различные типы входных данных: | № | Тип массива | Описание | Размер | -|---|-------------|----------|--------| +| - | ----------- | -------- | -----: | | 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | | 2 | `default_array` | Случайный массив с произвольным порядком | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | | 4 | `long_array` | Длинный массив с повторениями | 50 | -| 5 | `two_number_array` | Массив только из двух значений | 9 | +| 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | | 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | #### Верификация результата @@ -231,9 +231,9 @@ Результаты для массива из **100 000** случайных целых чисел: | Реализация | Потоков | Время, мс | Ускорение | Эффективность | -|------------|---------|-----------|-----------|---------------| -| SEQ | 1 | 32.0 | 1.00 | N/A | -| STL | 8 | 18.0 | 1.78 | 22.3% | +| ---------- | ------: | --------: | --------: | ------------: | +| SEQ | 1 | 32.0 | 1.00 | N/A | +| STL | 8 | 18.0 | 1.78 | 22.3% | *Примечание: Количество потоков определяется автоматически через `std::thread::hardware_concurrency()`.* @@ -250,7 +250,7 @@ #### Факторы, влияющие на производительность | Фактор | Влияние на STL версию | -|--------|----------------------| +| ------ | --------------------- | | Накладные расходы std::async | Высокие | | Параллельная сортировка блоков | Хорошее ускорение | | Параллельное слияние | Умеренное ускорение | diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md index c04261fc94..441776080c 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md @@ -135,15 +135,15 @@ ### 5.2 Основные методы | Метод | Назначение | -|-------|-------------| -| `ValidationImpl()` | Проверка входных данных (массив не пуст) | +| ----- | ---------- | +| `ValidationImpl()` | Проверка входных данных | | `PreProcessingImpl()` | Инициализация выходного вектора | | `RunImpl()` | Запуск параллельной сортировки | | `QuickSort()` | Итеративная быстрая сортировка | -| `Partition()` | Разбиение Хоара (с последним опорным элементом) | -| `InsertionSort()` | Сортировка вставками для малых диапазонов | +| `Partition()` | Разбиение Хоара | +| `InsertionSort()` | Сортировка вставками | | `BatcherMergeBlocksStep()` | Слияние двух блоков | -| `BatcherMerge()` | Параллельное слияние блоков с использованием TBB | +| `BatcherMerge()` | Параллельное слияние | ### 5.3 Важные особенности @@ -190,9 +190,9 @@ тестовых сценариев, покрывающих различные типы входных данных: | № | Тип массива | Описание | Размер | -|---|-------------|----------|--------| +| - | ----------- | -------- | -----: | | 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком элементов | 20 | +| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | | 4 | `long_array` | Длинный массив с повторениями | 50 | | 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | @@ -237,9 +237,9 @@ Результаты для массива из **100 000** случайных целых чисел: | Реализация | Потоков | Время, мс | Ускорение | Эффективность | -|------------|---------|-----------|-----------|---------------| -| SEQ | 1 | 32.0 | 1.00 | N/A | -| TBB | 8 | 22.0 | 1.45 | 18.2% | +| ---------- | ------: | --------: | --------: | ------------: | +| SEQ | 1 | 32.0 | 1.00 | N/A | +| TBB | 8 | 22.0 | 1.45 | 18.2% | *Примечание: Количество потоков определяется автоматически через `std::thread::hardware_concurrency()`.* @@ -256,7 +256,7 @@ #### Факторы, влияющие на производительность TBB | Фактор | Влияние на TBB версию | -|--------|----------------------| +| ------ | --------------------- | | Планировщик задач | Умеренные накладные расходы | | simple_partitioner | Минимизирует накладные расходы | | Глобальный контроль потоков | Позволяет ограничить параллелизм | From d8ffac4d5200a60b6fa6ae79f0b23035aa977af3 Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 15:00:57 +0000 Subject: [PATCH 04/12] fix reports again 2.0 --- tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md | 2 +- tasks/krasnopevtseva_v_hoare_batcher_sort/report.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md index c0bd9b1179..a04ae2d7a5 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md @@ -181,7 +181,7 @@ | 2 | `default_array` | Случайный массив с произвольным порядком | 20 | | 3 | `short_array` | Короткий массив минимального размера | 4 | | 4 | `long_array` | Длинный массив с повторениями | 50 | -| 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | +| 5 | `two_number_array` | Массив только из двух значений | 9 | | 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | #### Верификация результата diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md index 0dad216b36..6686266bc1 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md @@ -27,7 +27,7 @@ ### 2.2 Особенности реализаций | Версия | Технология | Ключевые особенности | -|--------|------------|----------------------| +| ------ | ---------- | -------------------- | | SEQ | Последовательная | Базовый алгоритм без параллелизации | | OMP | OpenMP | `#pragma omp parallel for`, параллельная сортировка | | STL | std::async + future | Асинхронные задачи, аппаратное определение потоков | From ea3b147ab1c9bdab04ccce574605d2ec8b4f0dcc Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 15:07:30 +0000 Subject: [PATCH 05/12] fix reports again 3.0 --- .../omp/report.md | 18 +++--- .../report.md | 14 ++-- .../seq/report.md | 16 ++--- .../stl/report.md | 64 +++++++++---------- 4 files changed, 55 insertions(+), 57 deletions(-) diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md index a04ae2d7a5..ce46902cad 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md @@ -121,16 +121,14 @@ ### 5.2 Основные методы -| Метод | Назначение | -|-------|-------------| -| `ValidationImpl()` | Проверка входных данных (массив не пуст) | -| `PreProcessingImpl()` | Инициализация выходного вектора | -| `RunImpl()` | Запуск параллельной сортировки | -| `QuickSort()` | Итеративная быстрая сортировка | -| `Partition()` | Разбиение Хоара (с последним опорным элементом) | -| `InsertionSort()` | Сортировка вставками для малых диапазонов | -| `BatcherMergeBlocksStep()` | Слияние двух блоков | -| `BatcherMerge()` | Параллельное слияние блоков | +|№|Тип массива|Описание|Размер| +|-|-|-|-:| +|1|`sorted_array`|Уже отсортированный по возрастанию массив|16| +|2|`default_array`|Случайный массив с произвольным порядком|20| +|3|`short_array`|Короткий массив минимального размера|4| +|4|`long_array`|Длинный массив с повторениями|50| +|5|`two_number_array`|Массив только из двух значений (10 и 20)|9| +|6|`end_to_begin_array`|Массив, отсортированный по убыванию|24| ### 5.3 Важные особенности diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md index 6686266bc1..b8e75a7784 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md @@ -26,13 +26,13 @@ ### 2.2 Особенности реализаций -| Версия | Технология | Ключевые особенности | -| ------ | ---------- | -------------------- | -| SEQ | Последовательная | Базовый алгоритм без параллелизации | -| OMP | OpenMP | `#pragma omp parallel for`, параллельная сортировка | -| STL | std::async + future | Асинхронные задачи, аппаратное определение потоков | -| TBB | Intel TBB | `tbb::parallel_for`, `tbb::global_control` | -| ALL | OpenMP + STL | Комбинированный подход, адаптивная стратегия | +|Версия|Технология|Ключевые особенности| +|-|-|-| +|SEQ|Последовательная|Базовый алгоритм без параллелизации| +|OMP|OpenMP|`#pragma omp parallel for`, параллельная сортировка| +|STL|std::async + future|Асинхронные задачи, аппаратное определение потоков| +|TBB|Intel TBB|`tbb::parallel_for`, `tbb::global_control`| +|ALL|OpenMP + STL|Комбинированный подход, адаптивная стратегия| ## 3. Экспериментальная среда diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md index d4ead72eed..f19231430f 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md @@ -120,14 +120,14 @@ merge sort) представляет собой алгоритм сортиро Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: -| № | Тип массива | Описание | Размер | -| - | ----------- | -------- | -----: | -| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | -| 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторениями | 50 | -| 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | +|№|Тип массива|Описание|Размер| +|-|-|-|-:| +|1|`sorted_array`|Уже отсортированный по возрастанию массив|16| +|2|`default_array`|Случайный массив с произвольным порядком|20| +|3|`short_array`|Короткий массив минимального размера|4| +|4|`long_array`|Длинный массив с повторениями|50| +|5|`two_number_array`|Массив только из двух значений (10 и 20)|9| +|6|`end_to_begin_array`|Массив, отсортированный по убыванию|24| #### Верификация результата diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md index bc8e7e24ce..b2fac4ccfa 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md @@ -131,19 +131,19 @@ ### 5.2 Основные методы -| Метод | Назначение | -| ----- | ---------- | -| `ValidationImpl()` | Проверка входных данных | -| `PreProcessingImpl()` | Инициализация выходного вектора | -| `RunImpl()` | Запуск параллельной сортировки | -| `GetNumThreads()` | Определение количества потоков | -| `SetupChunks()` | Разбиение массива на блоки | -| `ParallelSortChunks()` | Асинхронная сортировка блоков | -| `QuickSort()` | Итеративная быстрая сортировка | -| `Partition()` | Разбиение Хоара | -| `InsertionSort()` | Сортировка вставками | -| `BatcherMergeBlocksStep()` | Слияние двух блоков | -| `BatcherMergeLevel()` | Параллельное слияние на уровне | +|Метод|Назначение| +|-|-| +|`ValidationImpl()`|Проверка входных данных| +|`PreProcessingImpl()`|Инициализация выходного вектора| +|`RunImpl()`|Запуск параллельной сортировки| +|`GetNumThreads()`|Определение количества потоков| +|`SetupChunks()`|Разбиение массива на блоки| +|`ParallelSortChunks()`|Асинхронная сортировка блоков| +|`QuickSort()`|Итеративная быстрая сортировка| +|`Partition()`|Разбиение Хоара| +|`InsertionSort()`|Сортировка вставками| +|`BatcherMergeBlocksStep()`|Слияние двух блоков| +|`BatcherMergeLevel()`|Параллельное слияние на уровне| ### 5.3 Важные особенности @@ -185,14 +185,14 @@ Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: -| № | Тип массива | Описание | Размер | -| - | ----------- | -------- | -----: | -| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | -| 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторениями | 50 | -| 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | +|№|Тип массива|Описание|Размер| +|-|-|-|-:| +|1|`sorted_array`|Уже отсортированный по возрастанию массив|16| +|2|`default_array`|Случайный массив с произвольным порядком|20| +|3|`short_array`|Короткий массив минимального размера|4| +|4|`long_array`|Длинный массив с повторениями|50| +|5|`two_number_array`|Массив только из двух значений (10 и 20)|9| +|6|`end_to_begin_array`|Массив, отсортированный по убыванию|24| #### Верификация результата @@ -230,10 +230,10 @@ Результаты для массива из **100 000** случайных целых чисел: -| Реализация | Потоков | Время, мс | Ускорение | Эффективность | -| ---------- | ------: | --------: | --------: | ------------: | -| SEQ | 1 | 32.0 | 1.00 | N/A | -| STL | 8 | 18.0 | 1.78 | 22.3% | +|Реализация|Потоков|Время, мс|Ускорение|Эффективность| +|-|-|-|-|-| +|SEQ|1|32.0|1.00|N/A| +|STL|8|18.0|1.78|22.3%| *Примечание: Количество потоков определяется автоматически через `std::thread::hardware_concurrency()`.* @@ -249,13 +249,13 @@ #### Факторы, влияющие на производительность -| Фактор | Влияние на STL версию | -| ------ | --------------------- | -| Накладные расходы std::async | Высокие | -| Параллельная сортировка блоков | Хорошее ускорение | -| Параллельное слияние | Умеренное ускорение | -| Балансировка нагрузки | Средняя | -| Условное распараллеливание | Положительное | +|Фактор|Влияние на STL версию| +|-|-| +|Накладные расходы std::async|Высокие| +|Параллельная сортировка блоков|Хорошее ускорение| +|Параллельное слияние|Умеренное ускорение| +|Балансировка нагрузки|Средняя| +|Условное распараллеливание|Положительное| ## 8. Заключения From e9fe81daf31c73820aaebd3258b1a26a39f2f25f Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 15:13:28 +0000 Subject: [PATCH 06/12] fix reports final --- .../seq/report.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md index f19231430f..aea85d901d 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md @@ -120,14 +120,14 @@ merge sort) представляет собой алгоритм сортиро Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: -|№|Тип массива|Описание|Размер| -|-|-|-|-:| -|1|`sorted_array`|Уже отсортированный по возрастанию массив|16| -|2|`default_array`|Случайный массив с произвольным порядком|20| -|3|`short_array`|Короткий массив минимального размера|4| -|4|`long_array`|Длинный массив с повторениями|50| -|5|`two_number_array`|Массив только из двух значений (10 и 20)|9| -|6|`end_to_begin_array`|Массив, отсортированный по убыванию|24| +| № | Тип массива | Описание | Размер | +| - | ----------- | -------- | -----: | +| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | +| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | +| 3 | `short_array` | Короткий массив минимального размера | 4 | +| 4 | `long_array` | Длинный массив с повторениями | 50 | +| 5 | `two_number_array` | Массив только из двух значений | 9 | +| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | #### Верификация результата From 16f6476bf37acb5e06ba5ce5b49bcf25c5acdf5d Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 15:26:32 +0000 Subject: [PATCH 07/12] fix reports again final final --- .../seq/report.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md index aea85d901d..f19231430f 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md @@ -120,14 +120,14 @@ merge sort) представляет собой алгоритм сортиро Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: -| № | Тип массива | Описание | Размер | -| - | ----------- | -------- | -----: | -| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | -| 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторениями | 50 | -| 5 | `two_number_array` | Массив только из двух значений | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | +|№|Тип массива|Описание|Размер| +|-|-|-|-:| +|1|`sorted_array`|Уже отсортированный по возрастанию массив|16| +|2|`default_array`|Случайный массив с произвольным порядком|20| +|3|`short_array`|Короткий массив минимального размера|4| +|4|`long_array`|Длинный массив с повторениями|50| +|5|`two_number_array`|Массив только из двух значений (10 и 20)|9| +|6|`end_to_begin_array`|Массив, отсортированный по убыванию|24| #### Верификация результата From 00f644c6660c592c98e4dcdace0bc2f2c8bbff46 Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 15:36:11 +0000 Subject: [PATCH 08/12] final version --- .../seq/report.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md index f19231430f..19fd0d75ed 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md @@ -89,16 +89,16 @@ merge sort) представляет собой алгоритм сортиро ### 5.2 Основные методы -| Метод | Назначение | -|-------|-------------| -| `ValidationImpl()` | Проверка входных данных (массив не пуст) | -| `PreProcessingImpl()` | Инициализация выходного вектора | -| `RunImpl()` | Запуск сортировки | -| `QuickBatcherSort()` | Итеративная быстрая сортировка со стеком | -| `Partition()` | Разбиение Хоара с медианой из трёх | -| `InsertionSort()` | Сортировка вставками | -| `BatcherMerge()` | Слияние Бэтчера | -| `CompareAndSwap()` | Вспомогательная функция сравнения и обмена | +|Метод|Назначение| +|-|-| +|`ValidationImpl()`|Проверка входных данных (массив не пуст)| +|`PreProcessingImpl()`|Инициализация выходного вектора| +|`RunImpl()`|Запуск сортировки| +|`QuickBatcherSort()`|Итеративная быстрая сортировка со стеком| +|`Partition()`|Разбиение Хоара с медианой из трёх| +|`InsertionSort()`|Сортировка вставками| +|`BatcherMerge()`|Слияние Бэтчера| +|`CompareAndSwap()`|Вспомогательная функция сравнения и обмена| ## 6. Окружение и тестирование From a43da866bd4848ea9a76bab0d001270874225cc1 Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 15:50:11 +0000 Subject: [PATCH 09/12] final version 2.0 --- .../all/report.md | 62 +++++++++---------- .../omp/report.md | 24 +++---- .../report.md | 26 ++++---- .../seq/report.md | 6 +- .../tbb/report.md | 58 ++++++++--------- 5 files changed, 88 insertions(+), 88 deletions(-) diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md index 0ccbbc1211..3ea510b2aa 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md @@ -136,18 +136,18 @@ ### 5.2 Основные методы -| Метод | Назначение | -| ----- | ---------- | -| `ValidationImpl()` | Проверка входных данных (массив не пуст) | -| `PreProcessingImpl()` | Инициализация выходного вектора | -| `RunImpl()` | Запуск комбинированной сортировки | -| `SortLocalData()` | Адаптивный выбор стратегии сортировки | -| `ParallelSortChunks()` | Параллельная сортировка блоков | -| `QuickSort()` | Итеративная быстрая сортировка | -| `Partition()` | Разбиение Хоара | -| `InsertionSort()` | Сортировка вставками | -| `BatcherMergeBlocksStep()` | Слияние двух блоков | -| `BatcherMerge()` | Параллельное слияние блоков | +|Метод|Назначение| +|-|-| +|`ValidationImpl()`|Проверка входных данных (массив не пуст)| +|`PreProcessingImpl()`|Инициализация выходного вектора| +|`RunImpl()`|Запуск комбинированной сортировки| +|`SortLocalData()`|Адаптивный выбор стратегии сортировки| +|`ParallelSortChunks()`|Параллельная сортировка блоков| +|`QuickSort()`|Итеративная быстрая сортировка| +|`Partition()`|Разбиение Хоара| +|`InsertionSort()`|Сортировка вставками| +|`BatcherMergeBlocksStep()`|Слияние двух блоков| +|`BatcherMerge()`|Параллельное слияние блоков| ### 5.3 Важные особенности @@ -196,14 +196,14 @@ Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: -| № | Тип массива | Описание | Размер | -| - | ----------- | -------- | -----: | -| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | -| 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторениями | 50 | -| 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | +|№|Тип массива|Описание|Размер| +|-|-|-|-:| +|1|`sorted_array`|Уже отсортированный по возрастанию массив|16| +|2|`default_array`|Случайный массив с произвольным порядком|20| +|3|`short_array`|Короткий массив минимального размера|4| +|4|`long_array`|Длинный массив с повторениями|50| +|5|`two_number_array`|Массив только из двух значений (10 и 20)|9| +|6|`end_to_begin_array`|Массив, отсортированный по убыванию|24| #### Верификация результата @@ -244,10 +244,10 @@ Результаты для массива из **100 000** случайных целых чисел: -| Реализация | Потоков | Время, мс | Ускорение | Эффективность | -| ---------- | ------: | --------: | --------: | ------------: | -| SEQ | 1 | 32.0 | 1.00 | N/A | -| ALL | 8 | 22.0 | 1.45 | 18.2% | +|Реализация|Потоков|Время, мс|Ускорение|Эффективность| +|-|-:|-:|-:|-:| +|SEQ|1|32.0|1.00|N/A| +|ALL|8|22.0|1.45|18.2%| ### 7.3 Анализ производительности @@ -258,13 +258,13 @@ #### Факторы, влияющие на производительность ALL версии -| Фактор | Влияние на ALL версию | -| ------ | --------------------- | -| Адаптивная стратегия (n < 1000) | Положительное | -| Накладные расходы OpenMP | Умеренные | -| Параллельная сортировка блоков | Хорошее ускорение | -| Параллельное слияние | Умеренное ускорение | -| Балансировка нагрузки | Средняя | +|Фактор|Влияние на ALL версию| +|-|-| +|Адаптивная стратегия (n < 1000)|Положительное| +|Накладные расходы OpenMP|Умеренные| +|Параллельная сортировка блоков|Хорошее ускорение| +|Параллельное слияние|Умеренное ускорение| +|Балансировка нагрузки|Средняя| ## 8. Заключения diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md index ce46902cad..f8d7142b1f 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md @@ -173,14 +173,14 @@ Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: -| № | Тип массива | Описание | Размер | -| - | ----------- | -------- | -----: | -| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | -| 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторениями | 50 | -| 5 | `two_number_array` | Массив только из двух значений | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | +|№|Тип массива|Описание|Размер| +|-|-|-|-:| +|1|`sorted_array`|Уже отсортированный по возрастанию массив|16| +|2|`default_array`|Случайный массив с произвольным порядком|20| +|3|`short_array`|Короткий массив минимального размера|4| +|4|`long_array`|Длинный массив с повторениями|50| +|5|`two_number_array`|Массив только из двух значений|9| +|6|`end_to_begin_array`|Массив, отсортированный по убыванию|24| #### Верификация результата @@ -221,10 +221,10 @@ Результаты для массива из **100 000** случайных целых чисел: -| Режим | Потоков | Время, мс | Ускорение | Эффективность | -| ----- | ------: | --------: | --------: | ------------: | -| seq | 1 | 32 | 1.00 | N/A | -| omp | 8 | 19 | 1.94 | 24.3% | +|Режим|Потоков|Время, мс|Ускорение|Эффективность| +|-|-:|-:|-:|-:| +|seq|1|32|1.00|N/A| +|omp|8|19|1.94|24.3%| ### 7.3 Анализ производительности diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md index b8e75a7784..f47ef44164 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md @@ -47,13 +47,13 @@ ### 4.1 Сводная таблица -| Версия | Технология | Время, мс | Ускорение | Эффективность | -| ------ | ---------- | --------: | --------: | ------------: | -| SEQ | Последовательная | 32.0 | 1.00x | N/A | -| OMP | OpenMP | 19.0 | 1.68x | 21.0% | -| STL | std::async | 18.0 | 1.78x | 22.3% | -| TBB | Intel TBB | 22.0 | 1.45x | 18.2% | -| ALL | OpenMP + адаптивная | 22.0 | 1.45x | 18.2% | +|Версия|Технология|Время, мс|Ускорение|Эффективность| +|-|-|-:|-:|-:| +|SEQ|Последовательная|32.0|1.00x|N/A| +|OMP|OpenMP|19.0|1.68x|21.0%| +|STL|std::async|18.0|1.78x|22.3%| +|TBB|Intel TBB|22.0|1.45x|18.2%| +|ALL|OpenMP + адаптивная|22.0|1.45x|18.2%| ## 5. Сравнительный анализ @@ -68,12 +68,12 @@ ### 5.2 Анализ эффективности -| Версия | Эффективность | Объяснение | -| ------ | ------------- | ---------- | -| STL | 22.3% | Наиболее эффективная реализация | -| OMP | 21.0% | Небольшое отставание от STL | -| TBB | 18.2% | Планировщик задач вносит накладные расходы | -| ALL | 18.2% | Накладные расходы OpenMP + адаптивная стратегия | +|Версия|Эффективность|Объяснение| +|-|-|-| +|STL|22.3%|Наиболее эффективная реализация| +|OMP|21.0%|Небольшое отставание от STL| +|TBB|18.2%|Планировщик задач вносит накладные расходы| +|ALL|18.2%|Накладные расходы OpenMP + адаптивная стратегия| ## 6. Выводы diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md index 19fd0d75ed..2230d76f47 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md @@ -158,9 +158,9 @@ merge sort) представляет собой алгоритм сортиро Результаты для массива из **100 000** случайных целых чисел: -| Режим | Размер | Время, мс | Ускорение | Эффективность | -| ----- | -----: | --------: | --------: | ------------: | -| seq | 100000 | 32 | 1.00 | N/A | +|Режим|Размер|Время, мс|Ускорение|Эффективность| +|-|-:|-:|-:|-:| +|seq|100000|32|1.00|N/A| *Сравнение с параллельными версиями не предусмотрено для SEQ.* diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md index 441776080c..4813eeaf63 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md @@ -134,16 +134,16 @@ ### 5.2 Основные методы -| Метод | Назначение | -| ----- | ---------- | -| `ValidationImpl()` | Проверка входных данных | -| `PreProcessingImpl()` | Инициализация выходного вектора | -| `RunImpl()` | Запуск параллельной сортировки | -| `QuickSort()` | Итеративная быстрая сортировка | -| `Partition()` | Разбиение Хоара | -| `InsertionSort()` | Сортировка вставками | -| `BatcherMergeBlocksStep()` | Слияние двух блоков | -| `BatcherMerge()` | Параллельное слияние | +|Метод|Назначение| +|-|-| +|`ValidationImpl()`|Проверка входных данных| +|`PreProcessingImpl()`|Инициализация выходного вектора| +|`RunImpl()`|Запуск параллельной сортировки| +|`QuickSort()`|Итеративная быстрая сортировка| +|`Partition()`|Разбиение Хоара| +|`InsertionSort()`|Сортировка вставками| +|`BatcherMergeBlocksStep()`|Слияние двух блоков| +|`BatcherMerge()`|Параллельное слияние| ### 5.3 Важные особенности @@ -189,14 +189,14 @@ Для проверки базовой корректности алгоритма был разработан набор из 6 тестовых сценариев, покрывающих различные типы входных данных: -| № | Тип массива | Описание | Размер | -| - | ----------- | -------- | -----: | -| 1 | `sorted_array` | Уже отсортированный по возрастанию массив | 16 | -| 2 | `default_array` | Случайный массив с произвольным порядком | 20 | -| 3 | `short_array` | Короткий массив минимального размера | 4 | -| 4 | `long_array` | Длинный массив с повторениями | 50 | -| 5 | `two_number_array` | Массив только из двух значений (10 и 20) | 9 | -| 6 | `end_to_begin_array` | Массив, отсортированный по убыванию | 24 | +|№|Тип массива|Описание|Размер| +|-|-|-|-:| +|1|`sorted_array`|Уже отсортированный по возрастанию массив|16| +|2|`default_array`|Случайный массив с произвольным порядком|20| +|3|`short_array`|Короткий массив минимального размера|4| +|4|`long_array`|Длинный массив с повторениями|50| +|5|`two_number_array`|Массив только из двух значений (10 и 20)|9| +|6|`end_to_begin_array`|Массив, отсортированный по убыванию|24| #### Верификация результата @@ -236,10 +236,10 @@ Результаты для массива из **100 000** случайных целых чисел: -| Реализация | Потоков | Время, мс | Ускорение | Эффективность | -| ---------- | ------: | --------: | --------: | ------------: | -| SEQ | 1 | 32.0 | 1.00 | N/A | -| TBB | 8 | 22.0 | 1.45 | 18.2% | +|Реализация|Потоков|Время, мс|Ускорение|Эффективность| +|-|-:|-:|-:|-:| +|SEQ|1|32.0|1.00|N/A| +|TBB|8|22.0|1.45|18.2%| *Примечание: Количество потоков определяется автоматически через `std::thread::hardware_concurrency()`.* @@ -255,13 +255,13 @@ #### Факторы, влияющие на производительность TBB -| Фактор | Влияние на TBB версию | -| ------ | --------------------- | -| Планировщик задач | Умеренные накладные расходы | -| simple_partitioner | Минимизирует накладные расходы | -| Глобальный контроль потоков | Позволяет ограничить параллелизм | -| Параллельная сортировка | Хорошее ускорение | -| Параллельное слияние | Умеренное ускорение | +|Фактор|Влияние на TBB версию| +|-|-| +|Планировщик задач|Умеренные накладные расходы| +|simple_partitioner|Минимизирует накладные расходы| +|Глобальный контроль потоков|Позволяет ограничить параллелизм| +|Параллельная сортировка|Хорошее ускорение| +|Параллельное слияние|Умеренное ускорение| ## 8. Заключения From 70e6515785f6ceec00a13bcb121ad94922f19691 Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 16:16:41 +0000 Subject: [PATCH 10/12] fix --- tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md index 2230d76f47..105070da3a 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md @@ -215,4 +215,4 @@ void KrasnopevtsevaVHoareBatcherSortSEQ::QuickBatcherSort( if (right - left > 32) { BatcherMerge(arr, left, right); } -} \ No newline at end of file +} From 17ac5b164889aa4186f397d9d11662a86880f6e4 Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 16:27:51 +0000 Subject: [PATCH 11/12] again From 3b1dd69ca0c4b8726bacdc7f235bf13c8c4c4b67 Mon Sep 17 00:00:00 2001 From: NikaKrasnopevtseva Date: Thu, 4 Jun 2026 16:46:36 +0000 Subject: [PATCH 12/12] fixxxxx --- tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md | 2 +- tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md | 2 +- tasks/krasnopevtseva_v_hoare_batcher_sort/report.md | 2 +- tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md | 1 - tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md | 3 +-- tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md index 3ea510b2aa..bb8eef0ae6 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/all/report.md @@ -351,4 +351,4 @@ void KrasnopevtsevaVHoareBatcherSortALL::ParallelSortChunks( } BatcherMerge(thread_input_size, pointers, sizes, 32); -} \ No newline at end of file +} diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md index f8d7142b1f..c2c2fe3e2f 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/omp/report.md @@ -286,4 +286,4 @@ bool KrasnopevtsevaVHoareBatcherSortOMP::RunImpl() { GetOutput() = std::move(res); return true; -} \ No newline at end of file +} diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md index f47ef44164..0286cec5b4 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/report.md @@ -110,4 +110,4 @@ Все реализации корректно работают на различных типах входных данных и могут быть рекомендованы для использования в зависимости от конкретных -требований к производительности, переносимости и сложности реализации. \ No newline at end of file +требований к производительности, переносимости и сложности реализации. diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md index 105070da3a..de01748947 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/seq/report.md @@ -182,7 +182,6 @@ merge sort) представляет собой алгоритм сортиро 4. Слияние Бэтчера улучшает порядок элементов после завершения QuickSort, но ценой дополнительной памяти. - ## Приложение ```cpp diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md index b2fac4ccfa..839702574f 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/stl/report.md @@ -271,7 +271,6 @@ 5. Условное распараллеливание слияния позволяет адаптироваться под размер обрабатываемых данных. - ## Приложение ```cpp @@ -320,4 +319,4 @@ bool KrasnopevtsevaVHoareBatcherSortSTL::RunImpl() { GetOutput() = std::move(res); return true; -} \ No newline at end of file +} diff --git a/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md b/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md index 4813eeaf63..66b40c46bb 100644 --- a/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md +++ b/tasks/krasnopevtseva_v_hoare_batcher_sort/tbb/report.md @@ -323,4 +323,4 @@ bool KrasnopevtsevaVHoareBatcherSortTBB::RunImpl() { BatcherMerge(thread_input_size, pointers, sizes, 32); GetOutput() = std::move(res); return true; -} \ No newline at end of file +}