文章目录
- 前言
- 一、搭建环境
- 二、简单介绍
- 三、 具体例子
- 四、疑问
前言
CUBLAS库是NVIDIA CUDA用于线性代数计算的库。使用CUBLAS库的原因是我不想去直接写核函数。
(当然,你还是得学习核函数该怎么写。但是人家写好的肯定比我自己写的更准确!)
一、搭建环境
- 安装CUDA库,具体可以看我上一篇文章:在C++项目中集成CUDA程序加速(从环境配置讲起)
- 如果你是装在默认路径下,那么 CUBLAS库的头文件就在:
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.0\include
路径下面,以cublas
开头的.h
文件。 - 所以,还是按照步骤1的文章进行环境配置,然后只需要多在
添加依赖项
中增加一个cublas.lib
就可以了。
二、简单介绍
- CUBLAS Introdution 是官方文档。
(全英文的,还有不少数学公式。大家有不理解的可以直接留言区问相关API,我们一起讨论学习。)
- CUBLAS Samples 是官方示例,所有API都有。
- 对于API名称,都是
cublasl<t>...
,其中有下述类型选择:
CUBLAS库的矩阵是列向量的
,跟glm一致。CUBLAS对于矩阵或者向量的index是从1开始的
。所以,如果有函数的返回结果是个index(比如查找矩阵中的最大值),记得要index - 1才是我们要的。
三、 具体例子
下面我以矩阵与向量相乘的函数进行举例,看看是怎么用的。
- 首先,通过查找官方文档,知道是如下的函数:
cublasStatus_t cublasDgemv(cublasHandle_t handle, cublasOperation_t trans,
int m, int n,
const double *alpha,
const double *A, int lda,
const double *x, int incx,
const double *beta,
double *y, int incy)
/*
* handle : CUBLAS的句柄,用以管理CUBLAS库的上下文和资源
* CUBLAS_OP_N : 指定矩阵操作模式。CUBLAS_OP_N代表正常模式(列向量);CUBLAS_OP_T代表转置模式(行向量)
* m : 矩阵A的行数
* n : 矩阵A的列数
* alpha : 与矩阵A相乘的标量
* A : 指向存储在device上面的矩阵数据指针
* lda : 矩阵的列数,代表矩阵在内存中的存储方式
* x : 向量X
* incx : 向量x中相邻两个元素的index间隔,一般为1
* beta : 与向量y相乘的标量
* y : 向量y
* incy : 向量y中相邻两个元素的index间隔,一般为1
*/
具体计算公式如下:
- 如果我们只是想计算矩阵和向量相乘,那么我们只需要令
α = 1.0, β = 0.0
,然后传入我们要的A
和x
就行了。 - 最后,具体代码如下:
/// MyCublas.cuh
#pragma once
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "cublas_v2.h"
extern "C" void MatrixMulVectorCublas(
const double* matrix, const int row, const int col,
const double* vector, double* result
);
/// MyCublas.cu
#include "MyCublas.cuh"
#include "CublasUtility.h"
void MatrixMulVectorCublas(
const double* matrix, const int row, const int col,
const double* vector, double* result)
{
// 1. 初始化句柄
cublasHandle_t handle;
CUBLAS_CHECK(cublasCreate(&handle));
// 2. 分配内存
double* dev_matrix = NULL;
double* dev_vector = NULL;
CUDA_CHECK(cudaMalloc((void**)&dev_matrix, sizeof(double) * row * col));
CUDA_CHECK(cudaMalloc((void**)&dev_vector, sizeof(double) * row));
CUDA_CHECK(cudaMemcpy(dev_matrix, matrix, sizeof(double) * row * col, cudaMemcpyHostToDevice));
CUDA_CHECK(cudaMemcpy(dev_vector, vector, sizeof(double) * row, cudaMemcpyHostToDevice));
// 3. 执行矩阵乘法
double* dev_result = NULL;
CUDA_CHECK(cudaMalloc((void**)&dev_result, sizeof(double) * col));
CUDA_CHECK(cudaMemset(dev_result, 0, sizeof(double) * col));
const double alpha = 1.0;
const double beta = 0.0;
CUBLAS_CHECK(cublasDgemv(handle, CUBLAS_OP_N, row, col, &alpha, dev_matrix, col, dev_vector, 1, &beta, dev_result, 1));
CUDA_CHECK(cudaMemcpy(result, dev_result, sizeof(double) * col, cudaMemcpyDeviceToHost));
// 4. 释放内存
CUDA_CHECK(cudaFree(dev_matrix));
CUDA_CHECK(cudaFree(dev_vector));
CUDA_CHECK(cudaFree(dev_result));
CUBLAS_CHECK(cublasDestroy(handle));
}
/// CublasUtility.h
#pragma once
#include <string>
#include <stdexcept>
// CUDA API error checking
#define CUDA_CHECK(err) \
do { \
cudaError_t err_ = (err); \
if (err_ != cudaSuccess) { \
std::printf("CUDA error %d at %s:%d\n", err_, __FILE__, __LINE__); \
throw std::runtime_error("CUDA error"); \
} \
} while (0)
// cublas API error checking
#define CUBLAS_CHECK(err) \
do { \
cublasStatus_t err_ = (err); \
if (err_ != CUBLAS_STATUS_SUCCESS) { \
std::printf("cublas error %d at %s:%d\n", err_, __FILE__, __LINE__); \
throw std::runtime_error("cublas error"); \
} \
} while (0)
/// main.cpp
#include "MyCublas.cuh"
#include <iostream>
int main()
{
double matrix[12] = { 1.0, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0, 8.0,
9.0, 10.0, 11.0, 12.0};
double vector[4] = { 1.0, 2.0, 3.0};
double result[4] = { 0.0 };
MatrixMulVectorCublas(matrix, 3, 4, vector, result);
for (int i = 0; i < 4; ++i)
{
std::cout << result[i] << ", ";
}
return 0;
}
四、疑问
对于上述代码,我还有以下的疑问:
- 我在运行下面这句的时候,VS显示我的进程内存会到2.2GB左右,难道真的需要这么大吗?
CUBLAS_CHECK(cublasCreate(&handle));
- 上述代码运行的结果是:
38, 44, 50, 0
。但是实际结果应该是:38, 44, 50, 56
。查了很久还是没差出来为什么。希望有细心的小伙伴帮我检查一下!