前言
我现在入CUDA的大坑啦,大概看了一下CUDA并行计算的基本编程方法,现在开始 学习CUDA的基本线性代数库cuBLAS。因为我刚开始学,所以不要期待有太多干货。 本文其实主要是库文档的内容,只不过官方文档没有中文版,这个就当做是学习笔记吧。
cuBLAS使用
从版本4.0开始,除了现有的旧版API外,cuBLAS库还提供了新的更新的API。 通过包含头文件“ cublas_v2.h”来使用新的cuBLAS库API。
从6.5版开始,cuBLAS库在Linux和Mac OS上也以静态形式libcublas_static.a交付。 静态cuBLAS库和所有其他静态数学库都依赖于称为libculibos.a的公共线程抽象层库。
例如,在Linux上,要使用cuBLAS针对动态库编译小型应用程序,可以使用以下命令:
nvcc myCublasApp.c -lcublas -o myCublasApp
而要针对静态cuBLAS库进行编译,则必须使用以下命令:
nvcc myCublasApp.c -lcublas_static -lculibos -o myCublasApp
数据布局
为了与现有的Fortran环境最大程度地兼容,cuBLAS库使用列主存储和基于1的索引。 由于C和C ++使用行优先存储,因此用这些语言编写的应用程序不能对二维数组使用本机数组语义。 相反,应定义宏或内联函数以在一维数组的顶部实现矩阵。对于以机械方式移植到C的Fortran代码, 可以选择保留基于1的索引,以避免需要转换循环。在这种情况下, 可以通过以下宏计算“ i”行和“ j”列中矩阵元素的数组索引
#define IDX2F(i,j,ld)(((((j)-1)*(ld))+((i)-1))
在这里,ld是指矩阵的leading dimension,在列为主存储的情况下, 它是分配的矩阵的行数(即使仅使用其子矩阵)。 对于本机编写的C和C ++代码,很可能会选择基于0的索引,在这种情况下, 可以通过以下宏计算“ i”行和“ j”列中矩阵元素的数组索引
#define IDX2C(i,j,ld)(((j)*(ld))+(i))
解释一下,比如说: 矩阵空间是 3x4,其左上角有一个子矩阵2x3,表示如下
11 22 33 0 44 55 66 0 0 0 0 0
i, j分别表示行索引,列索引。如果用列存储的话,leading dimension = 3 (矩阵空间的行个数), 数组索引与元素位置的换算公式是i + j *ld,数组在C语言中存储为
[11, 44, 0, 22, 55, 0, 33, 66, 0, 0, 0, 0]
如果是用行存储, leading dimension = 4(矩阵空间的列个数),换算公式是 i*ld + j
基本使用
初始化句柄
cublasStatus_t cublasCreate(cublasHandle_t *handle) cublasStatus_t cublasDestroy(cublasHandle_t handle)
第一个函数将初始化CUBLAS库,并为保存CUBLAS库上下文创建一个句柄。 它在主机和设备上分配硬件资源,并且在进行任何其他CUBLAS库调用时必须使用它。 CUBLAS库上下文绑定到当前CUDA设备。要在多个设备上使用该库, 需要为每个设备创建一个CUBLAS句柄。此外,对于给定的设备,可以创建具有不同配置的多个CUBLAS句柄。 因为cublasCreate 调用分配一些内部资源并且cublasDestroy 释放这些资源将隐式调用 cublasDeviceSynchronize,建议尽量减少cublasCreate /cublasDestroy 。 对于在不同线程中使用同一设备的多线程应用程序,建议的编程模式是为每个线程创建一个CUBLAS句柄, 并在整个线程寿命中使用该CUBLAS句柄。
向量、矩阵在device和host间的数据交互
cublasStatus_t cublasSetVector(int n,int elemSize, const void * x,int incx,void * y,int incy) cublasStatus_t cublasGetVector(int n, int elemSize, const void *x, int incx, void *y, int incy) cublasStatus_t cublasSetMatrix(int rows, int cols, int elemSize, const void *A, int lda, void *B, int ldb) cublasStatus_t cublasGetMatrix(int rows, int cols, int elemSize, const void *A, int lda, void *B, int ldb)
cublasSetVector 把在主机内存空间中的向量x的n个元素复制到GPU内存空间中作为向量y。 默认假定两个向量中的元素的大小都为elemSize个字节。连续元素之间的存储间距为incx 用于源向量 X 和通过 印西 为目的地矢量 ÿ。
一般来说, y指向通过cublasAlloc() 分配了空间的一个对象或对象的一部分 。 由于假定了二维矩阵采用列主格式,如果这个向量是矩阵的一部分,则向量增量等于1个访问该矩阵的(部分)列。 类似地,使用等于矩阵的前导维的增量会导致访问该矩阵的(部分)行。
cublasGetVector 与第一个函数刚好相反,是将向量从GPU内存空间复制到主机内存空间。
cublasSetMatrix 与cublasGetMatrix 同上面两个类似,只不过操作的是矩阵。 lda和ldb分别是A,B矩阵的leading dimension 。
cublasStatus_t cublasGetVectorAsync(int n,int elemSize,const void * devicePtr,int incx, void * hostPtr,int incy,cudaStream_t stream)
此函数与cublasGetVector() 功能相同,但使用给定的CUDA™流参数以异步方式(相对于主机)进行数据传输。 上面的其他几个函数都有对应的 Async 函数。
运算API
总览
cuBLAS中把基本的运算API分成3个level 。从level 1 到level 3 分别定义了向量-向量运算、 向量矩阵运算、矩阵矩阵运算。 介绍时与官方文档一致,我将使用缩写< type >代表类型(type),使用< t >代替对应的短类型, 以更简洁明了地介绍所实现的功能。除非另有说明,否则< type >和< t >具有以下含义:
< type > | < t > | 意思 |
---|---|---|
float | ‘s’ or ‘S’ | 单精度浮点实数 |
double | ‘d’ or ‘D’ | 双精度浮点实数 |
cuComplex | ‘c’ or ‘C’ | 单精度浮点复数 |
cuDoubleComplex | ‘z’ or ‘Z’ | 双精度浮点复数 |
例如, cublasI< t >amax()代表以下函数族:
cublasStatus_t cublasIsamax(cublasHandle_t handle, int n, const float *x, int incx, int *result) cublasStatus_t cublasIdamax(cublasHandle_t handle, int n, const double *x, int incx, int *result) cublasStatus_t cublasIcamax(cublasHandle_t handle, int n, const cuComplex *x, int incx, int *result) cublasStatus_t cublasIzamax(cublasHandle_t handle, int n, const cuDoubleComplex *x, int incx, int *result)
它们分别是用来处理上述四种数据的。
当函数的参数和返回值不同时(有时在复杂的输入中会发生这种情况),< t >还可以具有以下含义: “ Sc”,“ Cs”,“ Dz”和“ Zd”。
缩写Re(。)和Im(。)分别代表数字的实部和虚部。由于实数的虚部不存在,因此我们将其视为零, 并且通常可以简单地从使用它的方程式中将其丢弃。另外, α ̄ 将表示的复共轭 α 。
小写的希腊符号 α 和 β 表示标量,小写英文字母x, y将表示向量,而大写英文字母A, B 和 C 将表示矩阵。
我不会介绍所有的API,事实上,函数接口基本上都是相似的,而且可以从函数名很容易猜测出函数功能, 参考官方文档困难不会很大。
level 1
cublas< t >asum() 计算向量内元素的绝对值(或者模)的和。签名示例:
cublasStatus_t cublasSasum(cublasHandle_t handle, int n, const float *x, int incx, float *result)
cublas< t >axpy() 将向量 x 与标量 α 相乘并将其加到向量 y 上,用计算结果覆盖y 变量。 签名示例:
cublasStatus_t cublasSaxpy(cublasHandle_t handle, int n, const float *alpha, const float *x, int incx, float *y, int incy)
cublas
cublasStatus_t cublasScopy(cublasHandle_t handle, int n, const float *x, int incx, float *y, int incy)
cublas
cublasStatus_t cublasSdot (cublasHandle_t handle, int n, const float *x, int incx, const float *y, int incy, float *result)