GCC Code Coverage Report


Directory: ./
File: tasks/sabirov_s_hypercube/mpi/src/ops_mpi.cpp
Date: 2026-01-27 01:59:34
Exec Total Coverage
Lines: 0 104 0.0%
Functions: 0 11 0.0%
Branches: 0 116 0.0%

Line Branch Exec Source
1 #include "sabirov_s_hypercube/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <cmath>
6 #include <cstddef>
7 #include <vector>
8
9 #include "sabirov_s_hypercube/common/include/common.hpp"
10
11 namespace sabirov_s_hypercube {
12
13 SabirovSHypercubeMPI::SabirovSHypercubeMPI(const InType &in) {
14 SetTypeOfTask(GetStaticTypeOfTask());
15 GetInput() = in;
16 GetOutput() = HypercubeOutput{.received_data = {}, .route = {}, .success = false};
17 }
18
19 bool SabirovSHypercubeMPI::ValidationImpl() {
20 const auto &input = GetInput();
21
22 // Проверка размерности
23 if (input.dimension < 0) {
24 return false;
25 }
26
27 int num_nodes = 1 << input.dimension; // 2^dimension
28
29 // Проверка, что количество процессов соответствует топологии гиперкуба
30 int world_size = 0;
31 MPI_Comm_size(MPI_COMM_WORLD, &world_size);
32 if (world_size < num_nodes) {
33 return false;
34 }
35
36 // Проверка корректности рангов источника и получателя
37 if (input.source_rank < 0 || input.source_rank >= num_nodes) {
38 return false;
39 }
40
41 if (input.dest_rank < 0 || input.dest_rank >= num_nodes) {
42 return false;
43 }
44
45 return true;
46 }
47
48 bool SabirovSHypercubeMPI::PreProcessingImpl() {
49 MPI_Comm_size(MPI_COMM_WORLD, &world_size_);
50 MPI_Comm_rank(MPI_COMM_WORLD, &world_rank_);
51
52 dimension_ = GetInput().dimension;
53
54 // Прогрев MPI коммуникаций - обмен данными для инициализации буферов
55 // и соединений. Это критически важно для стабильного измерения времени,
56 // т.к. первые MPI операции обычно медленнее ("холодный старт").
57 // Используем те же типы операций, что и в основном алгоритме.
58 int warmup_data = world_rank_;
59 MPI_Bcast(&warmup_data, 1, MPI_INT, 0, MPI_COMM_WORLD);
60 MPI_Barrier(MPI_COMM_WORLD);
61
62 // Инициализация выходных данных
63 GetOutput().success = false;
64 GetOutput().received_data.clear();
65 GetOutput().route.clear();
66
67 return true;
68 }
69
70 int SabirovSHypercubeMPI::FindRoutePosition(const std::vector<int> &route) const {
71 for (size_t i = 0; i < route.size(); ++i) {
72 if (route[i] == world_rank_) {
73 return static_cast<int>(i);
74 }
75 }
76 return -1;
77 }
78
79 void SabirovSHypercubeMPI::ProcessSourceNode(const std::vector<int> &route, const std::vector<int> &buffer) {
80 if (route.size() <= 1) {
81 return;
82 }
83
84 int next_node = route[1];
85 auto data_size = static_cast<int>(buffer.size());
86 MPI_Send(&data_size, 1, MPI_INT, next_node, 0, MPI_COMM_WORLD);
87
88 if (data_size > 0 && !buffer.empty()) {
89 MPI_Send(buffer.data(), data_size, MPI_INT, next_node, 1, MPI_COMM_WORLD);
90 }
91 }
92
93 [[nodiscard]] std::vector<int> SabirovSHypercubeMPI::ProcessIntermediateNode(const std::vector<int> &route,
94 int route_position, int dest) const {
95 std::vector<int> buffer;
96 int prev_node = route[route_position - 1];
97
98 // Получаем данные от предыдущего узла
99 int data_size = 0;
100 MPI_Recv(&data_size, 1, MPI_INT, prev_node, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
101
102 if (data_size > 0) {
103 buffer.resize(data_size);
104 if (!buffer.empty()) {
105 MPI_Recv(buffer.data(), data_size, MPI_INT, prev_node, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
106 }
107 }
108
109 // Если это не конечный узел, передаём дальше
110 if (route_position < static_cast<int>(route.size()) - 1) {
111 int next_node = route[route_position + 1];
112 MPI_Send(&data_size, 1, MPI_INT, next_node, 0, MPI_COMM_WORLD);
113
114 if (data_size > 0 && !buffer.empty()) {
115 MPI_Send(buffer.data(), data_size, MPI_INT, next_node, 1, MPI_COMM_WORLD);
116 }
117 }
118
119 // Если это получатель, возвращаем данные
120 if (world_rank_ == dest) {
121 return buffer;
122 }
123
124 return {};
125 }
126
127 void SabirovSHypercubeMPI::BroadcastReceivedData(int dest) {
128 // Рассылаем результат всем процессам для корректной проверки
129 int success_int = 0;
130 if (world_rank_ == dest) {
131 success_int = GetOutput().success ? 1 : 0;
132 }
133 MPI_Bcast(&success_int, 1, MPI_INT, dest, MPI_COMM_WORLD);
134 GetOutput().success = (success_int == 1);
135
136 // Рассылаем полученные данные всем процессам
137 int recv_size = 0;
138 if (world_rank_ == dest) {
139 recv_size = static_cast<int>(GetOutput().received_data.size());
140 }
141 MPI_Bcast(&recv_size, 1, MPI_INT, dest, MPI_COMM_WORLD);
142
143 if (recv_size > 0) {
144 if (world_rank_ != dest) {
145 GetOutput().received_data.resize(recv_size);
146 }
147 if (!GetOutput().received_data.empty()) {
148 MPI_Bcast(GetOutput().received_data.data(), recv_size, MPI_INT, dest, MPI_COMM_WORLD);
149 }
150 }
151 }
152
153 void SabirovSHypercubeMPI::BroadcastRoute() {
154 auto route_size = static_cast<int>(GetOutput().route.size());
155 MPI_Bcast(&route_size, 1, MPI_INT, 0, MPI_COMM_WORLD);
156
157 if (world_rank_ != 0) {
158 GetOutput().route.resize(route_size);
159 }
160 if (route_size > 0 && !GetOutput().route.empty()) {
161 MPI_Bcast(GetOutput().route.data(), route_size, MPI_INT, 0, MPI_COMM_WORLD);
162 }
163 }
164
165 std::vector<int> SabirovSHypercubeMPI::SendThroughHypercube(int source, int dest, const std::vector<int> &data) {
166 std::vector<int> result;
167 std::vector<int> route = BuildRoute(source, dest);
168 GetOutput().route = route;
169
170 // Если маршрут состоит только из одного узла (source == dest)
171 if (route.size() == 1) {
172 if (world_rank_ == source) {
173 if (!data.empty()) {
174 result.assign(data.begin(), data.end());
175 }
176 }
177 return result;
178 }
179
180 int num_nodes = 1 << dimension_;
181 int route_position = FindRoutePosition(route);
182
183 // Если процесс не участвует в маршруте или его ранг >= num_nodes, пропускаем
184 if (route_position == -1 || world_rank_ >= num_nodes) {
185 MPI_Barrier(MPI_COMM_WORLD);
186 return result;
187 }
188
189 // Обрабатываем в зависимости от роли в маршруте
190 if (world_rank_ == source) {
191 SabirovSHypercubeMPI::ProcessSourceNode(route, data);
192 } else if (route_position > 0) {
193 result = ProcessIntermediateNode(route, route_position, dest);
194 }
195
196 MPI_Barrier(MPI_COMM_WORLD);
197 return result;
198 }
199
200 bool SabirovSHypercubeMPI::RunImpl() {
201 // Синхронизация всех процессов перед началом передачи данных.
202 // Это критически важно для корректного измерения времени при
203 // многократных вызовах Run() без PreProcessing между ними (режим TaskRun).
204 MPI_Barrier(MPI_COMM_WORLD);
205
206 const auto &input = GetInput();
207
208 int source = input.source_rank;
209 int dest = input.dest_rank;
210 const auto &data = input.data;
211
212 // Передача данных через гиперкуб
213 std::vector<int> received = SendThroughHypercube(source, dest, data);
214
215 // Получатель сохраняет данные
216 if (world_rank_ == dest) {
217 if (!received.empty()) {
218 GetOutput().received_data.assign(received.begin(), received.end());
219 } else {
220 GetOutput().received_data.clear();
221 }
222 GetOutput().success = true;
223 }
224
225 // Рассылаем результаты всем процессам
226 BroadcastReceivedData(dest);
227 BroadcastRoute();
228
229 return GetOutput().success;
230 }
231
232 bool SabirovSHypercubeMPI::PostProcessingImpl() {
233 return GetOutput().success;
234 }
235
236 } // namespace sabirov_s_hypercube
237