(git:5e7fe52)
Loading...
Searching...
No Matches
dbm_multiply_comm.c
Go to the documentation of this file.
1/*----------------------------------------------------------------------------*/
2/* CP2K: A general program to perform molecular dynamics simulations */
3/* Copyright 2000-2026 CP2K developers group <https://cp2k.org> */
4/* */
5/* SPDX-License-Identifier: BSD-3-Clause */
6/*----------------------------------------------------------------------------*/
7#include "dbm_multiply_comm.h"
8#include "../mpiwrap/cp_mpi.h"
10
11#include <assert.h>
12#include <limits.h>
13#include <stdlib.h>
14#include <string.h>
15
16#if 1
17#define DBM_MULTIPLY_COMM_MEMPOOL
18#endif
19
20/*******************************************************************************
21 * \brief Private routine for computing greatest common divisor of two numbers.
22 * \author Ole Schuett
23 ******************************************************************************/
24static int gcd(const int a, const int b) {
25 if (a == 0) {
26 return b;
27 }
28 return gcd(b % a, a); // Euclid's algorithm.
29}
30
31/*******************************************************************************
32 * \brief Private routine for computing least common multiple of two numbers.
33 * \author Ole Schuett
34 ******************************************************************************/
35static int lcm(const int a, const int b) { return (a * b) / gcd(a, b); }
36
37/*******************************************************************************
38 * \brief Private routine for converting element counts to byte counts.
39 * \author Hans Pabst
40 ******************************************************************************/
41static int checked_byte_count(const int nelements, const size_t element_size) {
42 // MPI displacements/counts are int; unchecked multiplication could overflow
43 // silently and corrupt the alltoallv layout.
44 assert(0 <= nelements);
45 assert(element_size <= INT_MAX);
46 assert(nelements <= INT_MAX / (int)element_size);
47 return nelements * (int)element_size;
48}
49
50/*******************************************************************************
51 * \brief Private routine for computing the sum of the given integers.
52 * \author Ole Schuett
53 ******************************************************************************/
54static inline int isum(const int n, const int input[n]) {
55 int output = 0;
56 for (int i = 0; i < n; i++) {
57 output += input[i];
58 }
59 return output;
60}
61
62/*******************************************************************************
63 * \brief Private routine for computing the cumulative sums of given numbers.
64 * \author Ole Schuett and Hans Pabst
65 ******************************************************************************/
66static inline void icumsum(const int n, const int input[n], int output[n]) {
67 // Exclusive prefix sum using carried accumulators to avoid a load from
68 // output[i-1] each iteration (removes loop-carried memory dependency).
69 int oval = output[0] = 0, ival = input[0];
70 for (int i = 1; i < n; i++) {
71 output[i] = (oval += ival);
72 ival = input[i];
73 }
74}
75
76/*******************************************************************************
77 * \brief Private routine computing received data counts from block metadata.
78 * \author Hans Pabst
79 ******************************************************************************/
80static void compute_data_recv_count(const int nranks,
81 const int blks_recv_count[nranks],
82 const int blks_recv_displ[nranks],
83 const int free_index_sizes[],
84 const int sum_index_sizes[],
85 const dbm_pack_block_t blks_recv[],
86 int data_recv_count[nranks]) {
87 // Derives per-rank data counts locally from the already-received block
88 // metadata, eliminating a collective alltoall for exchanging data amounts.
89 memset(data_recv_count, 0, nranks * sizeof(int));
90 for (int irank = 0; irank < nranks; irank++) {
91 for (int i = 0; i < blks_recv_count[irank]; i++) {
92 const dbm_pack_block_t *const blk =
93 &blks_recv[blks_recv_displ[irank] + i];
94 const int block_size =
95 free_index_sizes[blk->free_index] * sum_index_sizes[blk->sum_index];
96 assert(block_size >= 0);
97 assert(data_recv_count[irank] <= INT_MAX - block_size);
98 data_recv_count[irank] += block_size;
99 }
100 }
101}
102
103/*******************************************************************************
104 * \brief Private struct used for planing during pack_matrix.
105 * \author Ole Schuett
106 ******************************************************************************/
107typedef struct {
108 const dbm_block_t *blk; // source block
109 int rank; // target mpi rank
112} plan_t;
113
114/*******************************************************************************
115 * \brief Private routine for calculating tick indices in pack plans.
116 * \author Maximilian Graml
117 ******************************************************************************/
118static inline unsigned long long calculate_tick_index(int sum_index,
119 int nticks) {
120 // 1021 is used as a random prime to scramble the index
121 return ((unsigned long long)sum_index * 1021ULL) % (unsigned long long)nticks;
122}
123
124/*******************************************************************************
125 * \brief Private routine for planing packs.
126 * \author Ole Schuett
127 ******************************************************************************/
128static void create_pack_plans(const bool trans_matrix, const bool trans_dist,
129 const dbm_matrix_t *matrix,
130 const cp_mpi_comm_t comm,
131 const dbm_dist_1d_t *dist_indices,
132 const dbm_dist_1d_t *dist_ticks, const int nticks,
133 const int npacks, plan_t *plans_per_pack[npacks],
134 int nblks_per_pack[npacks],
135 int ndata_per_pack[npacks]) {
136 memset(nblks_per_pack, 0, npacks * sizeof(int));
137 memset(ndata_per_pack, 0, npacks * sizeof(int));
138
139#pragma omp parallel
140 {
141 // 1st pass: Compute number of blocks that will be send in each pack.
142 int nblks_mythread[npacks];
143 memset(nblks_mythread, 0, npacks * sizeof(int));
144#pragma omp for schedule(static)
145 for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
146 dbm_shard_t *shard = &matrix->shards[ishard];
147 for (int iblock = 0; iblock < shard->nblocks; iblock++) {
148 const dbm_block_t *blk = &shard->blocks[iblock];
149 const int sum_index = (trans_matrix) ? blk->row : blk->col;
150 unsigned long long itick64 = calculate_tick_index(sum_index, nticks);
151 const int ipack = itick64 / dist_ticks->nranks;
152 nblks_mythread[ipack]++;
153 }
154 }
155
156 // Sum nblocks across threads and allocate arrays for plans.
157#pragma omp critical
158 for (int ipack = 0; ipack < npacks; ipack++) {
159 nblks_per_pack[ipack] += nblks_mythread[ipack];
160 nblks_mythread[ipack] = nblks_per_pack[ipack];
161 }
162#pragma omp barrier
163#pragma omp for
164 for (int ipack = 0; ipack < npacks; ipack++) {
165 const int nblks = nblks_per_pack[ipack];
166 plans_per_pack[ipack] = malloc(nblks * sizeof(plan_t));
167 assert(plans_per_pack[ipack] != NULL || nblks == 0);
168 }
169
170 // 2nd pass: Plan where to send each block.
171 int ndata_mythread[npacks];
172 memset(ndata_mythread, 0, npacks * sizeof(int));
173#pragma omp for schedule(static) // Need static to match previous loop.
174 for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
175 dbm_shard_t *shard = &matrix->shards[ishard];
176 for (int iblock = 0; iblock < shard->nblocks; iblock++) {
177 const dbm_block_t *blk = &shard->blocks[iblock];
178 const int free_index = (trans_matrix) ? blk->col : blk->row;
179 const int sum_index = (trans_matrix) ? blk->row : blk->col;
180 unsigned long long itick64 = calculate_tick_index(sum_index, nticks);
181 const int ipack = itick64 / dist_ticks->nranks;
182 // Compute rank to which this block should be sent.
183 const int coord_free_idx = dist_indices->index2coord[free_index];
184 const int coord_sum_idx = itick64 % dist_ticks->nranks;
185 const int coords[2] = {(trans_dist) ? coord_sum_idx : coord_free_idx,
186 (trans_dist) ? coord_free_idx : coord_sum_idx};
187 const int rank = cp_mpi_cart_rank(comm, coords);
188 const int row_size = matrix->row_sizes[blk->row];
189 const int col_size = matrix->col_sizes[blk->col];
190 ndata_mythread[ipack] += row_size * col_size;
191 // Create plan.
192 const int iplan = --nblks_mythread[ipack];
193 plans_per_pack[ipack][iplan].blk = blk;
194 plans_per_pack[ipack][iplan].rank = rank;
195 plans_per_pack[ipack][iplan].row_size = row_size;
196 plans_per_pack[ipack][iplan].col_size = col_size;
197 }
198 }
199#pragma omp critical
200 for (int ipack = 0; ipack < npacks; ipack++) {
201 ndata_per_pack[ipack] += ndata_mythread[ipack];
202 }
203 } // end of omp parallel region
204}
205
206/*******************************************************************************
207 * \brief Private routine for filling send buffers.
208 * \author Ole Schuett
209 ******************************************************************************/
211 const dbm_matrix_t *matrix, const bool trans_matrix, const int nblks_send,
212 const int ndata_send, plan_t plans[nblks_send], const int nranks,
213 int blks_send_count[nranks], int data_send_count[nranks],
214 int blks_send_displ[nranks], int data_send_displ[nranks],
215 dbm_pack_block_t blks_send[nblks_send], double data_send[ndata_send]) {
216 memset(blks_send_count, 0, nranks * sizeof(int));
217 memset(data_send_count, 0, nranks * sizeof(int));
218
219#pragma omp parallel
220 {
221 // 3th pass: Compute per rank nblks and ndata.
222 int nblks_mythread[nranks], ndata_mythread[nranks];
223 memset(nblks_mythread, 0, nranks * sizeof(int));
224 memset(ndata_mythread, 0, nranks * sizeof(int));
225#pragma omp for schedule(static)
226 for (int iblock = 0; iblock < nblks_send; iblock++) {
227 const plan_t *plan = &plans[iblock];
228 nblks_mythread[plan->rank] += 1;
229 ndata_mythread[plan->rank] += plan->row_size * plan->col_size;
230 }
231
232 // Sum nblks and ndata across threads.
233#pragma omp critical
234 for (int irank = 0; irank < nranks; irank++) {
235 blks_send_count[irank] += nblks_mythread[irank];
236 data_send_count[irank] += ndata_mythread[irank];
237 nblks_mythread[irank] = blks_send_count[irank];
238 ndata_mythread[irank] = data_send_count[irank];
239 }
240#pragma omp barrier
241
242 // Compute send displacements.
243#pragma omp single
244 {
245 icumsum(nranks, blks_send_count, blks_send_displ);
246 icumsum(nranks, data_send_count, data_send_displ);
247 const int m = nranks - 1;
248 assert(nblks_send == blks_send_displ[m] + blks_send_count[m]);
249 assert(ndata_send == data_send_displ[m] + data_send_count[m]);
250 }
251#pragma omp barrier
252
253 // 4th pass: Fill blks_send and data_send arrays.
254#pragma omp for schedule(static) // Need static to match previous loop.
255 for (int iblock = 0; iblock < nblks_send; iblock++) {
256 const plan_t *const plan = &plans[iblock];
257 const dbm_block_t *const blk = plan->blk;
258 const int ishard = dbm_get_shard_index(matrix, blk->row, blk->col);
259 const dbm_shard_t *const shard = &matrix->shards[ishard];
260 const double *blk_data = &shard->data[blk->offset];
261 const int row_size = plan->row_size, col_size = plan->col_size;
262 const int plan_size = row_size * col_size;
263 const int irank = plan->rank;
264
265 // The blk_send_data is ordered by rank, thread, and block.
266 // data_send_displ[irank]: Start of data for irank within blk_send_data.
267 // ndata_mythread[irank]: Current threads offset within data for irank.
268 nblks_mythread[irank] -= 1;
269 ndata_mythread[irank] -= plan_size;
270 const int offset = data_send_displ[irank] + ndata_mythread[irank];
271 const int jblock = blks_send_displ[irank] + nblks_mythread[irank];
272
273 double norm = 0.0; // Compute norm as double...
274 if (trans_matrix) {
275 // Transpose block to allow for outer-product style multiplication.
276 for (int i = 0; i < row_size; i++) {
277 for (int j = 0; j < col_size; j++) {
278 const double element = blk_data[j * row_size + i];
279 data_send[offset + i * col_size + j] = element;
280 norm += element * element;
281 }
282 }
283 blks_send[jblock].free_index = plan->blk->col;
284 blks_send[jblock].sum_index = plan->blk->row;
285 } else {
286 for (int i = 0; i < plan_size; i++) {
287 const double element = blk_data[i];
288 data_send[offset + i] = element;
289 norm += element * element;
290 }
291 blks_send[jblock].free_index = plan->blk->row;
292 blks_send[jblock].sum_index = plan->blk->col;
293 }
294 blks_send[jblock].norm = (float)norm; // ...store norm as float.
295
296 // After the block exchange data_recv_displ will be added to the offsets.
297 blks_send[jblock].offset = offset - data_send_displ[irank];
298 }
299 } // end of omp parallel region
300}
301
302/*******************************************************************************
303 * \brief Private comperator passed to qsort to compare two blocks by sum_index.
304 * \author Ole Schuett
305 ******************************************************************************/
306static int compare_pack_blocks_by_sum_index(const void *a, const void *b) {
307 const dbm_pack_block_t *blk_a = (const dbm_pack_block_t *)a;
308 const dbm_pack_block_t *blk_b = (const dbm_pack_block_t *)b;
309 return blk_a->sum_index - blk_b->sum_index;
310}
311
312/*******************************************************************************
313 * \brief Private routine for post-processing received blocks.
314 * \author Ole Schuett
315 ******************************************************************************/
317 const int nranks, const int nshards, const int nblocks_recv,
318 const int blks_recv_count[nranks], const int blks_recv_displ[nranks],
319 const int data_recv_displ[nranks],
320 dbm_pack_block_t blks_recv[nblocks_recv]) {
321 int nblocks_per_shard[nshards], shard_start[nshards];
322 memset(nblocks_per_shard, 0, nshards * sizeof(int));
323 dbm_pack_block_t *blocks_tmp =
324 malloc(nblocks_recv * sizeof(dbm_pack_block_t));
325 assert(blocks_tmp != NULL || nblocks_recv == 0);
326
327#pragma omp parallel
328 {
329 // Add data_recv_displ to recveived block offsets.
330 for (int irank = 0; irank < nranks; irank++) {
331#pragma omp for
332 for (int i = 0; i < blks_recv_count[irank]; i++) {
333 blks_recv[blks_recv_displ[irank] + i].offset += data_recv_displ[irank];
334 }
335 }
336
337 // First use counting sort to group blocks by their free_index shard.
338 int nblocks_mythread[nshards];
339 memset(nblocks_mythread, 0, nshards * sizeof(int));
340#pragma omp for schedule(static)
341 for (int iblock = 0; iblock < nblocks_recv; iblock++) {
342 blocks_tmp[iblock] = blks_recv[iblock];
343 const int ishard = blks_recv[iblock].free_index % nshards;
344 nblocks_mythread[ishard]++;
345 }
346#pragma omp critical
347 for (int ishard = 0; ishard < nshards; ishard++) {
348 nblocks_per_shard[ishard] += nblocks_mythread[ishard];
349 nblocks_mythread[ishard] = nblocks_per_shard[ishard];
350 }
351#pragma omp barrier
352#pragma omp single
353 icumsum(nshards, nblocks_per_shard, shard_start);
354#pragma omp barrier
355#pragma omp for schedule(static) // Need static to match previous loop.
356 for (int iblock = 0; iblock < nblocks_recv; iblock++) {
357 const int ishard = blocks_tmp[iblock].free_index % nshards;
358 const int jblock = --nblocks_mythread[ishard] + shard_start[ishard];
359 blks_recv[jblock] = blocks_tmp[iblock];
360 }
361
362 // Then sort blocks within each shard by their sum_index.
363#pragma omp for
364 for (int ishard = 0; ishard < nshards; ishard++) {
365 if (nblocks_per_shard[ishard] > 1) {
366 qsort(&blks_recv[shard_start[ishard]], nblocks_per_shard[ishard],
368 }
369 }
370 } // end of omp parallel region
371
372 free(blocks_tmp);
373}
374
375/*******************************************************************************
376 * \brief Private routine for redistributing a matrix along selected dimensions.
377 * \author Ole Schuett
378 ******************************************************************************/
379static dbm_packed_matrix_t pack_matrix(const bool trans_matrix,
380 const bool trans_dist,
381 const dbm_matrix_t *restrict matrix,
382 const dbm_distribution_t *restrict dist,
383 const int nticks) {
384 assert(cp_mpi_comms_are_similar(matrix->dist->comm, dist->comm));
385
386 // The row/col indicies are distributed along one cart dimension and the
387 // ticks are distributed along the other cart dimension.
388 const dbm_dist_1d_t *dist_indices = (trans_dist) ? &dist->cols : &dist->rows;
389 const dbm_dist_1d_t *dist_ticks = (trans_dist) ? &dist->rows : &dist->cols;
390 const int *free_index_sizes =
391 (trans_matrix) ? matrix->col_sizes : matrix->row_sizes;
392 const int *sum_index_sizes =
393 (trans_matrix) ? matrix->row_sizes : matrix->col_sizes;
394
395 // Allocate packed matrix.
396 const int nsend_packs = nticks / dist_ticks->nranks;
397 assert(nsend_packs * dist_ticks->nranks == nticks);
398 dbm_packed_matrix_t packed;
399 packed.dist_indices = dist_indices;
400 packed.dist_ticks = dist_ticks;
401 packed.nsend_packs = nsend_packs;
402 packed.send_packs = malloc(nsend_packs * sizeof(dbm_pack_t));
403 assert(packed.send_packs != NULL || nsend_packs == 0);
404
405 // Plan all packs.
406 plan_t *plans_per_pack[nsend_packs];
407 int nblks_send_per_pack[nsend_packs], ndata_send_per_pack[nsend_packs];
408 create_pack_plans(trans_matrix, trans_dist, matrix, dist->comm, dist_indices,
409 dist_ticks, nticks, nsend_packs, plans_per_pack,
410 nblks_send_per_pack, ndata_send_per_pack);
411
412 // Allocate send buffers for maximum number of blocks/data over all packs.
413 int nblks_send_max = 0, ndata_send_max = 0;
414 for (int ipack = 0; ipack < nsend_packs; ++ipack) {
415 nblks_send_max = imax(nblks_send_max, nblks_send_per_pack[ipack]);
416 ndata_send_max = imax(ndata_send_max, ndata_send_per_pack[ipack]);
417 }
418 dbm_pack_block_t *blks_send =
419 cp_mpi_alloc_mem(nblks_send_max * sizeof(dbm_pack_block_t));
420 double *data_send = cp_mpi_alloc_mem(ndata_send_max * sizeof(double));
421
422 // Cannot parallelize over packs (there might be too few of them).
423 for (int ipack = 0; ipack < nsend_packs; ipack++) {
424 // Fill send buffers according to plans.
425 const int nranks = dist->nranks;
426 int blks_send_count[nranks], data_send_count[nranks];
427 int blks_send_displ[nranks], data_send_displ[nranks];
428 fill_send_buffers(matrix, trans_matrix, nblks_send_per_pack[ipack],
429 ndata_send_per_pack[ipack], plans_per_pack[ipack], nranks,
430 blks_send_count, data_send_count, blks_send_displ,
431 data_send_displ, blks_send, data_send);
432 free(plans_per_pack[ipack]);
433
434 // 1st communication: Exchange block counts.
435 int blks_recv_count[nranks], blks_recv_displ[nranks];
436 cp_mpi_alltoall_int(blks_send_count, 1, blks_recv_count, 1, dist->comm);
437 icumsum(nranks, blks_recv_count, blks_recv_displ);
438 const int nblocks_recv = isum(nranks, blks_recv_count);
439
440 // 2nd communication: Exchange blocks.
441 dbm_pack_block_t *blks_recv =
442 cp_mpi_alloc_mem(nblocks_recv * sizeof(dbm_pack_block_t));
443 int blks_send_count_byte[nranks], blks_send_displ_byte[nranks];
444 int blks_recv_count_byte[nranks], blks_recv_displ_byte[nranks];
445 for (int i = 0; i < nranks; i++) { // TODO: this is ugly!
446 blks_send_count_byte[i] =
447 checked_byte_count(blks_send_count[i], sizeof(dbm_pack_block_t));
448 blks_send_displ_byte[i] =
449 checked_byte_count(blks_send_displ[i], sizeof(dbm_pack_block_t));
450 blks_recv_count_byte[i] =
451 checked_byte_count(blks_recv_count[i], sizeof(dbm_pack_block_t));
452 blks_recv_displ_byte[i] =
453 checked_byte_count(blks_recv_displ[i], sizeof(dbm_pack_block_t));
454 }
455 cp_mpi_alltoallv_byte(blks_send, blks_send_count_byte, blks_send_displ_byte,
456 blks_recv, blks_recv_count_byte, blks_recv_displ_byte,
457 dist->comm);
458
459 // 3rd compute data counts from the received block metadata.
460 int data_recv_count[nranks], data_recv_displ[nranks];
461 compute_data_recv_count(nranks, blks_recv_count, blks_recv_displ,
462 free_index_sizes, sum_index_sizes, blks_recv,
463 data_recv_count);
464 icumsum(nranks, data_recv_count, data_recv_displ);
465 const int ndata_recv = isum(nranks, data_recv_count);
466
467 // 4th communication: Exchange data.
468#if defined(DBM_MULTIPLY_COMM_MEMPOOL)
469 double *data_recv =
470 offload_mempool_host_malloc(ndata_recv * sizeof(double));
471#else
472 double *data_recv = cp_mpi_alloc_mem(ndata_recv * sizeof(double));
473#endif
474 cp_mpi_alltoallv_double(data_send, data_send_count, data_send_displ,
475 data_recv, data_recv_count, data_recv_displ,
476 dist->comm);
477
478 // 5th post-process received blocks and assemble them into a pack.
479 postprocess_received_blocks(nranks, dist_indices->nshards, nblocks_recv,
480 blks_recv_count, blks_recv_displ,
481 data_recv_displ, blks_recv);
482 packed.send_packs[ipack].nblocks = nblocks_recv;
483 packed.send_packs[ipack].data_size = ndata_recv;
484 packed.send_packs[ipack].blocks = blks_recv;
485 packed.send_packs[ipack].data = data_recv;
486 }
487
488 // Deallocate send buffers.
489 cp_mpi_free_mem(blks_send);
490 cp_mpi_free_mem(data_send);
491
492 // Allocate pack_recv.
493 int max_nblocks = 0, max_data_size = 0;
494 for (int ipack = 0; ipack < packed.nsend_packs; ipack++) {
495 max_nblocks = imax(max_nblocks, packed.send_packs[ipack].nblocks);
496 max_data_size = imax(max_data_size, packed.send_packs[ipack].data_size);
497 }
498 cp_mpi_max_int(&max_nblocks, 1, packed.dist_ticks->comm);
499 cp_mpi_max_int(&max_data_size, 1, packed.dist_ticks->comm);
500 packed.max_nblocks = max_nblocks;
501 packed.max_data_size = max_data_size;
502 packed.recv_pack.blocks =
504#if defined(DBM_MULTIPLY_COMM_MEMPOOL)
505 packed.recv_pack.data =
506 offload_mempool_host_malloc(packed.max_data_size * sizeof(double));
507#else
508 packed.recv_pack.data =
509 cp_mpi_alloc_mem(packed.max_data_size * sizeof(double));
510#endif
511
512 return packed; // Ownership of packed transfers to caller.
513}
514
515/*******************************************************************************
516 * \brief Private routine for sending and receiving the pack for the given tick.
517 * \author Ole Schuett
518 ******************************************************************************/
519static dbm_pack_t *sendrecv_pack(const int itick, const int nticks,
520 dbm_packed_matrix_t *packed) {
521 const int nranks = packed->dist_ticks->nranks;
522 const int my_rank = packed->dist_ticks->my_rank;
523
524 // Compute send rank and pack.
525 const int itick_of_rank0 = (itick + nticks - my_rank) % nticks;
526 const int send_rank = (my_rank + nticks - itick_of_rank0) % nranks;
527 const int send_itick = (itick_of_rank0 + send_rank) % nticks;
528 const int send_ipack = send_itick / nranks;
529 assert(send_itick % nranks == my_rank);
530
531 // Compute receive rank and pack.
532 const int recv_rank = itick % nranks;
533 const int recv_ipack = itick / nranks;
534
535 dbm_pack_t *send_pack = &packed->send_packs[send_ipack];
536 if (send_rank == my_rank) {
537 assert(send_rank == recv_rank && send_ipack == recv_ipack);
538 return send_pack; // Local pack, no mpi needed.
539 } else {
540 // Exchange blocks.
541 const int nblocks_in_bytes = cp_mpi_sendrecv_byte(
542 /*sendbuf=*/send_pack->blocks,
543 /*sendcound=*/
544 checked_byte_count(send_pack->nblocks, sizeof(dbm_pack_block_t)),
545 /*dest=*/send_rank,
546 /*sendtag=*/send_ipack,
547 /*recvbuf=*/packed->recv_pack.blocks,
548 /*recvcount=*/
550 /*source=*/recv_rank,
551 /*recvtag=*/recv_ipack,
552 /*comm=*/packed->dist_ticks->comm);
553
554 assert(nblocks_in_bytes % sizeof(dbm_pack_block_t) == 0);
555 packed->recv_pack.nblocks = nblocks_in_bytes / sizeof(dbm_pack_block_t);
556
557 // Exchange data.
559 /*sendbuf=*/send_pack->data,
560 /*sendcound=*/send_pack->data_size,
561 /*dest=*/send_rank,
562 /*sendtag=*/send_ipack,
563 /*recvbuf=*/packed->recv_pack.data,
564 /*recvcount=*/packed->max_data_size,
565 /*source=*/recv_rank,
566 /*recvtag=*/recv_ipack,
567 /*comm=*/packed->dist_ticks->comm);
568
569 return &packed->recv_pack;
570 }
571}
572
573/*******************************************************************************
574 * \brief Private routine for releasing a packed matrix.
575 * \author Ole Schuett
576 ******************************************************************************/
579#if defined(DBM_MULTIPLY_COMM_MEMPOOL)
581#else
583#endif
584 for (int ipack = 0; ipack < packed->nsend_packs; ipack++) {
585 cp_mpi_free_mem(packed->send_packs[ipack].blocks);
586#if defined(DBM_MULTIPLY_COMM_MEMPOOL)
588#else
589 cp_mpi_free_mem(packed->send_packs[ipack].data);
590#endif
591 }
592 free(packed->send_packs);
593}
594
595/*******************************************************************************
596 * \brief Internal routine for creating a communication iterator.
597 * \author Ole Schuett
598 ******************************************************************************/
600 const bool transb,
601 const dbm_matrix_t *matrix_a,
602 const dbm_matrix_t *matrix_b,
603 const dbm_matrix_t *matrix_c) {
604 dbm_comm_iterator_t *iter = malloc(sizeof(dbm_comm_iterator_t));
605 assert(iter != NULL);
606 iter->dist = matrix_c->dist;
607
608 // During each communication tick we'll fetch a pack_a and pack_b.
609 // Since the cart might be non-squared, the number of communication ticks is
610 // chosen as the least common multiple of the cart's dimensions.
611 iter->nticks = lcm(iter->dist->rows.nranks, iter->dist->cols.nranks);
612 iter->itick = 0;
613
614 // 1.arg=source dimension, 2.arg=target dimension, false=rows, true=columns.
615 iter->packed_a =
616 pack_matrix(transa, false, matrix_a, iter->dist, iter->nticks);
617 iter->packed_b =
618 pack_matrix(!transb, true, matrix_b, iter->dist, iter->nticks);
619
620 return iter;
621}
622
623/*******************************************************************************
624 * \brief Internal routine for retrieving next pair of packs of given iterator.
625 * \author Ole Schuett
626 ******************************************************************************/
628 dbm_pack_t **pack_b) {
629 if (iter->itick >= iter->nticks) {
630 return false; // end of iterator reached
631 }
632
633 // Start each rank at a different tick to spread the load on the sources.
634 const int shift = iter->dist->rows.my_rank + iter->dist->cols.my_rank;
635 const int itick = (iter->itick + shift) % iter->nticks;
636 *pack_a = sendrecv_pack(itick, iter->nticks, &iter->packed_a);
637 *pack_b = sendrecv_pack(itick, iter->nticks, &iter->packed_b);
638
639 ++iter->itick;
640 return true;
641}
642
643/*******************************************************************************
644 * \brief Internal routine for releasing the given communication iterator.
645 * \author Ole Schuett
646 ******************************************************************************/
650 free(iter);
651}
652
653// EOF
void cp_mpi_free_mem(void *mem)
Wrapper around MPI_Free_mem.
Definition cp_mpi.c:625
void cp_mpi_max_int(int *values, const int count, const cp_mpi_comm_t comm)
Wrapper around MPI_Allreduce for op MPI_MAX and datatype MPI_INT.
Definition cp_mpi.c:330
int cp_mpi_sendrecv_byte(const void *sendbuf, const int sendcount, const int dest, const int sendtag, void *recvbuf, const int recvcount, const int source, const int recvtag, const cp_mpi_comm_t comm)
Wrapper around MPI_Sendrecv for datatype MPI_BYTE.
Definition cp_mpi.c:495
void cp_mpi_alltoallv_double(const double *sendbuf, const int *sendcounts, const int *sdispls, double *recvbuf, const int *recvcounts, const int *rdispls, const cp_mpi_comm_t comm)
Wrapper around MPI_Alltoallv for datatype MPI_DOUBLE.
Definition cp_mpi.c:590
int cp_mpi_cart_rank(const cp_mpi_comm_t comm, const int coords[])
Wrapper around MPI_Cart_rank.
Definition cp_mpi.c:266
void * cp_mpi_alloc_mem(size_t size)
Wrapper around MPI_Alloc_mem.
Definition cp_mpi.c:609
void cp_mpi_alltoall_int(const int *sendbuf, const int sendcount, int *recvbuf, const int recvcount, const cp_mpi_comm_t comm)
Wrapper around MPI_Alltoall for datatype MPI_INT.
Definition cp_mpi.c:555
bool cp_mpi_comms_are_similar(const cp_mpi_comm_t comm1, const cp_mpi_comm_t comm2)
Wrapper around MPI_Comm_compare.
Definition cp_mpi.c:313
void cp_mpi_alltoallv_byte(const void *sendbuf, const int *sendcounts, const int *sdispls, void *recvbuf, const int *recvcounts, const int *rdispls, const cp_mpi_comm_t comm)
Wrapper around MPI_Alltoallv for datatype MPI_BYTE.
Definition cp_mpi.c:571
int cp_mpi_sendrecv_double(const double *sendbuf, const int sendcount, const int dest, const int sendtag, double *recvbuf, const int recvcount, const int source, const int recvtag, const cp_mpi_comm_t comm)
Wrapper around MPI_Sendrecv for datatype MPI_DOUBLE.
Definition cp_mpi.c:525
int cp_mpi_comm_t
Definition cp_mpi.h:18
static int imax(int x, int y)
Returns the larger of two given integers (missing from the C standard).
static int dbm_get_shard_index(const dbm_matrix_t *matrix, const int row, const int col)
Internal routine for getting a block's shard index.
Definition dbm_matrix.h:245
static int dbm_get_num_shards(const dbm_matrix_t *matrix)
Internal routine that returns the number of shards for given matrix.
Definition dbm_matrix.h:237
static void free_packed_matrix(dbm_packed_matrix_t *packed)
Private routine for releasing a packed matrix.
static void icumsum(const int n, const int input[n], int output[n])
Private routine for computing the cumulative sums of given numbers.
static void create_pack_plans(const bool trans_matrix, const bool trans_dist, const dbm_matrix_t *matrix, const cp_mpi_comm_t comm, const dbm_dist_1d_t *dist_indices, const dbm_dist_1d_t *dist_ticks, const int nticks, const int npacks, plan_t *plans_per_pack[npacks], int nblks_per_pack[npacks], int ndata_per_pack[npacks])
Private routine for planing packs.
static void postprocess_received_blocks(const int nranks, const int nshards, const int nblocks_recv, const int blks_recv_count[nranks], const int blks_recv_displ[nranks], const int data_recv_displ[nranks], dbm_pack_block_t blks_recv[nblocks_recv])
Private routine for post-processing received blocks.
dbm_comm_iterator_t * dbm_comm_iterator_start(const bool transa, const bool transb, const dbm_matrix_t *matrix_a, const dbm_matrix_t *matrix_b, const dbm_matrix_t *matrix_c)
Internal routine for creating a communication iterator.
static void fill_send_buffers(const dbm_matrix_t *matrix, const bool trans_matrix, const int nblks_send, const int ndata_send, plan_t plans[nblks_send], const int nranks, int blks_send_count[nranks], int data_send_count[nranks], int blks_send_displ[nranks], int data_send_displ[nranks], dbm_pack_block_t blks_send[nblks_send], double data_send[ndata_send])
Private routine for filling send buffers.
static int gcd(const int a, const int b)
Private routine for computing greatest common divisor of two numbers.
static int lcm(const int a, const int b)
Private routine for computing least common multiple of two numbers.
void dbm_comm_iterator_stop(dbm_comm_iterator_t *iter)
Internal routine for releasing the given communication iterator.
static dbm_packed_matrix_t pack_matrix(const bool trans_matrix, const bool trans_dist, const dbm_matrix_t *restrict matrix, const dbm_distribution_t *restrict dist, const int nticks)
Private routine for redistributing a matrix along selected dimensions.
static dbm_pack_t * sendrecv_pack(const int itick, const int nticks, dbm_packed_matrix_t *packed)
Private routine for sending and receiving the pack for the given tick.
static int compare_pack_blocks_by_sum_index(const void *a, const void *b)
Private comperator passed to qsort to compare two blocks by sum_index.
bool dbm_comm_iterator_next(dbm_comm_iterator_t *iter, dbm_pack_t **pack_a, dbm_pack_t **pack_b)
Internal routine for retrieving next pair of packs of given iterator.
static unsigned long long calculate_tick_index(int sum_index, int nticks)
Private routine for calculating tick indices in pack plans.
static void compute_data_recv_count(const int nranks, const int blks_recv_count[nranks], const int blks_recv_displ[nranks], const int free_index_sizes[], const int sum_index_sizes[], const dbm_pack_block_t blks_recv[], int data_recv_count[nranks])
Private routine computing received data counts from block metadata.
static int isum(const int n, const int input[n])
Private routine for computing the sum of the given integers.
static int checked_byte_count(const int nelements, const size_t element_size)
Private routine for converting element counts to byte counts.
static void const int const int i
void offload_mempool_host_free(const void *memory)
Internal routine for releasing memory back to the pool.
void * offload_mempool_host_malloc(const size_t size)
Internal routine for allocating host memory from the pool.
Internal struct for storing a block's metadata.
Definition dbm_shard.h:20
Internal struct for storing a communication iterator.
dbm_packed_matrix_t packed_a
dbm_distribution_t * dist
dbm_packed_matrix_t packed_b
Internal struct for storing a one dimensional distribution.
cp_mpi_comm_t comm
Internal struct for storing a two dimensional distribution.
Internal struct for storing a matrix.
Definition dbm_matrix.h:19
int * row_sizes
Definition dbm_matrix.h:24
int * col_sizes
Definition dbm_matrix.h:25
dbm_shard_t * shards
Definition dbm_matrix.h:27
dbm_distribution_t * dist
Definition dbm_matrix.h:20
Internal struct for storing a dbm_block_t plus its norm.
Internal struct for storing a pack - essentially a shard for MPI.
double * data
dbm_pack_block_t * blocks
Internal struct for storing a packed matrix.
const dbm_dist_1d_t * dist_ticks
const dbm_dist_1d_t * dist_indices
Internal struct for storing a matrix shard.
Definition dbm_shard.h:30
double * data
Definition dbm_shard.h:42
dbm_block_t * blocks
Definition dbm_shard.h:33
Private struct used for planing during pack_matrix.
const dbm_block_t * blk