You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用cudaHostAlloc后cudaMemcpy性能未提升,求问题排查

问题描述

我有一个基于cuBLAS执行BLAS gemm()子程序的程序,代码如下:

size_t m = 10000;
size_t k = 4000;
size_t n = 6000;

// h_A 是 m×k 矩阵,h_B 是 k×n 矩阵,h_C 是 m×n 矩阵

double *h_C;
cudaHostAlloc((void **)(&h_C), m * n * sizeof(double), cudaHostAllocDefault);

uint64_t t0 = get_timestamp_in_microsec();

CUBLAS_CHECK(cublasCreate(&cublasH));

uint64_t t1 = get_timestamp_in_microsec();

CUDA_CHECK(cudaMalloc((void **)(&d_A), sizeof(double) * m * k));
CUDA_CHECK(cudaMalloc((void **)(&d_B), sizeof(double) * k * n));
CUDA_CHECK(cudaMalloc((void **)(&d_C), sizeof(double) * m * n));

uint64_t t2 = get_timestamp_in_microsec();

CUDA_CHECK(cudaMemcpy(d_A, h_A.data(), sizeof(double) * m * k, cudaMemcpyHostToDevice));
CUDA_CHECK(cudaMemcpy(d_B, h_B.data(), sizeof(double) * k * n, cudaMemcpyHostToDevice));

uint64_t t3 = get_timestamp_in_microsec();

CUBLAS_CHECK(cublasDgemm(cublasH, CUBLAS_OP_N, CUBLAS_OP_N, m, n, k, &alpha, d_A, lda, d_B, ldb, &beta, d_C, ldc));

uint64_t t4 = get_timestamp_in_microsec();

CUDA_CHECK(cudaMemcpy(h_C, d_C, sizeof(double) * m * n, cudaMemcpyDeviceToHost));

uint64_t t5 = get_timestamp_in_microsec();

程序整体性能尚可,但最后将d_C拷贝回h_C的cudaMemcpy()耗时近3秒,性能数据如下:

t1-t0: 12.929ms
t2-t1: 0.949ms
t3-t2: 53.256ms
t4-t3: 0.315ms
t5-t4: 2653.57ms

我查阅资料后,将malloc()替换为cudaHostAlloc(),但上述性能数据无任何变化。请问我的代码是否存在问题?当前性能是否已达最优?


分析与解答

代码问题排查

  1. 时间测量的核心误差
    cuBLAS的cublasDgemm是异步执行的,你在调用后立即记录t4,此时gemm计算可能还未完成。后续的cudaMemcpy(DeviceToHost)会隐式同步等待GPU任务结束,所以t5-t4的时间实际包含了gemm剩余执行时间和数据拷贝时间,并非单纯的拷贝耗时——这是你当前性能数据异常的主要原因。

  2. cudaHostAlloc的有效性
    你用cudaHostAllocDefault分配了h_C,但该标志在部分系统上仅分配可分页的 pinned 内存,若设备不支持相关特性,收益有限。另外,需确认h_A、h_B的分配方式是否也做了优化,但这不是当前拷贝耗时的直接诱因。

性能优化建议

  1. 修正时间测量逻辑
    在cublasDgemm后添加显式同步,确保GPU计算完成后再记录时间,才能准确区分计算和拷贝的耗时:

    CUBLAS_CHECK(cublasDgemm(cublasH, CUBLAS_OP_N, CUBLAS_OP_N, m, n, k, &alpha, d_A, lda, d_B, ldb, &beta, d_C, ldc));
    CUDA_CHECK(cudaDeviceSynchronize()); // 等待gemm执行完毕
    uint64_t t4 = get_timestamp_in_microsec();
    
  2. 优化数据拷贝效率

    • 换用cudaHostAllocWriteCombined标志分配h_C:该标志会让CPU不缓存这块内存,避免缓存污染,提升从设备到主机的拷贝速度(适合仅读取的场景)。
    • 检查PCIe链路状态:你的d_C数据量为10000*6000*8字节=480MB,PCIe 3.0 x16理论带宽约16GB/s,理想拷贝耗时约30ms。若修正测量后仍耗时过高,需排查系统PCIe链路宽度(是否处于x1等低带宽模式)、是否有其他进程占用GPU/PCIe资源。
    • 尝试异步拷贝:如果后续有其他GPU计算任务,可使用cudaMemcpyAsync配合CUDA流,让数据拷贝与后续计算重叠,隐藏拷贝耗时。
  3. 确认矩阵布局正确性
    cuBLAS默认要求列优先存储,检查lda、ldb、ldc参数是否符合列优先的布局要求(通常设为矩阵的行数)。布局错误可能导致gemm执行异常,间接影响整体耗时。

当前性能是否最优?

显然不是。当前的时间测量误差导致你无法评估真实的拷贝性能,修正后正常拷贝耗时应在几十毫秒级别。若修正后仍存在瓶颈,需排查系统层面的问题(如PCIe配置、驱动版本、设备功耗模式等)。

内容的提问来源于stack exchange,提问作者D.J. Elkind

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 02:12:29