【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 会将问题分解为多个子问题,并在每个子问题上运行内核。
- 支持混合精度计算,如 fp16、fp8 和 int8,可显著提升深度学习的推理速度。
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 matmulCpuMatmul(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 CcublasStatus_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, // 标量alphaconst float *A, // 矩阵A// 主维度(leading dimension),表示存储矩阵时每列或每行的跨度,如果矩阵是列主序,则 lda 是矩阵 A 的行数。int lda, // A的主维(leading dimension)const float *B, // 矩阵Bint ldb, // B的主维const float *beta, // 标量betafloat *C, // 输出矩阵Cint 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 HGEMMhalf *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 CPUhalf 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 * N * sizeof(half), cudaMemcpyHostToDevice));half alpha_h = __float2half(1.0f), bata_h = __float2half(0.0f);CHECK_CUBLAS(cublasHgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha_h, d_B_h, N, d_A_h, K, &bata_h, d_C_h, N));// Copy result back to host and convert to floathalf C_h[M * N];CHECK_CUDA(cudaMemcpy(C_h, d_C_h, M * N * sizeof(half), cudaMemcpyDeviceToHost));for (int i = 0; i < M * N; i++) {C_cublas_h[i] = __half2float(C_h[i]);}// Print resultsprintf("Matrix A (%dx%d):\n", M, K);PRINT_MATRIX(A, M, K);printf("Matrix B (%dx%d):\n", K, N);PRINT_MATRIX(B, K, N);printf("CPU Result (%dx%d):\n", M, N);PRINT_MATRIX(C_cpu, M, N);printf("cuBLAS SGEMM Result (%dx%d):\n", M, N);PRINT_MATRIX(C_cublas_s, M, N);printf("cuBLAS HGEMM Result (%dx%d):\n", M, N);PRINT_MATRIX(C_cublas_h, M, N);// Clean upCHECK_CUDA(cudaFree(d_A));CHECK_CUDA(cudaFree(d_B));CHECK_CUDA(cudaFree(d_C));CHECK_CUDA(cudaFree(d_A_h));CHECK_CUDA(cudaFree(d_B_h));CHECK_CUDA(cudaFree(d_C_h));CHECK_CUBLAS(cublasDestroy(handle));return 0;
}
输出:
Matrix A (3x4):1.000 2.000 3.000 4.000 5.000 6.000 7.000 8.000 9.000 10.000 11.000 12.000 Matrix B (4x2):1.000 2.000 3.000 4.000 5.000 6.000 7.000 8.000 CPU Result (3x2):50.000 60.000 114.000 140.000 178.000 220.000 cuBLAS SGEMM Result (3x2):50.000 60.000 114.000 140.000 178.000 220.000 cuBLAS HGEMM Result (3x2):50.000 60.000 114.000 140.000 178.000 220.000
cuBLASLt示例
#include <cublasLt.h> // cuBLASLt 是 NVIDIA 的 cuBLAS 库的扩展,提供了更灵活和可配置的矩阵乘法接口。
#include <cuda_fp16.h> // 提供对 half(FP16)数据类型的支持
#include <cuda_runtime.h>#include <iomanip>
#include <iostream>
#include <vector>#define CHECK_CUDA(call) \do { \cudaError_t status = call; \if (status != cudaSuccess) { \std::cerr << "CUDA error at line " << __LINE__ << ": " << cudaGetErrorString(status) << std::endl; \exit(EXIT_FAILURE); \} \} while (0)#define CHECK_CUBLAS(call) \do { \cublasStatus_t status = call; \if (status != CUBLAS_STATUS_SUCCESS) { \std::cerr << "cuBLAS error at line " << __LINE__ << ": " << status << std::endl; \exit(EXIT_FAILURE); \} \} while (0)void CpuMatmul(const float* A, const float* B, float* C, int M, int N, int K) {for (int i = 0; i < M; ++i) {for (int j = 0; j < N; ++j) {float sum = 0.0f;for (int k = 0; k < K; ++k) {sum += A[i * K + k] * B[k * N + j];}C[i * N + j] = sum;}}
}void PrintMatrix(const float* matrix, int rows, int cols, const char* name) {std::cout << "Matrix " << name << ":" << std::endl;for (int i = 0; i < rows; ++i) {for (int j = 0; j < cols; ++j) {// std::setw(8)用于设置输出字段的宽度为 8 个字符,std::fixed用于设置浮点数的输出格式为固定小数点格式// std::setprecision(2)用于设置浮点数的小数点后的精度为 2 位。std::cout << std::setw(8) << std::fixed << std::setprecision(2) << matrix[i * cols + j] << ' ';}std::cout << std::endl;}
}int main() {const int M = 4, K = 4, N = 4;float h_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, 13.0f, 14.0f, 15.0f, 16.0f};float h_B[K * N] = {1.0f, 2.0f, 4.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f,9.0f, 10.0f, 11.0f, 12.0f, 17.0f, 18.0f, 19.0f, 20.0f};float h_C_cpu[M * N] = {0};float h_C_gpu_fp32[M * N] = {0};float h_C_gpu_fp16[M * N] = {0};// Print input matricesPrintMatrix(h_A, M, K, "A");PrintMatrix(h_B, K, N, "B");float *d_A_fp32, *d_B_fp32, *d_C_fp32;CHECK_CUDA(cudaMalloc(&d_A_fp32, M * K * sizeof(float)));CHECK_CUDA(cudaMalloc(&d_B_fp32, K * N * sizeof(float)));CHECK_CUDA(cudaMalloc(&d_C_fp32, M * N * sizeof(float)));half *d_A_fp16, *d_B_fp16, *d_C_fp16;CHECK_CUDA(cudaMalloc(&d_A_fp16, M * K * sizeof(half)));CHECK_CUDA(cudaMalloc(&d_B_fp16, K * N * sizeof(half)));CHECK_CUDA(cudaMalloc(&d_C_fp16, M * N * sizeof(half)));CHECK_CUDA(cudaMemcpy(d_A_fp32, h_A, M * K * sizeof(float), cudaMemcpyHostToDevice));CHECK_CUDA(cudaMemcpy(d_B_fp32, h_B, K * N * sizeof(float), cudaMemcpyHostToDevice));// Convert and copy data to device(FP16)std::vector<half> h_A_half(M * K);std::vector<half> h_B_half(K * N);for (int i = 0; i < M * K; ++i) h_A_half[i] = __float2half(h_A[i]);for (int i = 0; i < K * N; ++i) h_B_half[i] = __float2half(h_B[i]);CHECK_CUDA(cudaMemcpy(d_A_fp16, h_A_half.data(), M * K * sizeof(half), cudaMemcpyHostToDevice));CHECK_CUDA(cudaMemcpy(d_B_fp16, h_B_half.data(), K * N * sizeof(half), cudaMemcpyHostToDevice));// Create cuBLAS handlecublasLtHandle_t handle;CHECK_CUBLAS(cublasLtCreate(&handle));// Set up matrix descriptors for FP32/*cublasStatus_t cublasLtMatrixLayoutCreate(cublasLtMatrixLayout_t *matLayout, cudaDataType type, uint64_t rows,uint64_t cols, int64_t ld)矩阵描述符的作用 矩阵描述符告诉 cuBLAS Lt: 矩阵的数据类型:如CUDA_R_32F(单精度浮点)、CUDA_R_16F(半精度浮点)。矩阵的维度:行数和列数。矩阵在内存中的步幅:矩阵的行或列在内存中的存储间隔(通常等于矩阵的列数或行数)。*/cublasLtMatrixLayout_t matA_fp32, matB_fp32, matC_fp32;CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matA_fp32, CUDA_R_32F, K, M, K));CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matB_fp32, CUDA_R_32F, N, K, N));CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matC_fp32, CUDA_R_32F, N, M, N));// Set up matrix descriptors for FP16cublasLtMatrixLayout_t matA_fp16, matB_fp16, matC_fp16;CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matA_fp16, CUDA_R_16F, K, M, K)); // original MKKCHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matB_fp16, CUDA_R_16F, N, K, N)); // original KNNCHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matC_fp16, CUDA_R_16F, N, M, N)); // original MNN// Set up matrix multiplication descriptor for FP32/*矩阵乘法描述符 cublasLtMatmulDescCreate:Create new matmul operation descriptor.cublasStatus_t cublasLtMatmulDescCreate(cublasLtMatmulDesc_t *matmulDesc, cublasComputeType_t computeType,cudaDataType_t scaleType)通过创建矩阵乘法描述符,用户能够设置:1)矩阵是否需要转置、共轭转置。2)计算的精度。3)加法、激活函数等附加操作。*/cublasLtMatmulDesc_t matmulDesc_fp32;CHECK_CUBLAS(cublasLtMatmulDescCreate(&matmulDesc_fp32, CUBLAS_COMPUTE_32F, CUDA_R_32F));// Set up matrix multiplication descriptor for FP16cublasLtMatmulDesc_t matmulDesc_fp16;CHECK_CUBLAS(cublasLtMatmulDescCreate(&matmulDesc_fp16, CUBLAS_COMPUTE_16F, CUDA_R_16F));// Set matrix operation for A and BcublasOperation_t transa = CUBLAS_OP_N;cublasOperation_t transb = CUBLAS_OP_N;/*cublasLtMatmulDescSetAttribute 用于设置矩阵乘法描述符 (cublasLtMatmulDesc_t)的属性。这个函数非常关键,因为它允许用户根据不同的需求配置矩阵乘法的行为,例如设置加法、转置、矩阵的精度等。通过这种方式,您可以定制和优化矩阵乘法的计算过程。cublasStatus_t cublasLtMatmulDescSetAttribute(cublasLtMatmulDesc_t matmul_desc, // 矩阵乘法描述符cublasLtMatmulDescAttribute_t attr, // 需要设置的属性const void *value, // 属性值size_t size // 属性值的大小);CUBLASLT_MATMUL_DESC_TRANSA作用:指定矩阵 A 是否需要转置。值:CUBLAS_OP_N:矩阵 A 不需要转置(即使用原始矩阵 A)。CUBLAS_OP_T:矩阵 A 需要转置(即对矩阵 A 执行转置操作)。CUBLAS_OP_CONJ_T:矩阵 A 需要进行共轭转置(即对矩阵 A 执行共轭转置操作)。*/CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(matmulDesc_fp32, CUBLASLT_MATMUL_DESC_TRANSA, &transa,sizeof(cublasOperation_t)));CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(matmulDesc_fp32, CUBLASLT_MATMUL_DESC_TRANSB, &transb,sizeof(cublasOperation_t)));CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(matmulDesc_fp16, CUBLASLT_MATMUL_DESC_TRANSA, &transa,sizeof(cublasOperation_t)));CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(matmulDesc_fp16, CUBLASLT_MATMUL_DESC_TRANSB, &transb,sizeof(cublasOperation_t)));// Set up alpha and betaconst float alpha = 1.0f;const float beta = 0.0f;// Perform matrix multiplication using cublasLtMatmul(FP32)/*Execute matrix multiplication (D = alpha * op(A) * op(B) + beta * C).cublasStatus_t cublasLtMatmul(cublasLtHandle_t lightHandle, // 句柄cublasLtMatmulDesc_t computeDesc, // 矩阵乘法的描述符const void *alpha,const void *A, cublasLtMatrixLayout_t Adesc, //输入矩阵A,以及矩阵 A 的布局描述符Adescconst void *B, cublasLtMatrixLayout_t Bdesc,const void *beta,const void *C, cublasLtMatrixLayout_t Cdesc, //输入矩阵 C,以及布局描述符Cdescvoid *D, cublasLtMatrixLayout_t Ddesc, //输出矩阵 D,以及布局描述符Ddesc// // algo 是指向 cublasLtMatmulAlgo_t结构体的指针,定义了具体的矩阵乘法算法const cublasLtMatmulAlgo_t *algo,// workspace 是指向用于矩阵乘法运算的工作空间的指针,某些算法可能需要额外的内存来存储中间数据void *workspace, size_t workspaceSizeInBytes,cudaStream_t stream //CUDA 流);*/CHECK_CUBLAS(cublasLtMatmul(handle, matmulDesc_fp32, &alpha, d_B_fp32, matB_fp32, d_A_fp32, matA_fp32, &beta,d_C_fp32, matC_fp32, d_C_fp32, matC_fp32, nullptr, nullptr, 0, 0));// half alpha and betaconst half alpha_half = __float2half(1.0f);const half beta_half = __float2half(0.0f);// Perform matrix multiplication using cublasLtMatmul (FP16)CHECK_CUBLAS(cublasLtMatmul(handle, matmulDesc_fp16, &alpha_half, d_B_fp16, matB_fp16, d_A_fp16, matA_fp16,&beta_half, d_C_fp16, matC_fp16, d_C_fp16, matC_fp16, nullptr, nullptr, 0, 0));// Copy results back to hostCHECK_CUDA(cudaMemcpy(h_C_gpu_fp32, d_C_fp32, M * N * sizeof(float), cudaMemcpyDeviceToHost));std::vector<half> h_C_gpu_fp16_half(M * N);CHECK_CUDA(cudaMemcpy(h_C_gpu_fp16_half.data(), d_C_fp16, M * N * sizeof(half), cudaMemcpyDeviceToHost));// Convert half precision results to single precisionfor (int i = 0; i < M * N; ++i) {h_C_gpu_fp16[i] = __half2float(h_C_gpu_fp16_half[i]);}// Perform CPU matrix multiplicationCpuMatmul(h_A, h_B, h_C_cpu, M, N, K);// Print resultsPrintMatrix(h_C_cpu, M, N, "C (CPU)");PrintMatrix(h_C_gpu_fp32, M, N, "C (GPU FP32)");PrintMatrix(h_C_gpu_fp16, M, N, "C (GPU FP16)");// Compare CPU and GPU resultsbool fp32_match = true;bool fp16_match = true;for (int i = 0; i < M * N; ++i) {if (std::abs(h_C_cpu[i] - h_C_gpu_fp32[i]) > 1e-5) {fp32_match = false;}if (std::abs(h_C_cpu[i] - h_C_gpu_fp16[i]) > 1e-2) { // Increased tolerance for FP16fp16_match = false;}}std::cout << "FP32 Results " << (fp32_match ? "match" : "do not match") << std::endl;std::cout << "FP16 Results " << (fp16_match ? "match" : "do not match") << std::endl;// Clean upCHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matA_fp32));CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matB_fp32));CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matC_fp32));CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matA_fp16));CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matB_fp16));CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matC_fp16));CHECK_CUBLAS(cublasLtMatmulDescDestroy(matmulDesc_fp32));CHECK_CUBLAS(cublasLtMatmulDescDestroy(matmulDesc_fp16));CHECK_CUBLAS(cublasLtDestroy(handle));CHECK_CUDA(cudaFree(d_A_fp32));CHECK_CUDA(cudaFree(d_B_fp32));CHECK_CUDA(cudaFree(d_C_fp32));CHECK_CUDA(cudaFree(d_A_fp16));CHECK_CUDA(cudaFree(d_B_fp16));CHECK_CUDA(cudaFree(d_C_fp16));return 0;
}
cuBLASXt示例
cuBLASXt使用起来和cuBLAS类似。
首先和CPU的性能比较:
#include <cublasXt.h>
#include <cublas_v2.h>
#include <cuda_runtime.h>#include <cstdlib>
#include <ctime>
#include <iostream>// 定义矩阵维度,矩阵尺寸为 256 x 256
const int M = 1024 / 4; // Rows of matrix A and C
const int N = 1024 / 4; // Columns of matrix B and C
const int K = 1024 / 4; // Columns of matrix A and rows of matrix B// 定义检查 cuBLAS 调用状态的宏
#define CHECK_CUBLAS(call) \{ \cublasStatus_t err = call; \if (err != CUBLAS_STATUS_SUCCESS) { \std::cerr << "Error in " << #call << ", line " << __LINE__ << std::endl; \exit(1); \} \}int main() {// 初始化随机数生成器srand(time(0));// 在主机(CPU)上分配内存用于存储矩阵 A, B, Cfloat* A_host = new float[M * K];float* B_host = new float[K * N];float* C_host_cpu = new float[M * N]; // 用于 CPU 计算结果float* C_host_gpu = new float[M * N]; // 用于 GPU 计算结果// 初始化矩阵 A 和 B 的值为随机浮点数for (int i = 0; i < M * K; i++) {A_host[i] = (float)rand() / RAND_MAX;}for (int i = 0; i < K * N; i++) {B_host[i] = (float)rand() / RAND_MAX;}// 在 CPU 上进行矩阵乘法 C = A * Bfloat alpha = 1.0f; // 缩放因子 alphafloat beta = 0.0f; // 缩放因子 betafor (int i = 0; i < M; i++) {for (int j = 0; j < N; j++) {C_host_cpu[i * N + j] = 0.0f; // 初始化 C 矩阵的元素for (int k = 0; k < K; k++) {C_host_cpu[i * N + j] += A_host[i * K + k] * B_host[k * N + j];}}}// 创建 cuBLASXt 句柄cublasXtHandle_t handle;CHECK_CUBLAS(cublasXtCreate(&handle));// 设置 GPU 设备,选择 GPU 0int devices[1] = {0};/*cublasXtDeviceSelect 用于指定 cuBLASXt 将使用哪些 GPU 设备进行计算。cuBLASXt 支持多GPU的并行计算,通过这个函数用户可以灵活选择目标设备。cublasStatus_t cublasXtDeviceSelect(cublasXtHandle_t handle, int nbDevices, const int* deviceIds);*/CHECK_CUBLAS(cublasXtDeviceSelect(handle, 1, devices));// 进行一次 warmup 运行,调用 cuBLASXt 的矩阵乘法接口进行计算/*cublasXtSgemm 是 cuBLASXt 提供的单精度矩阵乘法 (GEMM) 函数。它支持单 GPU 和多 GPU 加速,执行计算公式:C=α⋅op(A)⋅op(B)+β⋅CcublasStatus_t cublasXtSgemm(cublasXtHandle_t handle,cublasOperation_t transA,cublasOperation_t transB,int m,int n,int k,const float* alpha,const float* A,int lda,const float* B,int ldb,const float* beta,float* C,int ldc);m:矩阵 C 的行数(矩阵 A 的行数).n:矩阵 C 的列数(矩阵 B 的列数).k:矩阵 A 的列数(矩阵 B 的行数)。*/CHECK_CUBLAS(cublasXtSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha, B_host, N, A_host, K, &beta, C_host_gpu, N));// 比较 CPU 和 GPU 计算结果float max_diff = 1e-4f; // 最大容许误差for (int i = 0; i < M * N; i++) {float diff = std::abs(C_host_cpu[i] - C_host_gpu[i]);if (diff > max_diff) {std::cout << "i: " << i << " CPU: " << C_host_cpu[i] << ", GPU: " << C_host_gpu[i] << std::endl;}}std::cout << "Maximum difference between CPU and GPU results: " << max_diff << std::endl;// 释放主机内存delete[] A_host;delete[] B_host;delete[] C_host_cpu;delete[] C_host_gpu;return 0;
}
CUBLAS和CUBLAS-XT的性能比较:
#include <cublasXt.h>
#include <cublas_v2.h>
#include <cuda_runtime.h>#include <chrono>
#include <iostream>
#include <vector>#define CHECK_CUDA(call) \{ \cudaError_t err = call; \if (err != cudaSuccess) { \printf("CUDA error: %s, line %d\n", cudaGetErrorString(err), __LINE__); \exit(1); \} \}
#define CHECK_CUBLAS(call) \{ \cublasStatus_t status = call; \if (status != CUBLAS_STATUS_SUCCESS) { \printf("CUBLAS error: %d, line %d\n", status, __LINE__); \exit(1); \} \}void initMatrix(float *matrix, int rows, int cols) {for (int i = 0; i < rows * cols; ++i) {matrix[i] = static_cast<float>(rand()) / RAND_MAX;}
}bool compareResults(float *result1, float *result2, int size, float tolerance) {for (int i = 0; i < size; ++i) {float diff = std::abs(result1[i] - result2[i]);float max_val = std::max(std::abs(result1[i]), std::abs(result2[i]));if (diff / max_val > tolerance) {std::cout << "Results do not match at index " << i << std::endl;std::cout << "CUBLAS: " << result1[i] << ", CUBLAS-XT: " << result2[i] << std::endl;std::cout << "Relative difference: " << diff / max_val << std::endl;return false;}}return true;
}int main() {int M = 16384;int N = 16384;int K = 16384;size_t size_A = M * K * sizeof(float);size_t size_B = K * N * sizeof(float);size_t size_C = M * N * sizeof(float);float *h_A = (float *)malloc(size_A);float *h_B = (float *)malloc(size_B);float *h_C_cublas = (float *)malloc(size_C);float *h_C_cublasxt = (float *)malloc(size_C);initMatrix(h_A, M, K);initMatrix(h_B, K, N);const int num_runs = 5;std::vector<double> cublas_times;std::vector<double> cublasxt_times;// CUBLAS{cublasHandle_t handle;CHECK_CUBLAS(cublasCreate(&handle));float *d_A, *d_B, *d_C;CHECK_CUDA(cudaMalloc(&d_A, size_A));CHECK_CUDA(cudaMalloc(&d_B, size_B));CHECK_CUDA(cudaMalloc(&d_C, size_C));CHECK_CUDA(cudaMemcpy(d_A, h_A, size_A, cudaMemcpyHostToDevice));CHECK_CUDA(cudaMemcpy(d_B, h_B, size_B, cudaMemcpyHostToDevice));const float alpha = 1.0f;const float beta = 0.0f;// Warmup runCHECK_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(cudaDeviceSynchronize());// Benchmark runsfor (int i = 0; i < num_runs; ++i) {auto start = std::chrono::high_resolution_clock::now();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(cudaDeviceSynchronize());auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double> diff = end - start;cublas_times.push_back(diff.count());std::cout << "CUBLAS run " << i + 1 << " time: " << diff.count() << " seconds" << std::endl;}CHECK_CUDA(cudaMemcpy(h_C_cublas, d_C, size_C, cudaMemcpyDeviceToHost));CHECK_CUDA(cudaFree(d_A));CHECK_CUDA(cudaFree(d_B));CHECK_CUDA(cudaFree(d_C));CHECK_CUBLAS(cublasDestroy(handle));}// CUBLAS-XT{cublasXtHandle_t handle;CHECK_CUBLAS(cublasXtCreate(&handle));int devices[1] = {0};CHECK_CUBLAS(cublasXtDeviceSelect(handle, 1, devices));const float alpha = 1.0f;const float beta = 0.0f;// Warmup runCHECK_CUBLAS(cublasXtSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha, h_B, N, h_A, K, &beta, h_C_cublasxt, N));// Benchmark runsfor (int i = 0; i < num_runs; ++i) {auto start = std::chrono::high_resolution_clock::now();CHECK_CUBLAS(cublasXtSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha, h_B, N, h_A, K, &beta,h_C_cublasxt, N));auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double> diff = end - start;cublasxt_times.push_back(diff.count());std::cout << "CUBLAS-XT run " << i + 1 << " time: " << diff.count() << " seconds" << std::endl;}CHECK_CUBLAS(cublasXtDestroy(handle));}// Calculate and print average timesdouble avg_cublas = 0.0, avg_cublasxt = 0.0;for (int i = 0; i < num_runs; ++i) {avg_cublas += cublas_times[i];avg_cublasxt += cublasxt_times[i];}avg_cublas /= num_runs;avg_cublasxt /= num_runs;std::cout << "Average CUBLAS time: " << avg_cublas << " seconds" << std::endl;std::cout << "Average CUBLAS-XT time: " << avg_cublasxt << " seconds" << std::endl;// Verify resultsfloat tolerance = 1e-4f;bool results_match = compareResults(h_C_cublas, h_C_cublasxt, M * N, tolerance);if (results_match) {std::cout << "Results match within tolerance." << std::endl;} else {std::cout << "Results do not match within tolerance." << std::endl;}free(h_A);free(h_B);free(h_C_cublas);free(h_C_cublasxt);return 0;
}
参考:https://github.com/Infatoshi/cuda-course/tree/master
相关文章:
【CUDA】CUBLAS
【CUDA】CUBLAS 在深入了解之前,提前运行预热(warmup)和基准测试(benchmark runs) 是获得准确执行时间的关键。如果不进行预热运行,cuBLAS 的首次运行会有较大的开销(大约 45 毫秒)…...
YOLOv8-ultralytics-8.2.103部分代码阅读笔记-predict.py
predict.py ultralytics\models\yolo\detect\predict.py 目录 predict.py 1.所需的库和模块 2.class DetectionPredictor(BasePredictor): 1.所需的库和模块 # Ultralytics YOLO 🚀, AGPL-3.0 licensefrom ultralytics.engine.predictor import BasePredicto…...

细说Flash存储芯片W25Q128FW和W25Q16BV
目录 一、Flash存储芯片W25Q128FW 1、W25Q128硬件接口和连接 2、存储空间划分 3、数据读写的原则 4、操作指令 (1)“写使能”指令 (2)“读数据”指令 (3)“写数据”指令 5、状态寄存器SR1 二、Fl…...

python爬虫--小白篇【爬取B站视频】
目录 一、任务分析 二、网页分析 三、任务实现 一、任务分析 将B站视频爬取并保存到本地,经过分析可知可以分为四个步骤,分别是: 爬取视频页的网页源代码;提取视频和音频的播放地址;下载并保存视频和音频&#x…...

Three.js入门-模型加载
Three.js 支持多种 3D 模型格式,每种格式有其独特的优势和适用场景。根据项目的需求,选择合适的格式可以提高开发效率和用户体验。下面将详细介绍几种常见的模型格式及其特点,并补充每种格式的典型使用场景。 支持的模型类型及特点 Three.j…...

ECharts实现数据可视化入门详解
文章目录 ECharts实现数据可视化入门详解一、引言二、基础配置1.1、代码示例 三、动态数据与交互2.1、代码示例 四、高级用法1、多图表组合1.1、在同一容器中绘制多个图表1.2、创建多个容器并分别初始化 ECharts 实例1.3、实现多图联动 五、总结 ECharts实现数据可视化入门详解…...
C++(举例说明类的实例化方式)
太多的信息会让你抓不住重点,下面通过间短的举例说明了类的几种实例化方式,熟悉以后再阅读代码的时候就能减少疑惑。 1.直接实例化:使用类名直接实例化对象 MyClass obj; 2.使用 new 关键字动态分配内存:使用 new 关键字来在堆上…...
LeetCode32. 最长有效括号(2024冬季每日一题 32)
给你一个只包含 ( 和 ) 的字符串,找出最长有效(格式正确且连续)括号子串的长度。 示例 1: 输入:s “(()” 输出:2 解释:最长有效括号子串是 “()” 示例 2: 输入:s “…...

Textfocals ——基于大言模型的用户驱动型文本改进工具让用户在审阅自己的写作时对其进行修改
概述 论文地址:https://arxiv.org/abs/2403.01055 大规模语言模型可以生成媲美专业作家撰写的文本。目前使用的对话技术主要有两种:一种是交互式(如 OpenAI 的 ChatGPT 和 Google 的 Gemini),另一种是预测性文本补全&…...

docker 部署 redis
docker 部署 redis 1. 下载 redis 镜像 # docker images | grep redis bitnami/redis 7.2.4-debian-11-r5 45de196aef7e 10 months ago 95.2MB2. docker-compose 部署 version: "3" services:redis:image: bitnami/redis:7.2.4-debian-11-…...

微信小程序横屏页面跳转后,自定义navbar样式跑了?
文章目录 问题原因:解决方案: 今天刚遇到的问题,横屏的页面完成操作后跳转页面后,自定义的tabbar样式乱了,跑到最顶了,真机调试后发现navbar跑到手机状态栏了,它正常应该跟右边胶囊一行。 知道问…...

回归预测 | MATLAB实现BiGRU(双向门控循环单元)多输入单输出
回归预测 | MATLAB实现BiGRU(双向门控循环单元)多输入单输出 文章目录 回归预测 | MATLAB实现BiGRU(双向门控循环单元)多输入单输出预测效果基本介绍程序设计参考资料致谢预测效果 基本介绍 BiGRU(双向门控循环单元)多输入单输出模型是一种结合了双向门控循环单元(BiGRU)的…...
智能时代的基石:神经网络
智能时代的基石:神经网络 第一节:神经网络简介 课程目标 本节课程旨在全面介绍神经网络的基本概念、结构以及其在历史发展中的重要里程碑。通过深入理解神经网络的工作原理和演变过程,学员将能够掌握神经网络在现实世界中的多种应用&#…...
红与黑,,
有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。 你站在其中一块黑色的瓷砖上,只能向相邻(上下左右四个方向)的黑色瓷砖移动。 请写一个程序,计算你总共能够到达多少块黑色的瓷砖。 输入格式 输入包…...

嵌入式驱动开发详解16(音频驱动开发)
文章目录 前言WM8960简介I2S协议接口说明 SAI音频接口简介驱动框架简介设备树配置内核使能声卡设置与测试 后续参考文献 前言 该专栏主要是讲解嵌入式相关的驱动开发,但是由于ALSA驱动框架过于复杂,实现音频编解码芯片的驱动不是一个人能完成的…...

【嵌入式软件】跑开发板的前置服务配置
在嵌入式开发中,通常需要在 开发板和主机之间共享、传输和挂载文件。 这篇文章是关于如何在 Ubuntu 中配置 Samba、TFTP 和 NFS 协议的详细步骤。这些协议分别用于远程文件共享、文件传输和内核挂载文件系统。 如何安装协议: 参考:ubuntu18配置:详细的内容我手写了一份文档。…...
如何高效实现进程间通信
实现进程间通信(IPC)有多种高效的方法,以下是一些常见的技术及其简要说明: 1. 共享内存: 共享内存是一种高效的进程间通信机制,允许多个进程共享同一块内存区域以实现快速的数据交换。与其他IPC机制相比&a…...
scala基础学习_变量
文章目录 scala中的变量常量 val(不可变变量)变量 var变量声明多变量声明匿名变量 _ 声明 变量类型声明变量命名规范 scala中的变量 常量 val(不可变变量) 使用val关键字声明变量是不可变的,一旦赋值后不能被修改 对…...
Java 身份证校验工具类(15位校验、18位校验与15转18)
文章目录 身份证简介(一)身份证号码的组成(二)一代和二代身份证一代身份证二代身份证 检验思路分析(一)15位身份证号码(二)18位身份证号码(三)校验算法示例&a…...

HTML+CSS+Vue3的静态网页,免费开源,可当作作业使用
拿走请吱一声,点个关注吧,代码如下,网页有移动端适配 HTML <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...