Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Cuda Cummalloc/Cummfree的性能变化很大_Cuda - Fatal编程技术网

Cuda Cummalloc/Cummfree的性能变化很大

Cuda Cummalloc/Cummfree的性能变化很大,cuda,Cuda,在我的应用程序中,CumeAlloc/cuMemFree在大多数情况下看起来都非常慢。然而,我发现它们有时比平时快10倍。下面的测试程序在两台机器上以大约0.4s的速度完成,两台机器都使用cuda 5.5,但一台具有计算能力2.0卡,另一台具有3.5卡 如果取消cublas初始化,则需要大约5s的时间。随着cublas初始化的进行,但分配不同的字节数(如4000字节),其速度也会降低。不用说,我对此感到困惑 这是什么原因造成的?如果这不是我代码中的错误,我有什么解决方法?我唯一能想到的就是预先分

在我的应用程序中,CumeAlloc/cuMemFree在大多数情况下看起来都非常慢。然而,我发现它们有时比平时快10倍。下面的测试程序在两台机器上以大约0.4s的速度完成,两台机器都使用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));
        }
    }
}
#包括
#包括
#包括
#定义cudaCheck(ans){gpuAssert((ans),_文件_,_行_)}
内联void gpuAssert(CUresult代码、char*文件、int行)
{
如果(代码!=CUDA_成功){
fprintf(标准,“GPUassert:%d%s%d\n”,代码、文件、行);
出口(1);
}
}
void main(int argc,char*argv[])
{
语境;
CU器件;
国际发展账户;
cudaCheck(cuInit(0));
cudaCheck(cuDeviceGetCount(&devCount));
cudaCheck(cuDeviceGet(&device,0));
cudaCheck(cuCtxCreate(&context,0,device));
库布拉斯塔图斯统计局;
立方手柄;
stat=cublasCreate(&handle);
if(stat!=CUBLAS\u STATUS\u SUCCESS){
printf(“CUBLAS初始化失败\n”);
出口(1);
}
{
int i;
对于(i=0;i<30000;i++){
CUdeviceptr devBufferA;
cudaCheck(cuMemAlloc(&devBufferA,8000));
cudaCheck(cumenfree(devBufferA));
}
}
}

众所周知,CUDA内存管理器速度较慢。我看到有人提到它比主机
malloc()
free()
慢“两个数量级”。此信息可能注明日期,但此处有一些图表:

我认为这是因为CUDA内存管理器针对处理少量内存分配进行了优化,但在分配大量内存时会降低速度。这是因为,一般来说,在内核中处理许多小的缓冲区是没有效率的

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

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

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


使用CUDA处理内存的最有效方法通常是在内核中分配几个大数据块并将它们编入索引。

我用319.21驱动程序、CUDA 5.5和非显示计算3.0设备在linux 64位系统上分析了您的代码。我的第一个观察结果是运行时间大约为0.5s,这似乎比您报告的速度快得多。如果我分析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微秒

因此,我基于这些结果得出的结论是

  • cuMemfree
    cuMemAlloc
    都非常快,在您的示例中,总共60000次对这些API的调用中,每一次都不到12.25微秒
  • 两个API的平均调用时间为2.7微秒,标准偏差为0.25微秒,这表明API延迟的变化也很小
  • 非常偶尔(约0.01%的时间),两种API的速度都可能比该中值慢六倍左右。这可能是由于操作系统级资源争用造成的
  • 以上每一点都与你在问题中的每一个断言完全矛盾

  • 考虑到您的结果明显不同,我只能猜测您运行在已知的高延迟平台(如WDDM Windows)上,并且驱动程序批处理和WDDM子系统延迟完全控制着代码的性能。在这种情况下,最简单的解决办法似乎是改变平台……

    这并不能解释为什么有时速度要快10倍。此外,虽然在这个人为的例子中确实有很多分配,但在生产代码中,每个神经网络训练历元只有400个1210个浮点数组的分配。如果我重复使用同一个数组,那么一个历元需要3秒,否则需要5秒。奇怪。这两个结果都是由cuda 5.5(驱动程序版本319.76)生成的,该驱动程序在Debian测试安装的Linux 3.12上运行,直到几天前才更新。两个系统都没有运行X。您发布的测量结果与测试程序在此处生成的结果相匹配。注释出cublas init的7行,使cuMemAlloc花费约67毫秒,cuMemFree花费约47.5毫秒。谢谢你这么做。你能重现与初始化cublas有关的性能差异吗?你使用了发布版本吗?@RogerDahl:是的,这是发布版本,不,我没有触摸