为啥清除对象后GPU中的内存仍在使用中?
Posted
技术标签:
【中文标题】为啥清除对象后GPU中的内存仍在使用中?【英文标题】:Why is the memory in GPU still in use after clearing the object?为什么清除对象后GPU中的内存仍在使用中? 【发布时间】:2019-12-21 02:31:01 【问题描述】:从零使用开始:
>>> import gc
>>> import GPUtil
>>> import torch
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
| 0 | 0% | 0% |
| 1 | 0% | 0% |
| 2 | 0% | 0% |
| 3 | 0% | 0% |
然后我创建一个足够大的张量并占用内存:
>>> x = torch.rand(10000,300,200).cuda()
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
| 0 | 0% | 26% |
| 1 | 0% | 0% |
| 2 | 0% | 0% |
| 3 | 0% | 0% |
然后我尝试了几种方法来查看张量是否消失。
尝试1:分离,发送到CPU并覆盖变量
不,不起作用。
>>> x = x.detach().cpu()
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
| 0 | 0% | 26% |
| 1 | 0% | 0% |
| 2 | 0% | 0% |
| 3 | 0% | 0% |
尝试2:删除变量
不,这也不起作用
>>> del x
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
| 0 | 0% | 26% |
| 1 | 0% | 0% |
| 2 | 0% | 0% |
| 3 | 0% | 0% |
尝试3:使用torch.cuda.empty_cache()
函数
似乎可行,但似乎有一些挥之不去的开销......
>>> torch.cuda.empty_cache()
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
| 0 | 0% | 5% |
| 1 | 0% | 0% |
| 2 | 0% | 0% |
| 3 | 0% | 0% |
尝试 4: 可能清除垃圾收集器。
不,5% 仍在被占用
>>> gc.collect()
0
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
| 0 | 0% | 5% |
| 1 | 0% | 0% |
| 2 | 0% | 0% |
| 3 | 0% | 0% |
尝试 5: 尝试完全删除 torch
(就好像 del x
不起作用时那样可行 -_- )
不,它没有...*
>>> del torch
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
| 0 | 0% | 5% |
| 1 | 0% | 0% |
| 2 | 0% | 0% |
| 3 | 0% | 0% |
然后我尝试检查gc.get_objects()
,看起来里面仍然有很多奇怪的THCTensor
东西......
知道为什么清除缓存后内存仍在使用吗?
【问题讨论】:
首先,使用nvidia-smi
确认哪个进程正在使用GPU内存。那里的进程 id pid
可用于查找进程。如果没有显示任何进程但GPU内存仍在使用,您可以尝试this method清除内存。
【参考方案1】:
看起来 PyTorch 的缓存分配器即使没有张量也会保留一些固定数量的内存,并且这种分配是由第一次 CUDA 内存访问触发的
(torch.cuda.empty_cache()
从缓存中删除未使用的张量,但缓存本身仍然使用一些内存。
即使是很小的 1 元素张量,在 del
和 torch.cuda.empty_cache()
之后,GPUtil.showUtilization(all=True)
报告的 GPU 内存量与用于巨大张量的完全相同(torch.cuda.memory_cached()
和 torch.cuda.memory_allocated()
都返回零)。
【讨论】:
【参考方案2】:来自PyTorch docs:
内存管理
PyTorch 使用缓存内存分配器来加速内存 分配。这允许在没有设备的情况下快速释放内存 同步。但是,分配器管理的未使用内存 仍会显示为在 nvidia-smi 中使用。您可以使用
memory_allocated()
和max_memory_allocated()
监控内存 被张量占用,并使用memory_cached()
和max_memory_cached()
监控缓存分配器管理的内存。打电话empty_cache()
从 PyTorch 中释放所有未使用的缓存内存,以便 这些可以被其他 GPU 应用程序使用。但是占用的GPU 张量的内存不会被释放,所以它不能增加数量 可用于 PyTorch 的 GPU 内存。
我将提到 nvidia-smi 的部分加粗,据我所知 GPUtil 使用它。
【讨论】:
Torch 也可能为内部系统/管理器之类的东西分配内存。可能是它的分配器。 关于empty_cache()
的那句话呢?对我来说,这显然听起来像是真的释放了内存,也就是说它也以“nvidia-smi-free”的方式免费。
确实如此,但不幸的是,它也声明它只释放 pytorch 认为未使用的内存......这可能是这个挥之不去的 5%。
是的。正如@Tiphaine 所建议的那样,必须有一些内部的东西。但是这 5% 的 OP 大约是 0.5GB!再一次,文档只知道“未使用的缓存内存”和“张量占用的 GPU 内存”,这 5% 似乎都不属于。
还要注意cuda和torch为内核分配了一些内存,所以即使你在gpu上做一个非常小的张量,它仍然可以占用1.5GB的GPU内存:github.com/pytorch/pytorch/issues/12873#issuecomment-482916237这个内存不是被 pytorch 的分配器视为已分配或保留【参考方案3】:
感谢分享!我遇到了同样的问题,我用你的例子来调试。基本上,我的发现是:
collect() 和 empty_cache() 仅在删除变量后有效 del var + empty_cache() 释放缓存和分配的内存 del var + collect() 仅释放分配的内存 不管怎样,从 nvidia-smi 中仍然可以看到一些开销内存使用情况这里有一些重现实验的代码:
import gc
import torch
def _get_less_used_gpu():
from torch import cuda
cur_allocated_mem =
cur_cached_mem =
max_allocated_mem =
max_cached_mem =
for i in range(cuda.device_count()):
cur_allocated_mem[i] = cuda.memory_allocated(i)
cur_cached_mem[i] = cuda.memory_reserved(i)
max_allocated_mem[i] = cuda.max_memory_allocated(i)
max_cached_mem[i] = cuda.max_memory_reserved(i)
print(cur_allocated_mem)
print(cur_cached_mem)
print(max_allocated_mem)
print(max_cached_mem)
min_all = min(cur_allocated_mem, key=cur_allocated_mem.get)
print(min_all)
return min_all
x = torch.rand(10000,300,200, device=0)
# see memory usage
_get_less_used_gpu()
>0: 2400000000, 1: 0, 2: 0, 3: 0
>0: 2401239040, 1: 0, 2: 0, 3: 0
>0: 2400000000, 1: 0, 2: 0, 3: 0
>0: 2401239040, 1: 0, 2: 0, 3: 0
> *nvidia-smi*: 3416MiB
# try delete with empty_cache()
torch.cuda.empty_cache()
_get_less_used_gpu()
>0: 2400000000, 1: 0, 2: 0, 3: 0
>0: 2401239040, 1: 0, 2: 0, 3: 0
>0: 2400000000, 1: 0, 2: 0, 3: 0
>0: 2401239040, 1: 0, 2: 0, 3: 0
> *nvidia-smi*: 3416MiB
# try delete with gc.collect()
gc.collect()
_get_less_used_gpu()
>0: 2400000000, 1: 0, 2: 0, 3: 0
>0: 2401239040, 1: 0, 2: 0, 3: 0
>0: 2400000000, 1: 0, 2: 0, 3: 0
>0: 2401239040, 1: 0, 2: 0, 3: 0
> *nvidia-smi*: 3416MiB
# try del + gc.collect()
del x
gc.collect()
_get_less_used_gpu()
>0: **0**, 1: 0, 2: 0, 3: 0
>0: 2401239040, 1: 0, 2: 0, 3: 0
>0: 2400000000, 1: 0, 2: 0, 3: 0
>0: 2401239040, 1: 0, 2: 0, 3: 0
> *nvidia-smi*: 3416MiB
# try empty_cache() after deleting
torch.cuda.empty_cache()
_get_less_used_gpu()
>0: 0, 1: 0, 2: 0, 3: 0
>0: **0**, 1: 0, 2: 0, 3: 0
>0: 2400000000, 1: 0, 2: 0, 3: 0
>0: 2401239040, 1: 0, 2: 0, 3: 0
> *nvidia-smi*: **1126MiB**
# re-create obj and try del + empty_cache()
x = torch.rand(10000,300,200, device=0)
del x
torch.cuda.empty_cache()
_get_less_used_gpu()
>0: **0**, 1: 0, 2: 0, 3: 0
>0: **0**, 1: 0, 2: 0, 3: 0
>0: 2400000000, 1: 0, 2: 0, 3: 0
>0: 2401239040, 1: 0, 2: 0, 3: 0
> *nvidia-smi*: **1126MiB**
尽管如此,这种方法仅适用于确切知道哪些变量持有内存的情况......我猜在训练深度学习模式时并非总是如此,尤其是在使用第三方库时。
【讨论】:
以上是关于为啥清除对象后GPU中的内存仍在使用中?的主要内容,如果未能解决你的问题,请参考以下文章