GCC Code Coverage Report


Directory: ./
File: tasks/nikitin_a_vec_sign_rotation/mpi/src/ops_mpi.cpp
Date: 2026-01-10 02:40:41
Exec Total Coverage
Lines: 64 65 98.5%
Functions: 7 8 87.5%
Branches: 40 50 80.0%

Line Branch Exec Source
1 #include "nikitin_a_vec_sign_rotation/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <algorithm>
6 #include <vector>
7
8 #include "nikitin_a_vec_sign_rotation/common/include/common.hpp"
9
10 namespace nikitin_a_vec_sign_rotation {
11
12
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 NikitinAVecSignRotationMPI::NikitinAVecSignRotationMPI(const InType &in) {
13 SetTypeOfTask(GetStaticTypeOfTask());
14
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 GetInput() = in;
15 32 GetOutput() = 0;
16 32 }
17
18 32 bool NikitinAVecSignRotationMPI::ValidationImpl() {
19 32 return true;
20 }
21
22 32 bool NikitinAVecSignRotationMPI::PreProcessingImpl() {
23 32 return true;
24 }
25
26 32 bool NikitinAVecSignRotationMPI::RunImpl() {
27 // Получаем общее количество процессов в MPI коммуникаторе
28 32 int process_count = 0;
29 32 MPI_Comm_size(MPI_COMM_WORLD, &process_count);
30
31 // Получаем ранг (номер) текущего процесса
32 32 int process_rank = 0;
33 32 MPI_Comm_rank(MPI_COMM_WORLD, &process_rank);
34
35 // Вектор для хранения входных данных (заполняется только процессом 0)
36 32 std::vector<double> vector_data;
37
38 // Инициализируем выходное значение для некорневых процессов
39
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
32 if (process_rank != 0) {
40 16 GetOutput() = -1;
41 }
42
43 // Только процесс 0 получает входные данные
44
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
32 if (process_rank == 0) {
45
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 vector_data = GetInput();
46 // Корректируем количество процессов, если их больше чем элементов в векторе
47 16 process_count = std::min<int>(process_count, static_cast<int>(vector_data.size()));
48 }
49
50 // Рассылаем актуальное количество процессов всем участникам
51
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 MPI_Bcast(&process_count, 1, MPI_INT, 0, MPI_COMM_WORLD);
52
53 // Если вектор пустой или процессов нет - завершаем работу
54
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2 times.
32 if (process_count == 0) {
55 return true;
56 }
57
58 // Распределяем работу между процессами
59
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
30 if (process_rank == 0) {
60 // Главный процесс распределяет данные и собирает результаты
61
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 GetOutput() = MainProcess(process_count, vector_data);
62
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1 times.
15 } else if (process_rank < process_count) {
63 // Рабочие процессы получают данные и вычисляют свою часть
64
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 WorkerProcess();
65 }
66
67 return true;
68 }
69
70 32 bool NikitinAVecSignRotationMPI::PostProcessingImpl() {
71 32 return true;
72 }
73
74 // Аналогична методу из SEQ версии
75 bool NikitinAVecSignRotationMPI::IsSignChange(double first_value, double second_value) {
76 194 const bool first_negative = first_value < 0.0;
77 194 const bool second_non_negative = second_value >= 0.0;
78 194 const bool first_non_negative = first_value >= 0.0;
79 194 const bool second_negative = second_value < 0.0;
80
81
4/6
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 60 times.
✓ Branch 3 taken 27 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
194 return (first_negative && second_non_negative) || (first_non_negative && second_negative);
82 }
83
84 15 int NikitinAVecSignRotationMPI::MainProcess(int process_count, const std::vector<double> &vector_data) {
85 int total_swaps = 0;
86 15 const int data_size = static_cast<int>(vector_data.size());
87
88 // Вычисляем базовый размер части для каждого процесса
89 15 const int base_chunk_size = data_size / process_count;
90 15 int remainder = data_size % process_count; // Остаток элементов для распределения
91
92 int start_index = 0; // Начальный индекс для распределения данных
93
94 // Распределяем данные между рабочими процессами (rank 1, 2, ...)
95
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 15 times.
29 for (int worker = 1; worker < process_count; worker++) {
96 14 int chunk_size = base_chunk_size;
97
98 // Распределяем остаточные элементы по одному первым процессам
99
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 8 times.
14 if (remainder > 0) {
100 6 chunk_size++;
101 6 remainder--;
102 }
103
104 // Добавляем один элемент справа для проверки чередования на границе блоков
105 14 chunk_size++;
106
107 // Отправляем размер части рабочему процессу
108 14 MPI_Send(&chunk_size, 1, MPI_INT, worker, 0, MPI_COMM_WORLD);
109
110 // Отправляем сами данные рабочему процессу
111 14 MPI_Send(vector_data.data() + start_index, chunk_size, MPI_DOUBLE, worker, 0, MPI_COMM_WORLD);
112
113 // Сдвигаем начальный индекс для следующего блока (минус перекрывающий элемент)
114 14 start_index += chunk_size - 1;
115 }
116
117 // Главный процесс обрабатывает оставшуюся часть данных
118 const int master_chunk_size = data_size - start_index;
119
2/2
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 15 times.
102 for (int i = start_index + 1; i < start_index + master_chunk_size; i++) {
120
4/4
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 32 times.
87 if (IsSignChange(vector_data[i - 1], vector_data[i])) {
121 55 total_swaps++;
122 }
123 }
124
125 // Собираем результаты от всех рабочих процессов
126
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 15 times.
29 for (int worker = 1; worker < process_count; worker++) {
127 14 int worker_swaps = 0;
128 14 MPI_Recv(&worker_swaps, 1, MPI_INT, worker, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
129 14 total_swaps += worker_swaps;
130 }
131
132 15 return total_swaps;
133 }
134
135 14 void NikitinAVecSignRotationMPI::WorkerProcess() {
136 14 int chunk_size = 0;
137
138 // Получаем размер своей части данных от главного процесса
139 14 MPI_Recv(&chunk_size, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
140
141 // Выделяем память для локальных данных
142 14 std::vector<double> local_data(chunk_size);
143
144 // Получаем свои данные от главного процесса
145
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 MPI_Recv(local_data.data(), chunk_size, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
146
147 // Подсчитываем количество чередований знаков в своей части
148 14 int local_swaps = 0;
149
2/2
✓ Branch 0 taken 107 times.
✓ Branch 1 taken 14 times.
121 for (int i = 1; i < chunk_size; i++) {
150
4/4
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 35 times.
✓ Branch 3 taken 42 times.
107 if (IsSignChange(local_data[i - 1], local_data[i])) {
151 65 local_swaps++;
152 }
153 }
154
155 // Отправляем результат обратно главному процессу
156
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 MPI_Send(&local_swaps, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
157 14 }
158
159 } // namespace nikitin_a_vec_sign_rotation
160