MKL是否针对*大订单优化CBLA?
我正在使用MKL是否针对*大订单优化CBLA?,c,algorithm,matrix,blas,intel-mkl,C,Algorithm,Matrix,Blas,Intel Mkl,我正在使用mklcblas_dgemm,目前它与CblasRowMajor,CblasNoTrans,CblasNoTrans一起用于我的矩阵 我知道c是行主语言,而dgemm是列主算法。我想知道,如果我链接到mkl,切换矩阵的顺序是否会对cblas_ggemm算法产生任何影响。mkl是否足够聪明,可以在幕后做一些我试图优化矩阵乘法的事情?如果不是,用mkl执行矩阵乘法的最佳方法是什么?TL;DR:简言之使用行主顺序还是列主顺序与MKL(以及其他BLAS实现)执行矩阵乘法并不重要 我知道c是一
mkl
cblas_dgemm
,目前它与CblasRowMajor
,CblasNoTrans
,CblasNoTrans
一起用于我的矩阵
我知道
c
是行主语言,而dgemm
是列主算法。我想知道,如果我链接到mkl
,切换矩阵的顺序是否会对cblas_ggemm
算法产生任何影响。mkl
是否足够聪明,可以在幕后做一些我试图优化矩阵乘法的事情?如果不是,用mkl
执行矩阵乘法的最佳方法是什么?TL;DR:简言之使用行主顺序还是列主顺序与MKL(以及其他BLAS实现)执行矩阵乘法并不重要
我知道c是一种行主语言,而dgemm是一种列主算法 DGEMM是而不是一种列主算法,它是BLAS接口,用于计算具有一般矩阵的矩阵积。DGEMM(以及大多数BLAS)的通用参考实现是用Fortran编写的。它假定列主顺序的唯一原因是因为Fortran是一种列主顺序语言。DGEMM(以及相应的BLAS 3级功能)不是专门针对列主要数据的 DGEMM计算什么? 基础数学中的DGEMM执行2D。二维矩阵相乘的标准算法要求一个矩阵沿其行遍历,另一个沿其列遍历。要执行矩阵乘法,AB=C,我们将A的行乘以B的列,以生成C。因此,输入矩阵的顺序并不重要,因为一个矩阵必须沿其行遍历,另一个沿其列遍历 用MKL研究行主和列主DGEMM计算 “英特尔MKL”非常聪明,可以在幕后利用这一点,并为行主数据和列主数据提供完全相同的性能 及 将以类似的性能执行。我们可以用一个相对简单的程序来测试这一点
#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);
}
我们可以看到,列主排序时间和行主排序时间之间的差异非常小<列主调的strong>0.0595秒,行主调的strong>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
我想我曾经读过它,但我现在找不到它。为什么不将两个大矩阵相乘,看看是否有差异?您似乎假设有一个DGEMM实现,用于正常和转置有序情况。在性能优化的BLAS(如MKL)中,几乎肯定不是这样,将DGEMM称为“算法”可能是不正确的。这是一个由一个或多个算法实现的函数,这些算法的选择取决于您作为程序员不应该知道或不需要知道的内部启发式。谢谢。我一直想做这样的分析,但被其他项目耽搁了。你调查过行与列对非方矩阵的影响吗?我有非平方矩阵(800x10),我将其乘以转置,得到一个条件矩阵(10x10),我想知道当行和列的维度不相同时,这种分析是否成立。@Laurbert515我刚刚对这些矩阵的大小进行了基准测试,行主键和列主键之间的差异约为1微秒,在我的笔记本电脑上计算大约需要5微秒。在我的回答中,有时行主修更快,有时列主修更快。英特尔MKL多数情况下会根据输入矩阵大小选择最佳方法来计算结果。然而,有时您确实需要自己执行基准测试来确定最佳方法。对于大多数人(和案例)来说,这无关紧要。我执行了几个测试(通过我们的C#MKL包装器),发现“col”和“row”矩阵之间存在真正的差异(因子x2)。下一个代码(向量作为矩阵X矩阵)MKLAccess.AlignedMath.cblasdgemm(NotTranspose,NotTranspose,1.0,vectorMatrDouble,matrixMatrDouble,0.0,resultMatrDouble);此外,我还测试了cblas_dgemm与cblas_sgemm,由于“列”的原因,测试同时显示,但在“行”的情况下,显示“x2”因子。测试数据:cblas_dgemm(A[1,35]xb[35125324]=>R[1125324]
#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);
}
$ ./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
$ ./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