| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "kotelnikova_a_from_all_to_one/mpi/include/ops_mpi.hpp" | ||
| 2 | |||
| 3 | #include <mpi.h> | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cstring> | ||
| 7 | #include <stdexcept> | ||
| 8 | #include <variant> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "kotelnikova_a_from_all_to_one/common/include/common.hpp" | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | 30 | void PerformOperationImpl(void *inbuf, void *inoutbuf, int count, MPI_Datatype datatype) { | |
| 15 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 24 times.
|
30 | if (datatype == MPI_INT) { |
| 16 | auto *in = static_cast<int *>(inbuf); | ||
| 17 | auto *inout = static_cast<int *>(inoutbuf); | ||
| 18 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 6 times.
|
36 | for (int i = 0; i < count; i++) { |
| 19 | 30 | inout[i] += in[i]; | |
| 20 | } | ||
| 21 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 16 times.
|
24 | } else if (datatype == MPI_FLOAT) { |
| 22 | auto *in = static_cast<float *>(inbuf); | ||
| 23 | auto *inout = static_cast<float *>(inoutbuf); | ||
| 24 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 8 times.
|
48 | for (int i = 0; i < count; i++) { |
| 25 | 40 | inout[i] += in[i]; | |
| 26 | } | ||
| 27 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | } else if (datatype == MPI_DOUBLE) { |
| 28 | auto *in = static_cast<double *>(inbuf); | ||
| 29 | auto *inout = static_cast<double *>(inoutbuf); | ||
| 30 |
2/2✓ Branch 0 taken 2068 times.
✓ Branch 1 taken 16 times.
|
2084 | for (int i = 0; i < count; i++) { |
| 31 | 2068 | inout[i] += in[i]; | |
| 32 | } | ||
| 33 | } else { | ||
| 34 | ✗ | throw std::runtime_error("Unsupported datatype"); | |
| 35 | } | ||
| 36 | 30 | } | |
| 37 | } // namespace | ||
| 38 | |||
| 39 | namespace kotelnikova_a_from_all_to_one { | ||
| 40 | |||
| 41 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | KotelnikovaAFromAllToOneMPI::KotelnikovaAFromAllToOneMPI(const InType &in) { |
| 42 | SetTypeOfTask(GetStaticTypeOfTask()); | ||
| 43 | GetInput() = in; | ||
| 44 | |||
| 45 | 64 | int rank = 0; | |
| 46 | 64 | int mpi_initialized = 0; | |
| 47 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | MPI_Initialized(&mpi_initialized); |
| 48 | |||
| 49 |
1/2✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
|
64 | if (mpi_initialized != 0) { |
| 50 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); |
| 51 | } | ||
| 52 | |||
| 53 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 32 times.
|
64 | if (rank == 0) { |
| 54 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 26 times.
|
32 | if (std::holds_alternative<std::vector<int>>(in)) { |
| 55 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | auto vec = std::get<std::vector<int>>(in); |
| 56 |
2/6✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
12 | GetOutput() = InTypeVariant{std::vector<int>(vec.size(), 0)}; |
| 57 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 18 times.
|
26 | } else if (std::holds_alternative<std::vector<float>>(in)) { |
| 58 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | auto vec = std::get<std::vector<float>>(in); |
| 59 |
2/6✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
16 | GetOutput() = InTypeVariant{std::vector<float>(vec.size(), 0.0F)}; |
| 60 |
1/2✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
|
18 | } else if (std::holds_alternative<std::vector<double>>(in)) { |
| 61 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | auto vec = std::get<std::vector<double>>(in); |
| 62 |
3/6✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
36 | GetOutput() = InTypeVariant{std::vector<double>(vec.size(), 0.0)}; |
| 63 | } else { | ||
| 64 | ✗ | throw std::runtime_error("Unsupported data type"); | |
| 65 | } | ||
| 66 | } | ||
| 67 | 64 | } | |
| 68 | |||
| 69 | 64 | bool KotelnikovaAFromAllToOneMPI::ValidationImpl() { | |
| 70 | 64 | return true; | |
| 71 | } | ||
| 72 | |||
| 73 | 64 | bool KotelnikovaAFromAllToOneMPI::PreProcessingImpl() { | |
| 74 | 64 | return true; | |
| 75 | } | ||
| 76 | |||
| 77 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | bool KotelnikovaAFromAllToOneMPI::RunImpl() { |
| 78 | try { | ||
| 79 | auto input = GetInput(); | ||
| 80 | 64 | int rank = 0; | |
| 81 | int root = 0; | ||
| 82 |
1/2✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
|
64 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); |
| 83 | |||
| 84 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 52 times.
|
64 | if (std::holds_alternative<std::vector<int>>(input)) { |
| 85 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | return ProcessVector<int>(input, rank, root, MPI_INT); |
| 86 | } | ||
| 87 | |||
| 88 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 36 times.
|
52 | if (std::holds_alternative<std::vector<float>>(input)) { |
| 89 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | return ProcessVector<float>(input, rank, root, MPI_FLOAT); |
| 90 | } | ||
| 91 | |||
| 92 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | if (std::holds_alternative<std::vector<double>>(input)) { |
| 93 |
1/2✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
|
36 | return ProcessVector<double>(input, rank, root, MPI_DOUBLE); |
| 94 | } | ||
| 95 | |||
| 96 | return false; | ||
| 97 | ✗ | } catch (...) { | |
| 98 | return false; | ||
| 99 | ✗ | } | |
| 100 | } | ||
| 101 | |||
| 102 | template <typename T> | ||
| 103 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
|
128 | bool KotelnikovaAFromAllToOneMPI::ProcessVector(const InType &input, int rank, int root, MPI_Datatype mpi_type) { |
| 104 | auto &original_data = std::get<std::vector<T>>(input); | ||
| 105 | |||
| 106 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 4 times.
|
128 | if (original_data.empty()) { |
| 107 | return true; | ||
| 108 | } | ||
| 109 | |||
| 110 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 30 times.
|
120 | if (rank == root) { |
| 111 | auto &output_variant = GetOutput(); | ||
| 112 | auto &result_data = std::get<std::vector<T>>(output_variant); | ||
| 113 | std::ranges::copy(original_data, result_data.begin()); | ||
| 114 | 60 | CustomReduce(result_data.data(), result_data.data(), static_cast<int>(original_data.size()), mpi_type, MPI_SUM, | |
| 115 | MPI_COMM_WORLD, root); | ||
| 116 | } else { | ||
| 117 | 60 | std::vector<T> send_buffer = original_data; | |
| 118 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
60 | CustomReduce(send_buffer.data(), nullptr, static_cast<int>(original_data.size()), mpi_type, MPI_SUM, MPI_COMM_WORLD, |
| 119 | root); | ||
| 120 | } | ||
| 121 | return true; | ||
| 122 | } | ||
| 123 | |||
| 124 | 60 | void KotelnikovaAFromAllToOneMPI::CustomReduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, | |
| 125 | MPI_Op op, MPI_Comm comm, int root) { | ||
| 126 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
|
60 | if (count == 0) { |
| 127 | ✗ | MPI_Barrier(comm); | |
| 128 | ✗ | return; | |
| 129 | } | ||
| 130 | |||
| 131 | 60 | int size = 0; | |
| 132 | 60 | MPI_Comm_size(comm, &size); | |
| 133 | |||
| 134 | 60 | int rank = 0; | |
| 135 | 60 | MPI_Comm_rank(comm, &rank); | |
| 136 | |||
| 137 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 30 times.
|
60 | if (rank == root) { |
| 138 | 30 | TreeReduce(sendbuf, recvbuf, count, datatype, op, comm, root); | |
| 139 | } else { | ||
| 140 | 30 | int type_size = 0; | |
| 141 | 30 | MPI_Type_size(datatype, &type_size); | |
| 142 | 30 | size_t total_bytes = static_cast<size_t>(count) * static_cast<size_t>(type_size); | |
| 143 |
1/2✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
|
30 | std::vector<unsigned char> temp_buf(total_bytes); |
| 144 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | TreeReduce(sendbuf, temp_buf.data(), count, datatype, op, comm, root); |
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | 60 | void KotelnikovaAFromAllToOneMPI::TreeReduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, | |
| 149 | MPI_Comm comm, int root) { | ||
| 150 | 60 | int size = 0; | |
| 151 | 60 | MPI_Comm_size(comm, &size); | |
| 152 | |||
| 153 | 60 | int rank = 0; | |
| 154 | 60 | MPI_Comm_rank(comm, &rank); | |
| 155 | |||
| 156 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
|
60 | if (count == 0) { |
| 157 | ✗ | MPI_Barrier(comm); | |
| 158 | ✗ | return; | |
| 159 | } | ||
| 160 | |||
| 161 |
1/2✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
|
60 | if (op != MPI_SUM) { |
| 162 | return; | ||
| 163 | } | ||
| 164 | |||
| 165 | 60 | int type_size = 0; | |
| 166 | 60 | MPI_Type_size(datatype, &type_size); | |
| 167 | 60 | size_t total_bytes = static_cast<size_t>(count) * static_cast<size_t>(type_size); | |
| 168 | |||
| 169 | 60 | std::vector<unsigned char> local_buf(total_bytes); | |
| 170 | std::memcpy(local_buf.data(), sendbuf, total_bytes); | ||
| 171 | |||
| 172 | int depth = 0; | ||
| 173 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
|
120 | while ((1 << depth) < size) { |
| 174 | 60 | depth++; | |
| 175 | } | ||
| 176 | |||
| 177 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 30 times.
|
90 | for (int level = 0; level < depth; level++) { |
| 178 | 60 | int mask = 1 << level; | |
| 179 | 60 | int partner = rank ^ mask; | |
| 180 | |||
| 181 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
|
60 | if (partner >= size) { |
| 182 | ✗ | continue; | |
| 183 | } | ||
| 184 | |||
| 185 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 30 times.
|
60 | if ((rank & mask) == 0) { |
| 186 | if (partner < size) { | ||
| 187 |
2/6✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
30 | std::vector<unsigned char> recv_buf(total_bytes); |
| 188 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | MPI_Recv(recv_buf.data(), count, datatype, partner, 0, comm, MPI_STATUS_IGNORE); |
| 189 | PerformOperation(recv_buf.data(), local_buf.data(), count, datatype); | ||
| 190 | } | ||
| 191 | } else { | ||
| 192 |
1/2✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
|
30 | MPI_Send(local_buf.data(), count, datatype, partner, 0, comm); |
| 193 | break; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 |
3/4✓ Branch 0 taken 30 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
|
60 | if (rank == root && recvbuf != nullptr) { |
| 198 | std::memcpy(recvbuf, local_buf.data(), total_bytes); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | ✗ | void KotelnikovaAFromAllToOneMPI::PerformOperation(void *inbuf, void *inoutbuf, int count, MPI_Datatype datatype) { | |
| 203 |
1/2✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
|
30 | PerformOperationImpl(inbuf, inoutbuf, count, datatype); |
| 204 | 30 | } | |
| 205 | |||
| 206 | 64 | bool KotelnikovaAFromAllToOneMPI::PostProcessingImpl() { | |
| 207 | 64 | return true; | |
| 208 | } | ||
| 209 | |||
| 210 | template bool KotelnikovaAFromAllToOneMPI::ProcessVector<int>(const InType &input, int rank, int root, | ||
| 211 | MPI_Datatype mpi_type); | ||
| 212 | template bool KotelnikovaAFromAllToOneMPI::ProcessVector<float>(const InType &input, int rank, int root, | ||
| 213 | MPI_Datatype mpi_type); | ||
| 214 | template bool KotelnikovaAFromAllToOneMPI::ProcessVector<double>(const InType &input, int rank, int root, | ||
| 215 | MPI_Datatype mpi_type); | ||
| 216 | |||
| 217 | } // namespace kotelnikova_a_from_all_to_one | ||
| 218 |