cuMemAlloc / cuMemFree的性能差异很大

时间:2014-02-04 11:04:54

标签: cuda

在我的应用程序中,cuMemAlloc / cuMemFree大部分时间都显得非常缓慢。但是,我发现它们有时比平常快10倍。下面的测试程序在两台机器上完成大约0.4秒,两者都使用cuda 5.5,但一台具有计算能力2.0卡,另一台具有3.5分卡。

如果删除了cublas初始化,那么大约需要5s。随着cublas初始化,但分配不同的字节数,如4000,它减慢大约相同。毋庸置疑,我对此感到困惑。

导致这种情况的原因是什么?如果它不是我的代码中的错误,我有什么样的解决方法?我唯一能想到的就是预先分配一个实现我自己的分配器的竞技场。

#include <stdio.h>
#include <cuda.h>
#include <cublas_v2.h>

#define cudaCheck(ans) { gpuAssert((ans), __FILE__, __LINE__); }

inline void gpuAssert(CUresult code, char *file, int line)
{
    if (code != CUDA_SUCCESS) { 
        fprintf(stderr,"GPUassert: %d %s %d\n", code, file, line);
        exit(1);
    }
}

void main(int argc, char *argv[])
{
    CUcontext   context;
    CUdevice    device;
    int         devCount;

    cudaCheck(cuInit(0));
    cudaCheck(cuDeviceGetCount(&devCount));
    cudaCheck(cuDeviceGet(&device, 0));
    cudaCheck(cuCtxCreate(&context, 0, device));

    cublasStatus_t stat;
    cublasHandle_t handle;
    stat = cublasCreate(&handle);
    if (stat != CUBLAS_STATUS_SUCCESS) {
        printf ("CUBLAS initialization failed\n");
        exit(1);
    }

    {
        int i;
        for (i = 0; i < 30000; i++) {
            CUdeviceptr devBufferA;
            cudaCheck(cuMemAlloc(&devBufferA, 8000));
            cudaCheck(cuMemFree(devBufferA));
        }
    }
}

2 个答案:

答案 0 :(得分:2)

我带了你的代码并在一个带有319.21驱动程序和CUDA 5.5以及非显示计算3.0设备的linux 64位系统上对其进行了分析。我的第一个观察是运行时间约为0.5秒,这似乎比你报告的要快得多。如果我分析nvprof输出,我得到这些直方图:

          cuMemFree               
    Time (us)       Frequency     
 3.65190000e+00   2.96670000e+04
 4.59380000e+00   2.76000000e+02 
 5.53570000e+00   3.20000000e+01
 6.47760000e+00   1.00000000e+00
 7.41950000e+00   1.00000000e+00
 8.36140000e+00   6.00000000e+00
 9.30330000e+00   0.00000000e+00
 1.02452000e+01   1.00000000e+00
 1.11871000e+01   2.00000000e+00
 1.21290000e+01   1.40000000e+01

          cuMemAlloc               
    Time (us)       Frequency     
 3.53840000e+00   2.98690000e+04
 4.50580000e+00   8.60000000e+01
 5.47320000e+00   2.00000000e+01
 6.44060000e+00   0.00000000e+00 
 7.40800000e+00   0.00000000e+00
 8.37540000e+00   6.00000000e+00 
 9.34280000e+00   0.00000000e+00
 1.03102000e+01   0.00000000e+00
 1.12776000e+01   1.20000000e+01
 1.22450000e+01   5.00000000e+00

告诉我99.6%的cuMemAlloc次呼叫少于3.5384微秒,98.9%的cuMemFree次呼叫少于3.6519微秒。没有免费或分配操作花费超过12.25微秒。

所以基于这些结果的结论是

  1. cuMemfreecuMemAlloc 非常快,在您的示例中,对这些API的60000次调用中的每一次调用时间都少于12.25微秒
  2. 这两个API的中位调用时间为2.7微秒,标准差为0.25微秒,这表明API延迟的变化非常小
  3. 非常偶尔(大约0.01%的时间),两个API的速度都比这个中值慢大约六倍。这可能是由于操作系统级资源争用造成的。
  4. 上述每一点都完全违背了你在问题中提出的每一个主张。
  5. 鉴于您的结果显然有多么不同,我只能猜测您是在WDDM Windows这样的已知高延迟平台上运行,并且驱动程序批处理和WDDM子系统延迟完全支配代码的性能。在这种情况下,似乎最简单的解决方法是变更平台......

答案 1 :(得分:0)

众所周知,CUDA内存管理器很慢。我已经看到提到它比主机malloc()free()慢了“两个数量级”。此信息可能已过时,但此处有一些图表:

http://www.cs.virginia.edu/~mwb7w/cuda_support/memory_management_overhead.html

我认为这是因为CUDA内存管理器针对处理少量内存分配进行了优化,代价是在大量分配时减慢速度。而这是因为,通常,处理内核中的许多小缓冲区效率不高。

在内核中处理许多缓冲区有两个主要问题:

1)它意味着将一个指针表传递给内核。如果每个线程都有一个指针,则在开始使用内存之前,会产生从全局内存中的表加载指针的初始成本。跟随一系列指针有时被称为“指针追逐”,它在GPU上特别昂贵,因为内存访问相对更昂贵。

2)更重要的是,每个线程的指针意味着非合并的内存访问模式。在当前体系结构中,如果warp中的每个线程从全局内存加载32位值,该值超过其他128字节,则需要32次内存事务来提供warp。每个事务将加载128个字节,然后丢弃124个字节。如果warp中的所有线程加载来自相同的本机对齐的128字节区域,则所有加载都由单个内存事务提供。因此,在内存绑定内核中,内存吞吐量可能仅为潜在的1/32。

使用CUDA处理内存的最有效方法通常是在内核中为它们分配一些大块和索引。