【CUDA】CUBLAS

【CUDA】CUBLAS

在深入了解之前,提前运行预热(warmup)和基准测试(benchmark runs) 是获得准确执行时间的关键。如果不进行预热运行,cuBLAS 的首次运行会有较大的开销(大约 45 毫秒),会导致结果偏差。基准测试能够更准确地计算出平均执行时间。


cuBLAS:CUDA 基本线性代数子程序库

  • 简介:cuBLAS 是 NVIDIA 的 GPU 加速线性代数运算库,广泛应用于 人工智能(AI)高性能计算(HPC) 领域。
  • 功能:提供了行业标准的 BLAS 和 GEMM(矩阵乘法)API,并支持高度优化的融合操作(fusion),充分发挥 NVIDIA GPU 的性能。
  • 使用提示:在使用 GEMM 操作时,需要特别注意矩阵的存储布局(行优先或列优先),例如参考这里

cuBLASLt:轻量级扩展

  • 简介:cuBLASLt 是 cuBLAS 的轻量级扩展,提供了更灵活的 API,专注于提升特定工作负载(如深度学习模型)的性能。
  • 特点:
    • 如果单个内核无法处理问题,cuBLASLt 会将问题分解为多个子问题,并在每个子问题上运行内核。
    • 支持混合精度计算,如 fp16fp8int8,可显著提升深度学习的推理速度。

cuBLASXt:支持多 GPU 扩展

  • 简介:cuBLASXt 是 cuBLAS 的扩展版,主要针对超大规模计算,支持多 GPU 运行。
  • 特点:
    • 多 GPU 支持:能够将 BLAS 操作分布到多块 GPU 上,适合处理需要扩展 GPU 计算的大型数据集。
    • 线程安全:支持多线程并发执行,在不同的 GPU 上同时运行多个 BLAS 操作。
    • 适用场景:特别适用于超出单块 GPU 显存限制的大规模线性代数问题。
  • 缺点:由于需要在 主板 DRAM 和 GPU VRAM 之间频繁传输数据,会造成内存带宽瓶颈,导致计算速度较慢。

cuBLASDx:设备端扩展(未在课程中使用)

  • 简介:cuBLASDx 是一个设备端 API 扩展,用于直接在 CUDA 内核中执行 BLAS 计算。
  • 特点:
    • 通过融合(fusion)数值操作,进一步降低延迟,提升应用程序性能。
    • 注意:cuBLASDx 并不包含在 CUDA Toolkit 中,需要单独下载。

CUTLASS:CUDA 模板线性代数子程序库

  • 简介:cuBLAS 及其变体主要在 主机(host)端 运行,而 CUTLASS 提供了模板库,允许开发者实现高度自定义和优化的线性代数操作。
  • 特点:
    • 支持在 CUDA 中轻松融合矩阵运算。
    • 对于深度学习,矩阵乘法是最重要的操作,而 cuBLAS 无法轻松实现复杂操作的融合。
  • 补充说明:
    • CUTLASS 并未用于实现 Flash Attention,后者是通过高度优化的 CUDA 内核实现的(详见论文)。

总结与应用场景

特点与适用场景
cuBLAS 高性能线性代数运算,适用于 AI 和 HPC。
cuBLASLt 灵活 API 和混合精度支持,专注深度学习工作负载。
cuBLASXt 多 GPU 支持,适合超大规模计算,但受限于内存带宽瓶颈。
cuBLASDx 设备端 API,可在 CUDA 内核中实现 BLAS 操作,进一步优化延迟。
CUTLASS 提供模板库,支持复杂运算融合,深度学习中高效矩阵运算的选择。

通过根据工作负载的需求选择合适的库,可以充分利用 NVIDIA GPU 的计算能力。

cuBLAS示例

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

#define M 3
#define K 4
#define N 2

#define CHECK_CUDA(call)                                                                               \
    {
                                                                                                       \
        cudaError_t err = call;                                                                        \
        if (err != cudaSuccess) {
                                                                           \
            fprintf(stderr, "CUDA error in %s:%d: %s\n", __FILE__, __LINE__, cudaGetErrorString(err)); \
            exit(EXIT_FAILURE);                                                                        \
        }                                                                                              \
    }

#define CHECK_CUBLAS(call)                                                              \
    {
                                                                                        \
        cublasStatus_t status = call;                                                   \
        if (status != CUBLAS_STATUS_SUCCESS) {
                                               \
            fprintf(stderr, "cuBLAS error in %s:%d: %d\n", __FILE__, __LINE__, status); \
            exit(EXIT_FAILURE);                                                         \
        }                                                                               \
    }

#undef PRINT_MATRIX
#define PRINT_MATRIX(mat, rows, cols)                                       \
    for (int i = 0; i < rows; i++) {
                                             \
        for (int j = 0; j < cols; j++) printf("%8.3f ", mat[i * cols + j]); \
        printf("\n");                                                       \
    }                                                                       \
    printf("\n");

void CpuMatmul(float* A, float* B, float* C) {
   
    for (int i = 0; i < M; ++i) {
   
        for (int j = 0; j < N; ++j) {
   
            float sum = 0;
            for (int k = 0; k < K; ++k) {
   
                sum += A[i * K + k] * B[k * N + j];
            }
            C[i * N + j] = sum;
        }
    }
}

int main() {
   
    float A[M * K] = {
   1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f};
    float B[K * N] = {
   1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
    float C_cpu[M * N], C_cublas_s[M * N], C_cublas_h[M * N];

    // CPU matmul
    CpuMatmul(A, B, C_cpu);

    cublasHandle_t handle;
    CHECK_CUBLAS(cublasCreate(&handle));

    float *d_A, *d_B, *d_C;
    CHECK_CUDA(cudaMalloc(&d_A, M * K * sizeof(float)));
    CHECK_CUDA(cudaMalloc(&d_B, K * N * sizeof(float)));
    CHECK_CUDA(cudaMalloc(&d_C, M * N * sizeof(float)));

    CHECK_CUDA(cudaMemcpy(d_A, A, M * K * sizeof(float), cudaMemcpyHostToDevice));
    CHECK_CUDA(cudaMemcpy(d_B, B, K * N * sizeof(float), cudaMemcpyHostToDevice));

    // cuBLAS SGEMM(单精度)
    float alpha = 1.0f, beta = 0.0f;
    /*cublasSgemm :
    矩阵乘法公式 C = alpha x op(A) @ op(B) + beta x C
    cublasStatus_t cublasSgemm(
        cublasHandle_t handle,      // cuBLAS上下文
        // CUBLAS_OP_N:不转置(默认)。 CUBLAS_OP_T:转置。CUBLAS_OP_C:共轭转置。
        cublasOperation_t transa,  //指定矩阵A是否转置,
        cublasOperation_t transb,  // 指定矩阵B是否转置
        int m,                     // 矩阵C的行数
        int n,                     // 矩阵C的列数
        int k,                     // A的列数或转置后行数;B的行数或转置后列数
        const float *alpha,        // 标量alpha
        const float *A,            // 矩阵A
        // 主维度(leading dimension),表示存储矩阵时每列或每行的跨度,如果矩阵是列主序,则 lda 是矩阵 A 的行数。
        int lda,                   // A的主维(leading dimension)
        const float *B,            // 矩阵B
        int ldb,                   // B的主维
        const float *beta,         // 标量beta
        float *C,                  // 输出矩阵C
        int ldc                    // C的主维
    );
    */
    // cuBLAS 默认使用列主序存储矩阵。如果输入矩阵是行主序存储,则需要手动调整主维度,或使用技巧重新解释。
    /*
    通过技巧将问题转换为计算 (B^T * A^T)^T,从而直接获得期望结果:
    矩阵 A 和 B 在内存中以行主序存储,等价于将其转置解释为列主序存储。
    调用 cublasSgemm 时,将矩阵按未转置(CUBLAS_OP_N)处理,令 cuBLAS 按列主序解释输入。
    调整矩阵的传递顺序和参数,实际计算 (B^T * A^T)^T,最终结果矩阵在内存中直接符合行主序的期望。
    */
    CHECK_CUBLAS(cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha, d_B, N, d_A, K, &beta, d_C, N));
    CHECK_CUDA(cudaMemcpy(C_cublas_s, d_C, M * N * sizeof(float), cudaMemcpyDeviceToHost));

    // cuBLAS HGEMM
    half *d_A_h, *d_B_h, *d_C_h;
    CHECK_CUDA(cudaMalloc(&d_A_h, M * K * sizeof(half)));
    CHECK_CUDA(cudaMalloc(&d_B_h, K * N * sizeof(half)));
    CHECK_CUDA(cudaMalloc(&d_C_h, M * N * sizeof(half)));

    // Convert to half percision to CPU
    half A_h[M * K], B_h[K * N];
    for (int i = 0; i < M * K; ++i) {
   
        A_h[i] = __float2half(A[i]);
    }
    for (int i = 0; i < K * N; ++i) {
   
        B_h[i] = __float2half(B[i]);
    }

    CHECK_CUDA(cudaMemcpy(d_A_h, A_h, M * K * sizeof(half), cudaMemcpyHostToDevice));
    CHECK_CUDA(cudaMemcpy(d_B_h, B_h, K 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值