两种方法利用CUDA实现矩阵乘法

2023-11-14

方法一:自己写

创建.cu文件。

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>
#include "cuda_runtime.h"

template <int BLOCK_SIZE>

__global__ void MatrixMulCUDA(float* C, float* A, float* B, int wA, int wB) 
{
    int bx = blockIdx.x;   // Block index
    int by = blockIdx.y;   // Block index
    int tx = threadIdx.x;  // Thread index  thIdx.x=threadIdx.x+blockDim.x*blockIdx.x
    int ty = threadIdx.y;  // Thread index  thIdx.y=threadIdx.y+blockDim.y*blockIdx.y
    
    int aBegin = wA * BLOCK_SIZE * by;  // 320*32*by      A的行划分10份
    int aEnd = aBegin + wA - 1;         // 320*32*by+319  每份320个值*(32)
    int aStep = BLOCK_SIZE;             // 32 per block   Astep = 32
    int bBegin = BLOCK_SIZE * bx;       // 32*bx          B的列划分20份  每份320*32个值
    int bStep = BLOCK_SIZE * wB;        // 32*640         Bstep = 
    float Csub = 0;
    // a=wA*32*by:32:wA*32*by+319  b=32*bx:32*wB:end
    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {
        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];  // 声明共享内存 32*32
        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];  // 声明共享内存 32*32
        As[ty][tx] = A[a + wA * ty + tx];   // load matrices from device to shared
        Bs[ty][tx] = B[b + wB * ty + tx];   // load matrices from device to shared
        __syncthreads();                    // make sure the matrices are loaded
#pragma unroll  // 用于循环展开
        for (int k = 0; k < BLOCK_SIZE; ++k) {
            Csub += As[ty][k] * Bs[k][tx];  // C_ty_tx = sum_k(A_ty_k * B_k_ty)
        }
        __syncthreads();                    // make sure computation is done
    }

    int c = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;  // Write to device memory;
    C[c + wB * ty + tx] = Csub;
}

void ConstantInit(float* data, int size, float val) {
    for (int i = 0; i < size; ++i) {
        data[i] = val;
    }
}

int MatrixMultiply(int block_size, const dim3& dimsA, const dim3& dimsB) 
{
    // 1. Allocate host memory
    float* h_A, * h_B, * h_C;
    unsigned int size_A = dimsA.x * dimsA.y;           // 320*320
    unsigned int mem_size_A = sizeof(float) * size_A;  // memory size
    cudaMallocHost(&h_A, mem_size_A);                  // Allocate host memory
    unsigned int size_B = dimsB.x * dimsB.y;           // 640*320
    unsigned int mem_size_B = sizeof(float) * size_B;  // memory size
    cudaMallocHost(&h_B, mem_size_B);                  // Allocate host memory

    ConstantInit(h_A, size_A, 1.0f);  // initial host memory
    ConstantInit(h_B, size_B, 0.1f);  // initial host memory

    dim3 dimsC(dimsB.x, dimsA.y, 1);                   // 640*320*1
    unsigned int size_C = dimsC.x * dimsC.y;           // 640*320
    unsigned int mem_size_C = sizeof(float) * size_C;  // memory size
    cudaMallocHost(&h_C, mem_size_C);                  // Allocate host memory
    if (h_C == NULL) {
        fprintf(stderr, "Failed to allocate host matrix C!\n");
        exit(EXIT_FAILURE);
    }

    // 2. Allocate device memory
    float* d_A, * d_B, * d_C;
    cudaMalloc(reinterpret_cast<void**>(&d_A), mem_size_A);
    cudaMalloc(reinterpret_cast<void**>(&d_B), mem_size_B);
    cudaMalloc(reinterpret_cast<void**>(&d_C), mem_size_C);

    cudaEvent_t start, stop;
    cudaEventCreate(&start);  // Allocate CUDA events for timing
    cudaEventCreate(&stop);   // Allocate CUDA events for timing

    cudaStream_t stream;
    cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking);  // 异步系统

    // 3. Copy host memory to device memory
    cudaMemcpyAsync(d_A, h_A, mem_size_A, cudaMemcpyHostToDevice, stream);  // copy
    cudaMemcpyAsync(d_B, h_B, mem_size_B, cudaMemcpyHostToDevice, stream);  // copy

    dim3 threads(block_size, block_size);                 // 32*32
    dim3 grid(dimsB.x / threads.x, dimsA.y / threads.y);  // 20*10

    // 4. Compute on GPU
    printf("Computing result using CUDA Kernel...\n");
    MatrixMulCUDA<32> <<< grid, threads, 0, stream >>> (d_C, d_A, d_B, dimsA.x, dimsB.x);
    printf("done\n");
    cudaStreamSynchronize(stream);

    // 5. Compute 300 times
    cudaEventRecord(start, stream);
    int nIter = 300;
    for (int j = 0; j < nIter; j++) {
        if (block_size == 32) {
            MatrixMulCUDA<32> <<< grid, threads, 0, stream >>> (d_C, d_A, d_B, dimsA.x, dimsB.x);
        }
    }
    cudaEventRecord(stop, stream);
    cudaEventSynchronize(stop);
    float msecTotal = 0.0f;
    cudaEventElapsedTime(&msecTotal, start, stop);

    float msecPerMatrixMul = msecTotal / nIter;
    double flopsPerMatrixMul = 2.0 * static_cast<double>(dimsA.x) *
        static_cast<double>(dimsA.y) * static_cast<double>(dimsB.x);
    double gigaFlops = (flopsPerMatrixMul * 1.0e-9f) / (msecPerMatrixMul / 1000.0f);
    printf(
        "Performance= %.2f GFlop/s, Time= %.3f msec, Size= %.0f Ops,"
        " WorkgroupSize= %u threads/block\n",
        gigaFlops, msecPerMatrixMul, flopsPerMatrixMul, threads.x * threads.y);

    // 6. Copy result from device to host
    cudaMemcpyAsync(h_C, d_C, mem_size_C, cudaMemcpyDeviceToHost, stream);
    cudaStreamSynchronize(stream);

    printf("Checking computed result for correctness: ");
    bool correct = true;
    double eps = 1.e-6;
    for (int i = 0; i < static_cast<int>(dimsC.x * dimsC.y); i++) {
        double abs_err = fabs(h_C[i] - (dimsA.x * 0.1f));
        double dot_length = dimsA.x;
        double abs_val = fabs(h_C[i]);
        double rel_err = abs_err / abs_val / dot_length;
        if (rel_err > eps) {
            printf("Error! Matrix[%05d]=%.8f, ref=%.8f error term is > %E\n", i,
                h_C[i], dimsA.x * 0.1f, eps);
            correct = false;
        }
    }
    printf("%s\n", correct ? "Result = PASS" : "Result = FAIL");

    // 7. Clean up memory
    cudaFreeHost(h_A);
    cudaFreeHost(h_B);
    cudaFreeHost(h_C);
    cudaFree(d_A);
    cudaFree(d_B);
    cudaFree(d_C);
    cudaEventDestroy(start);
    cudaEventDestroy(stop);

    if (correct) {
        return EXIT_SUCCESS;
    }
    else {
        return EXIT_FAILURE;
    }
}

int main() 
{
    int num_devices;
    cudaGetDeviceCount(&num_devices);

    struct cudaDeviceProp device_0_prop;
    cudaGetDeviceProperties(&device_0_prop, 0);

    printf("[Matrix Multiply Using CUDA] - Starting...\n");
    int block_size = 32;
    dim3 dimsA(5 * 2 * block_size, 5 * 2 * block_size, 1);  // 320*320
    dim3 dimsB(5 * 4 * block_size, 5 * 2 * block_size, 1);  // 640*320
    if (dimsA.x != dimsB.y) 
    {
        printf("Error: outer matrix dim must be equal. (%d != %d)\n", dimsA.x, dimsB.y);
        exit(EXIT_FAILURE);
    }
    printf("MatrixA(%d,%d), MatrixB(%d,%d)\n", dimsA.x, dimsA.y, dimsB.x, dimsB.y);
    int matrix_result = MatrixMultiply(block_size, dimsA, dimsB);  // 320*320*1  640*320*1
    exit(matrix_result);
}

方法二:利用cuBLAS函数

创建.cpp文件。

#include <stdlib.h>
#include <stdio.h>
#include <cuda_runtime.h>
#include <cublas_v2.h>

typedef struct _matrixSize
{
    unsigned int uiWA, uiHA, uiWB, uiHB, uiWC, uiHC;
} sMatrixSize;

void matrixMulCPU(float* C, const float* A, const float* B, unsigned int hA, unsigned int wA, unsigned int wB)
{
    for (unsigned int i = 0; i < hA; ++i)
        for (unsigned int j = 0; j < wB; ++j)
        {
            double sum = 0;

            for (unsigned int k = 0; k < wA; ++k)
            {
                double a = A[i * wA + k];
                double b = B[k * wB + j];
                sum += a * b;
            }

            C[i * wB + j] = (float)sum;
        }
}

void randomInit(float* data, int size)
{
    for (int i = 0; i < size; ++i)
        data[i] = rand() / (float)RAND_MAX;
}

void initializeCUDA(int& devID, int& iSizeMultiple, sMatrixSize& matrix_size)
{
    cudaDeviceProp deviceProp;
    cudaError_t error = cudaGetDeviceProperties(&deviceProp, devID);
    if (error != cudaSuccess)
    {
        printf("cudaGetDeviceProperties returned error code %d\n", error);
        exit(EXIT_FAILURE);
    }
    printf("GPU Device %d: \"%s\" with compute capability %d.%d\n\n", devID, deviceProp.name, deviceProp.major, deviceProp.minor);

    int block_size = 32;
    matrix_size.uiWA = 3 * block_size * iSizeMultiple;
    matrix_size.uiHA = 4 * block_size * iSizeMultiple;
    matrix_size.uiWB = 2 * block_size * iSizeMultiple;
    matrix_size.uiHB = 3 * block_size * iSizeMultiple;
    matrix_size.uiWC = 2 * block_size * iSizeMultiple;
    matrix_size.uiHC = 4 * block_size * iSizeMultiple;

    printf("MatrixA(%u,%u), MatrixB(%u,%u), MatrixC(%u,%u)\n",
        matrix_size.uiHA, matrix_size.uiWA,
        matrix_size.uiHB, matrix_size.uiWB,
        matrix_size.uiHC, matrix_size.uiWC);
}

int matrixMultiply(int devID, sMatrixSize& matrix_size)
{
    // allocate host memory for matrices A and B
    unsigned int size_A = matrix_size.uiWA * matrix_size.uiHA;
    unsigned int mem_size_A = sizeof(float) * size_A;
    float* h_A = (float*)malloc(mem_size_A);
    unsigned int size_B = matrix_size.uiWB * matrix_size.uiHB;
    unsigned int mem_size_B = sizeof(float) * size_B;
    float* h_B = (float*)malloc(mem_size_B);

    srand(2006);
    // initialize host memory
    randomInit(h_A, size_A);
    randomInit(h_B, size_B);

    // allocate host memory for the result
    unsigned int size_C = matrix_size.uiWC * matrix_size.uiHC;
    unsigned int mem_size_C = sizeof(float) * size_C;
    float* h_C = (float*)malloc(mem_size_C);

    // allocate device memory
    float* d_A, * d_B, * d_C;
    cudaMalloc((void**)&d_A, mem_size_A);
    cudaMalloc((void**)&d_B, mem_size_B);
    cudaMalloc((void**)&d_C, mem_size_C);

    // copy host to device
    cudaMemcpy(d_A, h_A, mem_size_A, cudaMemcpyHostToDevice);
    cudaMemcpy(d_B, h_B, mem_size_B, cudaMemcpyHostToDevice);

    // setup execution parameters
    int block_size = 32;
    dim3 threads(block_size, block_size);
    dim3 grid(matrix_size.uiWC / threads.x, matrix_size.uiHC / threads.y);

    printf("Computing result using CUBLAS...");
    // execute the kernel
    int nIter = 30;

    // CUBLAS version 2.0
    const float alpha = 1.0f;
    const float beta = 0.0f;
    cublasHandle_t handle;
    cudaEvent_t start, stop;

    cublasCreate(&handle);

    //Perform warmup operation with cublas
    cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, matrix_size.uiWB, matrix_size.uiHA, matrix_size.uiWA, 
        &alpha, d_B, matrix_size.uiWB, d_A, matrix_size.uiWA, &beta, d_C, matrix_size.uiWB);

    // Allocate CUDA events that we'll use for timing
    cudaEventCreate(&start);
    cudaEventCreate(&stop);
    cudaEventRecord(start, NULL);  // Record the start event
    for (int j = 0; j < nIter; j++)
    {
        // note cublas is column primary! need to transpose the order
        cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, matrix_size.uiWB, matrix_size.uiHA, matrix_size.uiWA, 
            &alpha, d_B, matrix_size.uiWB, d_A, matrix_size.uiWA, &beta, d_C, matrix_size.uiWB);
    }
    printf("done.\n");
    cudaEventRecord(stop, NULL);  // Record the stop event
    cudaEventSynchronize(stop);  // Wait for the stop event to complete

    float msecTotal = 0.0f;
    cudaEventElapsedTime(&msecTotal, start, stop);

    // Compute and print the performance
    float msecPerMatrixMul = msecTotal / nIter;
    double flopsPerMatrixMul = 2.0 * (double)matrix_size.uiHC * (double)matrix_size.uiWC * (double)matrix_size.uiHB;
    double gigaFlops = (flopsPerMatrixMul * 1.0e-9f) / (msecPerMatrixMul / 1000.0f);
    printf("Performance= %.2f GFlop/s, Time= %.3f msec, Size= %.0f Ops\n",
        gigaFlops, msecPerMatrixMul, flopsPerMatrixMul);

    // copy result from device to host
    cudaMemcpy(h_C, d_C, mem_size_C, cudaMemcpyDeviceToHost);

    // Destroy the handle
    cublasDestroy(handle);

    // compute reference solution
    printf("Computing result using host CPU...");
    float* reference = (float*)malloc(mem_size_C);
    matrixMulCPU(reference, h_A, h_B, matrix_size.uiHA, matrix_size.uiWA, matrix_size.uiWB);
    printf("done.\n");

    // clean up memory
    free(h_A);
    free(h_B);
    free(h_C);
    free(reference);
    cudaFree(d_A);
    cudaFree(d_B);
    cudaFree(d_C);

    return EXIT_SUCCESS;
}

int main(int argc, char** argv)
{
    printf("[Matrix Multiply CUBLAS] - Starting...\n");

    int devID = 0, sizeMult = 5;
    sMatrixSize matrix_size;
    initializeCUDA(devID, sizeMult, matrix_size);

    int matrix_result = matrixMultiply(devID, matrix_size);

    return matrix_result;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

两种方法利用CUDA实现矩阵乘法 的相关文章

  • TensorRT 多线程

    我正在尝试使用 python API 来使用 TensorRt 我试图在多个线程中使用它 其中 Cuda 上下文与所有线程一起使用 在单个线程中一切正常 我使用 docker 和 tensorrt 20 06 py3 图像 onnx 模型和
  • 有没有一种有效的方法来优化我的序列化代码?

    这个问题缺乏细节 因此 我决定创建另一个问题而不是编辑这个问题 新问题在这里 我可以并行化我的代码吗 还是不值得 https stackoverflow com questions 17937438 can i parallelize my
  • __device__ __constant__ 常量

    有什么区别吗 在 CUDA 程序中定义设备常量的最佳方法是什么 在 C 主机 设备程序中 如果我想将常量定义在设备常量内存中 我可以这样做 device constant float a 5 constant float a 5 问题 1
  • CUDA - 将 CPU 变量传输到 GPU __constant__ 变量

    与 CUDA 的任何事情一样 最基本的事情有时也是最难的 所以 我只想将变量从 CPU 复制到 GPUconstant变量 我很难过 这就是我所拥有的 constant int contadorlinhasx d int main int
  • 如何为 CUDA 内核选择网格和块尺寸?

    这是一个关于如何确定CUDA网格 块和线程大小的问题 这是对已发布问题的附加问题here https stackoverflow com a 5643838 1292251 通过此链接 talonmies 的答案包含一个代码片段 见下文 我
  • CUDA 中指令重放的其他原因

    这是我从 nvprof CUDA 5 5 获得的输出 Invocations Metric Name Metric Description Min Max Avg Device Tesla K40c 0 Kernel MyKernel do
  • cuda 文件组织的有效方式:.cpp .h .cu .cuh .curnel 文件

    cuda最容易理解 最高效的代码组织是什么 经过一番调查后 我发现 cuda 函数声明应位于 cuh 文件中 实现位于 cu 文件中 内核函数实现位于 curnel 文件中 其他 C 内容通常在 cpp 和 h 文件中 最近我发布了一个问题
  • 有条件减少 CUDA

    我需要总结一下100000值存储在数组中 但带有条件 有没有办法在 CUDA 中做到这一点以快速产生结果 任何人都可以发布一个小代码来做到这一点吗 我认为 要执行条件约简 您可以直接将条件引入为乘法0 假 或1 真 加数 换句话说 假设您希
  • 为什么 cuCtxCreate 返回旧上下文?

    我已经安装了 CUDA SDK 4 2 64 CUDA工具包4 2 64 CUDA 驱动程序 4 2 64 我检查了 windows 中的每个 nvcuda dll 所有这些都是 4 2 版本 但是当我使用驱动程序 api 创建上下文并使用
  • CUDA计算能力2.0。全局内存访问模式

    CUDA 计算能力 2 0 Fermi 全局内存访问通过 768 KB L2 缓存进行 看起来 开发人员不再关心全局内存库 但全局内存仍然非常慢 因此正确的访问模式很重要 现在的重点是尽可能多地使用 重用 L2 我的问题是 如何 我将感谢一
  • C 中的 CUDA:如何使用 cudaMemcpyAsync 修复错误 11

    我目前正在尝试使用 CUDA 运行一个简单的多 GPU 程序 它的基本作用是将一个包含一些虚拟数据的大型数组复制到 GPU GPU 进行一些数学计算 然后将结果数组复制回来 我在 VS2017 的输出中没有收到任何错误 但我设置的一些错误消
  • Cuda:最小二乘求解,速度较差

    最近 我使用Cuda编写了一个名为 正交匹配追踪 的算法 在我丑陋的 Cuda 代码中 整个迭代需要 60 秒 而 Eigen lib 只需 3 秒 在我的代码中 矩阵 A 是 640 1024 y 是 640 1 在每一步中 我从 A 中
  • 无法在 CUDA 中找到 1 到 100 数字的简单和?

    我正在研究使用 CUDA 的图像处理算法 在我的算法中 我想使用 CUDA 内核找到图像所有像素的总和 所以我在cuda中制作了内核方法 来测量16位灰度图像的所有像素的总和 但我得到了错误的答案 所以我在cuda中编写了一个简单的程序来查
  • 为什么 cudaGLSetGLDevice 失败,即使它是在 main 函数的第一行中调用的

    我想使用 OpenGL 和 CUDA 之间的互操作性 我知道 正如一些教程所说 第一步是选择设备 但是 当我在主函数的第一行中调用 cudaGLSetGLDevice 0 时 程序退出并显示信息 cudaSafeCall 运行时 API 错
  • 将内核链接到 PTX 函数

    我可以使用 PTX 文件中包含的 PTX 函数作为外部设备函数 将其链接到另一个应调用该函数的 cu 文件吗 这是另一个问题CUDA 将内核链接在一起 https stackoverflow com questions 20636800 c
  • 无法编译cuda_ndarray.cu:libcublas.so.7.5:无法打开共享对象文件

    我正在尝试在 aws 实例中导入 theano 库以使用 GPU 我已经使用 boto 编写了一个 python 脚本来自动执行 aws 设置 该脚本本质上会从我的本地计算机对实例执行 ssh 然后启动一个 bash 脚本 其中我执行 py
  • 对 CUDA 操作进行计时

    我需要计算 CUDA 内核执行的时间 最佳实践指南说我们可以使用事件或标准计时函数 例如clock 在Windows中 我的问题是使用这两个函数给出了完全不同的结果 事实上 与实践中的实际速度相比 事件给出的结果似乎是巨大的 我实际上需要这
  • 将数据从 GPU 复制到 CPU - CUDA

    我在将数据从 GPU 复制到 CPU 时遇到问题 一开始我在 GPU 空间中创建变量 device float gpu array 在此 GPU 函数中 我想将数据从 od fS gi 值 0 43 复制到 gpu array global
  • 初学者 CUDA - 简单的 var 增量不起作用

    我正在使用 CUDA 开发一个项目 为了掌握它 我有以下代码 include
  • 当键是由 zip_iterator 使用自定义比较谓词处理的元组时,CUDA 推力 sort_by_key

    我在这里浏览了很多类似的问题 虽然有一个小小的变化 但还是有很多 我正在尝试使用 zip iterator 作为复合键对值进行排序 具体来说 我有以下功能 void thrustSort unsigned int primaryKey fl

随机推荐