-
Notifications
You must be signed in to change notification settings - Fork 80
Хруев Антон. Технология SEQ-MPI. Минимальное значение элементов вектора. Вариант 4 #31
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
Changes from all commits
7f09273
563b5d9
7d66e4d
a617d53
43650df
5754666
fbfc296
67e23de
2ca4673
47a7eab
814d521
a045d41
04ebc7b
7003bd2
ef5edcb
11be03a
cc48ddf
bb59937
ef6a318
0860dfd
456bb3f
d711bb2
45baa39
6d943e3
53c975c
49dfa8e
817d36e
245428e
56f8407
b4701eb
927f4bc
8cf09b2
f287430
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| #pragma once | ||
|
|
||
| #include <string> | ||
| #include <tuple> | ||
| #include <vector> | ||
|
|
||
| #include "task/include/task.hpp" | ||
|
|
||
| namespace khruev_a_min_elem_vec { | ||
|
|
||
| using InType = std::vector<int>; | ||
| using OutType = int; | ||
| using TestType = std::tuple<std::tuple<std::vector<int>, int>, std::string>; // meta information | ||
| using BaseTask = ppc::task::Task<InType, OutType>; | ||
|
|
||
| } // namespace khruev_a_min_elem_vec | ||
| 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": "1" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| #pragma once | ||
|
|
||
| #include "khruev_a_min_elem_vec/common/include/common.hpp" | ||
| #include "task/include/task.hpp" | ||
|
|
||
| namespace khruev_a_min_elem_vec { | ||
|
|
||
| class KhruevAMinElemVecMPI : public BaseTask { | ||
| public: | ||
| static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { | ||
| return ppc::task::TypeOfTask::kMPI; | ||
| } | ||
| explicit KhruevAMinElemVecMPI(const InType &in); | ||
|
|
||
| private: | ||
| bool ValidationImpl() override; | ||
| bool PreProcessingImpl() override; | ||
| bool RunImpl() override; | ||
| bool PostProcessingImpl() override; | ||
| }; | ||
|
|
||
| } // namespace khruev_a_min_elem_vec |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| #include "khruev_a_min_elem_vec/mpi/include/ops_mpi.hpp" | ||
|
|
||
| #include <mpi.h> | ||
|
|
||
| #include <algorithm> | ||
| #include <climits> | ||
| #include <vector> | ||
|
|
||
| #include "khruev_a_min_elem_vec/common/include/common.hpp" | ||
|
|
||
| namespace khruev_a_min_elem_vec { | ||
|
|
||
| KhruevAMinElemVecMPI::KhruevAMinElemVecMPI(const InType &in) { | ||
|
allnes marked this conversation as resolved.
|
||
| SetTypeOfTask(GetStaticTypeOfTask()); // mpi scoreboard | ||
| GetInput() = in; // dannie doljna bit vidna vsem func rodytelya and stabilizaciya | ||
| GetOutput() = 0; | ||
| } | ||
|
|
||
| bool KhruevAMinElemVecMPI::ValidationImpl() { // input check | ||
| return GetOutput() == 0; | ||
| } | ||
|
|
||
| bool KhruevAMinElemVecMPI::PreProcessingImpl() { | ||
| return true; | ||
| } | ||
|
|
||
| bool KhruevAMinElemVecMPI::RunImpl() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Data is not being scattered from rank 0 to other ranks
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Не уверен, что правильно понял вас. Вы имеете в виду, что программа в принципе должна быть так написана или про мою прошлую реализацию? Если второе, то я ведь уже поправил report.md и там актуальное описание. |
||
| const auto &input = GetInput(); | ||
| if (input.empty()) { | ||
| GetOutput() = INT_MAX; | ||
| return true; | ||
| } | ||
|
|
||
| int rank = 0; | ||
| int size = 0; | ||
| MPI_Comm_rank(MPI_COMM_WORLD, &rank); | ||
| MPI_Comm_size(MPI_COMM_WORLD, &size); | ||
|
|
||
| int n = static_cast<int>(input.size()); | ||
| int int_part = n / size; | ||
| int remainder = n % size; | ||
|
|
||
| std::vector<int> sendcounts(size); | ||
| std::vector<int> displs(size); | ||
|
|
||
| for (int i = 0; i < size; ++i) { | ||
| sendcounts[i] = int_part + (i < remainder ? 1 : 0); | ||
| displs[i] = (i == 0 ? 0 : displs[i - 1] + sendcounts[i - 1]); | ||
| } | ||
|
|
||
| std::vector<int> local_chunk(sendcounts[rank] > 0 ? sendcounts[rank] : 1); | ||
| MPI_Scatterv(input.data(), sendcounts.data(), displs.data(), MPI_INT, | ||
| sendcounts[rank] > 0 ? local_chunk.data() : nullptr, sendcounts[rank], MPI_INT, 0, MPI_COMM_WORLD); | ||
|
|
||
| int local_min = INT_MAX; | ||
| if (sendcounts[rank] > 0) { | ||
| local_min = *std::min_element(local_chunk.begin(), local_chunk.begin() + sendcounts[rank]); | ||
| } | ||
|
|
||
| int global_min = 0; | ||
|
|
||
| MPI_Reduce(&local_min, &global_min, 1, MPI_INT, MPI_MIN, 0, MPI_COMM_WORLD); | ||
| MPI_Bcast(&global_min, 1, MPI_INT, 0, MPI_COMM_WORLD); | ||
|
|
||
| GetOutput() = global_min; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool KhruevAMinElemVecMPI::PostProcessingImpl() { | ||
| return true; | ||
| } | ||
|
|
||
| } // namespace khruev_a_min_elem_vec | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # Минимальное значение элементов вектора | ||
|
|
||
| - Student: Хруев Антон Олегович, group 3823Б1ФИ1 | ||
| - Technology: SEQ | MPI | ||
| - Variant: 4 | ||
|
|
||
| ## 1. Введение | ||
| В этом задании нужно было реализовать последовательный и параллельный алгоритмы поиска минимального элемента списка. | ||
|
|
||
| ## 2. Постановка задачи | ||
| Есть вектор $V$ состоящий из $n$ элементов, необходимо найти наименьший его элемент $a$. | ||
|
|
||
|
|
||
| ## 3. Baseline Algorithm (Sequential) | ||
| Последовательно в цикле перебираем элементы вектора $V$, сравнивая минимум с текущим элементом и если элемент меньше, то обновляем минимум. После $n$ итераций гарантировано находим $a$. | ||
|
|
||
| ## 4. Parallelization Scheme | ||
| - Разделим вектор $V$ на $k$ частей, где $k$ - количество процессов. Каждому процессу отправим данные из 0-го с помощью функции ScatterV. Предварительно определяется какую часть вектора возьмет каждый из процессов. Для этого инициализируется 2 вектора: **sendcounts**, который определяет сколько элементов получит процесс с индексом $i$ (это количество определяется с учетом остатка $n$ % $k$ = $r$, который распределяется равномерно между процессами с рангом меньшим $r$); **displs**, определяющий смещение. После нахождения локальных минимумов, вызовем функцию MPI_Reduce, которая найдет минимум среди локальных и отправит результат 0-му процессу. | ||
|
|
||
| ## 5. Experimental Setup | ||
| - Hardware/OS: CPU model - AMD Ryzen 3 7320U, cores/threads - 4/2, RAM=8GB, OS version - Ubuntu 24.04.2 LTS | ||
| - Toolchain: compiler - g++, version - g++ 13.3.0, build type - Release | ||
|
|
||
|
|
||
|
|
||
| ## 6. Результаты | ||
|
|
||
| ### 6.1 Корректность | ||
| **Функциональные тесты.** $V$ может состоять из: положительных, отрицательных, одинаковых чисел. | ||
| Так же может содержать один или несколько отрицательных чисел среди положительных или не содержать чисел вовсе. | ||
| Параллельная реализация алгоритма запускалась на 4 ядрах, поэтому эталонное усокрение - 4 раза. | ||
| **Тест на производительность.** Генерация n чисел в порядке возрастания. | ||
|
|
||
| ### 6.2 Производительность | ||
|
|
||
| | Размер данных (n) | SEQ версия (с) | MPI версия (с) | Эффективность | | ||
| |-------------|-------|---------|---------| | ||
| | 100 | 0.000005 | 0.000225 | 0.02× | | ||
| | 10000 | 0.000115 | 0.0003031 | 0.37× | | ||
| | 1000000 | 0.011989 | 0.0142523 | 0.83× | | ||
| | 100000000| 1.08006 | 1.30234 | 0.83× | | ||
|
|
||
| ## 7. Заключение | ||
| Результатом работы стали реализации последовательных и параллельных алгоритмов нахождения минимального элемента $a$ вектора $V$. Можно заметить, что SEQ версия работает быстрее MPI. Это происходит из-за большого количества накладных расходов при вызовах MPI_Scatterv, MPI_Reduce, MPI_Bcast, и малого количества ядер процессора. | ||
|
|
||
| ## 8. Ссылки | ||
| 1. Лекции по параллельному программированию | ||
| 2. Практические занятия по параллельному программированию | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| #pragma once | ||
|
|
||
| #include <vector> | ||
|
|
||
| #include "khruev_a_min_elem_vec/common/include/common.hpp" | ||
| #include "task/include/task.hpp" | ||
|
|
||
| namespace khruev_a_min_elem_vec { | ||
|
|
||
| class KhruevAMinElemVecSEQ : public BaseTask { | ||
| public: | ||
| static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { | ||
| return ppc::task::TypeOfTask::kSEQ; | ||
| } | ||
| explicit KhruevAMinElemVecSEQ(const InType &in); | ||
|
|
||
| private: | ||
| std::vector<int> data_; | ||
| bool ValidationImpl() override; | ||
| bool PreProcessingImpl() override; | ||
| bool RunImpl() override; | ||
| bool PostProcessingImpl() override; | ||
| }; | ||
|
|
||
| } // namespace khruev_a_min_elem_vec |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| #include "khruev_a_min_elem_vec/seq/include/ops_seq.hpp" | ||
|
|
||
| #include <algorithm> | ||
| #include <climits> | ||
| #include <cstddef> | ||
| #include <vector> | ||
|
|
||
| #include "khruev_a_min_elem_vec/common/include/common.hpp" | ||
|
|
||
| namespace khruev_a_min_elem_vec { | ||
|
|
||
| KhruevAMinElemVecSEQ::KhruevAMinElemVecSEQ(const InType &in) { | ||
| SetTypeOfTask(GetStaticTypeOfTask()); | ||
| GetInput() = in; | ||
| GetOutput() = 0; | ||
| } | ||
|
|
||
| bool KhruevAMinElemVecSEQ::ValidationImpl() { | ||
| return (GetOutput() == 0); | ||
| } | ||
|
|
||
| bool KhruevAMinElemVecSEQ::PreProcessingImpl() { | ||
| return true; | ||
| } | ||
|
allnes marked this conversation as resolved.
|
||
|
|
||
| bool KhruevAMinElemVecSEQ::RunImpl() { | ||
| if (GetInput().empty()) { | ||
| GetOutput() = INT_MAX; | ||
| return true; | ||
| } | ||
| int mininmum = GetInput()[0]; | ||
| size_t vec_size = GetInput().size(); | ||
| for (size_t i = 1; i < vec_size; i++) { | ||
| mininmum = std::min(GetInput()[i], mininmum); | ||
| } | ||
| GetOutput() = mininmum; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool KhruevAMinElemVecSEQ::PostProcessingImpl() { | ||
| return true; | ||
| } | ||
|
|
||
| } // namespace khruev_a_min_elem_vec | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "tasks_type": "processes", | ||
| "tasks": { | ||
| "mpi": "enabled", | ||
| "seq": "enabled" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| #include <gtest/gtest.h> | ||
| #include <stb/stb_image.h> | ||
|
|
||
| #include <array> | ||
| #include <climits> | ||
| #include <cstddef> | ||
| #include <iostream> | ||
| #include <string> | ||
| #include <tuple> | ||
| #include <vector> | ||
|
|
||
| #include "khruev_a_min_elem_vec/common/include/common.hpp" | ||
| #include "khruev_a_min_elem_vec/mpi/include/ops_mpi.hpp" | ||
| #include "khruev_a_min_elem_vec/seq/include/ops_seq.hpp" | ||
| #include "util/include/func_test_util.hpp" | ||
| #include "util/include/util.hpp" | ||
|
|
||
| namespace khruev_a_min_elem_vec { | ||
|
|
||
| class KhruevAMinElemVecFuncTests : public ppc::util::BaseRunFuncTests<InType, OutType, TestType> { | ||
| public: | ||
| static std::string PrintTestParam(const TestType &test_param) { | ||
| return std::get<1>(test_param); | ||
| } | ||
|
|
||
| protected: | ||
| void SetUp() override { | ||
| // size_t size = 10; | ||
| // for (size_t i = 1; i <= size; i++) { | ||
| // input_data_.push_back(i); | ||
| // } | ||
| // expected_ = 1; | ||
| TestType params = std::get<static_cast<std::size_t>(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); | ||
| std::cout << "SetUp for " << std::get<1>(params) << '\n'; | ||
| input_data_ = std::vector(std::get<0>(std::get<0>(params))); | ||
| std::cout << "We set input data: \n"; | ||
| for (auto x : input_data_) { | ||
| std::cout << x << ' '; | ||
| } | ||
| std::cout << '\n'; | ||
| // expected_ = std::get<1>(std::get<0>(params)); | ||
| std::cout << "We set expected result: " << std::get<1>(std::get<0>(params)) << '\n'; | ||
| } | ||
|
|
||
| bool CheckTestOutputData(OutType &output_data) final { | ||
| std::cout << "CheckTestOutputData for " | ||
| << std::get<1>(std::get<static_cast<std::size_t>(ppc::util::GTestParamIndex::kTestParams)>(GetParam())) | ||
| << '\n'; | ||
| return (std::get<1>(std::get<0>(std::get<static_cast<std::size_t>(ppc::util::GTestParamIndex::kTestParams)>( | ||
| GetParam()))) == output_data); | ||
| } | ||
|
|
||
| InType GetTestInputData() final { | ||
| return input_data_; | ||
| } | ||
|
|
||
| private: | ||
| InType input_data_; | ||
| // OutType expected_; | ||
| }; | ||
|
|
||
| namespace { | ||
|
|
||
| TEST_P(KhruevAMinElemVecFuncTests, MinElemVecc) { | ||
| ExecuteTest(GetParam()); | ||
| } | ||
| std::vector<int> instanse1{1, 2, 3, 4, 5, 6}; | ||
| std::vector<int> instanse2{2, 2, -13, 4, 1, 5, 6}; | ||
| std::vector<int> instanse3{-100, 2, 30, 4, 12, 6, 1, 6, 2}; | ||
| std::vector<int> instanse4{1, 2, 3, 4, 5, 6, 9, 1, 1, 0}; | ||
| std::vector<int> instanse5{1, 2, 3, 4}; | ||
| std::vector<int> instanse6{2, 5, 2, 0, -10}; | ||
| std::vector<int> instanse7{1, 2}; | ||
| std::vector<int> instanse8{-1, -2, -8, -10, -11}; | ||
| std::vector<int> instanse9{1, 1, 1, 1, 1}; | ||
| std::vector<int> instanse10{}; | ||
|
|
||
| const std::array<TestType, 10> kTestParam = {std::make_tuple(std::make_tuple(instanse1, 1), "test1"), | ||
| std::make_tuple(std::make_tuple(instanse2, -13), "test2"), | ||
| std::make_tuple(std::make_tuple(instanse3, -100), "test3"), | ||
| std::make_tuple(std::make_tuple(instanse4, 0), "test4"), | ||
| std::make_tuple(std::make_tuple(instanse5, 1), "test5"), | ||
| std::make_tuple(std::make_tuple(instanse6, -10), "test6"), | ||
| std::make_tuple(std::make_tuple(instanse7, 1), "test7"), | ||
| std::make_tuple(std::make_tuple(instanse8, -11), "test8"), | ||
| std::make_tuple(std::make_tuple(instanse9, 1), "test9"), | ||
| std::make_tuple(std::make_tuple(instanse10, INT_MAX), "test10")}; | ||
|
|
||
| const auto kTestTasksList = std::tuple_cat( | ||
| ppc::util::AddFuncTask<KhruevAMinElemVecMPI, InType>(kTestParam, PPC_SETTINGS_khruev_a_min_elem_vec), | ||
| ppc::util::AddFuncTask<KhruevAMinElemVecSEQ, InType>(kTestParam, PPC_SETTINGS_khruev_a_min_elem_vec)); | ||
|
|
||
| const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); | ||
|
|
||
| const auto kPerfTestName = KhruevAMinElemVecFuncTests::PrintFuncTestName<KhruevAMinElemVecFuncTests>; | ||
|
|
||
| INSTANTIATE_TEST_SUITE_P(MinElemVec, KhruevAMinElemVecFuncTests, kGtestValues, kPerfTestName); | ||
|
|
||
| } // namespace | ||
|
|
||
| } // namespace khruev_a_min_elem_vec |
Uh oh!
There was an error while loading. Please reload this page.