Yet Another eXchange Tool 0.11.1
Loading...
Searching...
No Matches
xt_mpi.c
Go to the documentation of this file.
1
12/*
13 * Keywords:
14 * Maintainer: Jörg Behrens <behrens@dkrz.de>
15 * Moritz Hanke <hanke@dkrz.de>
16 * Thomas Jahns <jahns@dkrz.de>
17 * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yaxt/
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, are permitted provided that the following conditions are
21 * met:
22 *
23 * Redistributions of source code must retain the above copyright notice,
24 * this list of conditions and the following disclaimer.
25 *
26 * Redistributions in binary form must reproduce the above copyright
27 * notice, this list of conditions and the following disclaimer in the
28 * documentation and/or other materials provided with the distribution.
29 *
30 * Neither the name of the DKRZ GmbH nor the names of its contributors
31 * may be used to endorse or promote products derived from this software
32 * without specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
35 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
36 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
37 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
38 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
39 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
40 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
41 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
42 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
43 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
44 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 */
46#ifdef HAVE_CONFIG_H
47#include "config.h"
48#endif
49
50#include <assert.h>
51#include <inttypes.h>
52#include <limits.h>
53#include <stdbool.h>
54#include <stdlib.h>
55#include <stdio.h>
56
57#include <mpi.h>
58#ifdef _OPENMP
59#include <omp.h>
60#endif
61
62#include "core/core.h"
63#include "core/ppm_xfuncs.h"
64#include "xt/xt_core.h"
65#include "xt/xt_mpi.h"
66#include "xt_mpi_internal.h"
67
68#if ! (HAVE_DECL___BUILTIN_CTZL || HAVE_DECL___BUILTIN_CLZL) \
69 && (HAVE_DECL___LZCNT && SIZEOF_LONG == SIZEOF_INT \
70 || HAVE_DECL___LZCNT64 && SIZEOF_LONG == 8 && CHAR_BIT == 8)
71#include <intrin.h>
72#endif
73
74//taken from http://beige.ucs.indiana.edu/I590/node85.html
75void xt_mpi_error(int error_code, MPI_Comm comm) {
76 int rank;
77 MPI_Comm_rank(comm, &rank);
78
79 char error_string[MPI_MAX_ERROR_STRING];
80 int length_of_error_string, error_class;
81
82 MPI_Error_class(error_code, &error_class);
83 MPI_Error_string(error_class, error_string, &length_of_error_string);
84 fprintf(stderr, "%3d: %s\n", rank, error_string);
85 MPI_Error_string(error_code, error_string, &length_of_error_string);
86 fprintf(stderr, "%3d: %s\n", rank, error_string);
87 MPI_Abort(comm, error_code);
88}
89
90
91size_t
92xt_disp2ext_count(size_t disp_len, const int *disp)
93{
94 if (!disp_len) return 0;
95 size_t i = 0;
96 int cur_stride = 1, cur_size = 1;
97 int last_disp = disp[0];
98 for (size_t p = 1; p < disp_len; ++p) {
99 int new_disp = disp[p];
100 int new_stride = new_disp - last_disp;
101 if (cur_size == 1) {
102 cur_stride = new_stride;
103 cur_size = 2;
104 } else if (new_stride == cur_stride) {
105 // cur_size >= 2:
106 cur_size++;
107 } else if (cur_size > 2 || (cur_size == 2 && cur_stride == 1) ) {
108 // we accept small contiguous vectors (nstrides==2, stride==1)
109 i++;
110 cur_stride = 1;
111 cur_size = 1;
112 } else { // cur_size == 2, next offset doesn't match current stride
113 // break up trivial vec:
114 i++;
115 cur_size = 2;
116 cur_stride = new_stride;
117 }
118 last_disp = new_disp;
119 }
120 // tail cases:
121 if (cur_size > 2 || (cur_size == 2 && cur_stride == 1)) {
122 i++;
123 } else if (cur_size == 2) {
124 i+=2;
125 } else { // cur_size == 1
126 i++;
127 }
128
129 return i;
130}
131
132size_t
133xt_disp2ext(size_t disp_len, const int *disp,
134 struct Xt_offset_ext *restrict v)
135{
136 if (disp_len<1) return 0;
137
138 int cur_start = disp[0], cur_stride = 1, cur_size = 1;
139 int last_disp = cur_start;
140 size_t i = 0;
141 for (size_t p = 1; p < disp_len; ++p) {
142 int new_disp = disp[p];
143 int new_stride = new_disp - last_disp;
144 if (cur_size == 1) {
145 cur_stride = new_stride;
146 cur_size = 2;
147 } else if (new_stride == cur_stride) {
148 // cur_size >= 2:
149 cur_size++;
150 } else if (cur_size > 2 || (cur_size == 2 && cur_stride == 1) ) {
151 // we accept small contiguous vectors (nstrides==2, stride==1)
152 v[i] = (struct Xt_offset_ext){ .start = cur_start, .stride = cur_stride,
153 .size = cur_size };
154 i++;
155 cur_start = new_disp;
156 cur_stride = 1;
157 cur_size = 1;
158 } else { // cur_size == 2, next offset doesn't match current stride
159 // break up trivial vec:
160 v[i].start = cur_start;
161 v[i].size = 1;
162 v[i].stride = 1;
163 i++;
164 cur_start += cur_stride;
165 cur_size = 2;
166 cur_stride = new_stride;
167 }
168 last_disp = new_disp;
169 }
170 // tail cases:
171 if (cur_size > 2 || (cur_size == 2 && cur_stride == 1)) {
172 v[i] = (struct Xt_offset_ext){ .start = cur_start, .stride = cur_stride,
173 .size = cur_size };
174 i++;
175 } else if (cur_size == 2) {
176 v[i].start = cur_start;
177 v[i].size = 1;
178 v[i].stride = 1;
179 i++;
180 v[i].start = cur_start + cur_stride;
181 v[i].size = 1;
182 v[i].stride = 1;
183 i++;
184 } else { // cur_size == 1
185 v[i].start = cur_start;
186 v[i].size = 1;
187 v[i].stride = 1;
188 i++;
189 }
190
191 return i;
192}
193
194/* functions to handle optimizations on communicators */
195static int xt_mpi_comm_internal_keyval = MPI_KEYVAL_INVALID;
196
197typedef unsigned long used_map_elem;
198
199enum {
200 used_map_elem_bits = sizeof (used_map_elem) * CHAR_BIT,
201};
202
208
209static int
211 MPI_Comm XT_UNUSED(oldcomm), int XT_UNUSED(keyval),
212 void *XT_UNUSED(extra_state), void *XT_UNUSED(attribute_val_in),
213 void *attribute_val_out, int *flag)
214{
215 struct xt_mpi_comm_internal_attr *new_comm_attr
216 = malloc(sizeof (struct xt_mpi_comm_internal_attr)
217 + sizeof (used_map_elem));
218 int retval;
219 if (new_comm_attr)
220 {
221 new_comm_attr->refcount = 1;
222 new_comm_attr->used_map_size = 1;
223 new_comm_attr->used_map[0] = 1U;
224 *(void **)attribute_val_out = new_comm_attr;
225 *flag = 1;
226 retval = MPI_SUCCESS;
227 } else {
228 *flag = 0;
229 retval = MPI_ERR_NO_MEM;
230 }
231 return retval;
232}
233
234static int
236 MPI_Comm XT_UNUSED(comm), int XT_UNUSED(comm_keyval),
237 void *attribute_val, void *XT_UNUSED(extra_state))
238{
239 free(attribute_val);
240 return MPI_SUCCESS;
241}
242
244
245void
247 assert(xt_mpi_comm_internal_keyval == MPI_KEYVAL_INVALID);
248 xt_mpi_call(MPI_Comm_create_keyval(xt_mpi_comm_internal_keyval_copy,
251 Xt_default_comm);
252 void *attr;
253 int flag;
254 xt_mpi_call(MPI_Comm_get_attr(MPI_COMM_WORLD, MPI_TAG_UB, &attr, &flag),
256 assert(flag);
257 xt_mpi_tag_ub_val = *(int *)attr;
258}
259
260void
262 assert(xt_mpi_comm_internal_keyval != MPI_KEYVAL_INVALID);
263 xt_mpi_call(MPI_Comm_free_keyval(&xt_mpi_comm_internal_keyval),
264 Xt_default_comm);
265}
266
267static struct xt_mpi_comm_internal_attr *
269{
270 int attr_found;
271 void *attr_val;
272 assert(xt_mpi_comm_internal_keyval != MPI_KEYVAL_INVALID);
273 xt_mpi_call(MPI_Comm_get_attr(comm, xt_mpi_comm_internal_keyval,
274 &attr_val, &attr_found),
275 comm);
276 return attr_found ? attr_val : NULL;
277}
278
279#if HAVE_DECL___BUILTIN_CTZL
280#define ctzl(v) (__builtin_ctzl(v))
281#elif HAVE_DECL___BUILTIN_CLZL \
282 || HAVE_DECL___LZCNT && SIZEOF_LONG == SIZEOF_INT \
283 || HAVE_DECL___LZCNT64 && SIZEOF_LONG == 8 && CHAR_BIT == 8
284static inline int
285ctzl(unsigned long v) {
286 enum {
287 ulong_bits = sizeof (unsigned long) * CHAR_BIT,
288 };
289 /* clear all but lowest 1 bit */
290 v = v & ~(v - 1);
291 int c = ulong_bits - 1 - (int)
292#if HAVE_DECL___BUILTIN_CTZL
293 __builtin_clzl(v)
294#elif HAVE_DECL___LZCNT && SIZEOF_LONG == SIZEOF_INT
295 __lzcnt(v)
296#else
297 __lzcnt64(v)
298#endif
299 ;
300 return c;
301}
302#else
303static inline int
304ctzl(unsigned long v) {
305 enum {
306 ulong_bits = sizeof (unsigned long) * CHAR_BIT,
307 };
308 // c will be the number of zero bits on the right
309 unsigned int c = ulong_bits;
310 v &= (unsigned long)-(long)v;
311 if (v) c--;
312#if SIZEOF_UNSIGNED_LONG * CHAR_BIT == 64
313 if (v & UINT64_C(0x00000000ffffffff)) c -= 32;
314 if (v & UINT64_C(0x0000ffff0000ffff)) c -= 16;
315 if (v & UINT64_C(0x00ff00ff00ff00ff)) c -= 8;
316 if (v & UINT64_C(0x0f0f0f0f0f0f0f0f)) c -= 4;
317 if (v & UINT64_C(0x3333333333333333)) c -= 2;
318 if (v & UINT64_C(0x5555555555555555)) c -= 1;
319#elif SIZEOF_UNSIGNED_LONG * CHAR_BIT == 32
320 if (v & 0x0000FFFFUL) c -= 16;
321 if (v & 0x00FF00FFUL) c -= 8;
322 if (v & 0x0F0F0F0FUL) c -= 4;
323 if (v & 0x33333333UL) c -= 2;
324 if (v & 0x55555555UL) c -= 1;
325#else
326 error "Unexpected size of long.\n"
327#endif
328 return (int)c;
329}
330#endif
331
333xt_mpi_comm_smart_dup(MPI_Comm comm, int *tag_offset)
334{
335 MPI_Comm comm_dest;
336 struct xt_mpi_comm_internal_attr *comm_xt_attr_val
338 size_t position = 0;
339 int refcount = comm_xt_attr_val ? comm_xt_attr_val->refcount : 0;
340 if (comm_xt_attr_val
342 comm_dest = comm;
343 comm_xt_attr_val->refcount = ++refcount;
344 size_t used_map_size = comm_xt_attr_val->used_map_size;
345 while (position < used_map_size
346 && comm_xt_attr_val->used_map[position] == ~(used_map_elem)0)
347 ++position;
348 if (position >= used_map_size) {
349 /* sadly, we need to recreate the value to enlarge it */
350 struct xt_mpi_comm_internal_attr *new_comm_xt_attr_val
351 = xmalloc(sizeof (*new_comm_xt_attr_val)
352 + (used_map_size + 1) * sizeof (used_map_elem));
353 new_comm_xt_attr_val->refcount = refcount;
354 new_comm_xt_attr_val->used_map_size = (unsigned)(used_map_size + 1);
355 for (size_t i = 0; i < used_map_size; ++i)
356 new_comm_xt_attr_val->used_map[i] = comm_xt_attr_val->used_map[i];
357 new_comm_xt_attr_val->used_map[used_map_size] = 1U;
358 position *= used_map_elem_bits;
359 assert(xt_mpi_comm_internal_keyval != MPI_KEYVAL_INVALID);
360 xt_mpi_call(MPI_Comm_set_attr(comm_dest, xt_mpi_comm_internal_keyval,
361 new_comm_xt_attr_val), comm_dest);
362 } else {
363 /* not all bits are set, find first unset position and insert */
364 used_map_elem used_map_entry = comm_xt_attr_val->used_map[position],
365 unset_lsb = ~used_map_entry & (used_map_entry + 1),
366 bit_pos = (used_map_elem)ctzl(unset_lsb);
367 comm_xt_attr_val->used_map[position] = used_map_entry | unset_lsb;
368 position = position * used_map_elem_bits + (size_t)bit_pos;
369 }
370 } else {
371 struct xt_mpi_comm_internal_attr *comm_attr
372 = xmalloc(sizeof (*comm_attr) + sizeof (used_map_elem));
373 comm_attr->refcount = 1;
374 comm_attr->used_map_size = 1;
375 comm_attr->used_map[0] = 1U;
376 xt_mpi_call(MPI_Comm_dup(comm, &comm_dest), comm);
377 assert(xt_mpi_comm_internal_keyval != MPI_KEYVAL_INVALID);
378 xt_mpi_call(MPI_Comm_set_attr(comm_dest, xt_mpi_comm_internal_keyval,
379 comm_attr), comm_dest);
380 }
381 *tag_offset = (int)(position * xt_mpi_num_tags);
382 return comm_dest;
383}
384
385void
386xt_mpi_comm_smart_dedup(MPI_Comm *comm, int tag_offset)
387{
388 struct xt_mpi_comm_internal_attr *comm_xt_attr_val
390 int refcount = comm_xt_attr_val ? --(comm_xt_attr_val->refcount) : 0;
391 if (refcount < 1) {
392 xt_mpi_call(MPI_Comm_free(comm), MPI_COMM_WORLD);
393 *comm = MPI_COMM_NULL;
394 } else {
395 size_t position = (size_t)tag_offset / xt_mpi_num_tags,
396 map_elem = position / used_map_elem_bits,
397 in_elem_bit = position % used_map_elem_bits;
398 comm_xt_attr_val->used_map[map_elem] &= ~((used_map_elem)1 << in_elem_bit);
399 }
400}
401
402void
404 struct xt_mpi_comm_internal_attr *comm_attr
405 = xmalloc(sizeof (*comm_attr) + sizeof (used_map_elem));
406 comm_attr->refcount = 1;
407 comm_attr->used_map_size = 1;
408 comm_attr->used_map[0] = 1U;
409 assert(xt_mpi_comm_internal_keyval != MPI_KEYVAL_INVALID);
410 xt_mpi_call(MPI_Comm_set_attr(comm, xt_mpi_comm_internal_keyval,
411 comm_attr), comm);
412}
413
414bool
415xt_mpi_test_some(int *restrict num_req,
416 MPI_Request *restrict req,
417 int *restrict ops_completed, MPI_Comm comm)
418{
419 int done_count;
420 size_t num_req_ = (size_t)*num_req;
421
422#if __GNUC__ >= 11 && __GNUC__ <= 13
423 /* GCC 11 has no means to specify that the special value pointer
424 * MPI_STATUSES_IGNORE does not need to point to something of size > 0 */
425#pragma GCC diagnostic push
426#pragma GCC diagnostic ignored "-Wstringop-overflow"
427#pragma GCC diagnostic ignored "-Wstringop-overread"
428#endif
429 xt_mpi_call(MPI_Testsome(*num_req, req, &done_count, ops_completed,
430 MPI_STATUSES_IGNORE), comm);
431#if __GNUC__ >= 11 && __GNUC__ <= 13
432#pragma GCC diagnostic pop
433#endif
434
435 if (done_count != MPI_UNDEFINED) {
436 if (num_req_ > (size_t)done_count) {
437 for (size_t i = 0, j = num_req_;
438 i < (size_t)done_count && j >= num_req_ - (size_t)done_count;
439 ++i)
440 if (ops_completed[i] < (int)num_req_ - done_count) {
441 while (req[--j] == MPI_REQUEST_NULL);
442 req[ops_completed[i]] = req[j];
443 }
444 num_req_ -= (size_t)done_count;
445 }
446 else
447 num_req_ = 0;
448 }
449 *num_req = (int)num_req_;
450 return num_req_ == 0;
451}
452
453#ifdef _OPENMP
454bool
455xt_mpi_test_some_mt(int *restrict num_req,
456 MPI_Request *restrict req,
457 int *restrict ops_completed, MPI_Comm comm)
458{
459 int done_count;
460 size_t num_req_ = (size_t)*num_req;
461
462 size_t num_threads = (size_t)omp_get_num_threads(),
463 tid = (size_t)omp_get_thread_num();
464 size_t start_req = (num_req_ * tid) / num_threads,
465 nreq_ = (num_req_ * (tid+1)) / num_threads - start_req;
466
467 for (size_t i = start_req; i < start_req + nreq_; ++i)
468 ops_completed[i] = -1;
469#if __GNUC__ >= 11 && __GNUC__ <= 13
470 /* GCC 11 has no means to specify that the special value pointer
471 * MPI_STATUSES_IGNORE does not need to point to something of size > 0 */
472#pragma GCC diagnostic push
473#pragma GCC diagnostic ignored "-Wstringop-overflow"
474#pragma GCC diagnostic ignored "-Wstringop-overread"
475#endif
476 xt_mpi_call(MPI_Testsome((int)nreq_, req+start_req, &done_count,
477 ops_completed+start_req, MPI_STATUSES_IGNORE), comm);
478#if __GNUC__ >= 11 && __GNUC__ <= 13
479#pragma GCC diagnostic pop
480#endif
481 if (done_count == MPI_UNDEFINED)
482 done_count = 0;
483#pragma omp barrier
484#pragma omp atomic
485 *num_req -= done_count;
486#pragma omp barrier
487 done_count = (int)num_req_ - *num_req;
488#pragma omp single
489 {
490 if (num_req_ > (size_t)done_count) {
491 for (size_t i = 0, j = 0; i < num_req_; ++i)
492 if (req[i] != MPI_REQUEST_NULL)
493 req[j++] = req[i];
494 }
495 *num_req = (int)num_req_ - done_count;
496 }
497 num_req_ -= (size_t)done_count;
498 return num_req_ == 0;
499}
500#endif
501
502
503/*
504 * Local Variables:
505 * c-basic-offset: 2
506 * coding: utf-8
507 * indent-tabs-mode: nil
508 * show-trailing-whitespace: t
509 * require-trailing-newline: t
510 * End:
511 */
@ MPI_COMM_WORLD
Definition core.h:73
@ MPI_COMM_NULL
Definition core.h:74
int MPI_Comm
Definition core.h:64
#define XT_UNUSED(x)
Definition core.h:84
add versions of standard API functions not returning on error
#define xmalloc(size)
Definition ppm_xfuncs.h:70
used_map_elem used_map[]
Definition xt_mpi.c:206
base definitions header file
MPI_Comm xt_mpi_comm_smart_dup(MPI_Comm comm, int *tag_offset)
Definition xt_mpi.c:333
unsigned long used_map_elem
Definition xt_mpi.c:197
size_t xt_disp2ext_count(size_t disp_len, const int *disp)
Definition xt_mpi.c:92
void xt_mpi_error(int error_code, MPI_Comm comm)
Definition xt_mpi.c:75
static int xt_mpi_tag_ub_val
Definition xt_mpi.c:243
void xt_mpi_init(void)
Definition xt_mpi.c:246
void xt_mpi_finalize(void)
Definition xt_mpi.c:261
size_t xt_disp2ext(size_t disp_len, const int *disp, struct Xt_offset_ext *restrict v)
Definition xt_mpi.c:133
static int xt_mpi_comm_internal_keyval_delete(MPI_Comm XT_UNUSED(comm), int XT_UNUSED(comm_keyval), void *attribute_val, void *XT_UNUSED(extra_state))
Definition xt_mpi.c:235
static int xt_mpi_comm_internal_keyval_copy(MPI_Comm XT_UNUSED(oldcomm), int XT_UNUSED(keyval), void *XT_UNUSED(extra_state), void *XT_UNUSED(attribute_val_in), void *attribute_val_out, int *flag)
Definition xt_mpi.c:210
static int xt_mpi_comm_internal_keyval
Definition xt_mpi.c:195
void xt_mpi_comm_smart_dedup(MPI_Comm *comm, int tag_offset)
Definition xt_mpi.c:386
bool xt_mpi_test_some(int *restrict num_req, MPI_Request *restrict req, int *restrict ops_completed, MPI_Comm comm)
Definition xt_mpi.c:415
@ used_map_elem_bits
Definition xt_mpi.c:200
static int ctzl(unsigned long v)
Definition xt_mpi.c:304
static struct xt_mpi_comm_internal_attr * xt_mpi_comm_get_internal_attr(MPI_Comm comm)
Definition xt_mpi.c:268
utility routines for MPI
#define xt_mpi_call(call, comm)
Definition xt_mpi.h:68
void xt_mpi_comm_mark_exclusive(MPI_Comm comm)
Definition xt_mpi.c:403
@ xt_mpi_num_tags