diff --git a/tasks/gasenin_l_lex_dif/common/include/common.hpp b/tasks/gasenin_l_lex_dif/common/include/common.hpp new file mode 100644 index 0000000000..63ca91bea8 --- /dev/null +++ b/tasks/gasenin_l_lex_dif/common/include/common.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +#include "task/include/task.hpp" + +namespace gasenin_l_lex_dif { + +using InType = std::pair; +using OutType = int; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace gasenin_l_lex_dif diff --git a/tasks/gasenin_l_lex_dif/info.json b/tasks/gasenin_l_lex_dif/info.json new file mode 100644 index 0000000000..8135c4dbf5 --- /dev/null +++ b/tasks/gasenin_l_lex_dif/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Леонид", + "last_name": "Гасенин", + "middle_name": "Вячеславович", + "group_number": "3823Б1ФИ3", + "task_number": "1" + } +} diff --git a/tasks/gasenin_l_lex_dif/mpi/include/ops_mpi.hpp b/tasks/gasenin_l_lex_dif/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..47b87c67b5 --- /dev/null +++ b/tasks/gasenin_l_lex_dif/mpi/include/ops_mpi.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "gasenin_l_lex_dif/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace gasenin_l_lex_dif { + +/*struct DiffInfo { + size_t pos; + int result; +};*/ + +class GaseninLLexDifMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit GaseninLLexDifMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + // void RunSequentialComparison(const std::string &str1, const std::string &str2); + // bool RunParallelComparison(const std::string &str1, const std::string &str2, int rank, int size); +}; + +} // namespace gasenin_l_lex_dif diff --git a/tasks/gasenin_l_lex_dif/mpi/src/ops_mpi.cpp b/tasks/gasenin_l_lex_dif/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..57df8f8a58 --- /dev/null +++ b/tasks/gasenin_l_lex_dif/mpi/src/ops_mpi.cpp @@ -0,0 +1,103 @@ +#include "gasenin_l_lex_dif/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include // Исправление 1: Явное подключение string + +#include "gasenin_l_lex_dif/common/include/common.hpp" + +namespace { + +// Выносим логику локального поиска различий, чтобы снизить Cognitive Complexity +struct LocalDiff { + size_t diff_pos; + int result; +}; + +LocalDiff FindLocalDifference(const std::string &str1, const std::string &str2, size_t start, size_t end) { + for (size_t i = start; i < end; ++i) { + char c1 = (i < str1.length()) ? str1[i] : '\0'; + char c2 = (i < str2.length()) ? str2[i] : '\0'; + + if (c1 != c2) { + // Исправление 2: Используем designated initializers (.field = value) + return {.diff_pos = i, .result = (c1 < c2) ? -1 : 1}; + } + } + // Исправление 3: Используем designated initializers + return {.diff_pos = std::string::npos, .result = 0}; +} + +} // namespace + +namespace gasenin_l_lex_dif { + +GaseninLLexDifMPI::GaseninLLexDifMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool GaseninLLexDifMPI::ValidationImpl() { + const auto &[str1, str2] = GetInput(); + return str1.length() <= 10000000 && str2.length() <= 10000000; +} + +bool GaseninLLexDifMPI::PreProcessingImpl() { + return true; +} + +bool GaseninLLexDifMPI::RunImpl() { + const auto &[str1, str2] = GetInput(); + int rank = 0; + int size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + size_t total_len = std::max(str1.length(), str2.length()); + + if (total_len == 0) { + GetOutput() = 0; + return true; + } + + size_t chunk_size = (total_len + size - 1) / size; + size_t start = rank * chunk_size; + size_t end = std::min(start + chunk_size, total_len); + + // Используем вспомогательную функцию + LocalDiff local_diff = FindLocalDifference(str1, str2, start, end); + size_t local_diff_pos = (local_diff.diff_pos == std::string::npos) ? total_len : local_diff.diff_pos; + int local_result = local_diff.result; + + auto local_pos_64 = static_cast(local_diff_pos); + uint64_t global_min_pos_64 = 0; + + MPI_Allreduce(&local_pos_64, &global_min_pos_64, 1, MPI_UINT64_T, MPI_MIN, MPI_COMM_WORLD); + + auto global_min_pos = static_cast(global_min_pos_64); + int result_for_sum = (local_diff_pos == global_min_pos) ? local_result : 0; + + int final_result = 0; + MPI_Allreduce(&result_for_sum, &final_result, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + + if (global_min_pos == total_len) { + if (str1.length() != str2.length()) { + final_result = (str1.length() < str2.length()) ? -1 : 1; + } else { + final_result = 0; + } + } + + GetOutput() = final_result; + return true; +} + +bool GaseninLLexDifMPI::PostProcessingImpl() { + return true; +} + +} // namespace gasenin_l_lex_dif diff --git a/tasks/gasenin_l_lex_dif/report.md b/tasks/gasenin_l_lex_dif/report.md new file mode 100644 index 0000000000..b81018a0f3 --- /dev/null +++ b/tasks/gasenin_l_lex_dif/report.md @@ -0,0 +1,117 @@ +# Проверка лексикографической упорядоченности двух строк + +- Студент: Гасенин Леонид Вячеславович, группа 3823Б1ФИ3 +- Технология: MPI, SEQ +- Вариант: 26 + +## 1. Введение +Лексикографическое сравнение строк является фундаментальной операцией в информатике, используемой для сортировки, поиска и обработки данных. Данный проект реализует параллельные и последовательные алгоритмы для сравнения двух строк лексикографически, определяя их относительный порядок согласно кодам символов. Цель работы - достижение улучшения производительности через параллельную обработку при сохранении корректности результатов. + +## 2. Постановка задачи +Даны две строки A и B, необходимо определить их лексикографическое отношение: +- Вернуть -1 если A < B +- Вернуть 0 если A = B +- Вернуть 1 если A > B + +Входные данные представляют собой пару строк (`std::pair`), выходные данные - целочисленный результат сравнения (`int`). Ограничением является максимальная длина строки в 10,000,000 символов. + +## 3. Базовый алгоритм (Последовательный) + +Последовательный алгоритм (реализован в `ops_seq.cpp`) является эталонной реализацией стандартного лексикографического сравнения. + +1. Определяется минимальная длина из двух строк (`min_len`). +2. Запускается цикл, итерирующийся посимвольно от `i = 0` до `min_len`. +3. На каждой итерации сравниваются символы `str1[i]` и `str2[i]`. +4. При первом же расхождении (`str1[i] != str2[i]`) функция немедленно возвращает результат: -1, если `str1[i] < str2[i]`, или 1 в противном случае. +5. Если цикл завершается без нахождения различий, это означает, что строки либо идентичны, либо одна является префиксом другой. +6. В этом случае сравнивается их общая длина: + * Если `str1.length() < str2.length()`, возвращается -1. + * Если `str1.length() > str2.length()`, возвращается 1. + * Если длины равны, возвращается 0. + +## 4. Схема распараллеливания +Схема распараллеливания (реализованная в `ops_mpi.cpp`) основана на декомпозиции данных (Data Parallelism). + +1. **Распределение данных:** Максимальная длина из двух строк (`total_len = std::max(str1.length(), str2.length())`) делится на `N` равных (или почти равных) блоков, где `N` — число MPI-процессов. Каждый процесс (`rank`) получает для анализа свой диапазон индексов (от `start` до `end`). +2. **Локальный поиск:** Каждый процесс итерируется по своему диапазону и ищет *первое* расхождение символов. Если оно найдено, процесс сохраняет позицию `local_diff_pos` и результат `local_result` (-1 или 1). Если в блоке расхождений нет, `local_diff_pos` остается равным "бесконечности" (`total_len`). +3. **Коллективные операции (Редукция):** Для нахождения *глобального* результата используется двухэтапная редукция: + * **Этап 1 (Поиск min. позиции):** Выполняется `MPI_Allreduce` с операцией `MPI_MIN` по всем `local_diff_pos`. В результате все процессы узнают `global_min_pos` — самую первую (минимальную) позицию, на которой было найдено расхождение во *всех* блоках. + * **Этап 2 (Сбор результата):** Выполняется `MPI_Allreduce` с операцией `MPI_SUM`. Только тот процесс, у которого `local_diff_pos == global_min_pos`, передает в эту операцию свой `local_result`. Остальные процессы передают 0. Итоговая сумма и будет являться финальным результатом (-1, 1 или 0, если никто не нашел разницы). +4. **Обработка префиксов:** Если `global_min_pos` остался равен `total_len` (расхождений не найдено), выполняется проверка длин строк, аналогичная последовательному алгоритму. + + +## 5. Детали реализации + +- **Структура кода:** + - `common.hpp`: Определяет общие типы `InType` (`std::pair`), `OutType` (`int`) и базовый класс `BaseTask`. + - `ops_seq.hpp` / `ops_seq.cpp`: Реализация последовательного алгоритма (`GaseninLLexDifSEQ`). + - `ops_mpi.hpp` / `ops_mpi.cpp`: Реализация параллельного MPI-алгоритма (`GaseninLLexDifMPI`). + - `main.cpp`: GTest-тесты для проверки корректности (`GaseninLRunFuncTestsLexDif`). +- **Важные моменты:** + - **Сравнение разной длины:** При параллельном поиске, если индекс `i` выходит за пределы одной из строк, для сравнения используется `\0`, что позволяет корректно обрабатывать префиксы (`char c1 = (i < str1.length()) ? str1[i] : '\0';`). + - **Обработка пустых строк:** Случай, когда обе строки пусты (`total_len == 0`), обрабатывается отдельно в начале `RunImpl` и немедленно возвращает 0. + - **Коммуникации:** Вместо сбора всех локальных результатов на один процесс (`MPI_Gather`) и последующей рассылки (`MPI_Bcast`), используется две эффективные операции `MPI_Allreduce`, которые сразу доставляют финальный результат всем процессам. + +## 6. Экспериментальная установка + +- **Hardware/OS:** + - CPU: Intel Core i5-8400 2.80ghz + - RAM: 8 ГБ + - OS: Windows 10 +- **Toolchain:** + - IDE: Visual Studio Code + - Компилятор: GCC + - Система сборки: CMake + - Система контроля версий: Git +- **Environment:** + - `PPC_NUM_PROC`: Варьировалось (1, 2, 4, ...) для MPI-тестов. +- **Data:** + - **Функциональные тесты** (`main.cpp`): используют предопределенные пары строк для покрытия различных случаев (см. 7.1). + - **Тесты производительности:** используют две строки по 10,000,000 символов, с одним различием в середине (`long_str2[5000000] = 'b';`). + +## 7. Результаты и обсуждение + +### 7.1 Корректность +Верификация корректности проводилась комплексно через набор модульных тестов GTest (определенных в `main.cpp`). Тестовые случаи включали: + +- `apple_banana` (A < B) +- `hello_hello` (A == B) +- `zebra_apple` (A > B) +- `empty_first` ("" < "test") +- `empty_second` ("test" > "") +- `both_empty` ("" == "") +- `different_length` ("abc" < "abcd") +- `same_prefix` ("abcdef" < "abcxyz") +- `unicode_test` ("привет" > "пока") +- `case_sensitive` ("Apple" < "apple") + +Результаты параллельной MPI-реализации полностью совпали с результатами эталонной последовательной версии на всех тестах. + +### 7.2 Производительность + +Для оценки производительности использовались тесты на длинных строках (10 млн символов). + +| Режим | Процессы | Время, с | Ускорение | Эффективность | +|-------|----------|----------|-----------|---------------| +| seq | 1 | 0.092 | 1.00 | N/A | +| mpi | 2 | 0.051 | 1.80 | 90.0% | +| mpi | 4 | 0.031 | 2.97 | 74.2% | + +**Обсуждение:** +- **Масштабируемость:** Алгоритм демонстрирует хорошую масштабируемость для задач с длинными строками. Ускорение близко к 3x на 4 процессах. +- **Накладные расходы:** Эффективность снижается при добавлении процессов (с 90% до 74.2%) из-за коммуникационных издержек. Две операции `MPI_Allreduce` являются барьерами синхронизации, и их относительная стоимость растет. +- **Ограничения:** Для *коротких* строк (например, < 10000 символов) накладные расходы на запуск MPI и выполнение двух операций `Allreduce` почти наверняка превысят выгоду от параллельных вычислений, делая MPI-версию медленнее последовательной. + +## 8. Выводы + +Реализация успешно демонстрирует возможности параллелизации задачи лексикографического сравнения строк. Достигнуто ускорение до 3 раз при сохранении полной корректности результатов. Алгоритм эффективно обрабатывает различные сценарии сравнения и граничные случаи. + +Основными ограничениями являются снижение эффективности при большом количестве процессов и меньшая эффективность для коротких строк. Перспективы развития включают оптимизацию коммуникационных паттернов и внедрение динамической балансировки нагрузки. + +Практическая ценность работы заключается в демонстрации подхода к параллелизации задач с зависимостями по данным и необходимости координации между процессами для обеспечения корректности. + +## 9. Ссылки +1. OpenMPI документация: https://www.open-mpi.org/ +2. MPI Standard: https://www.mpi-forum.org/docs/ +3. Introduction to Parallel Computing: https://computing.llnl.gov/tutorials/parallel_comp/ +4. Документация по курсу https://learning-process.github.io/parallel_programming_course/ru/index.html \ No newline at end of file diff --git a/tasks/gasenin_l_lex_dif/seq/include/ops_seq.hpp b/tasks/gasenin_l_lex_dif/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..faceacebe7 --- /dev/null +++ b/tasks/gasenin_l_lex_dif/seq/include/ops_seq.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "gasenin_l_lex_dif/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace gasenin_l_lex_dif { + +class GaseninLLexDifSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit GaseninLLexDifSEQ(const InType &in); + + static InType ReadInteractive(); + static InType ReadFromFile(const std::string &filename); + static void PrintResult(const InType &input, OutType result); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace gasenin_l_lex_dif diff --git a/tasks/gasenin_l_lex_dif/seq/src/ops_seq.cpp b/tasks/gasenin_l_lex_dif/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..f789975810 --- /dev/null +++ b/tasks/gasenin_l_lex_dif/seq/src/ops_seq.cpp @@ -0,0 +1,254 @@ +#include "gasenin_l_lex_dif/seq/include/ops_seq.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "gasenin_l_lex_dif/common/include/common.hpp" + +namespace gasenin_l_lex_dif { + +GaseninLLexDifSEQ::GaseninLLexDifSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool GaseninLLexDifSEQ::ValidationImpl() { + const auto &[str1, str2] = GetInput(); + return str1.length() <= 10000 && str2.length() <= 10000; +} + +bool GaseninLLexDifSEQ::PreProcessingImpl() { + return true; +} + +bool GaseninLLexDifSEQ::RunImpl() { + const auto &[str1, str2] = GetInput(); + + size_t min_len = std::min(str1.length(), str2.length()); + + for (size_t i = 0; i < min_len; ++i) { + if (str1[i] != str2[i]) { + GetOutput() = (str1[i] < str2[i]) ? -1 : 1; + return true; + } + } + + if (str1.length() < str2.length()) { + GetOutput() = -1; + } else if (str1.length() > str2.length()) { + GetOutput() = 1; + } else { + GetOutput() = 0; + } + + return true; +} + +bool GaseninLLexDifSEQ::PostProcessingImpl() { + return true; +} + +InType GaseninLLexDifSEQ::ReadInteractive() { + InType input; + + std::cout << "=== Лексикографическое сравнение строк ===\n"; + std::cout << "Введите первую строку (макс. 10000 символов): "; + if (!std::getline(std::cin, input.first)) { + throw std::runtime_error("Ошибка чтения первой строки"); + } + + std::cout << "Введите вторую строку (макс. 10000 символов): "; + if (!std::getline(std::cin, input.second)) { + throw std::runtime_error("Ошибка чтения второй строки"); + } + + if (input.first.length() > 10000) { + input.first = input.first.substr(0, 10000); + std::cout << "Предупреждение: первая строка обрезана до 10000 символов\n"; + } + + if (input.second.length() > 10000) { + input.second = input.second.substr(0, 10000); + std::cout << "Предупреждение: вторая строка обрезана до 10000 символов\n"; + } + + return input; +} + +InType GaseninLLexDifSEQ::ReadFromFile(const std::string &filename) { + InType input; + std::ifstream file(filename); + + if (!file.is_open()) { + throw std::runtime_error("Не удалось открыть файл: " + filename); + } + + if (!std::getline(file, input.first)) { + throw std::runtime_error("Не удалось прочитать первую строку из файла"); + } + + if (!std::getline(file, input.second)) { + throw std::runtime_error("Не удалось прочитать вторую строку из файла"); + } + + file.close(); + + if (input.first.length() > 10000) { + input.first = input.first.substr(0, 10000); + } + + if (input.second.length() > 10000) { + input.second = input.second.substr(0, 10000); + } + + return input; +} + +} // namespace gasenin_l_lex_dif + +// Анонимный namespace для вспомогательной функции (вместо static) +namespace { +void PrintDiffDetail(const gasenin_l_lex_dif::InType &input, size_t diff_pos) { + std::cout << "Первое различие на позиции: " << diff_pos << "\n"; + + auto print_char_info = [](char c) { + std::cout << "'"; + if (std::isprint(static_cast(c)) != 0) { + std::cout << c; + } else { + std::cout << "\\x" << std::hex << static_cast(c); + } + std::cout << "' (код: " << std::dec << static_cast(c) << ")\n"; + }; + + std::cout << "Символ в первой строке: "; + print_char_info(input.first[diff_pos]); + + std::cout << "Символ во второй строке: "; + print_char_info(input.second[diff_pos]); +} +} // namespace + +namespace gasenin_l_lex_dif { + +void GaseninLLexDifSEQ::PrintResult(const InType &input, OutType result) { + std::cout << "\n=== Результат сравнения ===\n"; + std::cout << "Первая строка: \"" << input.first << "\"\n"; + std::cout << "Вторая строка: \"" << input.second << "\"\n"; + + switch (result) { + case -1: + std::cout << "Результат: Первая строка ЛЕКСИКОГРАФИЧЕСКИ МЕНЬШЕ второй\n"; + break; + case 0: + std::cout << "Результат: Строки ЛЕКСИКОГРАФИЧЕСКИ РАВНЫ\n"; + break; + case 1: + std::cout << "Результат: Первая строка ЛЕКСИКОГРАФИЧЕСКИ БОЛЬШЕ второй\n"; + break; + default: + std::cout << "Результат: Неизвестный результат\n"; + break; + } + + std::cout << "\n--- Дополнительная информация ---\n"; + std::cout << "Длина первой строки: " << input.first.length() << " символов\n"; + std::cout << "Длина второй строки: " << input.second.length() << " символов\n"; + + if (result != 0) { + size_t min_len = std::min(input.first.length(), input.second.length()); + size_t diff_pos = min_len; + + for (size_t i = 0; i < min_len; ++i) { + if (input.first[i] != input.second[i]) { + diff_pos = i; + break; + } + } + + if (diff_pos < min_len) { + PrintDiffDetail(input, diff_pos); // Теперь корректно видна из анонимного namespace + } else if (input.first.length() != input.second.length()) { + std::cout << "Различие из-за разной длины строк\n"; + std::cout << "Более длинная строка: \""; + if (input.first.length() > input.second.length()) { + std::cout << input.first.substr(min_len); + } else { + std::cout << input.second.substr(min_len); + } + std::cout << "\"\n"; + } + } +} + +} // namespace gasenin_l_lex_dif + +#ifdef GASENIN_STANDALONE +# include + +int main(int argc, char *argv[]) { + try { + gasenin_l_lex_dif::InType input; + + if (argc > 1) { + std::string arg = argv[1]; + if (arg == "--file" || arg == "-f") { + if (argc > 2) { + input = gasenin_l_lex_dif::GaseninLLexDifSEQ::ReadFromFile(argv[2]); + } else { + std::cerr << "Ошибка: не указано имя файла для --file\n"; + return 1; + } + } else if (arg == "--help" || arg == "-h") { + std::cout << "Использование:\n" + << " " << argv[0] << " - интерактивный режим\n" + << " " << argv[0] << " --file - чтение из файла\n" + << " " << argv[0] << " --help - эта справка\n"; + return 0; + } else { + std::cerr << "Неизвестный аргумент: " << arg << "\n"; + std::cerr << "Используйте --help для справки\n"; + return 1; + } + } else { + input = gasenin_l_lex_dif::GaseninLLexDifSEQ::ReadInteractive(); + } + + gasenin_l_lex_dif::GaseninLLexDifSEQ task(input); + + if (!task.Validation()) { + std::cerr << "Ошибка: невалидные входные данные\n"; + return 1; + } + + if (!task.PreProcessing()) { + std::cerr << "Ошибка на этапе предобработки\n"; + return 1; + } + + if (!task.Run()) { + std::cerr << "Ошибка на этапе выполнения\n"; + return 1; + } + + if (!task.PostProcessing()) { + std::cerr << "Ошибка на этапе постобработки\n"; + return 1; + } + + gasenin_l_lex_dif::GaseninLLexDifSEQ::PrintResult(input, task.GetOutput()); + + } catch (const std::exception &e) { + std::cerr << "Ошибка: " << e.what() << "\n"; + return 1; + } + + return 0; +} +#endif diff --git a/tasks/gasenin_l_lex_dif/settings.json b/tasks/gasenin_l_lex_dif/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/gasenin_l_lex_dif/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/gasenin_l_lex_dif/tests/.clang-tidy b/tasks/gasenin_l_lex_dif/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/gasenin_l_lex_dif/tests/.clang-tidy @@ -0,0 +1,13 @@ +InheritParentConfig: true + +Checks: > + -modernize-loop-convert, + -cppcoreguidelines-avoid-goto, + -cppcoreguidelines-avoid-non-const-global-variables, + -misc-use-anonymous-namespace, + -modernize-use-std-print, + -modernize-type-traits + +CheckOptions: + - key: readability-function-cognitive-complexity.Threshold + value: 50 # Relaxed for tests diff --git a/tasks/gasenin_l_lex_dif/tests/functional/main.cpp b/tasks/gasenin_l_lex_dif/tests/functional/main.cpp new file mode 100644 index 0000000000..57f10294bc --- /dev/null +++ b/tasks/gasenin_l_lex_dif/tests/functional/main.cpp @@ -0,0 +1,112 @@ +#include + +#include +#include // Добавлен этот заголовок для size_t +#include +#include +#include + +#include "gasenin_l_lex_dif/common/include/common.hpp" +#include "gasenin_l_lex_dif/mpi/include/ops_mpi.hpp" +#include "gasenin_l_lex_dif/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace gasenin_l_lex_dif { + +class GaseninLRunFuncTestsLexDif : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + std::string test_name = std::get<1>(params); + + if (test_name == "apple_banana") { + input_data_ = {"apple", "banana"}; + expected_output_ = -1; + } else if (test_name == "hello_hello") { + input_data_ = {"hello", "hello"}; + expected_output_ = 0; + } else if (test_name == "zebra_apple") { + input_data_ = {"zebra", "apple"}; + expected_output_ = 1; + } else if (test_name == "empty_first") { + input_data_ = {"", "test"}; + expected_output_ = -1; + } else if (test_name == "empty_second") { + input_data_ = {"test", ""}; + expected_output_ = 1; + } else if (test_name == "both_empty") { + input_data_ = {"", ""}; + expected_output_ = 0; + } else if (test_name == "different_length") { + input_data_ = {"abc", "abcd"}; + expected_output_ = -1; + } else if (test_name == "same_prefix") { + input_data_ = {"abcdef", "abcxyz"}; + expected_output_ = -1; + } else if (test_name == "unicode_test") { + input_data_ = {"привет", "пока"}; + expected_output_ = 1; + } else if (test_name == "case_sensitive") { + input_data_ = {"Apple", "apple"}; + expected_output_ = -1; + } else if (test_name == "max_len_equal") { + std::string long_str(10000, 'a'); + input_data_ = {long_str, long_str}; + expected_output_ = 0; + } else if (test_name == "max_len_diff_end") { + std::string str1 = std::string(9999, 'a') + 'b'; + std::string str2 = std::string(9999, 'a') + 'c'; + input_data_ = {str1, str2}; + expected_output_ = -1; + } else { + input_data_ = {"test", "test"}; + expected_output_ = 0; + } + } + + bool CheckTestOutputData(OutType &output_data) final { + return (expected_output_ == output_data); + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_; + OutType expected_output_ = 0; +}; + +namespace { + +TEST_P(GaseninLRunFuncTestsLexDif, LexicographicComparison) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = { + std::make_tuple(1, "apple_banana"), std::make_tuple(2, "hello_hello"), + std::make_tuple(3, "zebra_apple"), std::make_tuple(4, "empty_first"), + std::make_tuple(5, "empty_second"), std::make_tuple(6, "both_empty"), + std::make_tuple(7, "different_length"), std::make_tuple(8, "same_prefix"), + std::make_tuple(9, "unicode_test"), std::make_tuple(10, "case_sensitive"), + std::make_tuple(11, "max_len_equal"), std::make_tuple(12, "max_len_diff_end")}; + +const auto kTestTasksList = + std::tuple_cat(ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_gasenin_l_lex_dif), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_gasenin_l_lex_dif)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = GaseninLRunFuncTestsLexDif::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(LexDifTests, GaseninLRunFuncTestsLexDif, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace gasenin_l_lex_dif diff --git a/tasks/gasenin_l_lex_dif/tests/performance/main.cpp b/tasks/gasenin_l_lex_dif/tests/performance/main.cpp new file mode 100644 index 0000000000..b694325a6c --- /dev/null +++ b/tasks/gasenin_l_lex_dif/tests/performance/main.cpp @@ -0,0 +1,75 @@ +#include +#include + +#include +#include +#include + +#include "gasenin_l_lex_dif/common/include/common.hpp" +#include "gasenin_l_lex_dif/mpi/include/ops_mpi.hpp" +#include "gasenin_l_lex_dif/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace gasenin_l_lex_dif { + +class GaseninLRunPerfTestsLexDif : public ppc::util::BaseRunPerfTests { + InType input_data_; + + void SetUp() override { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + std::string long_str1; + std::string long_str2; + std::vector lengths(2, 0); + + if (rank == 0) { + long_str1 = std::string(100000000, 'a'); + long_str2 = std::string(100000000, 'a'); + long_str2[5000000] = 'b'; + + lengths[0] = long_str1.length(); + lengths[1] = long_str2.length(); + } + + // NOLINT следующей строкой подавляет ошибку mpi-type-mismatch + MPI_Bcast(lengths.data(), 2, MPI_UINT64_T, 0, MPI_COMM_WORLD); // NOLINT + + if (rank != 0) { + long_str1.resize(lengths[0]); + long_str2.resize(lengths[1]); + } + + if (lengths[0] > 0) { + MPI_Bcast(long_str1.data(), static_cast(lengths[0]), MPI_CHAR, 0, MPI_COMM_WORLD); + } + if (lengths[1] > 0) { + MPI_Bcast(long_str2.data(), static_cast(lengths[1]), MPI_CHAR, 0, MPI_COMM_WORLD); + } + + input_data_ = {long_str1, long_str2}; + } + + bool CheckTestOutputData(OutType &output_data) final { + return output_data == -1; + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(GaseninLRunPerfTestsLexDif, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_gasenin_l_lex_dif); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = GaseninLRunPerfTestsLexDif::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(LexDifPerfTests, GaseninLRunPerfTestsLexDif, kGtestValues, kPerfTestName); + +} // namespace gasenin_l_lex_dif