YAC 3.21.0
Yet Another Coupler
Loading...
Searching...
No Matches
dist_merge.c
Go to the documentation of this file.
1// Copyright (c) 2024 The YAC Authors
2//
3// SPDX-License-Identifier: BSD-3-Clause
4
5#include <string.h>
6#include <limits.h>
7
8#include <mpi.h>
9
10#include "dist_merge.h"
11#include "utils_common.h"
12
13static size_t get_pack_size(
14 size_t count, unsigned char * array, size_t element_size, MPI_Comm comm,
15 size_t(*element_get_pack_size)(void * element, MPI_Comm comm)) {
16
17 if (count == 0) return 0;
18
19 int count_pack_size;
20 yac_mpi_call(MPI_Pack_size(1, MPI_INT, comm, &count_pack_size), comm);
21
22 size_t array_pack_size = 0;
23 for (size_t i = 0; i < count; ++i)
24 array_pack_size +=
25 element_get_pack_size((void*)(array + i * element_size), comm);
26
27 return (size_t)count_pack_size + array_pack_size;
28}
29
30static void pack(
31 size_t count, unsigned char * array, size_t element_size, void * buffer,
32 int buffer_size, int * position, MPI_Comm comm,
33 void(*element_pack)(
34 void * element, void * buffer, int buffer_size,
35 int * position, MPI_Comm)) {
36
37 int count_int = (int)count;
39 MPI_Pack(
40 &count_int, 1, MPI_INT, buffer, buffer_size, position, comm), comm);
41
42 for (size_t i = 0; i < count; ++i)
43 element_pack(
44 (void*)(array + i * element_size), buffer, buffer_size, position, comm);
45}
46
47static void unpack(
48 void * buffer, int buffer_size, int * position, size_t * count,
49 unsigned char ** array, size_t element_size, MPI_Comm comm,
50 void(*element_unpack)(
51 void * buffer, int buffer_size, int * position,
52 void * element, MPI_Comm comm)) {
53
54 int count_int;
56 MPI_Unpack(
57 buffer, buffer_size, position, &count_int, 1, MPI_INT, comm), comm);
58
59 *count = (size_t)count_int;
60 *array = xmalloc(*count * element_size);
61
62 for (size_t i = 0; i < *count; ++i)
63 element_unpack(
64 buffer, buffer_size, position, (void*)(*array + i * element_size), comm);
65}
66
68 size_t * count, void ** array, size_t element_size, MPI_Comm comm,
69 struct yac_dist_merge_vtable * vtable, size_t ** idx_old_to_new) {
70
71 int rank;
72 MPI_Comm_rank(comm, &rank);
73
74 // initialise
75 unsigned char * input = *array;
76 size_t input_len = *count;
77 size_t* idx = NULL;
78 if (idx_old_to_new) {
79 *idx_old_to_new = xmalloc(input_len * sizeof(**idx_old_to_new));
80 idx = xmalloc(input_len * sizeof(*idx));
81 for(size_t i = 0;i<input_len;++i) idx[i] = i;
82 }
83 unsigned char * arr_new = NULL;
84 size_t len_new = 0;
85
86 YAC_ASSERT(input_len < INT_MAX, "too many elements");
87
88 // sort input data so that the processing order on all processes is identical
89 if (idx)
90 yac_qsort_index(input, input_len, element_size, vtable->compare, idx);
91 else
92 qsort((void*)input, input_len, element_size, vtable->compare);
93
94 // loop until no process has any unsychronised elements left
95 void * buffer = NULL;
96 while(1){
97
98 // determine pack size of all remaining local data
99 size_t pack_size =
101 input_len, input, element_size, comm, vtable->get_pack_size);
102 YAC_ASSERT(pack_size <= LONG_MAX, "packing size too big");
103
104 // determine rank with most amount of data
105 struct {
106 long pack_size;
107 int rank;
108 } data_pair = {.pack_size = (long)pack_size, .rank = rank};
110 MPI_Allreduce(
111 MPI_IN_PLACE, &data_pair, 1, MPI_LONG_INT, MPI_MAXLOC, comm), comm);
112
113 // if there is no more data to exchange, the sychronisation is finished
114 if (data_pair.pack_size == 0) break;
115
116 // allocate the buffer according to the processes with the most amount
117 // of unsychronised data
118 pack_size = (size_t)data_pair.pack_size;
119 if (!buffer) buffer = xmalloc(pack_size);
120 int position = 0;
121
122 // the process with the most amount of data packs it
123 if(data_pair.rank == rank)
124 pack(
125 input_len, input, element_size, buffer, pack_size, &position, comm,
126 vtable->pack);
127
128 // broadcast and unpack data (this only contains the basic information
129 // required to identify an element)
131 MPI_Bcast(buffer, pack_size, MPI_PACKED, data_pair.rank, comm), comm);
132 unsigned char * recved = NULL;
133 size_t num_recved;
134 position = 0;
135 unpack(
136 buffer, pack_size, &position, &num_recved, &recved, element_size, comm,
137 vtable->unpack);
138
139 // copy the received elements into the result array
140 arr_new = xrealloc(arr_new, (len_new + num_recved)*element_size);
141 memcpy(
142 arr_new + len_new*element_size, (void*)recved, num_recved*element_size);
143 free(recved);
144
145 // merge received elements into the result array
146 size_t input_idx, input_len_new, i = len_new;
147 len_new += num_recved;
148 for(input_idx = 0, input_len_new = 0; i < len_new; ++i) {
149 void* recved_element = arr_new + i*element_size;
150
151 // search for matching element in input list until an element is
152 // found, which is "bigger" (as defined by the compare function) or the
153 // end of the input list was reached
154 int cmp = 0;
155 void * input_element = NULL;
156 while ((input_idx < input_len) &&
157 (((cmp =
158 vtable->compare(
159 ((input_element = input + input_idx * element_size)),
160 recved_element))) < 0)) {
161
162 // elements from the input list that matched with a received element
163 // are removed from the input list
164 if (input_idx != input_len_new) {
165 memcpy(
166 input + input_len_new * element_size,
167 input_element, element_size);
168 if (idx) idx[input_len_new] = idx[input_idx];
169 }
170 ++input_len_new;
171 ++input_idx;
172 }
173
174 // if the end of the local input list was reached, no more merging is required
175 if (input_idx == input_len) break;
176
177 // if a matching element was found in the input list
178 if (!cmp) {
179
180 // merge input list element with received element
181 if (vtable->merge)
182 vtable->merge(recved_element, input_element, comm);
183 // free the element from the input list
184 vtable->free_data(input_element);
185 // keep track on where the original element is in the result list
186 if (idx_old_to_new) (*idx_old_to_new)[idx[input_idx]] = i;
187 // upate input list idx
188 input_idx++;
189
190 // if no matching element was found in the input list
191 } else if (vtable->merge) {
192 // since the merge operation can potentially be collective, we have
193 // to call it, even if no matching element was found
194 vtable->merge(recved_element, NULL, comm);
195 }
196 }
197
198 // if the end of the input list was reched:
199 // process remaining received elements
200 if (vtable->merge)
201 for(; i < len_new; ++i)
202 vtable->merge(arr_new + i*element_size, NULL, comm);
203
204 // if all received elements where processed:
205 // compress remaining elements in input list
206 for (; input_idx < input_len; ++input_idx, ++input_len_new) {
207 if (input_idx != input_len_new) {
208 memcpy(
209 input + input_len_new * element_size,
210 input + input_idx * element_size, element_size);
211 if (idx) idx[input_len_new] = idx[input_idx];
212 }
213 }
214 // update length of input list
215 input_len = input_len_new;
216 }
217 free(buffer);
218 free(input);
219 free(idx);
220 *array = arr_new;
221 *count = len_new;
222}
#define YAC_ASSERT(exp, msg)
static void pack(size_t count, unsigned char *array, size_t element_size, void *buffer, int buffer_size, int *position, MPI_Comm comm, void(*element_pack)(void *element, void *buffer, int buffer_size, int *position, MPI_Comm))
Definition dist_merge.c:30
void yac_dist_merge(size_t *count, void **array, size_t element_size, MPI_Comm comm, struct yac_dist_merge_vtable *vtable, size_t **idx_old_to_new)
Definition dist_merge.c:67
static void unpack(void *buffer, int buffer_size, int *position, size_t *count, unsigned char **array, size_t element_size, MPI_Comm comm, void(*element_unpack)(void *buffer, int buffer_size, int *position, void *element, MPI_Comm comm))
Definition dist_merge.c:47
static size_t get_pack_size(size_t count, unsigned char *array, size_t element_size, MPI_Comm comm, size_t(*element_get_pack_size)(void *element, MPI_Comm comm))
Definition dist_merge.c:13
#define xrealloc(ptr, size)
Definition ppm_xfuncs.h:67
#define xmalloc(size)
Definition ppm_xfuncs.h:66
void yac_qsort_index(void *a_, size_t count, size_t size, int(*compare)(void const *, void const *), size_t *idx)
Definition quicksort.c:185
void(* unpack)(void *buffer, int buffer_size, int *position, void *element, MPI_Comm comm)
Definition dist_merge.h:32
int(* compare)(void const *a, void const *b)
Definition dist_merge.h:39
void(* free_data)(void *element)
Definition dist_merge.h:61
void(* merge)(void *to, void *from, MPI_Comm comm)
Definition dist_merge.h:56
size_t(* get_pack_size)(void *element, MPI_Comm comm)
Definition dist_merge.h:20
void(* pack)(void *element, void *buffer, int buffer_size, int *position, MPI_Comm)
Definition dist_merge.h:25
double * buffer
#define yac_mpi_call(call, comm)