Python 在PyTorch中固定内存实际上较慢?

Python 在PyTorch中固定内存实际上较慢?,python,pytorch,Python,Pytorch,我想知道为什么将内存固定在PyTorch中会使事情变得更慢。通过阅读torch.utils.data.dataloader的代码,我发现dataloader的pin\u memory=True选项只需在返回前对每个批次调用.pin\u memory()。返回的张量仍然在CPU上,在此之后,我必须手动调用.cuda(non_blocking=True)。因此,整个过程将是 某些情况下x的: 产生x.pin_memory().cuda(非阻塞=真) 我将这一性能与其他性能进行了比较 某些情况下x的

我想知道为什么将内存固定在PyTorch中会使事情变得更慢。通过阅读
torch.utils.data.dataloader
的代码,我发现
dataloader
pin\u memory=True
选项只需在返回前对每个批次调用
.pin\u memory()
。返回的张量仍然在CPU上,在此之后,我必须手动调用
.cuda(non_blocking=True)
。因此,整个过程将是

某些情况下x的
:
产生x.pin_memory().cuda(非阻塞=真)
我将这一性能与其他性能进行了比较

某些情况下x的
:
产量x.cuda()
这是实际的代码

a=torch.rand(1024655360)
%%时间
对于我来说,在一个:
i、 pin_memory().cuda(非阻塞=真)
#CPU时间:用户1.35秒,系统55.8毫秒,总计1.41秒
#墙时间:396毫秒
%%时间
对于我来说,在一个:
i、 引脚_内存().cuda()
#CPU时间:用户1.6秒,系统12.2毫秒,总计1.62秒
#墙壁时间:404毫秒
%%时间
对于我来说,在一个:
i、 cuda(非阻塞=真)
#CPU时间:用户855毫秒,系统:3.87毫秒,总计:859毫秒
#墙时间:274毫秒
%%时间
对于我来说,在一个:
i、 cuda()
#CPU时间:用户314毫秒,系统:12µs,总计:314毫秒
#墙时间:313毫秒
因此,不固定内存不仅占用更少的CPU时间,而且实际时间更快。固定内存不应该使数据传输异步,从而更快吗?如果不是这样,我们为什么要使用pin内存


另外,我考虑了预先固定整个
TensorDataset
的可能性(而不是每次固定批次)。但这不能固定大于GPU内存的张量:

a=np.memmap('../dat/R/train.3,31,31B','3,31,31B','R')
a、 n字节//2**30
## 68
torch.from_numpy(a).pin_memory()
## ---------------------------------------------------------------------------
##运行时错误回溯(上次最近调用)
##在
##--->1个火炬。从\u numpy(a).引脚\u内存()
##
##运行时错误:cuda运行时错误(2):在/tmp/pip-req-build-58y_cjjjl/aten/src/THC/THCCachingHostAllocator处内存不足。cpp:296

如果我真的想固定一个小张量,为什么不提前将整个张量直接移动到GPU内存中?

Pytorch dev的回答:

“固定内存是页面锁定内存。如果用户对所有内容都启用页面锁定内存,那么用户很容易自食其果,因为它不能被抢占。这就是为什么我们没有将其设为默认值的原因。”

这意味着根据您当前的内存情况(RAM数量、碎片等),它可能会延迟您的系统。

TL:DR

代码速度较慢,因为每次调用生成器时都会分配一个新的固定内存块。每次分配新内存都需要同步,使其比非固定内存慢得多。很可能,您正在测量此开销

编辑中的代码示例在
THCCaching
主机中失败。不是GPU内存不足,而是主机拒绝您分配68GB的固定物理内存


在PyTorch中固定内存实际上较慢

创建或释放固定内存(
cudaHostAlloc()
/
cudaFreeHost()
通过)比
malloc
/
free
慢得多,因为它涉及设备(GPU和主机)之间的同步。很可能,在很大程度上,您所测量的是这种开销,因为您正在增量地分配固定内存

固定内存不应该使数据传输异步,从而更快吗?如果不是这样,我们为什么要使用pin内存

可以,但如果在每次传输之前暂停/加入同步以分配内存,则不能

固定内存的最终作用是防止操作系统交换内存块;它保证保持在RAM中。这一保证使GPU的DMA能够在该块上运行,而无需通过CPU(CPU必须检查数据是否需要重新交换)。因此,CPU在此期间可以自由地做其他事情

这不是一个完美的类比,但您可以将固定内存看作GPU和主机之间的共享内存。双方可在不通知对方的情况下对其进行操作;有点像进程中的多个线程。如果您实现非阻塞代码,这可能会快得多。然而,如果各方最终总是
join
ing,这也会慢得多

与此相反,非固定方法是CPU从RAM加载数据(必要时交换),然后将数据发送到GPU。它不仅速度较慢(需要通过northbridge两次),而且还使线程(以及一个CPU内核)保持繁忙。Python还有臭名昭著的GIL,所以可能是整个应用程序都在等待同步I/O

如果您想使用固定内存将成批的数据混洗到GPU中,那么一种方法是使用固定内存作为(循环)缓冲区。CPU可以从磁盘加载数据,应用预处理,并将批处理放入缓冲区。然后,GPU可以在自己的时间内从缓冲区获取批并进行推断。如果实现得很好,那么GPU的空闲时间就不会超过需要的时间,主机和GPU之间就不再需要同步

如果我真的想固定一个小张量,为什么不提前直接将整个张量移动到GPU内存中呢

如果您不需要从CPU访问张量并且它适合GPU,那么确实不需要将其放入固定内存

在您的示例中,您正在打开一个内存映射的numpy数组
memmap
,然后请求将其传输到固定内存。内存映射文件的工作原理与分页内存非常相似,因为数据不适合RAM anym