GCC Code Coverage Report


Directory: ./
File: tasks/krymova_k_scatter/mpi/src/ops_mpi.cpp
Date: 2026-01-09 01:27:18
Exec Total Coverage
Lines: 74 75 98.7%
Functions: 7 7 100.0%
Branches: 33 52 63.5%

Line Branch Exec Source
1 #include "krymova_k_scatter/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <algorithm>
6 #include <cmath>
7 #include <cstdint>
8 #include <cstring>
9 #include <utility>
10 #include <vector>
11
12 #include "krymova_k_scatter/common/include/common.hpp"
13
14 namespace krymova_k_scatter {
15
16 30 KrymovaKScatterMPI::KrymovaKScatterMPI(const InType &in) {
17 SetTypeOfTask(GetStaticTypeOfTask());
18 30 GetInput() = in;
19 30 }
20
21 30 bool KrymovaKScatterMPI::ValidationImpl() {
22 const auto &args = GetInput();
23
24
2/4
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 30 times.
30 bool is_invalid_counts = (args.send_count <= 0) || (args.send_count != args.recv_count);
25 30 bool is_invalid_root = (args.root_rank < 0);
26
27
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (is_invalid_counts || is_invalid_root) {
28 return false;
29 }
30
31
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (args.send_type != args.recv_type) {
32 return false;
33 }
34
35
5/6
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
30 auto is_supported = [](MPI_Datatype t) { return (t == MPI_INT || t == MPI_FLOAT || t == MPI_DOUBLE); };
36
37 return is_supported(args.send_type);
38 }
39
40 30 bool KrymovaKScatterMPI::PreProcessingImpl() {
41 auto &args = GetInput();
42 30 int comm_size = 0;
43 30 MPI_Comm_size(args.comm, &comm_size);
44
45
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 20 times.
30 if (args.root_rank >= comm_size) {
46 10 args.root_rank %= comm_size;
47 }
48
49 30 return true;
50 }
51
52 namespace {
53 size_t GetTypeByteSize(MPI_Datatype type) {
54 30 MPI_Aint lb = 0;
55 30 MPI_Aint extent = 0;
56 30 MPI_Type_get_extent(type, &lb, &extent);
57 30 return static_cast<size_t>(extent);
58 }
59
60 int MapVirtToReal(int virt_rank, int root, int size) {
61 30 return (virt_rank + root) % size;
62 }
63
64 15 std::vector<uint8_t> PrepareRootData(const void *src, int size, int root, int count, size_t type_size) {
65 15 size_t chunk_bytes = static_cast<size_t>(count) * type_size;
66 15 size_t total_bytes = static_cast<size_t>(size) * chunk_bytes;
67
68
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 std::vector<uint8_t> buffer(total_bytes);
69
70 const auto *src_bytes = static_cast<const uint8_t *>(src);
71 uint8_t *dst_bytes = buffer.data();
72
73 15 size_t offset = root * chunk_bytes;
74 15 size_t tail_size = total_bytes - offset;
75
76
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 std::copy(src_bytes + offset, src_bytes + total_bytes, dst_bytes);
77
78
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 9 times.
15 std::copy(src_bytes, src_bytes + offset, dst_bytes + tail_size);
79
80 15 return buffer;
81 }
82
83 30 void ExecScatterCycle(int size, int root, int rank, int count, MPI_Datatype type, size_t type_size, MPI_Comm comm,
84 const uint8_t *&active_ptr, std::vector<uint8_t> &buffer) {
85 30 int relative_rank = (rank - root + size) % size;
86
87 int start_stride = 1;
88
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 30 times.
60 while (start_stride < size) {
89 30 start_stride <<= 1;
90 }
91 30 start_stride >>= 1;
92
93
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 30 times.
60 for (int stride = start_stride; stride > 0; stride >>= 1) {
94
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (relative_rank % stride != 0) {
95 continue;
96 }
97
98 30 bool is_sender = (relative_rank % (stride << 1) == 0);
99
100
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
30 if (is_sender) {
101 15 int virt_dest = relative_rank + stride;
102
103
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (virt_dest < size) {
104 15 int limit = std::min(virt_dest + stride, size);
105 15 int send_amt = (limit - virt_dest) * count;
106
107 15 size_t byte_shift = static_cast<size_t>(virt_dest - relative_rank) * count * type_size;
108 int real_dest = MapVirtToReal(virt_dest, root, size);
109
110 15 MPI_Send(active_ptr + byte_shift, send_amt, type, real_dest, 0, comm);
111 }
112 } else {
113 15 int virt_src = relative_rank - stride;
114 int real_src = MapVirtToReal(virt_src, root, size);
115
116 15 int limit = std::min(relative_rank + stride, size);
117 15 int recv_amt = (limit - relative_rank) * count;
118
119 15 size_t required_bytes = static_cast<size_t>(recv_amt) * type_size;
120 15 buffer.resize(required_bytes);
121
122 15 MPI_Recv(buffer.data(), recv_amt, type, real_src, 0, comm, MPI_STATUS_IGNORE);
123
124 15 active_ptr = buffer.data();
125 }
126 }
127 30 }
128
129 } // namespace
130
131 30 bool KrymovaKScatterMPI::RunImpl() {
132 auto &args = GetInput();
133
134 30 int rank = 0;
135 30 int size = 0;
136 30 MPI_Comm_rank(args.comm, &rank);
137 30 MPI_Comm_size(args.comm, &size);
138
139 30 size_t type_size = GetTypeByteSize(args.recv_type);
140
141 30 std::vector<uint8_t> internal_buf;
142 30 const uint8_t *active_data_ptr = nullptr;
143
144
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
30 if (rank == args.root_rank) {
145
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
30 internal_buf = PrepareRootData(args.src_buffer, size, args.root_rank, args.recv_count, type_size);
146 15 active_data_ptr = internal_buf.data();
147 }
148
149
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 ExecScatterCycle(size, args.root_rank, rank, args.recv_count, args.recv_type, type_size, args.comm, active_data_ptr,
150 internal_buf);
151
152
2/4
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
30 if (args.dst_buffer != MPI_IN_PLACE && active_data_ptr != nullptr) {
153
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 size_t bytes_to_copy = args.recv_count * type_size;
154 auto *user_dst = static_cast<uint8_t *>(args.dst_buffer);
155 std::copy(active_data_ptr, active_data_ptr + bytes_to_copy, user_dst);
156 }
157
158 30 size_t result_bytes = args.recv_count * type_size;
159
1/4
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
30 std::vector<uint8_t> result(result_bytes);
160
161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 const void *final_src = (args.dst_buffer != nullptr) ? args.dst_buffer : active_data_ptr;
162
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 if (final_src != nullptr) {
163 const auto *ptr = static_cast<const uint8_t *>(final_src);
164 30 std::copy(ptr, ptr + result_bytes, result.begin());
165 }
166
167 GetOutput() = std::move(result);
168 30 return true;
169 }
170
171 30 bool KrymovaKScatterMPI::PostProcessingImpl() {
172 30 return true;
173 }
174
175 } // namespace krymova_k_scatter
176