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..796791920e --- /dev/null +++ b/tasks/sannikov_i_column_sum/mpi/include/ops_mpi.hpp @@ -0,0 +1,24 @@ +#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; + + static void PrepareSendBuffer(const InType &input_matrix, int rank, int rows, int columns, std::vector &sendbuf); +}; + +} // 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..a8459a0426 --- /dev/null +++ b/tasks/sannikov_i_column_sum/mpi/src/ops_mpi.cpp @@ -0,0 +1,102 @@ +#include "sannikov_i_column_sum/mpi/include/ops_mpi.hpp" + +#include + +#include + +#include "sannikov_i_column_sum/common/include/common.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().empty()) && (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) { + columns = static_cast(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(); +} + +void SannikovIColumnSumMPI::PrepareSendBuffer(const InType &input_matrix, int rank, int rows, int columns, + std::vector &sendbuf) { + if (rank != 0) { + return; + } + if (rank == 0) { + const int base = rows * columns; + sendbuf.resize(base); + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + sendbuf[(i * columns) + (j)] = input_matrix[i][j]; + } + } + } +} + +bool SannikovIColumnSumMPI::RunImpl() { + const auto &input_matrix = GetInput(); + + int rank = 0; + int size = 1; + int rows = 0; + int columns = 0; + columns = static_cast(GetOutput().size()); + + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + if (rank == 0) { + rows = static_cast(input_matrix.size()); + } + + MPI_Bcast(&rows, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&columns, 1, MPI_INT, 0, MPI_COMM_WORLD); + std::vector sendbuf; + PrepareSendBuffer(input_matrix, rank, rows, columns, sendbuf); + std::vector elem_for_proc(size); + std::vector id_elem(size); + int displacement = 0; + const int base = rows * columns; + for (int i = 0; i < size; i++) { + elem_for_proc[i] = static_cast(base / size) + (i < (base % size) ? 1 : 0); + id_elem[i] = displacement; + displacement += elem_for_proc[i]; + } + int mpi_displacement = 0; + 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..f7c7a0cf16 --- /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" +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().empty()); +} + +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 (const auto &row : input_matrix) { + std::size_t column = 0; + for (const auto &value : row) { + GetOutput()[column] += value; + column++; + } + } + 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..153b0073c3 --- /dev/null +++ b/tasks/sannikov_i_column_sum/tests/functional/main.cpp @@ -0,0 +1,83 @@ +#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 (std::size_t i = 0; i < (input_data_.size()); i++) { + for (std::size_t j = 0; j < (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"), + std::make_tuple(std::vector>{{-5, 0, 7, -3, 10}}, "matrix1x5_mix"), + std::make_tuple(std::vector>{{1}, {2}, {-3}, {4}, {-5}}, "matrix5x1_column"), + std::make_tuple(std::vector>{{0, -1, 2, -3}, {4, 0, -5, 6}}, "matrix2x4_mix"), + std::make_tuple(std::vector>{{1, 2, 3, 4, 5}, {-1, 0, 7, -2, 3}, {10, -5, 8, 0, -4}}, + "matrix3x5_mix")}; + +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..f19d9b9bef --- /dev/null +++ b/tasks/sannikov_i_column_sum/tests/performance/main.cpp @@ -0,0 +1,52 @@ +#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/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 (int i = 0; i < 10000; i++) { + for (int j = 0; j < 10000; j++) { + input_data_[i][j] = (i * 14) + (j * 21); + } + } + } + + bool CheckTestOutputData(OutType &output_data) final { + OutType sums_vec(input_data_.front().size(), 0); + for (std::size_t i = 0; i < input_data_.size(); i++) { + for (std::size_t j = 0; j < 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