cublasSgemm函數詳解
cublasSgemm是CUDA的cublas庫的矩陣相乘函數,由於cublas中矩陣的存儲是列優先,所以cublasSgemm函數的參數容易讓人誤解,防止忘記,留筆記如下。
首先,在一般的C/C++程序中,我們開闢一段連續的內存,放入1,2,3,4,5,6,7,8,9,指定矩陣行和列均爲3,則可表示矩陣[1,2,3 ; 4,5,6 ; 7,8,9],然而,在使用cublas時,這樣表示出來的矩陣應該是[1,4,7; 2,5,8; 3,6,9]。cublas的cublasSgemm函數完成
直接看例子吧,註釋中記錄參數意義。
看到一個總結很不錯,引用一下,:
如果前邊的參數是’CUBLAS_OP_T’,那麼leading dimesion 就是矩陣的列數,因爲此時的矩陣是按照C語言以行優先的方式來存儲的;反之如果前邊的參數是’CUBLAS_OP_N’,那麼leading dimesion 就是矩陣的行數,此時的矩陣保持CUBLAS的列優先存儲方式。
按轉置方式進行求解C=AB
// CUDA runtime 庫 + CUBLAS 庫
#include "cuda_runtime.h"
#include "cublas_v2.h"
#include <iostream>
#include <stdlib.h>
using namespace std;
// 定義測試矩陣的維度
int const A_ROW = 5;
int const A_COL = 6;
int const B_ROW = 6;
int const B_COL = 7;
int main()
{
// 定義狀態變量
cublasStatus_t status;
float *h_A,*h_B,*h_C; //存儲於內存中的矩陣
h_A = (float*)malloc(sizeof(float)*A_ROW*A_COL); //在內存中開闢空間
h_B = (float*)malloc(sizeof(float)*B_ROW*B_COL);
h_C = (float*)malloc(sizeof(float)*A_ROW*B_COL);
// 爲待運算矩陣的元素賦予 0-10 範圍內的隨機數
for (int i=0; i<A_ROW*A_COL; i++) {
h_A[i] = (float)(rand()%10+1);
}
for(int i=0;i<B_ROW*B_COL; i++) {
h_B[i] = (float)(rand()%10+1);
}
// 打印待測試的矩陣
cout << "矩陣 A :" << endl;
for (int i=0; i<A_ROW*A_COL; i++){
cout << h_A[i] << " ";
if ((i+1)%A_COL == 0) cout << endl;
}
cout << endl;
cout << "矩陣 B :" << endl;
for (int i=0; i<B_ROW*B_COL; i++){
cout << h_B[i] << " ";
if ((i+1)%B_COL == 0) cout << endl;
}
cout << endl;
float *d_A,*d_B,*d_C; //存儲於顯存中的矩陣
cudaMalloc((void**)&d_A,sizeof(float)*A_ROW*A_COL); //在顯存中開闢空間
cudaMalloc((void**)&d_B,sizeof(float)*B_ROW*B_COL);
cudaMalloc((void**)&d_C,sizeof(float)*A_ROW*B_COL);
cublasHandle_t handle;
cublasCreate(&handle);
cudaMemcpy(d_A,h_A,sizeof(float)*A_ROW*A_COL,cudaMemcpyHostToDevice); //數據從內存拷貝到顯存
cudaMemcpy(d_B,h_B,sizeof(float)*B_ROW*B_COL,cudaMemcpyHostToDevice);
float a = 1, b = 0;
cublasSgemm(
handle,
CUBLAS_OP_T, //矩陣A的屬性參數,轉置,按行優先
CUBLAS_OP_T, //矩陣B的屬性參數,轉置,按行優先
A_ROW, //矩陣A、C的行數
B_COL, //矩陣B、C的列數
A_COL, //A的列數,B的行數,此處也可爲B_ROW,一樣的
&a, //alpha的值
d_A, //左矩陣,爲A
A_COL, //A的leading dimension,此時選擇轉置,按行優先,則leading dimension爲A的列數
d_B, //右矩陣,爲B
B_COL, //B的leading dimension,此時選擇轉置,按行優先,則leading dimension爲B的列數
&b, //beta的值
d_C, //結果矩陣C
A_ROW //C的leading dimension,C矩陣一定按列優先,則leading dimension爲C的行數
);
//此時得到的結果便是C=AB,但由於C是按列優先,故此時得到的C應該是正確結果的轉置
std::cout << "計算結果的轉置 ( (A*B)的轉置 ):" << std::endl;
cudaMemcpy(h_C,d_C,sizeof(float)*A_ROW*B_COL,cudaMemcpyDeviceToHost);
for(int i=0;i<A_ROW*B_COL;++i) {
std::cout<<h_C[i]<<" ";
if((i+1)%B_COL==0) std::cout<<std::endl;
}
cudaFree(d_A);
cudaFree(d_B);
cudaFree(d_C);
free(h_A);
free(h_B);
free(h_C);
return 0;
}
不按轉置方式直接求解
前一種方法求得的C是正確結果的轉置,那麼我們可以直接求
// CUDA runtime 庫 + CUBLAS 庫
#include "cuda_runtime.h"
#include "cublas_v2.h"
#include <iostream>
#include <stdlib.h>
using namespace std;
// 定義測試矩陣的維度
int const A_ROW = 5;
int const A_COL = 6;
int const B_ROW = 6;
int const B_COL = 7;
int main()
{
// 定義狀態變量
cublasStatus_t status;
float *h_A,*h_B,*h_C; //存儲於內存中的矩陣
h_A = (float*)malloc(sizeof(float)*A_ROW*A_COL); //在內存中開闢空間
h_B = (float*)malloc(sizeof(float)*B_ROW*B_COL);
h_C = (float*)malloc(sizeof(float)*A_ROW*B_COL);
// 爲待運算矩陣的元素賦予 0-10 範圍內的隨機數
for (int i=0; i<A_ROW*A_COL; i++) {
h_A[i] = (float)(rand()%10+1);
}
for(int i=0;i<B_ROW*B_COL; i++) {
h_B[i] = (float)(rand()%10+1);
}
// 打印待測試的矩陣
cout << "矩陣 A :" << endl;
for (int i=0; i<A_ROW*A_COL; i++){
cout << h_A[i] << " ";
if ((i+1)%A_COL == 0) cout << endl;
}
cout << endl;
cout << "矩陣 B :" << endl;
for (int i=0; i<B_ROW*B_COL; i++){
cout << h_B[i] << " ";
if ((i+1)%B_COL == 0) cout << endl;
}
cout << endl;
float *d_A,*d_B,*d_C; //存儲於顯存中的矩陣
cudaMalloc((void**)&d_A,sizeof(float)*A_ROW*A_COL); //在顯存中開闢空間
cudaMalloc((void**)&d_B,sizeof(float)*B_ROW*B_COL);
cudaMalloc((void**)&d_C,sizeof(float)*A_ROW*B_COL);
cublasHandle_t handle;
cublasCreate(&handle);
cudaMemcpy(d_A,h_A,sizeof(float)*A_ROW*A_COL,cudaMemcpyHostToDevice); //數據從內存拷貝到顯存
cudaMemcpy(d_B,h_B,sizeof(float)*B_ROW*B_COL,cudaMemcpyHostToDevice);
float a = 1, b = 0;
cublasSgemm(
handle,
CUBLAS_OP_N, //矩陣A的屬性參數,不轉置,按列優先
CUBLAS_OP_N, //矩陣B的屬性參數,不轉置,按列優先
B_COL, //矩陣B^T、C^T的行數
A_ROW, //矩陣A^T、C^T的列數
B_ROW, //B^T的列數,A^T的行數,此處也可爲A_COL,一樣的
&a, //alpha的值
d_B, //左矩陣,爲B^T
B_COL, //B^T的leading dimension,按列優先,則leading dimension爲B^T的行數(B的列數)
d_A, //右矩陣,爲A^T
A_COL, //A^T的leading dimension,按列優先,則leading dimension爲A^T的行數(A的列數)
&b, //beta的值
d_C, //結果矩陣C
B_COL //C^T的leading dimension,C^T矩陣一定按列優先,則leading dimension爲C^T的行數(C的列數)
);
//此時得到的結果便是C=AB,但由於C是按列優先,故此時得到的C應該是正確結果的轉置
std::cout << "計算結果的轉置 ( (A*B)的轉置 ):" << std::endl;
cudaMemcpy(h_C,d_C,sizeof(float)*A_ROW*B_COL,cudaMemcpyDeviceToHost);
for(int i=0;i<A_ROW*B_COL;++i) {
std::cout<<h_C[i]<<" ";
if((i+1)%B_COL==0) std::cout<<std::endl;
}
cudaFree(d_A);
cudaFree(d_B);
cudaFree(d_C);
free(h_A);
free(h_B);
free(h_C);
return 0;
}