cublasSgemm詳解

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函數完成C=αop(A)op(B)+βC 的計算,當需要計算C=AB時,顯然可以直接設置α=1,β=0 。其中op操作對決定矩陣是否轉置,即決定該矩陣是按照行優先還是列優先。當我們選擇CUBLAS_OP_N時表示不轉置,按列優先存儲;當我們選擇CUBLAS_OP_T時表示需要轉置,按行優先存儲。
這裏寫圖片描述
直接看例子吧,註釋中記錄參數意義。
看到一個總結很不錯,引用一下,:
如果前邊的參數是’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是正確結果的轉置,那麼我們可以直接求CT ,不就是正確結果了嗎?CT=(AB)T=BTAT ,此時參與運算的是BTAT ,那麼我們都不需要像上一種方法一樣選擇CUBLAS_OP_T參數來對AB進行轉置了,指定了op(A)和op(B)的維數,按列填充得到的就是BTAT 。所以此時直接選擇CUBLAS_OP_N就好了。代碼如下:

// 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章