Keson's blog

Caffe解读1 -- Pinned Memory Vs. Non-Pinned Memory

Caffe解读1 – Pinned Memory Vs. Non-Pinned Memory

  • 在解读Caffe之前,我们先需要对GPU通信和CPU的内存分配和内存传输有一定的了解,具体可以参考CUDA编程方面的书。这里我们先了解一下CUDA中的显存分配与释放

Reference

内存分配

关于协调CPU和GPU之间的内存创建和分配以及传输

CPU分配内存

CPU分配内存主要有两种方式:

  • 通过C标准库中的malloc函数完成
  • 调用CUDA中的cudaMallocHost函数

cudaMallocHost函数通过页面锁定,可以提供更高的CPU和GPU传输速率,吞吐量增加是Barracuda10的2.4倍,Barracuda04的2.0倍,Barracuda01的1.5倍。但是缺点是使用cudaMallocHost分配内存比malloc更加慢,
每次调用cudaMallocHost分配1M的内存需要2300微妙左右,当需要分配512M,时间上升到61毫秒,比malloc在分配时慢了3-5个量级

GPU分配内存

GPU的分配方式有:

  • 通过CUDA的cudaMalloc函数
  • cudaMallocPitch函数
  • cudaMallocArray函数

cudaMallocPitchcudaMallocArray都没有cudaMalloc来的快。cudaMalloc的分配时间如图1所示,256bytes的调用分配耗时1微妙,2KB到4KB分配耗时有显著提升,接下来在512KB分配大约需要50微妙,大于512K时,耗时显著提升,当分配512M时,需要12.5毫秒左右。总而言之,在小于4M时,cudaMalloc分配速度比malloc慢1.5个量级,大于4MB时,cudaMallocmalloc慢2-4个量级。

不同函数分配内存的调用时间

图1.不同函数分配CPU和GPU内存的调用时间

不同函数每个Byte分配的平均时间

图2.不同函数分配CPU和GPU每个Byte内存的时间消耗

内存释放

每个内存分配函数都有对应的内存释放函数,如free对应于malloc,cudaFree对应于cudaMalloc,cudaFreeHost对应于cudaMallocHost

图3显示了不同的释放函数随着释放内存大小的调用耗时的比较,与分配时相似,cudaFreeHostfree慢3-5个量级,cudaFreefree慢2-3个量级。

不同函数释放内存的调用时间

图3.不同函数释放内存的调用时间

不同函数每个Byte数据的释放平均时间

图4.不同函数每个Byte内存的释放消耗时间

CPU和GPU之间的内存传输

在GPU核执行任务前,需要把数据都从CPU内存传输到GPU内存,任务完成后,再将运算结果或处理后的数据传回CPU,常用的函数是cudaMemcpy

图5显示了使用cudaMemcpy传输不同大小的内存的平均传输时间,硬件设置如下: 3.20 GHz Core 2 Extreme processor, GTX 280 GPU, and PCIe version 2.0。测试了4种不同的配置: Non-Pinned to Device, Non-Pinned from Device, Pinned to Device 和 Pinned from Device。

four measures

图5.cudaMemcpy传输不同大小的内存的平均传输时间

可以看到,对于传输小规模的内存,基本都是10微妙级别的常量时间。从8K大小开始,传输速度呈现线性增长。

图6显示了传输相同大小的字节的平均吞吐率,对于小块数据,差异不大,对于大块数据,Pinned memory提供了2.4倍于Non-Pinned memory的吞吐率。
吞吐率

图6.传输相同大小的字节的平均吞吐率

选择 Pinned 或者 Non-Pinned Memory

当需要分配CPU内存且会传输到GPU时,有两种方式可以选择:pinnednon-pinned

Pinned memory使用cudaMallocHost来分配CPU内存,可以防止内存页被交换出去,因此可以提供更高的传输速度。Non-pinned memory使用malloc函数分配CPU内存。但是Pinned memory 比Non-pinned memory有更昂贵的内存分配和释放,因为cudaMallocHost分配和释放CPU内存相比malloc更加耗时。

因此,一个很明显的问题是:当分配多少内存时,选择pinned-memory方式的性能更加优越?

主要有以下两类情况,在这两类情况下,我们都假设内存的分配和释放都执行了1次。
(1)假设分配的内存被用来从CPU传到GPU,然后从GPU传回CPU,两次传输的size相同
(2)假设分配的内存全部从CPU传到GPU,然后从GPU只是传递了运算结果回CPU,这种情况下GPU核通常执行的是reduction操作,比如常见的计算平均数。

在第一种情况下,我们可以看到图7的对比结果,结果显示,只有当内存的大小超过16MB时,使用Pinned memory才有更优越的性能。
two way

图7.Non-Pinned vs. Pinned (两次传输size相同)

在第二中情况下,我们可以看到图8的对比结果,结果显示,Pinned 方式没有优越性,应该选择 Non-Pinned方式。

one way

图8.Non-Pinned vs. Pinned (两次传输size不相同)

我们接着比较实用Pinned Memory来分配内存的情况下,(1)和(2)两种情况的性能提升对比,如图9所示:
pinned memmory two way

图9. 两种方式下Pinned Memory对于性能的提升

我们可以看到,对于第1种情况,当传输的内存大于10M时才有意义,对于第2种情况,传输的数据大于128M时才有意义。

结论

  • 使用Pinned Memory方式的CPU和GPU之间传输速度更大,但是分配和释放的耗时更大
  • 使用Non-Pinned Memory的方式CPU和GPU之间传输耗时更大,但是分配和释放更快
  • 具体使用哪种方式,要结合数据的大小以及CPU和GPU之间传输的类型,当传输的数据比较大,且均需从CPU传至GPU和从GPU传至CPU时,使用Pinned Memory有比较大的性能,具体可以看图9的对比。