在我的应用程序中,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));
}
}
}
答案 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微秒。
所以基于这些结果的结论是
cuMemfree
和cuMemAlloc
非常快,在您的示例中,对这些API的60000次调用中的每一次调用时间都少于12.25微秒鉴于您的结果显然有多么不同,我只能猜测您是在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处理内存的最有效方法通常是在内核中为它们分配一些大块和索引。