diff --git a/scripts/build/perf_stat_dir/perf_log.txt b/scripts/build/perf_stat_dir/perf_log.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tasks/sannikov_i_column_sum/common/include/common.hpp b/tasks/sannikov_i_column_sum/common/include/common.hpp new file mode 100644 index 0000000000..32f5d69a25 --- /dev/null +++ b/tasks/sannikov_i_column_sum/common/include/common.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +#include "task/include/task.hpp" + +namespace sannikov_i_column_sum { + +using InType = std::vector>; +using OutType = std::vector; +using TestType = std::tuple>, std::string>; +using BaseTask = ppc::task::Task; + +} // namespace sannikov_i_column_sum diff --git a/tasks/sannikov_i_column_sum/data/pic.jpg b/tasks/sannikov_i_column_sum/data/pic.jpg new file mode 100644 index 0000000000..3445802349 Binary files /dev/null and b/tasks/sannikov_i_column_sum/data/pic.jpg differ diff --git a/tasks/sannikov_i_column_sum/info.json b/tasks/sannikov_i_column_sum/info.json new file mode 100644 index 0000000000..00f793a5c5 --- /dev/null +++ b/tasks/sannikov_i_column_sum/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Иван", + "last_name": "Санников", + "middle_name": "Михайлович", + "group_number": "3823Б1ФИ2", + "task_number": "12" + } +} diff --git a/tasks/sannikov_i_column_sum/mpi/include/ops_mpi.hpp b/tasks/sannikov_i_column_sum/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..b9d5377933 --- /dev/null +++ b/tasks/sannikov_i_column_sum/mpi/include/ops_mpi.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "sannikov_i_column_sum/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace sannikov_i_column_sum { + +class SannikovIColumnSumMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit SannikovIColumnSumMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace sannikov_i_column_sum diff --git a/tasks/sannikov_i_column_sum/mpi/src/ops_mpi.cpp b/tasks/sannikov_i_column_sum/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..ad1f15b652 --- /dev/null +++ b/tasks/sannikov_i_column_sum/mpi/src/ops_mpi.cpp @@ -0,0 +1,101 @@ +#include "sannikov_i_column_sum/mpi/include/ops_mpi.hpp" + +#include + +#include +#include + +#include "sannikov_i_column_sum/common/include/common.hpp" +#include "util/include/util.hpp" + +namespace sannikov_i_column_sum { + +SannikovIColumnSumMPI::SannikovIColumnSumMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + auto &input_buffer = GetInput(); + InType tmp(in); + input_buffer.swap(tmp); + GetOutput().clear(); +} + +bool SannikovIColumnSumMPI::ValidationImpl() { + const auto &input_matrix = GetInput(); + return (!input_matrix.empty()) && (input_matrix.front().size() != 0) && (GetOutput().empty()); +} + +bool SannikovIColumnSumMPI::PreProcessingImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + const auto &input_matrix = GetInput(); + int columns = 0; + if (rank == 0) { + if (input_matrix.empty()) { + return false; + } + columns = (input_matrix.front().size()); + } + MPI_Bcast(&columns, 1, MPI_INT, 0, MPI_COMM_WORLD); + if (columns <= 0) { + return false; + } + GetOutput().resize((columns), 0); + return !GetOutput().empty(); +} + +bool SannikovIColumnSumMPI::RunImpl() { + const auto &input_matrix = GetInput(); + + int rank = 0; + int size = 1; + int rows = 0; + int columns = (GetOutput().size()); + + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + if (rank == 0) { + if (input_matrix.empty() || (int)input_matrix.front().size() != columns) { + return false; + } + rows = (input_matrix.size()); + } + + MPI_Bcast(&rows, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&columns, 1, MPI_INT, 0, MPI_COMM_WORLD); + if (rows <= 0 || columns <= 0) { + return false; + } + std::vector sendbuf; + if (rank == 0) { + sendbuf.resize((rows * columns)); + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + sendbuf[i * columns + j] = input_matrix[i][j]; + } + } + } + std::vector elem_for_proc(size); + std::vector id_elem(size); + int displacement = 0; + for (int i = 0; i < size; i++) { + elem_for_proc[i] = rows * columns / size + (i < rows * columns % size ? 1 : 0); + id_elem[i] = displacement; + displacement += elem_for_proc[i]; + } + int mpi_displacement = id_elem[rank] % columns; + std::vector buf(elem_for_proc[rank], 0); + MPI_Scatterv(rank == 0 ? sendbuf.data() : nullptr, elem_for_proc.data(), id_elem.data(), MPI_INT, buf.data(), + elem_for_proc[rank], MPI_INT, 0, MPI_COMM_WORLD); + std::vector sum(columns, 0); + for (int i = 0; i < elem_for_proc[rank]; i++) { + int new_col = (i + mpi_displacement) % columns; + sum[new_col] += buf[i]; + } + MPI_Allreduce(sum.data(), GetOutput().data(), columns, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + return !GetOutput().empty(); +} + +bool SannikovIColumnSumMPI::PostProcessingImpl() { + return !GetOutput().empty(); +} + +} // namespace sannikov_i_column_sum diff --git a/tasks/sannikov_i_column_sum/report.md b/tasks/sannikov_i_column_sum/report.md new file mode 100644 index 0000000000..d8c6530dd1 --- /dev/null +++ b/tasks/sannikov_i_column_sum/report.md @@ -0,0 +1,132 @@ +# Сумма значений по столбцам матрицы + +- Студент: Санников Иван Михайлович, Группа: 3823Б1ФИ2 +- Технология: SEQ, MPI +- Вариант: 12 +## 1. Введение +Вычисление сумм по столбцам матрицы является одной из основных в математике и часто используется в прикладных задачах, как машинное обучение(ML) и статистика. Часто приходится работать с большим объемом данных, что делает задачу ресурсоемкой при использовании последовательных алгоритмов. Цель данной лабораторной работы - написание алгоритма на основе технологии MPI для распределение нагрузки между несколькоми процессами. + +## 2. Постановка задачи +Входные данные: std::vector> - вектор векторов типа данных int представляющий из себя матрицу. + +Для матрицы размера A*B, строица std::vector размера B, где для элемента i хранится сумма столбца i из входной матрицы. + +## 3. Базовый алгоритм(seq) +Последовательный алгорритм: +- Проходим по всем строкам +- В каждой i строке проходим по всем столбцам +- Складываем элемент i строки j столбца c элементом j вектора суммы. + +```cpp +bool SannikovIColumnSumSEQ::RunImpl() { + const auto &input_matrix = GetInput(); + if (input_matrix.empty()) { + return false; + } + + for (int i = 0; i < (int)input_matrix.size(); i++) { + for (int j = 0; j < (int)input_matrix[i].size(); j++) { + GetOutput()[j] += input_matrix[i][j]; + } + } + return !GetOutput().empty(); +} +``` + +## 4. Описание параллельного алгоритма + +1. Обрабатываем входные данные: Превращаем std::vector> в последовательный std::vector, чтобы была возможность его разделить Scatterv. +2. Вычисляем сколько данных получит процесс: перемножаем количество столбцов на строки, делим на количество процессов и прибавляем 1, если номер вычисления меньше остатка (i sendbuf; + if (rank == 0) { + sendbuf.resize((rows * columns)); + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + sendbuf[i * columns + j] = input_matrix[i][j]; + } + } + } + std::vector elem_for_proc(size); + std::vector id_elem(size); + int displacement = 0; + for (int i = 0; i < size; i++) { + elem_for_proc[i] = rows * columns / size + (i < rows * columns % size ? 1 : 0); + id_elem[i] = displacement; + displacement += elem_for_proc[i]; + } + int mpi_displacement = id_elem[rank] % columns; + std::vector buf(elem_for_proc[rank], 0); + MPI_Scatterv(rank == 0 ? sendbuf.data() : nullptr, elem_for_proc.data(), id_elem.data(), MPI_INT, buf.data(), + elem_for_proc[rank], MPI_INT, 0, MPI_COMM_WORLD); + std::vector sum(columns, 0); + for (int i = 0; i < elem_for_proc[rank]; i++) { + int new_col = (i + mpi_displacement) % columns; + sum[new_col] += buf[i]; + } + MPI_Allreduce(sum.data(), GetOutput().data(), columns, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + return !GetOutput().empty(); +} + + +``` \ No newline at end of file diff --git a/tasks/sannikov_i_column_sum/seq/include/ops_seq.hpp b/tasks/sannikov_i_column_sum/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..3eee0a4c57 --- /dev/null +++ b/tasks/sannikov_i_column_sum/seq/include/ops_seq.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "sannikov_i_column_sum/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace sannikov_i_column_sum { + +class SannikovIColumnSumSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit SannikovIColumnSumSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace sannikov_i_column_sum diff --git a/tasks/sannikov_i_column_sum/seq/src/ops_seq.cpp b/tasks/sannikov_i_column_sum/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..47e708e58d --- /dev/null +++ b/tasks/sannikov_i_column_sum/seq/src/ops_seq.cpp @@ -0,0 +1,50 @@ +#include "sannikov_i_column_sum/seq/include/ops_seq.hpp" + +#include +#include + +#include "sannikov_i_column_sum/common/include/common.hpp" +#include "util/include/util.hpp" + +namespace sannikov_i_column_sum { + +SannikovIColumnSumSEQ::SannikovIColumnSumSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + auto &input_buffer = GetInput(); + InType tmp(in); + input_buffer.swap(tmp); + + GetOutput().clear(); +} + +bool SannikovIColumnSumSEQ::ValidationImpl() { + const auto &input_matrix = GetInput(); + return (!input_matrix.empty()) && (input_matrix.front().size() != 0); +} + +bool SannikovIColumnSumSEQ::PreProcessingImpl() { + const auto &input_matrix = GetInput(); + GetOutput().clear(); + GetOutput().resize(input_matrix.front().size(), 0); + return !GetOutput().empty(); +} + +bool SannikovIColumnSumSEQ::RunImpl() { + const auto &input_matrix = GetInput(); + if (input_matrix.empty()) { + return false; + } + + for (int i = 0; i < (int)input_matrix.size(); i++) { + for (int j = 0; j < (int)input_matrix[i].size(); j++) { + GetOutput()[j] += input_matrix[i][j]; + } + } + return !GetOutput().empty(); +} + +bool SannikovIColumnSumSEQ::PostProcessingImpl() { + return !GetOutput().empty(); +} + +} // namespace sannikov_i_column_sum diff --git a/tasks/sannikov_i_column_sum/settings.json b/tasks/sannikov_i_column_sum/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/sannikov_i_column_sum/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/sannikov_i_column_sum/tests/.clang-tidy b/tasks/sannikov_i_column_sum/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/sannikov_i_column_sum/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/sannikov_i_column_sum/tests/functional/main.cpp b/tasks/sannikov_i_column_sum/tests/functional/main.cpp new file mode 100644 index 0000000000..809edeea45 --- /dev/null +++ b/tasks/sannikov_i_column_sum/tests/functional/main.cpp @@ -0,0 +1,83 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sannikov_i_column_sum/common/include/common.hpp" +#include "sannikov_i_column_sum/mpi/include/ops_mpi.hpp" +#include "sannikov_i_column_sum/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace sannikov_i_column_sum { + +class SannikovIColumnSumFuncTests : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::get<1>(test_param); + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + input_data_ = std::get<0>(params); + } + + bool CheckTestOutputData(OutType &output_data) final { + OutType sums_vec(input_data_.front().size(), 0); + for (size_t i = 0; i < (size_t)input_data_.size(); i++) { + for (size_t j = 0; j < (size_t)input_data_[i].size(); j++) { + sums_vec[j] += input_data_[i][j]; + } + } + return (sums_vec == output_data); + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_{}; +}; + +namespace { + +TEST_P(SannikovIColumnSumFuncTests, MatmulFromPic) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = { + std::make_tuple(std::vector>{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, "matrix3x3"), + std::make_tuple(std::vector>{{-1, -2, -3}, {-4, -5, -6}, {-7, -8, -9}}, "matrix3x3negative"), + std::make_tuple(std::vector>{{1, 2}, {1, 2}, {1, 2}, {1, 2}}, "matrix4x2"), + std::make_tuple(std::vector>{{0}}, "matrix1x1zero"), + std::make_tuple(std::vector>{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, "matrix3x3zero"), + std::make_tuple(std::vector>{{1, 10, 15, 19, 90, 3}}, "matrix1x6"), + std::make_tuple(std::vector>{{1037, 2385, 8543}, {286, 629, 1094}, {8306, 6290, 375}}, + "matrix3x3big_nums"), + std::make_tuple(std::vector>{{1037, -2385, 8543}, {-286, 629, -1094}, {8306, 6290, -375}}, + "matrix3x3big_nums_with_negative")}; + +const auto kTestTasksList = std::tuple_cat( + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_sannikov_i_column_sum), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_sannikov_i_column_sum)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = SannikovIColumnSumFuncTests::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(PicMatrixTests, SannikovIColumnSumFuncTests, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace sannikov_i_column_sum diff --git a/tasks/sannikov_i_column_sum/tests/performance/main.cpp b/tasks/sannikov_i_column_sum/tests/performance/main.cpp new file mode 100644 index 0000000000..d368137d11 --- /dev/null +++ b/tasks/sannikov_i_column_sum/tests/performance/main.cpp @@ -0,0 +1,50 @@ +#include + +#include "sannikov_i_column_sum/common/include/common.hpp" +#include "sannikov_i_column_sum/mpi/include/ops_mpi.hpp" +#include "sannikov_i_column_sum/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace sannikov_i_column_sum { + +class SannikovIColumnSumPerfTests : public ppc::util::BaseRunPerfTests { + InType input_data_{}; + + void SetUp() override { + input_data_ = InType(10000, std::vector(10000)); + for (size_t i = 0; i < 10000; i++) { + for (size_t j = 0; j < 10000; j++) { + input_data_[i][j] = (size_t)(i * 14 + j * 21); + } + } + } + + bool CheckTestOutputData(OutType &output_data) final { + OutType sums_vec(input_data_.front().size(), 0); + for (size_t i = 0; i < (size_t)input_data_.size(); i++) { + for (size_t j = 0; j < (size_t)input_data_[i].size(); j++) { + sums_vec[j] += input_data_[i][j]; + } + } + return sums_vec == output_data; + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(SannikovIColumnSumPerfTests, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_sannikov_i_column_sum); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = SannikovIColumnSumPerfTests::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, SannikovIColumnSumPerfTests, kGtestValues, kPerfTestName); + +} // namespace sannikov_i_column_sum