MKL是否针对*主要订单优化cblas?

drj*_*rm3 5 c algorithm matrix blas intel-mkl

我在用 mkl cblas_dgemm,目前拥有它CblasRowMajor,CblasNoTrans,CblasNotrans,我的矩阵.

我知道这c是一种主要的主要语言,而是dgemm一个主要的列算法.我很想知道cblas_dgemm如果我链接的话,切换矩阵的顺序是否会对算法产生任何影响mkl.是否mkl足够聪明,可以在幕后做一些我试图优化矩阵乘法的事情?如果不是,执行矩阵乘法的最佳方法是什么mkl

IKa*_*agh 7

TL; DR:简而言之,使用MKL(和其他BLAS实现)使用行主要列主要排序执行矩阵 - 矩阵乘法并不重要.


我知道c是行主要语言,而dgemm是列主要算法.

DGEMM是列优先算法,它是BLAS接口,用于计算与一般的矩阵的矩阵矩阵乘积.DGEMM(以及大多数BLAS)的通用参考实现是Netlib,它是用Fortran编写的.假设列主要排序的唯一原因是因为Fortran是列主要的顺序语言.DGEMM(以及相应的BLAS Level 3功能)不是专门用于列主要数据.

DGEMM计算什么?

基本数学中的DGEMM执行2D 矩阵 - 矩阵乘法.标准订购N ^ 3用于乘以2D矩阵的算法要求您沿其行遍历一个矩阵,沿着其列遍历另一个矩阵.为了执行矩阵的矩阵乘法,AB = Ç,我们将相乘的行通过的列以产生Ç.因此,输入矩阵的排序无关紧要,因为一个矩阵必须沿其行遍历,另一个矩阵沿其列遍历.

用MKL研究行主和列主要DGEMM计算

英特尔MKL非常智能,可以充分利用它,并为行主要列主要数据提供完全相同的性能.

cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, ...);
Run Code Online (Sandbox Code Playgroud)

cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, ...);
Run Code Online (Sandbox Code Playgroud)

将以类似的性能执行.我们可以用一个相对简单的程序来测试它

#include <float.h>
#include <mkl.h>
#include <omp.h>
#include <stdio.h>

void init_matrix(double *A, int n, int m, double d);
void test_dgemm(CBLAS_LAYOUT Layout, double *A, double *B, double *C, const MKL_INT m, const MKL_INT n, const MKL_INT k, int nSamples, double *timing);
void print_summary(const MKL_INT m, const MKL_INT n, const MKL_INT k, const int nSamples, const double *timing);

int main(int argc, char **argv) {
    MKL_INT n, k, m;
    double *a, *b, *c;
    double *timing;
    int nSamples = 1;

    if (argc != 5){
        fprintf(stderr, "Error: Wrong number of arguments!\n");
        fprintf(stderr, "usage: %s mMatrix nMatrix kMatrix NSamples\n", argv[0]);
        return -1;
    }

    m = atoi(argv[1]);
    n = atoi(argv[2]);
    k = atoi(argv[3]);

    nSamples = atoi(argv[4]);

    timing = malloc(nSamples * sizeof *timing);

    a = mkl_malloc(m*k * sizeof *a, 64);
    b = mkl_malloc(k*n * sizeof *a, 64);
    c = mkl_calloc(m*n, sizeof *a, 64);

    /** ROW-MAJOR ORDERING **/
    test_dgemm(CblasRowMajor, a, b, c, m, n, k, nSamples, timing);

    /** COLUMN-MAJOR ORDERING **/
    test_dgemm(CblasColMajor, a, b, c, m, n, k, nSamples, timing);

    mkl_free(a);
    mkl_free(b);
    mkl_free(c);
    free(timing);
}

void init_matrix(double *A, int n, int m, double d) {
    int i, j;
    #pragma omp for schedule (static) private(i,j)
    for (i = 0; i < n; ++i) {
        for (j = 0; j < m; ++j) {
            A[j + i*n] = d * (double) ((i - j) / n);
        }
    }
}

void test_dgemm(CBLAS_LAYOUT Layout, double *A, double *B, double *C, const MKL_INT m, const MKL_INT n, const MKL_INT k, int nSamples, double *timing) {
    int i;
    MKL_INT lda = m, ldb = k, ldc = m;
    double alpha = 1.0, beta = 0.0;

    if (CblasRowMajor == Layout) {
        printf("\n*****ROW-MAJOR ORDERING*****\n\n");
    } else if (CblasColMajor == Layout) {
        printf("\n*****COLUMN-MAJOR ORDERING*****\n\n");
    }

    init_matrix(A, m, k, 0.5);
    init_matrix(B, k, n, 0.75);
    init_matrix(C, m, n, 0);

    // First call performs any buffer/thread initialisation
    cblas_dgemm(Layout, CblasNoTrans, CblasNoTrans, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc);

    double tmin = DBL_MAX, tmax = 0.0;
    for (i = 0; i < nSamples; ++i) {
        init_matrix(A, m, k, 0.5);
        init_matrix(B, k, n, 0.75);
        init_matrix(C, m, n, 0);

        timing[i] = dsecnd();
        cblas_dgemm(Layout, CblasNoTrans, CblasNoTrans, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc);
        timing[i] = dsecnd() - timing[i];

        if (tmin > timing[i]) tmin = timing[i];
        else if (tmax < timing[i]) tmax = timing[i];
    }

    print_summary(m, n, k, nSamples, timing);
}

void print_summary(const MKL_INT m, const MKL_INT n, const MKL_INT k, const int nSamples, const double *timing) {
    int i;

    double tavg = 0.0;
    for(i = 0; i < nSamples; i++) {
        tavg += timing[i];
    }
    tavg /= nSamples;

    printf("#Loop | Sizes  m   n   k  | Time (s)\n");
    for(i = 0; i < nSamples; i++) {
        printf("%4d %12d %3d %3d  %6.4f\n", i + 1 , m, n, k, timing[i]);
    }

    printf("Summary:\n");
    printf("Sizes  m   n   k  | Avg. Time (s)\n");
    printf(" %8d %3d %3d %12.8f\n", m, n, k, tavg);
}
Run Code Online (Sandbox Code Playgroud)

在我的系统上产生

$ ./benchmark_dgemm 1000 1000 1000 5
*****ROW-MAJOR ORDERING*****

#Loop | Sizes  m   n   k  | Time (s)
   1         1000 1000 1000  0.0589
   2         1000 1000 1000  0.0596
   3         1000 1000 1000  0.0603
   4         1000 1000 1000  0.0626
   5         1000 1000 1000  0.0584
Summary:
Sizes  m   n   k  | Avg. Time (s)
     1000 1000 1000   0.05995692

*****COLUMN-MAJOR ORDERING*****

#Loop | Sizes  m   n   k  | Time (s)
   1         1000 1000 1000  0.0597
   2         1000 1000 1000  0.0610
   3         1000 1000 1000  0.0581
   4         1000 1000 1000  0.0594
   5         1000 1000 1000  0.0596
Summary:
Sizes  m   n   k  | Avg. Time (s)
     1000 1000 1000   0.05955171
Run Code Online (Sandbox Code Playgroud)

我们可以看到列主要排序时间和行主要排序时间之间的差别很小.0.0595秒列为主0.0599行为主.再次执行此操作可能会产生以下结果,其中行主要计算速度提高0.00003秒.

$ ./benchmark_dgemm 1000 1000 1000 5
*****ROW-MAJOR ORDERING*****

#Loop | Sizes  m   n   k  | Time (s)
   1         1000 1000 1000  0.0674
   2         1000 1000 1000  0.0598
   3         1000 1000 1000  0.0595
   4         1000 1000 1000  0.0587
   5         1000 1000 1000  0.0584
Summary:
Sizes  m   n   k  | Avg. Time (s)
     1000 1000 1000   0.06075310

*****COLUMN-MAJOR ORDERING*****

#Loop | Sizes  m   n   k  | Time (s)
   1         1000 1000 1000  0.0634
   2         1000 1000 1000  0.0596
   3         1000 1000 1000  0.0582
   4         1000 1000 1000  0.0582
   5         1000 1000 1000  0.0645
Summary:
Sizes  m   n   k  | Avg. Time (s)
     1000 1000 1000   0.06078266
Run Code Online (Sandbox Code Playgroud)