GCC Code Coverage Report


Directory: ./
File: tasks/tsarkov_k_hypercube/mpi/src/ops_mpi.cpp
Date: 2026-02-23 23:20:07
Exec Total Coverage
Lines: 88 90 97.8%
Functions: 11 11 100.0%
Branches: 42 60 70.0%

Line Branch Exec Source
1 #include "tsarkov_k_hypercube/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <cstddef>
6 #include <cstdint>
7 #include <utility>
8 #include <vector>
9
10 #include "tsarkov_k_hypercube/common/include/common.hpp"
11
12 namespace tsarkov_k_hypercube {
13 namespace {
14
15 [[nodiscard]] bool IsPowerOfTwo(const int value) {
16 14 if (value <= 0) {
17 return false;
18 }
19 14 return (value & (value - 1)) == 0;
20 }
21
22 [[nodiscard]] int CalcDimensions(const int world_size) {
23 int dimensions = 0;
24 int tmp_size = world_size;
25
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 while (tmp_size > 1) {
26 2 tmp_size >>= 1;
27 2 dimensions++;
28 }
29 return dimensions;
30 }
31
32 [[nodiscard]] bool HasValidInputShape(const InType &input_data) {
33 return input_data.size() == 3U;
34 }
35
36 1 [[nodiscard]] std::vector<std::int32_t> CreatePayload(const int data_size) {
37 1 std::vector<std::int32_t> payload(static_cast<std::size_t>(data_size));
38
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (std::size_t index = 0; index < payload.size(); index++) {
39 4 payload[index] = static_cast<std::int32_t>(index);
40 }
41 1 return payload;
42 }
43
44 [[nodiscard]] bool IsWorldAndRanksOk(const int world_size, const int source_rank, const int destination_rank) {
45
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 if (!IsPowerOfTwo(world_size) || world_size <= 1) {
46 return false;
47 }
48
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (source_rank < 0 || destination_rank < 0) {
49 return false;
50 }
51
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 if (source_rank >= world_size || destination_rank >= world_size) {
52 return false;
53 }
54 return true;
55 }
56
57 2 void SendrecvCounts(MPI_Comm comm, const int partner_rank, const int tag, const int send_count, int *recv_count) {
58 2 MPI_Status status{};
59 2 MPI_Sendrecv(&send_count, 1, MPI_INT, partner_rank, tag, recv_count, 1, MPI_INT, partner_rank, tag, comm, &status);
60 2 }
61
62 void SendrecvPayload(MPI_Comm comm, const int partner_rank, const int tag, const std::int32_t *send_ptr,
63 const int send_count, std::int32_t *recv_ptr, const int recv_count) {
64 2 MPI_Status status{};
65 2 MPI_Sendrecv(send_ptr, send_count, MPI_INT, partner_rank, tag, recv_ptr, recv_count, MPI_INT, partner_rank, tag, comm,
66 &status);
67 }
68
69 2 void InitPayloadIfSource(const int world_rank, const int source_rank, const int data_size,
70 std::vector<std::int32_t> *payload_buffer, bool *has_payload) {
71
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (world_rank != source_rank) {
72 return;
73 }
74 1 *payload_buffer = CreatePayload(data_size);
75 1 *has_payload = true;
76 }
77
78 2 [[nodiscard]] bool RouteOneDimension(const int world_rank, const int destination_rank, const int dim_index,
79 std::vector<std::int32_t> *payload_buffer, bool *has_payload) {
80 2 const int bit_mask = (1 << dim_index);
81 2 const int color_value = world_rank & ~bit_mask;
82
83 2 MPI_Comm dim_comm = MPI_COMM_NULL;
84 2 MPI_Comm_split(MPI_COMM_WORLD, color_value, world_rank, &dim_comm);
85
86 2 int dim_rank = 0;
87 2 int dim_size = 0;
88 2 MPI_Comm_rank(dim_comm, &dim_rank);
89 2 MPI_Comm_size(dim_comm, &dim_size);
90
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (dim_size != 2) {
92 MPI_Comm_free(&dim_comm);
93 return false;
94 }
95
96 2 const int partner_dim_rank = 1 - dim_rank;
97
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 const bool should_send = (*has_payload) && (((world_rank ^ destination_rank) & bit_mask) != 0);
98
99 1 const int send_count = should_send ? static_cast<int>(payload_buffer->size()) : 0;
100 2 int recv_count = 0;
101
102 2 SendrecvCounts(dim_comm, partner_dim_rank, 1000 + dim_index, send_count, &recv_count);
103
104 2 std::vector<std::int32_t> recv_buffer{};
105
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (recv_count > 0) {
106
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 recv_buffer.resize(static_cast<std::size_t>(recv_count));
107 }
108
109
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 const std::int32_t *send_ptr = (send_count > 0) ? payload_buffer->data() : nullptr;
110
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 std::int32_t *recv_ptr = (recv_count > 0) ? recv_buffer.data() : nullptr;
111
112
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 SendrecvPayload(dim_comm, partner_dim_rank, 2000 + dim_index, send_ptr, send_count, recv_ptr, recv_count);
113
114
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (should_send) {
115 payload_buffer->clear();
116 1 *has_payload = false;
117 }
118
119
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (recv_count > 0) {
120 *payload_buffer = std::move(recv_buffer);
121 1 *has_payload = true;
122 }
123
124
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 MPI_Comm_free(&dim_comm);
125 return true;
126 }
127
128 2 [[nodiscard]] int FinalizeRoutedSize(const int world_rank, const int destination_rank, const bool has_payload,
129 const std::vector<std::int32_t> &payload_buffer) {
130 2 int routed_size = 0;
131
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (world_rank == destination_rank) {
132
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 routed_size = has_payload ? static_cast<int>(payload_buffer.size()) : 0;
133 }
134 2 MPI_Bcast(&routed_size, 1, MPI_INT, destination_rank, MPI_COMM_WORLD);
135 2 return routed_size;
136 }
137
138 2 [[nodiscard]] int RouteHypercubeAndGetSize(const int world_rank, const int world_size, const int source_rank,
139 const int destination_rank, const int data_size) {
140 const int dimensions = CalcDimensions(world_size);
141
142 2 std::vector<std::int32_t> payload_buffer{};
143 2 bool has_payload = false;
144
145
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 InitPayloadIfSource(world_rank, source_rank, data_size, &payload_buffer, &has_payload);
146
147
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for (int dim_index = 0; dim_index < dimensions; dim_index++) {
148
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const bool ok = RouteOneDimension(world_rank, destination_rank, dim_index, &payload_buffer, &has_payload);
149
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!ok) {
150 return data_size;
151 }
152 }
153
154
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 return FinalizeRoutedSize(world_rank, destination_rank, has_payload, payload_buffer);
155 }
156
157 } // namespace
158
159
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 TsarkovKHypercubeMPI::TsarkovKHypercubeMPI(const InType &in) {
160 SetTypeOfTask(GetStaticTypeOfTask());
161
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 GetInput() = in;
162 14 GetOutput() = 0;
163 14 }
164
165
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 bool TsarkovKHypercubeMPI::ValidationImpl() {
166 const InType &input_data = GetInput();
167
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (!HasValidInputShape(input_data)) {
168 return false;
169 }
170 14 return input_data[2] >= 0;
171 }
172
173 14 bool TsarkovKHypercubeMPI::PreProcessingImpl() {
174 14 GetOutput() = 0;
175 14 return true;
176 }
177
178 14 bool TsarkovKHypercubeMPI::RunImpl() {
179 14 int world_rank = 0;
180 14 int world_size = 0;
181 14 MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
182 14 MPI_Comm_size(MPI_COMM_WORLD, &world_size);
183
184 const InType &input_data = GetInput();
185 14 const int source_rank = input_data[0];
186 14 const int destination_rank = input_data[1];
187 14 const int data_size = input_data[2];
188
189
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (!IsWorldAndRanksOk(world_size, source_rank, destination_rank)) {
190 12 GetOutput() = data_size;
191 12 return true;
192 }
193
194 2 GetOutput() = RouteHypercubeAndGetSize(world_rank, world_size, source_rank, destination_rank, data_size);
195 2 return true;
196 }
197
198 14 bool TsarkovKHypercubeMPI::PostProcessingImpl() {
199 14 return true;
200 }
201
202 } // namespace tsarkov_k_hypercube
203