LCOV - code coverage report
Current view: top level - modules/linalg - dgemm_arr.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 75 117 64.1 %
Date: 2024-05-13 05:06:06 Functions: 7 9 77.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*************************************************************************
       2             :  *
       3             :  *  Project
       4             :  *                         _____ _____  __  __ _____
       5             :  *                        / ____|  __ \|  \/  |  __ \
       6             :  *  ___  _ __   ___ _ __ | |  __| |__) | \  / | |__) |
       7             :  * / _ \| '_ \ / _ \ '_ \| | |_ |  ___/| |\/| |  ___/
       8             :  *| (_) | |_) |  __/ | | | |__| | |    | |  | | |
       9             :  * \___/| .__/ \___|_| |_|\_____|_|    |_|  |_|_|
      10             :  *      | |
      11             :  *      |_|
      12             :  *
      13             :  * Copyright (C) Akiel Aries, <akiel@akiel.org>, et al.
      14             :  *
      15             :  * This software is licensed as described in the file LICENSE, which
      16             :  * you should have received as part of this distribution. The terms
      17             :  * among other details are referenced in the official documentation
      18             :  * seen here : https://akielaries.github.io/openGPMP/ along with
      19             :  * important files seen in this project.
      20             :  *
      21             :  * You may opt to use, copy, modify, merge, publish, distribute
      22             :  * and/or sell copies of the Software, and permit persons to whom
      23             :  * the Software is furnished to do so, under the terms of the
      24             :  * LICENSE file. As this is an Open Source effort, all implementations
      25             :  * must be of the same methodology.
      26             :  *
      27             :  *
      28             :  *
      29             :  * This software is distributed on an AS IS basis, WITHOUT
      30             :  * WARRANTY OF ANY KIND, either express or implied.
      31             :  *
      32             :  ************************************************************************/
      33             : 
      34             : /** Double precision GEneral Matrix-Matrix product */
      35             : #include <cmath>
      36             : #include <limits>
      37             : #include <openGPMP/linalg/_dgemm.hpp>
      38             : #include <stddef.h>
      39             : #include <stdio.h>
      40             : #include <stdlib.h>
      41             : #include <time.h>
      42             : 
      43             : #if defined(__SSE2__)
      44             : 
      45             : #ifdef __cplusplus
      46             : extern "C" {
      47             : #endif
      48             : 
      49             : // ASM micro kernel function
      50             : /**
      51             :  * @brief Performs matrix-matrix multiplication (DGEMM) using an
      52             :  * assembly implementation It computes the product of matrices A and B,
      53             :  * scaled by alpha and beta, and stores the result in matrix C
      54             :  *
      55             :  * @param A Pointer to the first matrix (A) in row-major order
      56             :  * @param B Pointer to the second matrix (B) in row-major order
      57             :  * @param C Pointer to the result matrix (C) in row-major order
      58             :  * @param nextA Pointer to the next matrix A
      59             :  * @param nextB Pointer to the next matrix B
      60             :  * @param kl Value representing the remaining columns of matrix A
      61             :  * @param kb Value representing the remaining rows of matrix B
      62             :  * @param incRowC Increment for moving to the next row of matrix C
      63             :  * @param incColC Increment for moving to the next column of matrix C
      64             :  * @param alpha Scalar value to scale the product of matrices A and B
      65             :  * @param beta Scalar value to scale matrix C before adding the product
      66             :  *
      67             :  * @note This calls an Assembly implementation depending on detected
      68             :  * host system. x86 (SSE, AVX2) and ARM NEON supported
      69             :  */
      70             : extern void dgemm_kernel_asm(const double *A,
      71             :                              const double *B,
      72             :                              double *C,
      73             :                              const double *nextA,
      74             :                              const double *nextB,
      75             :                              long kl,
      76             :                              long kb,
      77             :                              long incRowC,
      78             :                              long incColC,
      79             :                              double alpha,
      80             :                              double beta);
      81             : 
      82             : #ifdef __cplusplus
      83             : }
      84             : #endif
      85             : 
      86             : #endif
      87             : 
      88       65536 : void gpmp::linalg::DGEMM::dgemm_micro_kernel(long kc,
      89             :                                              double alpha,
      90             :                                              const double *A,
      91             :                                              const double *B,
      92             :                                              double beta,
      93             :                                              double *C,
      94             :                                              long incRowC,
      95             :                                              long incColC,
      96             :                                              const double *nextA,
      97             :                                              const double *nextB) {
      98       65536 :     long kb = kc / 4;
      99       65536 :     long kl = kc % 4;
     100             : 
     101       65536 :     dgemm_kernel_asm(A,
     102             :                      B,
     103             :                      C,
     104             :                      nextA,
     105             :                      nextB,
     106             :                      kl,
     107             :                      kb,
     108             :                      incRowC,
     109             :                      incColC,
     110             :                      alpha,
     111             :                      beta);
     112       65536 : }
     113             : 
     114             : // MATRIX BUFFERS
     115             : double gpmp::linalg::DGEMM::DGEMM_BUFF_A[BLOCK_SZ_M * BLOCK_SZ_K];
     116             : double gpmp::linalg::DGEMM::DGEMM_BUFF_B[BLOCK_SZ_K * BLOCK_SZ_N];
     117             : double gpmp::linalg::DGEMM::DGEMM_BUFF_C[BLOCK_SZ_MR * BLOCK_SZ_NR];
     118             : 
     119             : // pack micro panels of size BLOCK_SZ_MR rows by k columns from A without
     120             : // padding
     121         256 : void gpmp::linalg::DGEMM::pack_micro_A(int k,
     122             :                                        const double *A,
     123             :                                        int incRowA,
     124             :                                        int incColA,
     125             :                                        double *buffer) {
     126             :     int i, j;
     127             : 
     128      262400 :     for (j = 0; j < k; ++j) {
     129     1310720 :         for (i = 0; i < BLOCK_SZ_MR; ++i) {
     130     1048576 :             buffer[i] = A[i * incRowA];
     131             :         }
     132      262144 :         buffer += BLOCK_SZ_MR;
     133      262144 :         A += incColA;
     134             :     }
     135         256 : }
     136             : 
     137             : // packs panels from A with padding if needed
     138           1 : void gpmp::linalg::DGEMM::pack_buffer_A(int mc,
     139             :                                         int kc,
     140             :                                         const double *A,
     141             :                                         int incRowA,
     142             :                                         int incColA,
     143             :                                         double *buffer) {
     144           1 :     int mp = mc / BLOCK_SZ_MR;
     145           1 :     int _mr = mc % BLOCK_SZ_MR;
     146             : 
     147             :     int i, j;
     148             : 
     149         257 :     for (i = 0; i < mp; ++i) {
     150         256 :         pack_micro_A(kc, A, incRowA, incColA, buffer);
     151         256 :         buffer += kc * BLOCK_SZ_MR;
     152         256 :         A += BLOCK_SZ_MR * incRowA;
     153             :     }
     154           1 :     if (_mr > 0) {
     155           0 :         for (j = 0; j < kc; ++j) {
     156           0 :             for (i = 0; i < _mr; ++i) {
     157           0 :                 buffer[i] = A[i * incRowA];
     158             :             }
     159           0 :             for (i = _mr; i < BLOCK_SZ_MR; ++i) {
     160           0 :                 buffer[i] = 0.0;
     161             :             }
     162           0 :             buffer += BLOCK_SZ_MR;
     163           0 :             A += incColA;
     164             :         }
     165             :     }
     166           1 : }
     167             : 
     168             : // packing complete panels from B of size BLOCK_SZ_NR by k columns
     169         256 : void gpmp::linalg::DGEMM::pack_micro_B(int k,
     170             :                                        const double *B,
     171             :                                        int incRowB,
     172             :                                        int incColB,
     173             :                                        double *buffer) {
     174             :     int i, j;
     175             : 
     176      262400 :     for (i = 0; i < k; ++i) {
     177     1310720 :         for (j = 0; j < BLOCK_SZ_NR; ++j) {
     178     1048576 :             buffer[j] = B[j * incColB];
     179             :         }
     180      262144 :         buffer += BLOCK_SZ_NR;
     181      262144 :         B += incRowB;
     182             :     }
     183         256 : }
     184             : 
     185             : // packing panels from B with padding if needed
     186           1 : void gpmp::linalg::DGEMM::pack_buffer_B(int kc,
     187             :                                         int nc,
     188             :                                         const double *B,
     189             :                                         int incRowB,
     190             :                                         int incColB,
     191             :                                         double *buffer) {
     192           1 :     int np = nc / BLOCK_SZ_NR;
     193           1 :     int _nr = nc % BLOCK_SZ_NR;
     194             : 
     195             :     int i, j;
     196             : 
     197         257 :     for (j = 0; j < np; ++j) {
     198         256 :         pack_micro_B(kc, B, incRowB, incColB, buffer);
     199         256 :         buffer += kc * BLOCK_SZ_NR;
     200         256 :         B += BLOCK_SZ_NR * incColB;
     201             :     }
     202           1 :     if (_nr > 0) {
     203           0 :         for (i = 0; i < kc; ++i) {
     204           0 :             for (j = 0; j < _nr; ++j) {
     205           0 :                 buffer[j] = B[j * incColB];
     206             :             }
     207           0 :             for (j = _nr; j < BLOCK_SZ_NR; ++j) {
     208           0 :                 buffer[j] = 0.0;
     209             :             }
     210           0 :             buffer += BLOCK_SZ_NR;
     211           0 :             B += incRowB;
     212             :         }
     213             :     }
     214           1 : }
     215             : 
     216             : // Compute Y += alpha*X (double precision AX + Y)
     217           0 : void gpmp::linalg::DGEMM::dgeaxpy(int m,
     218             :                                   int n,
     219             :                                   double alpha,
     220             :                                   const double *X,
     221             :                                   int incRowX,
     222             :                                   int incColX,
     223             :                                   double *Y,
     224             :                                   int incRowY,
     225             :                                   int incColY) {
     226             :     int i, j;
     227             : 
     228           0 :     if (fabs(alpha - 1.0) > std::numeric_limits<double>::epsilon()) {
     229             : 
     230           0 :         for (j = 0; j < n; ++j) {
     231           0 :             for (i = 0; i < m; ++i) {
     232           0 :                 Y[i * incRowY + j * incColY] +=
     233           0 :                     alpha * X[i * incRowX + j * incColX];
     234             :             }
     235             :         }
     236             :     }
     237             : 
     238             :     else {
     239           0 :         for (j = 0; j < n; ++j) {
     240           0 :             for (i = 0; i < m; ++i) {
     241           0 :                 Y[i * incRowY + j * incColY] += X[i * incRowX + j * incColX];
     242             :             }
     243             :         }
     244             :     }
     245           0 : }
     246             : 
     247             : //  Compute X *= alpha (scale elements)
     248           0 : void gpmp::linalg::DGEMM::dgescal(int m,
     249             :                                   int n,
     250             :                                   double alpha,
     251             :                                   double *X,
     252             :                                   int incRowX,
     253             :                                   int incColX) {
     254             :     int i, j;
     255             : 
     256           0 :     if (fabs(alpha - 0.0) > std::numeric_limits<double>::epsilon()) {
     257           0 :         for (j = 0; j < n; ++j) {
     258           0 :             for (i = 0; i < m; ++i) {
     259           0 :                 X[i * incRowX + j * incColX] *= alpha;
     260             :             }
     261             :         }
     262             :     }
     263             : 
     264             :     else {
     265           0 :         for (j = 0; j < n; ++j) {
     266           0 :             for (i = 0; i < m; ++i) {
     267           0 :                 X[i * incRowX + j * incColX] = 0.0;
     268             :             }
     269             :         }
     270             :     }
     271           0 : }
     272             : 
     273             : // Macro Kernel for the multiplication of blocks of A and B.  We assume that
     274             : // these blocks were previously packed to buffers DGEMM_BUFF_A and DGEMM_BUFF_B.
     275           1 : void gpmp::linalg::DGEMM::dgemm_macro_kernel(int mc,
     276             :                                              int nc,
     277             :                                              int kc,
     278             :                                              double alpha,
     279             :                                              double beta,
     280             :                                              double *C,
     281             :                                              int incRowC,
     282             :                                              int incColC) {
     283             : 
     284           1 :     int mp = (mc + BLOCK_SZ_MR - 1) / BLOCK_SZ_MR;
     285           1 :     int np = (nc + BLOCK_SZ_NR - 1) / BLOCK_SZ_NR;
     286             : 
     287           1 :     int _mr = mc % BLOCK_SZ_MR;
     288           1 :     int _nr = nc % BLOCK_SZ_NR;
     289             : 
     290             :     int mr, nr;
     291             :     int i, j;
     292             : 
     293             : #if defined(__SSE__)
     294             : 
     295           1 :     const double *nextA = nullptr;
     296           1 :     const double *nextB = nullptr;
     297             : 
     298             : #endif
     299             : 
     300         257 :     for (j = 0; j < np; ++j) {
     301         256 :         nr = (j != np - 1 || _nr == 0) ? BLOCK_SZ_NR : _nr;
     302             : 
     303       65792 :         for (i = 0; i < mp; ++i) {
     304       65536 :             mr = (i != mp - 1 || _mr == 0) ? BLOCK_SZ_MR : _mr;
     305             : 
     306       65536 :             if (mr == BLOCK_SZ_MR && nr == BLOCK_SZ_NR) {
     307             : #if defined(__SSE__)
     308       65536 :                 dgemm_micro_kernel(
     309             :                     kc,
     310             :                     alpha,
     311       65536 :                     &DGEMM_BUFF_A[i * kc * BLOCK_SZ_MR],
     312       65536 :                     &DGEMM_BUFF_B[j * kc * BLOCK_SZ_NR],
     313             :                     beta,
     314       65536 :                     &C[i * BLOCK_SZ_MR * incRowC + j * BLOCK_SZ_NR * incColC],
     315             :                     incRowC,
     316             :                     incColC,
     317             :                     nextA,
     318             :                     nextB);
     319             : 
     320             : #else
     321             :                 dgemm_micro_kernel(
     322             :                     kc,
     323             :                     alpha,
     324             :                     &DGEMM_BUFF_A[i * kc * BLOCK_SZ_MR],
     325             :                     &DGEMM_BUFF_B[j * kc * BLOCK_SZ_NR],
     326             :                     beta,
     327             :                     &C[i * BLOCK_SZ_MR * incRowC + j * BLOCK_SZ_NR * incColC],
     328             :                     incRowC,
     329             :                     incColC);
     330             : 
     331             : #endif
     332             :             }
     333             : 
     334             :             else {
     335             : 
     336             : #if defined(__SSE__)
     337           0 :                 dgemm_micro_kernel(kc,
     338             :                                    alpha,
     339           0 :                                    &DGEMM_BUFF_A[i * kc * BLOCK_SZ_MR],
     340           0 :                                    &DGEMM_BUFF_B[j * kc * BLOCK_SZ_NR],
     341             :                                    0.0,
     342             :                                    DGEMM_BUFF_C,
     343             :                                    1,
     344             :                                    BLOCK_SZ_MR,
     345             :                                    nextA,
     346             :                                    nextB);
     347             : #else
     348             :                 dgemm_micro_kernel(kc,
     349             :                                    alpha,
     350             :                                    &DGEMM_BUFF_A[i * kc * BLOCK_SZ_MR],
     351             :                                    &DGEMM_BUFF_B[j * kc * BLOCK_SZ_NR],
     352             :                                    0.0,
     353             :                                    DGEMM_BUFF_C,
     354             :                                    1,
     355             :                                    BLOCK_SZ_MR);
     356             : 
     357             : #endif
     358           0 :                 dgescal(
     359             :                     mr,
     360             :                     nr,
     361             :                     beta,
     362           0 :                     &C[i * BLOCK_SZ_MR * incRowC + j * BLOCK_SZ_NR * incColC],
     363             :                     incRowC,
     364             :                     incColC);
     365           0 :                 dgeaxpy(
     366             :                     mr,
     367             :                     nr,
     368             :                     1.0,
     369             :                     DGEMM_BUFF_C,
     370             :                     1,
     371             :                     BLOCK_SZ_MR,
     372           0 :                     &C[i * BLOCK_SZ_MR * incRowC + j * BLOCK_SZ_NR * incColC],
     373             :                     incRowC,
     374             :                     incColC);
     375             :             }
     376             :         }
     377             :     }
     378           1 : }
     379             : 
     380             : // Main DGEMM entrypoint, compute C <- beta*C + alpha*A*B
     381           1 : void gpmp::linalg::DGEMM::dgemm_nn(int m,
     382             :                                    int n,
     383             :                                    int k,
     384             :                                    double alpha,
     385             :                                    const double *A,
     386             :                                    int incRowA,
     387             :                                    int incColA,
     388             :                                    const double *B,
     389             :                                    int incRowB,
     390             :                                    int incColB,
     391             :                                    double beta,
     392             :                                    double *C,
     393             :                                    int incRowC,
     394             :                                    int incColC) {
     395           1 :     int mb = (m + BLOCK_SZ_M - 1) / BLOCK_SZ_M;
     396           1 :     int nb = (n + BLOCK_SZ_N - 1) / BLOCK_SZ_N;
     397           1 :     int kb = (k + BLOCK_SZ_K - 1) / BLOCK_SZ_K;
     398             : 
     399           1 :     int _mc = m % BLOCK_SZ_M;
     400           1 :     int _nc = n % BLOCK_SZ_N;
     401           1 :     int _kc = k % BLOCK_SZ_K;
     402             : 
     403             :     int mc, nc, kc;
     404             :     int i, j, l;
     405             : 
     406             :     double _beta;
     407             : 
     408           1 :     if (fabs(alpha) < std::numeric_limits<double>::epsilon() || k == 0) {
     409           0 :         dgescal(m, n, beta, C, incRowC, incColC);
     410           0 :         return;
     411             :     }
     412             : 
     413           2 :     for (j = 0; j < nb; ++j) {
     414           1 :         nc = (j != nb - 1 || _nc == 0) ? BLOCK_SZ_N : _nc;
     415             : 
     416           2 :         for (l = 0; l < kb; ++l) {
     417           1 :             kc = (l != kb - 1 || _kc == 0) ? BLOCK_SZ_K : _kc;
     418           1 :             _beta = (l == 0) ? beta : 1.0;
     419             : 
     420           1 :             pack_buffer_B(
     421             :                 kc,
     422             :                 nc,
     423           1 :                 &B[l * BLOCK_SZ_K * incRowB + j * BLOCK_SZ_N * incColB],
     424             :                 incRowB,
     425             :                 incColB,
     426             :                 DGEMM_BUFF_B);
     427             : 
     428           2 :             for (i = 0; i < mb; ++i) {
     429           1 :                 mc = (i != mb - 1 || _mc == 0) ? BLOCK_SZ_M : _mc;
     430             : 
     431           1 :                 pack_buffer_A(
     432             :                     mc,
     433             :                     kc,
     434           1 :                     &A[i * BLOCK_SZ_M * incRowA + l * BLOCK_SZ_K * incColA],
     435             :                     incRowA,
     436             :                     incColA,
     437             :                     DGEMM_BUFF_A);
     438             : 
     439           1 :                 dgemm_macro_kernel(
     440             :                     mc,
     441             :                     nc,
     442             :                     kc,
     443             :                     alpha,
     444             :                     _beta,
     445           1 :                     &C[i * BLOCK_SZ_M * incRowC + j * BLOCK_SZ_N * incColC],
     446             :                     incRowC,
     447             :                     incColC);
     448             :             }
     449             :         }
     450             :     }
     451             : }

Generated by: LCOV version 1.14