首页 > 代码库 > 使用 CUBLAS 库给矩阵运算提速

使用 CUBLAS 库给矩阵运算提速

前言

  编写 CUDA 程序真心不是个简单的事儿,调试也不方便,很费时。那么有没有一些现成的 CUDA 库来调用呢?

  答案是有的,如 CUBLAS 就是 CUDA 专门用来解决线性代数运算的库。

  本文将大致介绍如何使用 CUBLAS 库,同时演示一个使用 CUBLAS 库进行矩阵乘法的例子。

CUBLAS 内容

  CUBLAS 是 CUDA 专门用来解决线性代数运算的库,它分为三个级别:

  Lev1. 向量相乘

  Lev2. 矩阵乘向量

  Lev3. 矩阵乘矩阵

  同时该库还包含状态结构和一些功能函数。

CUBLAS 用法

  大体分成以下几个步骤:

  1. 定义 CUBLAS 库对象

  2. 在显存中为待运算的数据以及需要存放结果的变量开辟显存空间。( cudaMalloc 函数实现 )

  3. 将待运算的数据传输进显存。( cudaMemcpy,cublasSetVector 等函数实现 )

  3. 调用 CUBLAS 库函数 ( 根据 CUBLAS 手册调用需要的函数 )

  4. 从显存中获取结果变量。( cudaMemcpy,cublasGetVector 等函数实现 )

  5. 释放申请的显存空间以及 CUBLAS 库对象。( cudaFree 及 cublasDestroy 函数实现 )

代码示例

  如下程序使用 CUBLAS 库进行矩阵乘法运算,请仔细阅读注释,尤其是 API 的参数说明:

  1 // CUDA runtime 库 + CUBLAS 库 
  2 #include "cuda_runtime.h"
  3 #include "cublas_v2.h"
  4 //#include "cublas.h"
  5 
  6 #include <time.h>
  7 #include <iostream>
  8 
  9 using namespace std;
 10 
 11 // 定义测试矩阵的维度
 12 int const M = 5;
 13 int const N = 10;
 14 
 15 int main() 
 16 {   
 17     // 定义状态变量
 18     cublasStatus_t status;
 19 
 20     // 在 内存 中为将要计算的矩阵开辟空间
 21     float *h_A = (float*)malloc (N*M*sizeof(float));
 22     float *h_B = (float*)malloc (N*M*sizeof(float));
 23     
 24     // 在 内存 中为将要存放运算结果的矩阵开辟空间
 25     float *h_C = (float*)malloc (M*M*sizeof(float));
 26 
 27     // 为待运算矩阵的元素赋予 0-10 范围内的随机数
 28     for (int i=0; i<N*M; i++) {
 29         h_A[i] = (float)(rand()%10+1);
 30         h_B[i] = (float)(rand()%10+1);
 31     
 32     }
 33     
 34     // 打印待测试的矩阵
 35     cout << "矩阵 A :" << endl;
 36     for (int i=0; i<N*M; i++){
 37         cout << h_A[i] << " ";
 38         if ((i+1)%N == 0) cout << endl;
 39     }
 40     cout << endl;
 41     cout << "矩阵 B :" << endl;
 42     for (int i=0; i<N*M; i++){
 43         cout << h_B[i] << " ";
 44         if ((i+1)%M == 0) cout << endl;
 45     }
 46     cout << endl;
 47     
 48     /*
 49     ** GPU 计算矩阵相乘
 50     */
 51 
 52     // 创建并初始化 CUBLAS 库对象
 53     cublasHandle_t handle;
 54     status = cublasCreate(&handle);
 55     
 56     if (status != CUBLAS_STATUS_SUCCESS)
 57     {
 58         if (status == CUBLAS_STATUS_NOT_INITIALIZED) {
 59             cout << "CUBLAS 对象实例化出错" << endl;
 60         }
 61         getchar ();
 62         return EXIT_FAILURE;
 63     }
 64 
 65     float *d_A, *d_B, *d_C;
 66     // 在 显存 中为将要计算的矩阵开辟空间
 67     cudaMalloc (
 68         (void**)&d_A,    // 指向开辟的空间的指针
 69         N*M * sizeof(float)    // 需要开辟空间的字节数
 70     );
 71     cudaMalloc (
 72         (void**)&d_B,    
 73         N*M * sizeof(float)    
 74     );
 75 
 76     // 在 显存 中为将要存放运算结果的矩阵开辟空间
 77     cudaMalloc (
 78         (void**)&d_C,
 79         M*M * sizeof(float)    
 80     );
 81 
 82     // 将矩阵数据传递进 显存 中已经开辟好了的空间
 83     cublasSetVector (
 84         N*M,    // 要存入显存的元素个数
 85         sizeof(float),    // 每个元素大小
 86         h_A,    // 主机端起始地址
 87         1,        // 连续元素之间的存储间隔
 88         d_A,    // GPU 端起始地址
 89         1        // 连续元素之间的存储间隔
 90     );
 91     cublasSetVector (
 92         N*M, 
 93         sizeof(float), 
 94         h_B, 
 95         1, 
 96         d_B, 
 97         1
 98     );
 99 
100     // 同步函数
101     cudaThreadSynchronize();
102 
103     // 传递进矩阵相乘函数中的参数对应的形参必须是指针类型
104     float a=1; float b=0;
105     const float *ca = &a;
106     const float *cb = &b;
107     // 矩阵相乘。该函数必然将数组解析成列优先数组
108     cublasSgemm (
109         handle,            // blas 库对象 
110         CUBLAS_OP_T,    // 矩阵 A 属性参数
111         CUBLAS_OP_T,    // 矩阵 B 属性参数
112         M,                // A, C 的行数 
113         M,                // B, C 的列数
114         N,                // A 的列数和 B 的行数
115         ca,                // 运算式的 α 值
116         d_A,            // A 在显存中的地址
117         N,                // lda
118         d_B,            // B 在显存中的地址
119         M,                // ldb
120         cb,                // 运算式的 β 值
121         d_C,            // C 在显存中的地址(结果矩阵)
122         M                // ldc
123     );
124     
125     // 同步函数
126     cudaThreadSynchronize();
127 
128     // 从 显存 中取出运算结果至 内存中去
129     cublasGetVector (
130         M*M,    //  要取出元素的个数
131         sizeof(float),    // 每个元素大小
132         d_C,    // GPU 端起始地址
133         1,    // 连续元素之间的存储间隔
134         h_C,    // 主机端起始地址
135         1    // 连续元素之间的存储间隔
136     );
137     
138     // 打印运算结果
139     cout << "计算结果的转置 ( (A*B)的转置 ):" << endl;
140 
141     for (int i=0;i<M*M; i++){
142             cout << h_C[i] << " ";
143             if ((i+1)%M == 0) cout << endl;
144     }
145     
146     // 清理掉使用过的内存
147     free (h_A);
148     free (h_B);
149     free (h_C);
150     cudaFree (d_A);
151     cudaFree (d_B);
152     cudaFree (d_C);
153 
154     // 释放 CUBLAS 库对象
155     cublasDestroy (handle);
156 
157     getchar();
158     
159     return 0;
160 }

运行测试

  

  PS:矩阵元素是随机生成的

小结

  使用 CUDA 库固然方便,但也要仔细的参阅函数手册,其中每个参数的含义都要很清晰才不容易出错。