Skip to content

Commit ce0edec

Browse files
Рычкова Диана. Технология SEQ-MPI. Сумма элементов матрицы по столбцам. Вариант 12 (#70)
Задача вычисления сумм по столбцам матрицы. На вход подается матрица целых чисел, на выходе - вектор сумм каждого столбца. Реализованы две версии: последовательная (SEQ) и параллельная (MPI). SEQ версия проходит по всем элементам матрицы, суммируя значения столбцов. MPI версия распределяет строки матрицы между процессами, каждый процесс вычисляет частичные суммы для своих строк, затем результаты собираются на процессе 0. Обрабатываются все случаи включая пустые матрицы, матрицы с отрицательными числами и различные размеры матриц. --------- Co-authored-by: Hymera <hymerared@gmail.com>
1 parent ab3996d commit ce0edec

11 files changed

Lines changed: 704 additions & 0 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <tuple>
5+
#include <vector>
6+
7+
#include "task/include/task.hpp"
8+
9+
namespace rychkova_d_sum_matrix_columns {
10+
11+
using InType = std::vector<std::vector<int>>;
12+
using OutType = std::vector<int>;
13+
using TestType = std::tuple<std::vector<std::vector<int>>, std::vector<int>, std::string>;
14+
using BaseTask = ppc::task::Task<InType, OutType>;
15+
16+
} // namespace rychkova_d_sum_matrix_columns
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"student": {
3+
"first_name": "Diana",
4+
"last_name": "Rychkova",
5+
"middle_name": "Dmitrievna",
6+
"group_number": "3823B1FI3",
7+
"task_number": "1"
8+
}
9+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include "rychkova_d_sum_matrix_columns/common/include/common.hpp"
4+
#include "task/include/task.hpp"
5+
6+
namespace rychkova_d_sum_matrix_columns {
7+
8+
class RychkovaDSumMatrixColumnsMPI : public BaseTask {
9+
public:
10+
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
11+
return ppc::task::TypeOfTask::kMPI;
12+
}
13+
14+
explicit RychkovaDSumMatrixColumnsMPI(const InType &in);
15+
16+
private:
17+
bool ValidationImpl() override;
18+
bool PreProcessingImpl() override;
19+
bool RunImpl() override;
20+
bool PostProcessingImpl() override;
21+
};
22+
23+
} // namespace rychkova_d_sum_matrix_columns
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#include "rychkova_d_sum_matrix_columns/mpi/include/ops_mpi.hpp"
2+
3+
#include <mpi.h>
4+
5+
#include <cstddef>
6+
#include <utility>
7+
#include <vector>
8+
9+
#include "rychkova_d_sum_matrix_columns/common/include/common.hpp"
10+
11+
namespace rychkova_d_sum_matrix_columns {
12+
13+
RychkovaDSumMatrixColumnsMPI::RychkovaDSumMatrixColumnsMPI(const InType &in) {
14+
SetTypeOfTask(GetStaticTypeOfTask());
15+
GetInput().resize(in.size());
16+
for (size_t i = 0; i < in.size(); ++i) {
17+
GetInput()[i] = in[i];
18+
}
19+
GetOutput() = OutType{};
20+
}
21+
22+
bool RychkovaDSumMatrixColumnsMPI::ValidationImpl() {
23+
int rank = 0;
24+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
25+
26+
bool is_valid = true;
27+
28+
if (rank == 0) {
29+
const auto &input = GetInput();
30+
31+
if (!input.empty()) {
32+
size_t cols = input[0].size();
33+
for (const auto &row : input) {
34+
if (row.size() != cols) {
35+
is_valid = false;
36+
break;
37+
}
38+
}
39+
}
40+
41+
is_valid = is_valid && GetOutput().empty();
42+
}
43+
44+
MPI_Bcast(&is_valid, 1, MPI_C_BOOL, 0, MPI_COMM_WORLD);
45+
46+
return is_valid;
47+
}
48+
49+
bool RychkovaDSumMatrixColumnsMPI::PreProcessingImpl() {
50+
int rank = 0;
51+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
52+
53+
bool has_data = false;
54+
size_t num_cols = 0;
55+
56+
if (rank == 0) {
57+
const auto &input = GetInput();
58+
has_data = !input.empty();
59+
if (has_data) {
60+
num_cols = input[0].size();
61+
}
62+
}
63+
64+
MPI_Bcast(&has_data, 1, MPI_C_BOOL, 0, MPI_COMM_WORLD);
65+
MPI_Bcast(&num_cols, 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD);
66+
67+
if (has_data) {
68+
GetOutput() = std::vector<int>(num_cols, 0);
69+
} else {
70+
GetOutput() = std::vector<int>{};
71+
}
72+
73+
return true;
74+
}
75+
76+
namespace {
77+
78+
void CalculateDistribution(int size, size_t num_rows, size_t num_cols, std::vector<int> &send_counts,
79+
std::vector<int> &displacements) {
80+
size_t rows_per_process = num_rows / static_cast<size_t>(size);
81+
size_t remainder = num_rows % static_cast<size_t>(size);
82+
83+
size_t current_displacement = 0;
84+
for (int i = 0; i < size; ++i) {
85+
auto process_index = static_cast<size_t>(i);
86+
size_t rows_for_process_i = rows_per_process + (process_index < remainder ? 1 : 0);
87+
send_counts[i] = static_cast<int>(rows_for_process_i * num_cols);
88+
displacements[i] = static_cast<int>(current_displacement);
89+
current_displacement += rows_for_process_i * num_cols;
90+
}
91+
}
92+
93+
std::vector<int> FlattenMatrix(const InType &matrix) {
94+
std::vector<int> flat_matrix;
95+
if (!matrix.empty()) {
96+
size_t num_rows = matrix.size();
97+
size_t num_cols = matrix[0].size();
98+
flat_matrix.reserve(num_rows * num_cols);
99+
for (const auto &row : matrix) {
100+
flat_matrix.insert(flat_matrix.end(), row.begin(), row.end());
101+
}
102+
}
103+
return flat_matrix;
104+
}
105+
106+
std::vector<int> ComputeLocalSums(const std::vector<int> &local_data, size_t local_rows, size_t num_cols) {
107+
std::vector<int> local_sums(num_cols, 0);
108+
for (size_t i = 0; i < local_rows; ++i) {
109+
for (size_t j = 0; j < num_cols; ++j) {
110+
local_sums[j] += local_data[(i * num_cols) + j];
111+
}
112+
}
113+
return local_sums;
114+
}
115+
116+
} // namespace
117+
118+
bool RychkovaDSumMatrixColumnsMPI::RunImpl() {
119+
int rank = 0;
120+
int size = 0;
121+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
122+
MPI_Comm_size(MPI_COMM_WORLD, &size);
123+
124+
const auto &output = GetOutput();
125+
bool has_data = !output.empty();
126+
size_t num_cols = output.size();
127+
128+
if (!has_data) {
129+
std::vector<int> empty_sums(0);
130+
MPI_Allreduce(MPI_IN_PLACE, empty_sums.data(), 0, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
131+
return true;
132+
}
133+
134+
size_t num_rows = 0;
135+
if (rank == 0) {
136+
const auto &input = GetInput();
137+
num_rows = input.size();
138+
}
139+
MPI_Bcast(&num_rows, 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD);
140+
141+
std::vector<int> send_counts(size);
142+
std::vector<int> displacements(size);
143+
144+
if (rank == 0) {
145+
CalculateDistribution(size, num_rows, num_cols, send_counts, displacements);
146+
}
147+
148+
MPI_Bcast(send_counts.data(), size, MPI_INT, 0, MPI_COMM_WORLD);
149+
MPI_Bcast(displacements.data(), size, MPI_INT, 0, MPI_COMM_WORLD);
150+
151+
size_t rows_per_process = num_rows / static_cast<size_t>(size);
152+
size_t remainder = num_rows % static_cast<size_t>(size);
153+
size_t local_rows = rows_per_process + (std::cmp_less(rank, remainder) ? 1 : 0);
154+
155+
std::vector<int> flat_matrix;
156+
if (rank == 0) {
157+
flat_matrix = FlattenMatrix(GetInput());
158+
}
159+
160+
std::vector<int> local_data(local_rows * num_cols);
161+
MPI_Scatterv(rank == 0 ? flat_matrix.data() : nullptr, send_counts.data(), displacements.data(), MPI_INT,
162+
local_data.data(), static_cast<int>(local_rows * num_cols), MPI_INT, 0, MPI_COMM_WORLD);
163+
164+
std::vector<int> local_sums = ComputeLocalSums(local_data, local_rows, num_cols);
165+
166+
MPI_Allreduce(local_sums.data(), GetOutput().data(), static_cast<int>(num_cols), MPI_INT, MPI_SUM, MPI_COMM_WORLD);
167+
168+
return true;
169+
}
170+
171+
bool RychkovaDSumMatrixColumnsMPI::PostProcessingImpl() {
172+
return true;
173+
}
174+
175+
} // namespace rychkova_d_sum_matrix_columns

0 commit comments

Comments
 (0)