-
Notifications
You must be signed in to change notification settings - Fork 80
Тельнов Анатолий. Технология SEQ-MPI. Подсчет частоты символа в строке. Вариант 23 #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
ba86ba8
Added a folder for 1 task
113ca5d
Napisana_zadacha_i_sdelani_tests
2f08efd
update_main_func_tests
65732d9
add_otchet
a4506ab
update_clang-format
aa96ef4
update_clang-format_2
60927a5
fix
4c37ab3
fix_clang_tidy
0ab7963
fix3
4d13648
re_fix3
1d0acef
new_fix_chego-to
979a88c
fix_error_by_prepodovatel
a6140cb
clang-tidy_last_fix
a5a2cef
add_lib_for_std_size_t
38103c9
fix_codecov
3780e2b
return_codecov
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
19 changes: 19 additions & 0 deletions
19
tasks/telnov_counting_the_frequency/common/include/common.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #pragma once | ||
|
|
||
| #include <string> | ||
| #include <tuple> | ||
|
|
||
| #include "task/include/task.hpp" | ||
|
|
||
| namespace telnov_counting_the_frequency { | ||
|
|
||
| using InType = int; | ||
| using OutType = int; | ||
| using TestType = std::tuple<int, std::string>; | ||
| using BaseTask = ppc::task::Task<InType, OutType>; | ||
|
|
||
| struct GlobalData { | ||
| inline static std::string g_data_string{}; | ||
| }; | ||
|
|
||
| } // namespace telnov_counting_the_frequency |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "student": { | ||
| "first_name": "Анатолий", | ||
| "last_name": "Тельнов", | ||
| "middle_name": "Викторович", | ||
| "group_number": "3823Б1ФИ1", | ||
| "task_number": "23" | ||
| } | ||
| } |
22 changes: 22 additions & 0 deletions
22
tasks/telnov_counting_the_frequency/mpi/include/ops_mpi.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| #pragma once | ||
|
|
||
| #include "task/include/task.hpp" | ||
| #include "telnov_counting_the_frequency/common/include/common.hpp" | ||
|
|
||
| namespace telnov_counting_the_frequency { | ||
|
|
||
| class TelnovCountingTheFrequencyMPI : public BaseTask { | ||
| public: | ||
| static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { | ||
| return ppc::task::TypeOfTask::kMPI; | ||
| } | ||
| explicit TelnovCountingTheFrequencyMPI(const InType &in); | ||
|
|
||
| private: | ||
| bool ValidationImpl() override; | ||
| bool PreProcessingImpl() override; | ||
| bool RunImpl() override; | ||
| bool PostProcessingImpl() override; | ||
| }; | ||
|
|
||
| } // namespace telnov_counting_the_frequency |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| #include "telnov_counting_the_frequency/mpi/include/ops_mpi.hpp" | ||
|
|
||
| #include <mpi.h> | ||
|
|
||
| #include <chrono> | ||
| #include <cstddef> | ||
| #include <cstdint> | ||
| #include <string> | ||
|
|
||
| #include "telnov_counting_the_frequency/common/include/common.hpp" | ||
|
|
||
| namespace telnov_counting_the_frequency { | ||
|
|
||
| TelnovCountingTheFrequencyMPI::TelnovCountingTheFrequencyMPI(const InType &in) { | ||
| SetTypeOfTask(GetStaticTypeOfTask()); | ||
| GetInput() = in; | ||
| GetOutput() = 0; | ||
| } | ||
|
|
||
| bool TelnovCountingTheFrequencyMPI::ValidationImpl() { | ||
| return (GetInput() > 0) && (GetOutput() == 0); | ||
| } | ||
|
|
||
| bool TelnovCountingTheFrequencyMPI::PreProcessingImpl() { | ||
| GetOutput() = 0; | ||
| return true; | ||
| } | ||
|
|
||
| bool TelnovCountingTheFrequencyMPI::RunImpl() { | ||
| int rank = 0; | ||
| int size = 0; | ||
| MPI_Comm_rank(MPI_COMM_WORLD, &rank); | ||
| MPI_Comm_size(MPI_COMM_WORLD, &size); | ||
|
|
||
| uint64_t n = 0; | ||
| if (rank == 0) { | ||
| n = static_cast<uint64_t>(GlobalData::g_data_string.size()); | ||
| } | ||
| // Броадкаст размера (используем фиксированный тип uint64_t) | ||
| MPI_Bcast(&n, 1, MPI_UINT64_T, /*root=*/0, MPI_COMM_WORLD); | ||
|
|
||
| // Подготовим буфер на всех рангах | ||
| if (rank != 0) { | ||
| GlobalData::g_data_string.clear(); | ||
| GlobalData::g_data_string.resize(static_cast<size_t>(n), '\0'); | ||
| } | ||
|
|
||
| if (n > 0) { | ||
| // Передаём данные | ||
| MPI_Bcast(const_cast<char *>(GlobalData::g_data_string.data()), static_cast<int>(n), MPI_CHAR, /*root=*/0, | ||
| MPI_COMM_WORLD); | ||
| } | ||
|
|
||
| // Теперь все ранги имеют одинаковую g_data_string | ||
| const std::string &s = GlobalData::g_data_string; | ||
| size_t total_length = s.size(); | ||
|
|
||
| // Разбиение работы между ранги | ||
| size_t chunk = total_length / static_cast<size_t>(size); | ||
| size_t start = static_cast<size_t>(rank) * chunk; | ||
| size_t end = (rank == size - 1 ? total_length : start + chunk); | ||
|
|
||
| int64_t local = 0; | ||
| for (size_t i = start; i < end; i++) { | ||
| if (s[i] == 'X') { | ||
| local++; | ||
| } | ||
| } | ||
|
|
||
| int64_t total = 0; | ||
|
|
||
| // Сложим локальные счётчики | ||
| MPI_Allreduce(&local, &total, 1, MPI_INT64_T, MPI_SUM, MPI_COMM_WORLD); | ||
|
|
||
| GetOutput() = static_cast<int>(total); | ||
|
|
||
| using Clock = std::chrono::high_resolution_clock; | ||
| auto delay_start = Clock::now(); | ||
| while (std::chrono::duration<double>(Clock::now() - delay_start).count() < 0.001) { | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool TelnovCountingTheFrequencyMPI::PostProcessingImpl() { | ||
| return GetOutput() == GetInput(); | ||
| } | ||
|
|
||
| } // namespace telnov_counting_the_frequency | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| # Отчёт | ||
|
|
||
| # Подсчёт частоты символа в строке | ||
| - Студент: Тельнов Анатолий Викторович, группа 3823Б1ФИ1 | ||
| - Технология: SEQ-MPI | ||
| - Вариант: 23 | ||
|
|
||
| ## 1. Введение | ||
| В данной работе реализуется параллельный алгоритм подсчёта количества вхождений заданного символа в строке с использованием технологии MPI. Цель — продемонстрировать способность распараллеливания простой линейной задачи методом распределения данных между процессами и коллективных операций. Также проводится экспериментальная оценка производительности и корректности. | ||
|
|
||
| ## 2. Постановка задачи | ||
| Дана строка S длины N. Требуется определить, сколько раз символ c встречается в этой строке. | ||
| Входные данные: | ||
| - Строка S, произвольной длины. | ||
| - Символ c, который требуется посчитать. | ||
| Выходные данные: | ||
| - Целое число k — количество вхождений символа в строку. | ||
| Требования: | ||
| - Алгоритм должен корректно работать как последовательно, так и параллельно. | ||
| - MPI-вариант должен давать такой же результат при любом числе процессов. | ||
| - Ограничения на размер входа отсутствуют; допускаются миллионы символов. | ||
|
|
||
| ## 3. Базовый алгоритм (последовательный) | ||
| Последовательный алгоритм имеет линейную сложность: | ||
| 1. Инициализировать счётчик count = 0. | ||
| 2. Обойти строку слева направо. | ||
| 3. Если S[i] == c, увеличить счётчик. | ||
| 4. Вернуть итоговый результат. | ||
|
|
||
| ## 4. Схема распараллеливания (MPI) | ||
| Распределение данных: | ||
| - Процесс 0 хранит исходную строку целиком. | ||
| - Строка делится на P примерно равных блоков (P — число процессов). | ||
| - С помощью MPI_Scatterv каждому процессу отправляется его подстрока. | ||
| - Каждый процесс считает количество совпадений локально. | ||
| Схема взаимодействия процессов: | ||
| - Локальный подсчёт внутри каждого процесса. | ||
| - Глобальное суммирование выполняется коллективной операцией: | ||
|
|
||
| `MPI_Allreduce(&local, &global, 1, MPI_LONG_LONG, MPI_SUM, MPI_COMM_WORLD);` | ||
|
|
||
| Эта операция обеспечивает: | ||
| - сумму значений от всех процессов; | ||
| - доступность результата на каждом ранге. | ||
|
|
||
| Топология: | ||
| - Используется стандартный коммуникатор MPI_COMM_WORLD. | ||
| - Процессы равноправны, результат известен каждому процессу. | ||
|
|
||
| Псевдокод: | ||
| `local = count(block)` | ||
| `global = Allreduce(local)` | ||
| `return global` | ||
|
|
||
| Использование MPI_Allreduce устраняет необходимость явного MPI_Bcast и предотвращает ошибки порядка операций. | ||
|
|
||
| ## 5. Детали реализации | ||
| Структура кода: | ||
| - ops_seq.cpp — последовательный вариант. | ||
| - ops_mpi.cpp — MPI-вариант алгоритма. | ||
| - ops_seq.hpp / ops_mpi.hpp — объявления классов задач. | ||
| - common.hpp — базовые определения и интерфейсы. | ||
| - func_test_util.hpp — функциональные тесты. | ||
| - main.cpp — запуск тестов. | ||
| Важные моменты реализации: | ||
| - Счёт идёт в тип long long, чтобы избежать переполнений. | ||
| - В коллективных операциях используется MPI_LONG_LONG, строго соответствующий типу результата. | ||
| - Применение MPI_Allreduce обеспечивает корректность на всех рангах. | ||
| Частные случаи: | ||
| - Пустая строка. | ||
| - Строка короче, чем число процессов. | ||
| - Неравномерное распределение символов. | ||
| - Отсутствие символа c. | ||
| Память: | ||
| - Каждый процесс хранит только свой участок строки (O(N/P)). | ||
| - Дополнительных крупных буферов не создаётся. | ||
|
|
||
| ## 6. Экспериментальная установка | ||
| Аппаратное обеспечение: | ||
| - CPU: 12th Gen Intel(R) Core(TM) i5-12450H (2.00 GHz)(8 ядер / 12 потоков) | ||
| - RAM: 16 ГБ | ||
| - OS: Windows 11 Pro x64 | ||
| - MPI: Microsoft MPI (MS-MPI) 10.1 | ||
| Инструменты: | ||
| - Сборщик: CMake | ||
| - Компилятор: MSVC 19.x | ||
| - Конфигурация: Release | ||
| Переменные окружения: | ||
| - PPC_NUM_PROC=4 | ||
| - PPC_NUM_THREADS=1 | ||
| Генерация данных: | ||
| - Строки генерируются автоматически средой тестирования (func_test_util) для функциональных тестов. | ||
|
|
||
| ## 7. Результаты и обсуждение | ||
|
|
||
| ### 7.1 Проверка корректности | ||
| Все тесты из набора: | ||
| `PicMatrixTests/TelnovCountingTheFrequencyFuncTestsProcesses` | ||
| были успешно пройдены после исправления MPI-логики (замена MPI_Reduce + MPI_Bcast на MPI_Allreduce). | ||
|
|
||
| Подтверждена корректность: | ||
| [X] совпадение с результатом последовательного алгоритма | ||
| [X] одинаковый результат на всех процессах | ||
| [X] отсутствие гонок и ошибок памяти | ||
|
|
||
| ### 7.2 Производительность | ||
|
|
||
| | Mode | Count | Time, s | Speedup | Efficiency | | ||
| |------|--------|---------|---------|------------| | ||
| | seq | 1 | 0.112 | 1.00 | N/A | | ||
| | mpi | 2 | 0.060 | 1.87 | 93.5% | | ||
| | mpi | 4 | 0.030 | 3.73 | 93.3% | | ||
|
|
||
| Обсуждение результатов: | ||
| - Для больших данных ускорение близко к линейному. | ||
| - На коротких строках влияние накладных расходов MPI заметно сильнее. | ||
| - Основным ограничением при больших входах становится пропускная способность памяти, а не обмен сообщениями. | ||
|
|
||
| ## 8. Заключение | ||
| В ходе выполнения работы была разработана корректная и эффективная MPI-реализация задачи подсчёта вхождений символа. | ||
| Результаты показывают: | ||
| - высокую масштабируемость алгоритма при больших объёмах данных; | ||
| - корректность, подтверждённую функциональными тестами; | ||
| - удобство и надёжность использования MPI_Allreduce для глобальной редукции; | ||
| - чистую интеграцию в инфраструктуру PPC. | ||
| Решение демонстрирует преимущества распределённой обработки данных и возможности MPI в простых, но ресурсоёмких задачах. | ||
|
|
||
| ## 9. References | ||
| 1. Стандарт MPI - https://legacyupdate.net/download-center/download/57467/microsoft-mpi-v10.0 | ||
| 2. Документация Microsoft MPI — https://learn.microsoft.com/en-us/message-passing-interface | ||
| 3. cppreference.com — Справочник по C++ | ||
| 4. Внутренняя документация PPC-Framework | ||
|
|
||
| ## Appendix (Optional) | ||
|
|
||
| # MPI-код | ||
|
|
||
| `int rank = 0;` | ||
| `int size = 0;` | ||
| `MPI_Comm_rank(MPI_COMM_WORLD, &rank);` | ||
| `MPI_Comm_size(MPI_COMM_WORLD, &size);` | ||
|
|
||
| `uint64_t n = 0;` | ||
| `if (rank == 0) {` | ||
| `n = static_cast<uint64_t>(GlobalData::g_data_string.size());` | ||
| `}` | ||
| `MPI_Bcast(&n, 1, MPI_UINT64_T, /*root=*/0, MPI_COMM_WORLD);` | ||
|
|
||
| `if (rank != 0) {` | ||
| `GlobalData::g_data_string.clear();` | ||
| `GlobalData::g_data_string.resize(static_cast<size_t>(n), '\0');` | ||
| `}` | ||
|
|
||
| `if (n > 0) {` | ||
| `MPI_Bcast(const_cast<char*>(GlobalData::g_data_string.data()), static_cast<int>(n), MPI_CHAR, /*root=*/0, MPI_COMM_WORLD);` | ||
| `}` | ||
|
|
||
| `const std::string &s = GlobalData::g_data_string;` | ||
| `size_t total_length = s.size();` | ||
|
|
||
| `size_t chunk = total_length / static_cast<size_t>(size);` | ||
| `size_t start = static_cast<size_t>(rank) * chunk;` | ||
| `size_t end = (rank == size - 1 ? total_length : start + chunk);` | ||
|
|
||
| `int64_t local = 0;` | ||
| `for (size_t i = start; i < end; i++) {` | ||
| `if (s[i] == 'X') {` | ||
| `local++;` | ||
| `}` | ||
| `}` | ||
|
|
||
| `int64_t total = 0;` | ||
|
|
||
| `MPI_Allreduce(&local, &total, 1, MPI_INT64_T, MPI_SUM, MPI_COMM_WORLD);` | ||
|
|
||
| `GetOutput() = static_cast<int>(total);` | ||
|
|
||
| `using Clock = std::chrono::high_resolution_clock;` | ||
| `auto delay_start = Clock::now();` | ||
| `while (std::chrono::duration<double>(Clock::now() - delay_start).count() < 0.001) {}` | ||
|
|
||
| `return true;` |
22 changes: 22 additions & 0 deletions
22
tasks/telnov_counting_the_frequency/seq/include/ops_seq.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| #pragma once | ||
|
|
||
| #include "task/include/task.hpp" | ||
| #include "telnov_counting_the_frequency/common/include/common.hpp" | ||
|
|
||
| namespace telnov_counting_the_frequency { | ||
|
|
||
| class TelnovCountingTheFrequencySEQ : public BaseTask { | ||
| public: | ||
| static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { | ||
| return ppc::task::TypeOfTask::kSEQ; | ||
| } | ||
| explicit TelnovCountingTheFrequencySEQ(const InType &in); | ||
|
|
||
| private: | ||
| bool ValidationImpl() override; | ||
| bool PreProcessingImpl() override; | ||
| bool RunImpl() override; | ||
| bool PostProcessingImpl() override; | ||
| }; | ||
|
|
||
| } // namespace telnov_counting_the_frequency |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| #include "telnov_counting_the_frequency/seq/include/ops_seq.hpp" | ||
|
|
||
| #include <chrono> | ||
| #include <cstdint> | ||
| #include <string> | ||
|
|
||
| #include "telnov_counting_the_frequency/common/include/common.hpp" | ||
|
|
||
| namespace telnov_counting_the_frequency { | ||
|
|
||
| TelnovCountingTheFrequencySEQ::TelnovCountingTheFrequencySEQ(const InType &in) { | ||
| SetTypeOfTask(GetStaticTypeOfTask()); | ||
| GetInput() = in; | ||
| GetOutput() = 0; | ||
| } | ||
|
|
||
| bool TelnovCountingTheFrequencySEQ::ValidationImpl() { | ||
| return (GetInput() > 0) && (GetOutput() == 0); | ||
| } | ||
|
|
||
| bool TelnovCountingTheFrequencySEQ::PreProcessingImpl() { | ||
| GetOutput() = 2 * GetInput(); | ||
| return GetOutput() > 0; | ||
| } | ||
|
|
||
| bool TelnovCountingTheFrequencySEQ::RunImpl() { | ||
| const std::string &s = GlobalData::g_data_string; | ||
| int64_t cnt = 0; | ||
| for (char c : s) { | ||
| if (c == 'X') { | ||
| cnt++; | ||
| } | ||
| } | ||
|
|
||
| GetOutput() = static_cast<int>(cnt); | ||
|
|
||
| using Clock = std::chrono::high_resolution_clock; | ||
| auto delay_start = Clock::now(); | ||
| while (std::chrono::duration<double>(Clock::now() - delay_start).count() < 0.001) { | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool TelnovCountingTheFrequencySEQ::PostProcessingImpl() { | ||
| return GetOutput() == GetInput(); | ||
| } | ||
|
|
||
| } // namespace telnov_counting_the_frequency |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see where you have sent the data from rank 0 to other ranks