LCOV - code coverage report
Current view: top level - src/dbm - dbm_multiply_comm.c (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 100.0 % 208 208
Test Date: 2026-11-04 00:58:17 Functions: 100.0 % 16 16

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

Generated by: LCOV version 2.0-1