diff --git a/tasks/konstantinov_s_broadcast/common/include/common.hpp b/tasks/konstantinov_s_broadcast/common/include/common.hpp new file mode 100644 index 0000000000..bfa6caacec --- /dev/null +++ b/tasks/konstantinov_s_broadcast/common/include/common.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +#include "task/include/task.hpp" + +namespace konstantinov_s_broadcast { + +using TestType = std::tuple; + +using ETypeInt = int; +using ETypeFloat = float; +using ETypeDouble = double; + +using EType = ETypeInt; +using InType = std::vector; +using OutType = InType; + +template +using BaseTask = ppc::task::Task, std::vector>; + +} // namespace konstantinov_s_broadcast diff --git a/tasks/konstantinov_s_broadcast/info.json b/tasks/konstantinov_s_broadcast/info.json new file mode 100644 index 0000000000..b0c7a6f67f --- /dev/null +++ b/tasks/konstantinov_s_broadcast/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Семён", + "last_name": "Константинов", + "middle_name": "Анатольевич", + "group_number": "3823Б1ФИ3", + "task_number": "2" + } +} diff --git a/tasks/konstantinov_s_broadcast/mpi/include/ops_mpi.hpp b/tasks/konstantinov_s_broadcast/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..fa1971a72d --- /dev/null +++ b/tasks/konstantinov_s_broadcast/mpi/include/ops_mpi.hpp @@ -0,0 +1,46 @@ +#pragma once +#include + +#include + +#include "konstantinov_s_broadcast/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace konstantinov_s_broadcast { + +template +class KonstantinovSBroadcastMPI : public BaseTask { + public: + using InType = std::vector; + using OutType = InType; + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit KonstantinovSBroadcastMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +template +constexpr MPI_Datatype GetMpiType(); + +template <> +constexpr MPI_Datatype GetMpiType() { + return MPI_INT; +} + +template <> +constexpr MPI_Datatype GetMpiType() { + return MPI_FLOAT; +} + +template <> +constexpr MPI_Datatype GetMpiType() { + return MPI_DOUBLE; +} + +} // namespace konstantinov_s_broadcast diff --git a/tasks/konstantinov_s_broadcast/mpi/src/ops_mpi.cpp b/tasks/konstantinov_s_broadcast/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..af692f2a34 --- /dev/null +++ b/tasks/konstantinov_s_broadcast/mpi/src/ops_mpi.cpp @@ -0,0 +1,101 @@ +#include "konstantinov_s_broadcast/mpi/include/ops_mpi.hpp" + +#include + +#include +// #include +#include + +#include "konstantinov_s_broadcast/common/include/common.hpp" +// #include "util/include/util.hpp" + +namespace konstantinov_s_broadcast { + +template +KonstantinovSBroadcastMPI::KonstantinovSBroadcastMPI(const InType &in) { + this->SetTypeOfTask(GetStaticTypeOfTask()); + this->GetInput() = in; + this->GetOutput().resize(this->GetInput().size()); +} + +template +bool KonstantinovSBroadcastMPI::ValidationImpl() { + // std::cout << "\t\tValidation mpi\n"; + return !this->GetInput().empty(); +} + +template +bool KonstantinovSBroadcastMPI::PreProcessingImpl() { + return true; +} + +template +bool KonstantinovSBroadcastMPI::RunImpl() { + int prank = 0; + int pcount = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &prank); + MPI_Comm_size(MPI_COMM_WORLD, &pcount); + + const int tag_count = 1; + const int tag_data = 2; + + MPI_Datatype mpi_type = GetMpiType(); + + T *odata = nullptr; + + int root_prank = pcount - 1; + int k = std::min(2, pcount); // k-арное дерево + + // ранг процесса в дереве + int tree_rank = (prank - root_prank + pcount) % pcount; + + // родитель в дереве + int parent_tree_rank = (tree_rank == 0) ? -1 : (tree_rank - 1) / k; + int parent_prank = (parent_tree_rank < 0) ? -1 : (parent_tree_rank + root_prank) % pcount; + + int recv_count = 0; + // приём данных от родителя (корень читает доступные ему данные) + if (tree_rank == 0) { + auto &ibuffer = this->GetInput(); + int elem_count = static_cast(ibuffer.size()); + odata = ibuffer.data(); + recv_count = elem_count; + this->GetOutput() = ibuffer; + } else { + // std::cout << "Rank " << prank << " (t " << tree_rank << ") recv from "<< parent_prank << " (t " << + // parent_tree_rank << ")\n"; + MPI_Recv(&recv_count, 1, MPI_INT, parent_prank, tag_count, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + odata = new T[recv_count]; + MPI_Recv(odata, recv_count, mpi_type, parent_prank, tag_data, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + memcpy(this->GetOutput().data(), odata, recv_count * sizeof(T)); + } + + // вычисляем потомков и рассылаем + // std::vector child_pranks; + for (int i = 1; i <= k; ++i) { + int child_tree_rank = (k * tree_rank) + i; + if (child_tree_rank < pcount) { + int child_prank = (child_tree_rank + root_prank) % pcount; + // std::cout << "Rank " << prank << " (t " << tree_rank << ") send to " << ch << "\n"; + MPI_Send(&recv_count, 1, MPI_INT, child_prank, tag_count, MPI_COMM_WORLD); + MPI_Send(odata, recv_count, mpi_type, child_prank, tag_data, MPI_COMM_WORLD); + // child_pranks.push_back(child_prank); + } + } + if (tree_rank != 0) { + delete[] odata; + } + + // this->GetOutput() = ibuffer; + return true; +} + +template +bool KonstantinovSBroadcastMPI::PostProcessingImpl() { + return true; +} +template class KonstantinovSBroadcastMPI; +template class KonstantinovSBroadcastMPI; +template class KonstantinovSBroadcastMPI; + +} // namespace konstantinov_s_broadcast diff --git a/tasks/konstantinov_s_broadcast/report.md b/tasks/konstantinov_s_broadcast/report.md new file mode 100644 index 0000000000..9a3129db0c --- /dev/null +++ b/tasks/konstantinov_s_broadcast/report.md @@ -0,0 +1,57 @@ +# Передача от одного всем (broadcast) + +- Student: Константинов Семён Анатольевич, group 3823Б1ФИ3 +- Technology: SEQ | MPI +- Variant: 1 + +## 1. Introduction +Эффективная рассылка данных от одного процесса всем остальным - важная часть в работе MPI, реализуемая функцией MPI_Bcast. Алгоритм пересылки данных можно повторить, взяв за основу идею k-арного дерева и используя только базовые операции `MPI_Recv` и `MPI_Send`. +## 2. Problem Statement +### Входные данные: +- Ранг процесса, который будет корневым в дереве +- Массив входных данных +### Выходные данные: +- Одинаковые массивы на всех процессах, совпадающие с входным массивом +## 3. Baseline Algorithm (Sequential) +Последовательный алгоритм не имеет смысла в контексте алгоритмов обмена данными между процессами. Реализовано копирование входного массива в выходной. + +## 4. Parallelization Scheme +Алгоритм основан на k-арном дереве: каждый процесс из своего ранга процесса `prank`, ранга корня дерева `root_prank` и количества процессов `pcount` получает свой ранг в дереве `tree_rank = (prank - root_prank + pcount) % pcount`. Из тех же данных вычисляются ранги в дереве и ранги процесса у родителя процесса и каждого из от 0 до k детей процесса. +``` + parent_tree_rank = (tree_rank == 0) ? -1 : (tree_rank - 1) / k; + parent_prank = (parent_tree_rank < 0) ? -1 : (parent_tree_rank + root_prank) % pcount; + child_tree_rank = (k * tree_rank) + i + +#include "konstantinov_s_broadcast/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace konstantinov_s_broadcast { +template +class KonstantinovSBroadcastSEQ : public BaseTask { + public: + using InType = std::vector; + using OutType = InType; + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit KonstantinovSBroadcastSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace konstantinov_s_broadcast diff --git a/tasks/konstantinov_s_broadcast/seq/src/ops_seq.cpp b/tasks/konstantinov_s_broadcast/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..227084a1b9 --- /dev/null +++ b/tasks/konstantinov_s_broadcast/seq/src/ops_seq.cpp @@ -0,0 +1,40 @@ +#include "konstantinov_s_broadcast/seq/include/ops_seq.hpp" + +// #include +// #include +#include // memcpy + +#include "konstantinov_s_broadcast/common/include/common.hpp" +// #include "util/include/util.hpp" + +namespace konstantinov_s_broadcast { +template +KonstantinovSBroadcastSEQ::KonstantinovSBroadcastSEQ(const InType &in) { + this->SetTypeOfTask(GetStaticTypeOfTask()); + this->GetInput() = in; + this->GetOutput().resize(this->GetInput().size()); +} +template +bool KonstantinovSBroadcastSEQ::ValidationImpl() { + // std::cout << "\t\tValidation seq\n"; + return !this->GetInput().empty(); +} +template +bool KonstantinovSBroadcastSEQ::PreProcessingImpl() { + return true; +} +template +bool KonstantinovSBroadcastSEQ::RunImpl() { + memcpy(this->GetOutput().data(), this->GetInput().data(), this->GetInput().size() * sizeof(T)); + return true; +} +template +bool KonstantinovSBroadcastSEQ::PostProcessingImpl() { + return true; +} + +template class KonstantinovSBroadcastSEQ; +template class KonstantinovSBroadcastSEQ; +template class KonstantinovSBroadcastSEQ; + +} // namespace konstantinov_s_broadcast diff --git a/tasks/konstantinov_s_broadcast/settings.json b/tasks/konstantinov_s_broadcast/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/konstantinov_s_broadcast/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/konstantinov_s_broadcast/tests/.clang-tidy b/tasks/konstantinov_s_broadcast/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/konstantinov_s_broadcast/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/konstantinov_s_broadcast/tests/functional/main.cpp b/tasks/konstantinov_s_broadcast/tests/functional/main.cpp new file mode 100644 index 0000000000..de12fb9c81 --- /dev/null +++ b/tasks/konstantinov_s_broadcast/tests/functional/main.cpp @@ -0,0 +1,78 @@ +#include +#include + +// #include +#include +#include +// #include +// #include +// #include +#include +#include +#include +// #include +// #include + +#include "konstantinov_s_broadcast/common/include/common.hpp" +#include "konstantinov_s_broadcast/mpi/include/ops_mpi.hpp" +#include "konstantinov_s_broadcast/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace konstantinov_s_broadcast { + +class KonstantinovSBroadcastTests : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::get<1>(test_param) + "_" + std::to_string(std::get<0>(test_param)); + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + const int insz = std::get<0>(params); + input_data_.resize(insz); + for (int i = 0; i < insz; ++i) { + input_data_[i] = i; + } + } + + bool CheckTestOutputData(OutType &output_data) final { + // for(int i=0; i kTestParam = { + std::make_tuple(1, "1"), std::make_tuple(2, "2"), std::make_tuple(10, "10"), + std::make_tuple(100, "100")}; // тесты с разным корнем дерева не получится создать (неизвестно колво процессов), но + // корнем выбирается последний процесс в самой реализации для разнообразия + +const auto kTestTasksList = std::tuple_cat( + ppc::util::AddFuncTask, InType>(kTestParam, PPC_SETTINGS_konstantinov_s_broadcast), + ppc::util::AddFuncTask, InType>(kTestParam, + PPC_SETTINGS_konstantinov_s_broadcast)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = KonstantinovSBroadcastTests::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(CustomBroadcastTests, KonstantinovSBroadcastTests, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace konstantinov_s_broadcast diff --git a/tasks/konstantinov_s_broadcast/tests/performance/main.cpp b/tasks/konstantinov_s_broadcast/tests/performance/main.cpp new file mode 100644 index 0000000000..7d7405f171 --- /dev/null +++ b/tasks/konstantinov_s_broadcast/tests/performance/main.cpp @@ -0,0 +1,44 @@ +#include + +#include + +#include "konstantinov_s_broadcast/common/include/common.hpp" +#include "konstantinov_s_broadcast/mpi/include/ops_mpi.hpp" +#include "konstantinov_s_broadcast/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace konstantinov_s_broadcast { + +class KonstantinovSBroadcastTests : public ppc::util::BaseRunPerfTests { + const int kCount_ = 10000000; + InType input_data_; + // OutType result_right_{}; + + void SetUp() override { + input_data_.resize(kCount_, 1); + } + + bool CheckTestOutputData(OutType &output_data) final { + return 0 == memcmp(input_data_.data(), output_data.data(), input_data_.size() * sizeof(EType)); + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(KonstantinovSBroadcastTests, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks, KonstantinovSBroadcastSEQ>( + PPC_SETTINGS_konstantinov_s_broadcast); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = KonstantinovSBroadcastTests::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, KonstantinovSBroadcastTests, kGtestValues, kPerfTestName); + +} // namespace konstantinov_s_broadcast