Cublas是一个可以与cuda一同使用的函数库,它提供了多种矩阵运算的API,但是它列主序的存储方式却让人十分疑惑,今天我就以cublas中的矩阵乘法运算简单说一下我的理解。
Cublas中的矩阵乘法运算函数有5个,分别是cublasSgemm、cublasDgemm、cublasCgemm、cublasZgemm、cublasHgemm,分别包括了不同数据类型的计算,比如单精度浮点、双精度浮点、已经包括了复数等的运算。
我们查看一个具体的函数:
cublasStatus_t cublasSgemm(
cublasHandle_t handle,
cublasOperation_t transa,
cublasOperation_t transb,
int m, int n, int k,
const float *alpha,
const float *A,
int lda,
const float *B,
int ldb,
const float *beta,
float *C, int ldc);
在文档中对该函数的介绍:
该函数实现了矩阵运算C= αop(A)op(B)+βC,op就是指使用原矩阵运算还是使用转置之后的矩阵运算。其中m、k为第一个矩阵的行列数,k、n为第二个矩阵行列数。
初一看,并没有什么奇怪的地方,但是需要注意上边的一点:column-major format,列主序。他这个列主序是什么意思呢,下面我通过一个例子来解释一下。
我们待计算的矩阵有
A4*3 = [1,2,3,
4, 5,6,
7,8, 9,
10,11,12]
B3*2 = [1,2,
3,4,
5,6]
若我们令m=4,k=3,n=2,令transa和transb均为CUBLAS_OP_N,那它会不会进行如下图的矩阵乘法?
图1
答案是不会的,我们需要注意一下前边提到的列主序。
对于一个数组A4*3= [1,2,3,4,5,6,7,8,9,10,11,12],cublas在填充矩阵时,是按照列的顺序来填充的,比如对于数组A,若将其填充到一个4*3的矩阵中,它是这样填充的:
图2
所以,若参数按前边那样的话,实际进行的运算是这样的:
图3
那么,如果我们还是想进行如图1的运算,该怎么办呢?这就用到了transa和transb这2个参数。将这2个参数设置为CUBLAS_OP_T,它会做一个矩阵的转置,最终的结果就是如图1所示。需要注意的是,参数m、k、n是转置后矩阵的行列数。
上边图3中的矩阵计算结果如下图:
那么,该矩阵是如何存储的呢?
计算结果矩阵也遵循列主序的方式,所以,若将上图矩阵从GPU拷贝回主机会发现实际是以下面的数组的方式存放:
C = [38, 44, 50, 56, 83, 98,113,128]。