GCC Code Coverage Report


Directory: ./
File: tasks/kotelnikova_a_from_all_to_one/mpi/src/ops_mpi.cpp
Date: 2026-01-27 01:59:34
Exec Total Coverage
Lines: 86 96 89.6%
Functions: 11 12 91.7%
Branches: 70 116 60.3%

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