LCOV - code coverage report
Current view: top level - src/dbm - dbm_multiply.c (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 79.2 % 125 99
Test Date: 2026-11-04 00:58:17 Functions: 87.5 % 8 7

            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.h"
       8              : #include "../offload/offload_mempool.h"
       9              : #include "../offload/offload_runtime.h"
      10              : #include "dbm_hyperparams.h"
      11              : #include "dbm_internal.h"
      12              : #include "dbm_library.h"
      13              : #include "dbm_multiply_comm.h"
      14              : #include "dbm_multiply_cpu.h"
      15              : #include "dbm_multiply_gpu.h"
      16              : 
      17              : #include <assert.h>
      18              : #include <limits.h>
      19              : #include <math.h>
      20              : #include <omp.h>
      21              : #include <stdio.h>
      22              : #include <stdlib.h>
      23              : #include <string.h>
      24              : 
      25              : /*******************************************************************************
      26              :  * \brief Private routine for computing the max filter threshold for each row.
      27              :  * \author Ole Schuett
      28              :  ******************************************************************************/
      29       349938 : static float *compute_rows_max_eps(const bool trans, const dbm_matrix_t *matrix,
      30              :                                    const double filter_eps) {
      31       349938 :   const int nrows = (trans) ? matrix->ncols : matrix->nrows;
      32       349938 :   int *nblocks_per_row = calloc(nrows, sizeof(int));
      33       349938 :   float *row_max_eps = malloc(nrows * sizeof(float));
      34       349938 :   assert((nblocks_per_row != NULL && row_max_eps != NULL) || nrows == 0);
      35              : 
      36       349938 : #pragma omp parallel
      37              :   {
      38              : #pragma omp for
      39              :     for (int ishard = 0; ishard < dbm_get_num_shards(matrix); ishard++) {
      40              :       dbm_shard_t *shard = &matrix->shards[ishard];
      41              :       for (int iblock = 0; iblock < shard->nblocks; iblock++) {
      42              :         const dbm_block_t *blk = &shard->blocks[iblock];
      43              :         const int row = (trans) ? blk->col : blk->row;
      44              : #pragma omp atomic
      45              :         ++nblocks_per_row[row];
      46              :       }
      47              :     }
      48              : #pragma omp master
      49              :     cp_mpi_sum_int(nblocks_per_row, nrows, matrix->dist->comm);
      50              : #pragma omp barrier
      51              : #pragma omp for
      52              :     for (int i = 0; i < nrows; i++) {
      53              :       const float f =
      54              :           ((float)filter_eps) / ((float)imax(1, nblocks_per_row[i]));
      55              :       row_max_eps[i] = f * f;
      56              :     }
      57              :   } // end of omp parallel region
      58              : 
      59       349938 :   free(nblocks_per_row);
      60       349938 :   return row_max_eps; // Ownership of row_max_eps transfers to caller.
      61              : }
      62              : 
      63              : /*******************************************************************************
      64              :  * \brief Private struct for storing the context of the multiplication backend.
      65              :  * \author Ole Schuett
      66              :  ******************************************************************************/
      67              : typedef struct {
      68              : #if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
      69              :   dbm_multiply_gpu_context_t gpu;
      70              : #endif
      71              :   int cpu_options; // Binary or'ed dbm_multiply_cpu_options (enum).
      72              : } backend_context_t;
      73              : 
      74              : /*******************************************************************************
      75              :  * \brief Private routine for initializing the multiplication backend.
      76              :  * \author Ole Schuett
      77              :  ******************************************************************************/
      78       349938 : static backend_context_t *backend_start(const dbm_matrix_t *matrix_c) {
      79       349938 :   backend_context_t *const ctx = calloc(1, sizeof(backend_context_t));
      80              :   // BLAS and LIBXS benefit in general from DBM_MULTIPLY_TASK_REORDER.
      81       349938 :   ctx->cpu_options = DBM_MULTIPLY_TASK_REORDER;
      82              : 
      83              : #if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
      84              :   dbm_multiply_gpu_start(DBM_MAX_BATCH_SIZE, dbm_get_num_shards(matrix_c),
      85              :                          matrix_c->shards, &ctx->gpu);
      86              : #else
      87       349938 :   (void)matrix_c; // mark as used
      88              : #endif
      89              : 
      90       349938 :   return ctx;
      91              : }
      92              : 
      93              : /*******************************************************************************
      94              :  * \brief Private routine for handing newly arrived packs to the backend.
      95              :  * \author Ole Schuett
      96              :  ******************************************************************************/
      97            0 : static bool backend_upload_packs(const dbm_pack_t *pack_a,
      98              :                                  const dbm_pack_t *pack_b,
      99              :                                  backend_context_t *ctx) {
     100              : #if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
     101              :   return dbm_multiply_gpu_upload_packs(pack_a, pack_b, &ctx->gpu);
     102              : #else
     103            0 :   (void)pack_a; // mark as used
     104            0 :   (void)pack_b;
     105            0 :   (void)ctx;
     106            0 :   return false;
     107              : #endif
     108              : }
     109              : 
     110              : /*******************************************************************************
     111              :  * \brief Private routine for sending a batch to the multiplication backend.
     112              :  * \author Ole Schuett
     113              :  ******************************************************************************/
     114       385000 : static void backend_process_batch(const int ntasks,
     115              :                                   const dbm_task_t batch[ntasks],
     116              :                                   const double alpha, const dbm_pack_t *pack_a,
     117              :                                   const dbm_pack_t *pack_b, const int kshard,
     118              :                                   dbm_shard_t *shard_c, const bool finish,
     119              :                                   const bool force_cpu,
     120              :                                   backend_context_t *ctx) {
     121       385000 :   if (NULL != ctx) {
     122              : #if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
     123              :     if (!force_cpu) {
     124              :       dbm_multiply_gpu_process_batch(ntasks, batch, alpha, shard_c, kshard,
     125              :                                      finish, &ctx->gpu);
     126              :     } else
     127              : #endif
     128              :     {
     129       385000 :       (void)kshard;
     130       385000 :       (void)finish;
     131       385000 :       (void)force_cpu;
     132       385000 :       dbm_multiply_cpu_process_batch(ntasks, batch, alpha, pack_a, pack_b,
     133              :                                      shard_c, ctx->cpu_options);
     134              :     }
     135              :   } else { // Validate against host (aka CPU).
     136            0 :     dbm_multiply_cpu_process_batch(ntasks, batch, alpha, pack_a, pack_b,
     137              :                                    shard_c, DBM_MULTIPLY_BLAS_LIBRARY);
     138              :   }
     139       385000 : }
     140              : 
     141              : /*******************************************************************************
     142              :  * \brief Private routine for shutting down the multiplication backend.
     143              :  * \author Ole Schuett
     144              :  ******************************************************************************/
     145       349938 : static void backend_stop(backend_context_t *ctx) {
     146              : #if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
     147              :   dbm_multiply_gpu_stop(&ctx->gpu);
     148              : #endif
     149       349938 :   free(ctx);
     150       349938 : }
     151              : 
     152              : /*******************************************************************************
     153              :  * \brief Private routine returning the seconds since tick, which it resets.
     154              :  * \author Hans Pabst
     155              :  ******************************************************************************/
     156      3254394 : static double phase_lap(double *tick) {
     157      3254394 :   const double now = omp_get_wtime();
     158      3254394 :   const double result = now - *tick;
     159      3254394 :   *tick = now;
     160      3254394 :   return result;
     161              : }
     162              : 
     163              : /*******************************************************************************
     164              :  * \brief Private routine for multiplying two packs (C += alpha * A * B).
     165              :  *
     166              :  * Blocks in each pack are grouped by shard (free_index % nshards) and sorted
     167              :  * by sum_index within each group. The algorithm:
     168              :  *  1. Builds shard-boundary lookup tables for A (rows) and B (cols).
     169              :  *  2. For each (shard_row, shard_col) pair, determines the contiguous A and B
     170              :  *     block ranges belonging to that shard.
     171              :  *  3. Performs a merge-join over sum_index: advances A and B cursors in
     172              :  *lockstep, caching the B sub-range for each sum_index so that multiple A blocks
     173              :  *with the same sum_index reuse it without rescanning.
     174              :  *  4. Applies a norm-based filter (alpha^2 * norm_a * norm_b < eps) for early
     175              :  *     rejection before looking up or allocating the C block.
     176              :  *  5. Accumulates matching pairs into a batched GEMM task list, flushing to the
     177              :  *     backend (CPU or GPU) every DBM_MAX_BATCH_SIZE tasks.
     178              :  *
     179              :  * \author Ole Schuett and Hans Pabst
     180              :  ******************************************************************************/
     181       384922 : static void multiply_packs(const bool transa, const bool transb,
     182              :                            const double alpha, const dbm_pack_t *pack_a,
     183              :                            const dbm_pack_t *pack_b,
     184              :                            const dbm_matrix_t *matrix_a,
     185              :                            const dbm_matrix_t *matrix_b, dbm_matrix_t *matrix_c,
     186              :                            const float *rows_max_eps,
     187              :                            const bool retain_sparsity, const bool force_cpu,
     188              :                            int64_t *flop, backend_context_t *ctx) {
     189              :   // For validation, FLOPS do not count, and relying on ctx is not necessary.
     190       384922 :   backend_context_t *const context = (NULL != flop ? ctx : NULL);
     191       384922 :   const float alpha2 = (float)(alpha * alpha);
     192       384922 :   int64_t flop_sum = 0;
     193              : 
     194       384922 :   const int nshard_rows = matrix_c->dist->rows.nshards;
     195       384922 :   const int nshard_cols = matrix_c->dist->cols.nshards;
     196       384922 :   int *shard_row_start = calloc(nshard_rows, sizeof(int));
     197       384922 :   int *shard_col_start = calloc(nshard_cols, sizeof(int));
     198       384922 :   assert(NULL != shard_row_start && NULL != shard_col_start);
     199              : 
     200       384922 :   const int *sum_index_sizes_a =
     201              :       (transa) ? matrix_a->row_sizes : matrix_a->col_sizes;
     202       384922 :   const int *sum_index_sizes_b =
     203              :       (transb) ? matrix_b->col_sizes : matrix_b->row_sizes;
     204       384922 :   const int *free_index_sizes_a =
     205              :       (transa) ? matrix_a->col_sizes : matrix_a->row_sizes;
     206       384922 :   const int *free_index_sizes_b =
     207              :       (transb) ? matrix_b->row_sizes : matrix_b->col_sizes;
     208              : 
     209       384922 : #pragma omp parallel reduction(+ : flop_sum)
     210              :   {
     211              :     // Thread-private array covering given work in piece-wise fashion.
     212              :     dbm_task_t *batch =
     213              :         offload_mempool_host_malloc(sizeof(dbm_task_t) * DBM_MAX_BATCH_SIZE);
     214              :     // Thread-local stats, added to the library's counters once at the end.
     215              :     int64_t counters[DBM_NUM_COUNTERS] = {0};
     216              : 
     217              :     // Blocks are ordered first by shard. Creating lookup tables of boundaries.
     218              : #pragma omp for nowait
     219              :     for (int iblock = 1; iblock < pack_a->nblocks; iblock++) {
     220              :       const int shard_row = pack_a->blocks[iblock].free_index % nshard_rows;
     221              :       const int prev_shard_row =
     222              :           pack_a->blocks[iblock - 1].free_index % nshard_rows;
     223              :       if (prev_shard_row != shard_row) {
     224              :         shard_row_start[shard_row] = iblock;
     225              :       }
     226              :     }
     227              : #pragma omp for
     228              :     for (int jblock = 1; jblock < pack_b->nblocks; jblock++) {
     229              :       const int shard_col = pack_b->blocks[jblock].free_index % nshard_cols;
     230              :       const int prev_shard_col =
     231              :           pack_b->blocks[jblock - 1].free_index % nshard_cols;
     232              :       if (prev_shard_col != shard_col) {
     233              :         shard_col_start[shard_col] = jblock;
     234              :       }
     235              :     }
     236              : 
     237              : #pragma omp for collapse(2) DBM_OMP_SCHEDULE
     238              :     for (int shard_row = 0; shard_row < nshard_rows; shard_row++) {
     239              :       for (int shard_col = 0; shard_col < nshard_cols; shard_col++) {
     240              :         const int ishard = shard_row * nshard_cols + shard_col;
     241              :         dbm_shard_t *const shard_c = &matrix_c->shards[ishard];
     242              :         int ntasks = 0;
     243              : 
     244              :         // Determine contiguous block ranges for this shard in A and B.
     245              :         // Use a merge-join to find pairs of blocks with matching sum indices.
     246              :         // This utilizes that blocks within a shard are ordered by sum_index.
     247              :         const int iblock_start = shard_row_start[shard_row];
     248              :         int iblock_end = pack_a->nblocks;
     249              :         for (int t = iblock_start; t < pack_a->nblocks; ++t) {
     250              :           if (pack_a->blocks[t].free_index % nshard_rows != shard_row) {
     251              :             iblock_end = t;
     252              :             break;
     253              :           }
     254              :         }
     255              :         const int jblock_start = shard_col_start[shard_col];
     256              :         int jblock_end = pack_b->nblocks;
     257              :         for (int t = jblock_start; t < pack_b->nblocks; ++t) {
     258              :           if (pack_b->blocks[t].free_index % nshard_cols != shard_col) {
     259              :             jblock_end = t;
     260              :             break;
     261              :           }
     262              :         }
     263              :         if (iblock_start >= iblock_end || jblock_start >= jblock_end) {
     264              :           backend_process_batch(ntasks, batch, alpha, pack_a, pack_b, ishard,
     265              :                                 shard_c, true, force_cpu, context);
     266              :           continue;
     267              :         }
     268              : 
     269              :         // Merge over sum_index (both ranges sorted by sum_index).
     270              :         // Cache the B sub-range for each sum_index so that multiple A blocks
     271              :         // sharing the same sum_index reuse it without re-scanning B.
     272              :         int i = iblock_start, j = jblock_start, last_sum_index = -1;
     273              :         int b_range_start = -1, b_range_end = -1;
     274              : 
     275              :         while (i < iblock_end) {
     276              :           const dbm_pack_block_t *blk_a = &pack_a->blocks[i];
     277              :           const int sum_a = blk_a->sum_index;
     278              : 
     279              :           // Advance j until sum_b >= sum_a.
     280              :           while (j < jblock_end && pack_b->blocks[j].sum_index < sum_a) {
     281              :             ++j;
     282              :           }
     283              :           if (j >= jblock_end) {
     284              :             break; // No more matches possible.
     285              :           }
     286              : 
     287              :           const int sum_b = pack_b->blocks[j].sum_index;
     288              :           if (sum_b > sum_a) {
     289              :             ++i;
     290              :             continue; // Need next A block with higher sum_index.
     291              :           }
     292              : 
     293              :           // sum_a == sum_b: establish (or reuse) B range with this sum_index.
     294              :           if (sum_a != last_sum_index) {
     295              :             b_range_start = j;
     296              :             int t = j + 1;
     297              :             while (t < jblock_end && pack_b->blocks[t].sum_index == sum_a) {
     298              :               ++t;
     299              :             }
     300              :             b_range_end = t;
     301              :             last_sum_index = sum_a;
     302              :           }
     303              : 
     304              :           // Iterate over B blocks in current sum_index range.
     305              :           for (int jb = b_range_start; jb < b_range_end; ++jb) {
     306              :             const dbm_pack_block_t *const blk_b = &pack_b->blocks[jb];
     307              : 
     308              :             // Overlap the two dependent cache misses of upcoming lookups.
     309              :             if (jb + DBM_PREFETCH_SLOT < b_range_end) {
     310              :               dbm_shard_prefetch_slot(
     311              :                   shard_c, blk_a->free_index,
     312              :                   pack_b->blocks[jb + DBM_PREFETCH_SLOT].free_index);
     313              :             }
     314              :             if (jb + DBM_PREFETCH_BLOCK < b_range_end) {
     315              :               dbm_shard_prefetch_block(
     316              :                   shard_c, blk_a->free_index,
     317              :                   pack_b->blocks[jb + DBM_PREFETCH_BLOCK].free_index);
     318              :             }
     319              : 
     320              :             // Norm filter first (early reject).
     321              :             const float result_norm = alpha2 * blk_a->norm * blk_b->norm;
     322              :             if (result_norm < rows_max_eps[blk_a->free_index]) {
     323              :               continue;
     324              :             }
     325              : 
     326              :             // Check block sizes.
     327              :             const int m = free_index_sizes_a[blk_a->free_index];
     328              :             const int n = free_index_sizes_b[blk_b->free_index];
     329              :             const int k = sum_index_sizes_a[sum_a];
     330              :             assert(m == matrix_c->row_sizes[blk_a->free_index]);
     331              :             assert(n == matrix_c->col_sizes[blk_b->free_index]);
     332              :             assert(k == sum_index_sizes_b[blk_b->sum_index]);
     333              : 
     334              :             if (m == 0 || n == 0 || k == 0) {
     335              :               continue;
     336              :             }
     337              : 
     338              :             // Get C block.
     339              :             const int row = blk_a->free_index, col = blk_b->free_index;
     340              :             dbm_block_t *blk_c = dbm_shard_lookup(shard_c, row, col);
     341              :             if (blk_c == NULL) {
     342              :               if (retain_sparsity) {
     343              :                 continue;
     344              :               }
     345              :               assert(dbm_get_shard_index(matrix_c, row, col) == ishard);
     346              :               assert(dbm_get_stored_coordinates(matrix_c, row, col) ==
     347              :                      matrix_c->dist->my_rank);
     348              :               blk_c = dbm_shard_promise_new_block(shard_c, row, col, m * n);
     349              :             }
     350              : 
     351              :             // Count flops.
     352              :             const int64_t task_flops = 2LL * m * n * k;
     353              :             flop_sum += task_flops;
     354              :             ++counters[dbm_library_counter_index(m, n, k)];
     355              : 
     356              :             // Add block multiplication to batch.
     357              :             dbm_task_t *const tptr = &batch[ntasks];
     358              :             tptr->offset_a = blk_a->offset;
     359              :             tptr->offset_b = blk_b->offset;
     360              :             tptr->offset_c = blk_c->offset;
     361              :             tptr->m = m;
     362              :             tptr->n = n;
     363              :             tptr->k = k;
     364              :             ++ntasks;
     365              : 
     366              :             if (ntasks == DBM_MAX_BATCH_SIZE) {
     367              :               backend_process_batch(ntasks, batch, alpha, pack_a, pack_b,
     368              :                                     ishard, shard_c, false, force_cpu, context);
     369              :               ntasks = 0;
     370              :             }
     371              :           }
     372              : 
     373              :           // Advance i; if next A block has same sum_index, B range is reused.
     374              :           ++i;
     375              :         }
     376              :         backend_process_batch(ntasks, batch, alpha, pack_a, pack_b, ishard,
     377              :                               shard_c, true, force_cpu, context);
     378              :       }
     379              :     }
     380              : 
     381              :     dbm_library_counters_add(counters);
     382              :     offload_mempool_host_free(batch);
     383              :   }
     384              : 
     385       384922 :   free(shard_row_start);
     386       384922 :   free(shard_col_start);
     387              : 
     388       384922 :   if (NULL != flop) {
     389       384922 :     *flop += flop_sum;
     390              :   }
     391       384922 : }
     392              : 
     393              : /*******************************************************************************
     394              :  * \brief Performs a multiplication of two dbm_matrix_t matrices.
     395              :  *        See dbm_matrix.h for details.
     396              :  * \author Ole Schuett
     397              :  ******************************************************************************/
     398       349938 : void dbm_multiply(const bool transa, const bool transb, const double alpha,
     399              :                   const dbm_matrix_t *matrix_a, const dbm_matrix_t *matrix_b,
     400              :                   const double beta, dbm_matrix_t *matrix_c,
     401              :                   const bool retain_sparsity, const double filter_eps,
     402              :                   int64_t *flop) {
     403       349938 :   assert(omp_get_num_threads() == 1);
     404       349938 :   assert(matrix_a != NULL && matrix_b != NULL && matrix_c != NULL);
     405       349938 :   double phase[DBM_NUM_PHASES] = {0}, tick = omp_get_wtime();
     406       349938 :   int64_t phase_bytes[DBM_NUM_PHASES] = {0};
     407              : 
     408              :   // Throughout the matrix multiplication code the "sum_index" and "free_index"
     409              :   // denote the summation (aka dummy) and free index from the Einstein notation.
     410       349938 :   const int num_sum_index_a = (transa) ? matrix_a->nrows : matrix_a->ncols;
     411       349938 :   const int num_sum_index_b = (transb) ? matrix_b->ncols : matrix_b->nrows;
     412       349938 :   const int num_free_index_a = (transa) ? matrix_a->ncols : matrix_a->nrows;
     413       349938 :   const int num_free_index_b = (transb) ? matrix_b->nrows : matrix_b->ncols;
     414              : 
     415              :   // Sanity check matrix dimensions.
     416       349938 :   assert(num_sum_index_a == num_sum_index_b);
     417       349938 :   assert(num_free_index_a == matrix_c->nrows);
     418       349938 :   assert(num_free_index_b == matrix_c->ncols);
     419              : 
     420              :   // Prepare matrix_c (host).
     421       349938 :   dbm_scale(matrix_c, beta);
     422              : 
     423              :   // Determine if validation shall be performed.
     424       349938 :   const char *const maxeps_env = getenv("DBM_MULTIPLY_MAXEPS");
     425       349938 :   const char *const verify_env = getenv("DBM_MULTIPLY_VERIFY");
     426       349938 :   const double maxeps = (NULL == maxeps_env ? 1E-1 : fabs(atof(maxeps_env)));
     427       699876 :   const int verify =
     428       349938 :       (NULL == verify_env ? (NULL == maxeps_env ? 0 : 1) : atoi(verify_env));
     429       349938 :   dbm_matrix_t *matrix_d = NULL;
     430       349938 :   phase[DBM_PHASE_SETUP] += phase_lap(&tick);
     431       349938 :   if (0 != verify) {
     432            0 :     dbm_distribution_t *const dist_shared = matrix_c->dist;
     433            0 :     dbm_create(&matrix_d, dist_shared, matrix_c->name, matrix_c->nrows,
     434            0 :                matrix_c->ncols, matrix_c->row_sizes, matrix_c->col_sizes);
     435            0 :     dbm_copy(matrix_d, matrix_c);
     436            0 :     tick = omp_get_wtime(); // validation is not a phase
     437              :   }
     438              : 
     439              :   // Compute filter thresholds for each row.
     440       349938 :   float *rows_max_eps = compute_rows_max_eps(transa, matrix_a, filter_eps);
     441              : 
     442              :   // Start uploading matrix_c to the GPU.
     443       349938 :   backend_context_t *ctx = backend_start(matrix_c);
     444       349938 :   phase[DBM_PHASE_SETUP] += phase_lap(&tick);
     445              : 
     446              :   // Redistribute matrix_a and matrix_b across MPI ranks.
     447       349938 :   dbm_comm_iterator_t *iter =
     448       349938 :       dbm_comm_iterator_start(transa, transb, matrix_a, matrix_b, matrix_c);
     449       349938 :   const double redistribute = phase_lap(&tick);
     450       349938 :   phase[DBM_PHASE_ALLTOALL] += iter->seconds_alltoall;
     451       349938 :   phase[DBM_PHASE_SORT] += iter->seconds_sort;
     452       349938 :   phase_bytes[DBM_PHASE_ALLTOALL] += iter->bytes_alltoall;
     453       349938 :   phase[DBM_PHASE_PACK] +=
     454       349938 :       redistribute - iter->seconds_alltoall - iter->seconds_sort;
     455              : 
     456              :   // Count flops if requested.
     457       349938 :   if (NULL != flop) {
     458       349938 :     *flop = 0;
     459              :   }
     460              : 
     461              :   // Main loop.
     462              :   dbm_pack_t *pack_a, *pack_b;
     463       734860 :   while (dbm_comm_iterator_next(iter, &pack_a, &pack_b)) {
     464       384922 :     phase[DBM_PHASE_SHIFT] += phase_lap(&tick);
     465       384922 :     const bool uploaded = backend_upload_packs(pack_a, pack_b, ctx);
     466       384922 :     (void)uploaded; // mark used
     467       384922 :     phase[DBM_PHASE_UPLOAD] += phase_lap(&tick);
     468       384922 :     multiply_packs(transa, transb, alpha, pack_a, pack_b, matrix_a, matrix_b,
     469              :                    matrix_c, rows_max_eps, retain_sparsity, false /*!uploaded*/,
     470              :                    flop, ctx);
     471       384922 :     phase[DBM_PHASE_MULTIPLY] += phase_lap(&tick);
     472              :   }
     473              : 
     474              :   // Wait for all other MPI ranks to complete, then release ressources.
     475       349938 :   phase_bytes[DBM_PHASE_SHIFT] += iter->bytes_shift;
     476       349938 :   dbm_comm_iterator_stop(iter);
     477       349938 :   phase[DBM_PHASE_SHIFT] += phase_lap(&tick);
     478       349938 :   backend_stop(ctx);
     479       349938 :   phase[DBM_PHASE_FINISH] += phase_lap(&tick);
     480              : 
     481       349938 :   if (NULL != matrix_d) {
     482            0 :     ctx = backend_start(matrix_d);
     483            0 :     iter =
     484            0 :         dbm_comm_iterator_start(transa, transb, matrix_a, matrix_b, matrix_d);
     485            0 :     while (dbm_comm_iterator_next(iter, &pack_a, &pack_b)) {
     486            0 :       multiply_packs(transa, transb, alpha, pack_a, pack_b, matrix_a, matrix_b,
     487              :                      matrix_d, rows_max_eps, retain_sparsity, true, NULL, ctx);
     488              :     }
     489            0 :     dbm_comm_iterator_stop(iter);
     490            0 :     backend_stop(ctx);
     491            0 :     const double epsilon = dbm_maxeps(matrix_d, matrix_c);
     492            0 :     if (maxeps < epsilon) {
     493            0 :       if (1 == verify) {
     494            0 :         fprintf(stderr, "WARN ACC/LIBDBM: diff=%g\n", epsilon);
     495              :       } else {
     496            0 :         fprintf(stderr, "ERROR ACC/LIBDBM: diff=%g\n", epsilon);
     497            0 :         exit(EXIT_FAILURE);
     498              :       }
     499              :     }
     500            0 :     dbm_release(matrix_d);
     501            0 :     tick = omp_get_wtime(); // validation is not a phase
     502              :   }
     503              : 
     504              :   // Release filter thresholds.
     505       349938 :   free(rows_max_eps);
     506              : 
     507              :   // Final filter pass.
     508       349938 :   dbm_filter(matrix_c, filter_eps);
     509       349938 :   phase[DBM_PHASE_SETUP] += phase_lap(&tick);
     510       349938 :   dbm_library_phases_add(phase, phase_bytes);
     511       349938 : }
     512              : 
     513              : // EOF
        

Generated by: LCOV version 2.0-1