目录
一.难点所在
解决了困扰我很久的cublas默认列主序load和store数据的结算结果不对齐的问题。
git clone https://gitee.com/xukang888/cublas-sgeem.git
#include <stdio.h>
#include <cuda_runtime.h>
#include <cublas_v2.h>
#define M 2
#define N 2
#define K 3
int main() {
cublasHandle_t handle;
cublasCreate(&handle);
// A: 2×3 (行主序)
float A[6] = {1, 2, 3, 4, 5, 6};
// B: 3×2 (行主序)
float B[6] = {7, 8, 9, 10, 11, 12};
// C: 2×2 结果矩阵
float C[4] = {0};
float alpha = 1.0f, beta = 0.0f;
// 分配 GPU 内存
float *d_A, *d_B, *d_C;
cudaMalloc(&d_A, sizeof(A));
cudaMalloc(&d_B, sizeof(B));
cudaMalloc(&d_C, sizeof(C));
cudaMemcpy(d_A, A, sizeof(A), cudaMemcpyHostToDevice);
cudaMemcpy(d_B, B, sizeof(B), cudaMemcpyHostToDevice);
cudaMemset(d_C, 0, sizeof(C));
// A 和 B 都是行主序,因此使用 CUBLAS_OP_T
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,N,M,K,&alpha,d_B,N,d_A,K,&beta,d_C,N);
// 拷贝结果回主机
cudaMemcpy(C, d_C, sizeof(C), cudaMemcpyDeviceToHost);
// 输出结果
printf("Result matrix C:\n");
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
printf("%f ", C[i * M + j]);
}
}
printf("\n");
cudaFree(d_A);
cudaFree(d_B);
cudaFree(d_C);
cublasDestroy(handle);
return 0;
}
二.难点解析
C++默认以行主序进行存储和读取,但是cublas默认利用列主序进行读取和存储如果我们利用行主序的数据输入cublas最终得到的结果会与预期结果不符合。
三.公式解析与函数调用
cublas默认列主序,那么我们如果预期Y=X@W;其实计算的是Y.T=X.T@W.T;geem运算结束之后,c++以默认行主序进行读取得到结果其实是Y=W@X;因此参数列表里面,权重矩阵W和变量X的位置应该互换;
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,N,M,K,&alpha,d_B,N,d_A,K,&beta,d_C,N)
在cublasSgeem参数列表里面d_B处于d_A之前;实际运算方式为Y.T = W.T@X.T;此时c++再以行主序进行访存,得到的结果就是(Y.T).T = Y=X@W;
四.cublas结果与numpy计算结果对比
root@ubuntu:/***/# nvcc 2.cublasSgemmA\@B.cu -lcublas -o geemA@B && ./geemA@B
Result matrix C:
58.000000 64.000000 139.000000 154.000000
>>> a = np.array([1, 2, 3, 4, 5, 6]).reshape(2,3)
>>> b = np.array([7, 8, 9, 10, 11, 12]).reshape(3,2)
>>> a@b
array([[ 58, 64],
[139, 154]])
实现cublas在不对数据进行手动转换行主序到列主序的情况下,实现计算结果对齐。