| 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 |