C++使用cuBLAS加速矩阵乘法运算

简介: C++使用cuBLAS加速矩阵乘法运算

将cuBLAS库的乘法运算进行了封装,方便了算法调用;

将原文的结果转置实现为了不转置,这样可以直接使用计算结果;

测试并更改了乘法参数,解决了原文中更改矩阵大小时报错的问题;

将int换成了long,缓解了矩阵过大时的越界问题;

将单精度cublasSgemm换成了cublasDgemm,缓解了矩阵元素数值过大时精度的丢失问题;

总的来说,本博客的代码利用cuBLAS库实现了两个矩阵相乘,提高了矩阵乘法的计算速度。


test.cpp

#include "cuda_runtime.h"
#include "cublas_v2.h"
#include <time.h>
#include <iostream>
using namespace std;
// cuBLAS实现矩阵乘法
long **matMult_cuBLAS(long **A, long **B, long rowSizeA, long colSizeA, long colSizeB, cublasHandle_t cuHandle){
  // 1.定义结果矩阵
    long** C = new long*[rowSizeA];
    for(long i = 0; i < rowSizeA; i++)
        C[i] = new long[colSizeB];
    // 2.在内存中为将要计算的矩阵开辟空间
    double* h_A = (double*)malloc(rowSizeA * colSizeA * sizeof(double));
    double* h_B = (double*)malloc(colSizeA * colSizeB * sizeof(double));
    double* h_C = (double*)malloc(rowSizeA * colSizeB * sizeof(double));
    // 3.初始化计算矩阵h_A和h_B
    for (long i = 0; i < rowSizeA; i++)
        for (long j = 0; j < colSizeA; j++)
            h_A[i * colSizeA + j] = (double)A[i][j];
    for (long i = 0; i < colSizeA; i++)
        for (long j = 0; j < colSizeB; j++)
            h_B[i * colSizeB + j] = (double)B[i][j];
    // 4.在显存中为将要计算矩阵与结果矩阵开辟空间
    double* d_A, * d_B, * d_C;
    cudaMalloc((void**)&d_A, rowSizeA * colSizeA * sizeof(double));
    cudaMalloc((void**)&d_B, colSizeA * colSizeB * sizeof(double));
    cudaMalloc((void**)&d_C, rowSizeA * colSizeB * sizeof(double));
    // 5.将CPU数据拷贝到GPU上
    cublasSetVector(rowSizeA * colSizeA, sizeof(double), h_A, 1, d_A, 1);
    cublasSetVector(colSizeA * colSizeB, sizeof(double), h_B, 1, d_B, 1);
    // 6.传递进矩阵相乘函数中的参数,具体含义请参考函数手册.并执行核函数,矩阵相乘
    double a = 1; double b = 0;
    cublasDgemm(cuHandle, CUBLAS_OP_T, CUBLAS_OP_T, rowSizeA, colSizeB, colSizeA, &a, d_A, colSizeA, d_B, colSizeB, &b, d_C, rowSizeA);
    // 7.从GPU中取出运算结果至CPU中去
    cublasGetVector(rowSizeA * colSizeB, sizeof(double), d_C, 1, h_C, 1);
  // 8.将结果赋值给结果矩阵
    for (long i = 0; i < rowSizeA; i++)
        for (long j=0; j<colSizeB; j++)
            C[i][j] = static_cast<long>(h_C[j * rowSizeA + i]);
    // 9.清理掉使用过的内存
    free(h_A); free(h_B); free(h_C); 
    cudaFree(d_A);cudaFree(d_B); cudaFree(d_C);
    return C;
}
// 构造一个随机二维数组(矩阵)
long** uniformMat(long rowSize, long colSize, long minValue, long maxValue) {
    long** mat = new long* [rowSize];
    for (long i = 0; i < rowSize; i++)
        mat[i] = new long[colSize];
    // srand(1024);
    srand((unsigned)time(NULL));  //随机数种子采用系统时钟
    for (long i = 0; i < rowSize; i++) {
        for (long j = 0; j < colSize; j++) {
            mat[i][j] = (long)(rand() % (maxValue - minValue + 1)) + minValue;
        }
    }
    return mat;
}
int main(void) 
{   
    // 创建并初始化 CUBLAS 库对象
    // 若是CUBLAS对象在主函数中初始化,cuBLAS方法在其他函数中调用,需要将cuHandle传入该函数,并在该函数内创建status对象
    cublasHandle_t cuHandle;
    cublasStatus_t status = cublasCreate(&cuHandle);
    if (status != CUBLAS_STATUS_SUCCESS)
    {
        if (status == CUBLAS_STATUS_NOT_INITIALIZED) {
            cout << "CUBLAS 对象实例化出错" << endl;
        }
        getchar ();
        return EXIT_FAILURE;
    }
    // 矩阵大小定义
    long rowSizeA = 3; // 矩阵A的行数
    long colSizeA = 4; // 矩阵A的列数和矩阵B的行数
    long colSizeB = 2; // 矩阵B的列数
    // 构造一个3行4列的矩阵A,矩阵元素在(0,4)内随机选取
    long **A = uniformMat(rowSizeA, colSizeA, 0, 4);
    // 构造一个4行2列的矩阵B,矩阵元素在(5,9)内随机选取
    long **B = uniformMat(colSizeA, colSizeB, 5, 9);
    // 输出矩阵A和B
    cout << "矩阵 A :" << endl;
    for (long i = 0; i < rowSizeA; i++) {
        for (long j = 0; j < colSizeA; j++) {
            cout << A[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
    cout << "矩阵 B :" << endl;
    for (long i = 0; i < colSizeA; i++) {
        for (long j = 0; j < colSizeB; j++) {
            cout << B[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
    // 使用cuBLAS进行矩阵乘法运算:C = A * B
    long **C = matMult_cuBLAS(A, B, rowSizeA, colSizeA, colSizeB, cuHandle);
    // 输出矩阵C,即运算结果
    cout << "矩阵 C :" << endl;
    for (long i = 0; i < rowSizeA; i++) {
        for (long j = 0; j < colSizeB; j++) {
            cout << C[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
    // 释放 CUBLAS 库对象
    cublasDestroy (cuHandle);
    return 0;
}


在终端输入:


nvcc -lcublas test.cpp -o t
./t
1
2
运算结果:
矩阵 A :
1 3 2 0 
2 1 2 1 
4 3 2 4 
矩阵 B :
6 8 
7 5 
7 6 
7 6 
矩阵 C :
41 35 
40 39 
87 83
相关文章
|
13天前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
128 75
|
13天前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
35 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
13天前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
37 12
|
13天前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
35 9
|
13天前
|
存储 算法 测试技术
【C++数据结构——线性表】求集合的并、交和差运算(头歌实践教学平台习题)【合集】
本任务要求编写程序求两个集合的并集、交集和差集。主要内容包括: 1. **单链表表示集合**:使用单链表存储集合元素,确保元素唯一且无序。 2. **求并集**:遍历两个集合,将所有不同元素加入新链表。 3. **求交集**:遍历集合A,检查元素是否在集合B中存在,若存在则加入结果链表。 4. **求差集**:遍历集合A,检查元素是否不在集合B中,若满足条件则加入结果链表。 通过C++代码实现上述操作,并提供测试用例验证结果。测试输入为两个集合的元素,输出为有序集合A、B,以及它们的并集、交集和差集。 示例测试输入: ``` a c e f a b d e h i ``` 预期输出:
37 7
|
13天前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
30 5
|
13天前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】顺序表的基本运算(头歌实践教学平台习题)【合集】
本文档介绍了线性表的基本运算任务,涵盖顺序表和链表的初始化、销毁、判定是否为空、求长度、输出、查找元素、插入和删除元素等内容。通过C++代码示例详细展示了每一步骤的具体实现方法,并提供了测试说明和通关代码。 主要内容包括: - **任务描述**:实现顺序表的基本运算。 - **相关知识**:介绍线性表的基本概念及操作,如初始化、销毁、判定是否为空表等。 - **具体操作**:详述顺序表和链表的初始化、求长度、输出、查找、插入和删除元素的方法,并附有代码示例。 - **测试说明**:提供测试输入和预期输出,确保代码正确性。 - **通关代码**:给出完整的C++代码实现,帮助完成任务。 文档
27 5
|
5月前
|
C++
C++ PCL 计算多个RT矩阵变换后的变换矩阵
C++ PCL 计算多个RT矩阵变换后的变换矩阵
65 0
|
7月前
|
C++
C++解决线性代数矩阵转置 小实践
【6月更文挑战第3天】C++解决线性代数矩阵转置
106 2
|
7月前
|
编译器 C++
《Effective C++ 改善程序与设计的55个具体做法》 第二章 构造/析构/赋值运算 笔记
《Effective C++ 改善程序与设计的55个具体做法》 第二章 构造/析构/赋值运算 笔记