(git:71c3ab0)
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"
9#include "../offload/offload_mempool.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:541
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:246
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:411
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:506
int cp_mpi_cart_rank(const cp_mpi_comm_t comm, const int coords[])
Wrapper around MPI_Cart_rank.
Definition cp_mpi.c:184
void * cp_mpi_alloc_mem(size_t size)
Wrapper around MPI_Alloc_mem.
Definition cp_mpi.c:525
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:471
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:229
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:487
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:441
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.
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