web-dev-qa-db-fra.com

Pourquoi la mémoire du GPU est-elle toujours utilisée après avoir effacé l'objet?

À partir de zéro utilisation:

>>> import gc
>>> import GPUtil
>>> import torch
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
|  0 |  0% |  0% |
|  1 |  0% |  0% |
|  2 |  0% |  0% |
|  3 |  0% |  0% |

Ensuite, je crée un tenseur assez grand et monopolise la mémoire:

>>> x = torch.Rand(10000,300,200).cuda()
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
|  0 |  0% | 26% |
|  1 |  0% |  0% |
|  2 |  0% |  0% |
|  3 |  0% |  0% |

Ensuite, j'ai essayé plusieurs façons de voir si le tenseur disparaît.

Tentative 1: Détachez, envoyez au CPU et écrasez la variable

Non, ça ne marche pas.

>>> x = x.detach().cpu()
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
|  0 |  0% | 26% |
|  1 |  0% |  0% |
|  2 |  0% |  0% |
|  3 |  0% |  0% |

Tentative 2: Supprimer la variable

Non, cela ne fonctionne pas non plus

>>> del x
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
|  0 |  0% | 26% |
|  1 |  0% |  0% |
|  2 |  0% |  0% |
|  3 |  0% |  0% |

Tentative 3: Utilisez la fonction torch.cuda.empty_cache()

Semble fonctionner, mais il semble qu'il y ait des frais généraux persistants ...

>>> torch.cuda.empty_cache()
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
|  0 |  0% |  5% |
|  1 |  0% |  0% |
|  2 |  0% |  0% |
|  3 |  0% |  0% |

Tentative 4: Peut-être effacer le ramasse-miettes.

Non, 5% sont encore accaparés

>>> gc.collect()
0
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
|  0 |  0% |  5% |
|  1 |  0% |  0% |
|  2 |  0% |  0% |
|  3 |  0% |  0% |

Tentative 5: Essayez de supprimer torch complètement (comme si cela fonctionnait lorsque del x Ne fonctionnait pas -_-)

Non, ce n'est pas le cas ... *

>>> del torch
>>> GPUtil.showUtilization()
| ID | GPU | MEM |
------------------
|  0 |  0% |  5% |
|  1 |  0% |  0% |
|  2 |  0% |  0% |
|  3 |  0% |  0% |

Et puis j'ai essayé de vérifier gc.get_objects() et il semble qu'il y ait encore pas mal de choses bizarres THCTensor à l'intérieur ...

ne idée de la raison pour laquelle la mémoire est toujours utilisée après avoir effacé le cache?

10
alvas

Il semble que l'allocateur de mise en cache de PyTorch réserve une certaine quantité de mémoire fixe même s'il n'y a pas de tenseurs, et cette allocation est déclenchée par le premier accès à la mémoire CUDA (torch.cuda.empty_cache() supprime le tenseur inutilisé du cache, mais le cache lui-même reste utilise de la mémoire).

Même avec un minuscule tenseur à 1 élément, après del et torch.cuda.empty_cache(), GPUtil.showUtilization(all=True) rapporte exactement la même quantité de mémoire GPU utilisée que pour un énorme tenseur (et les deux torch.cuda.memory_cached() et torch.cuda.memory_allocated() retourne zéro).

5
Sergii Dymchenko

Il y a une partie dans les documents PyTorch, qui semble assez pertinente:
https://pytorch.org/docs/stable/notes/cuda.html#memory-management

Gestion de la mémoire

PyTorch utilise un allocateur de mémoire cache pour accélérer les allocations de mémoire. Cela permet une désallocation rapide de la mémoire sans synchronisation de périphérique. Cependant, la mémoire inutilisée gérée par l'allocateur s'affichera toujours comme si elle était utilisée dans nvidia-smi . Vous pouvez utiliser memory_allocated () et max_memory_allocated () pour surveiller la mémoire occupée par les tenseurs, et utiliser memory_cached () et max_memory_cached () pour surveiller la mémoire gérée par l'allocateur de mise en cache. L'appel de empty_cache () libère toute la mémoire cache non utilisée de PyTorch afin que celles-ci puissent être utilisées par d'autres applications GPU. Cependant, la mémoire GPU occupée par les tenseurs ne sera pas libérée et ne pourra donc pas augmenter la quantité de mémoire GPU disponible pour PyTorch.

J'ai mis en gras une partie mentionnant nvidia-smi , qui, à ma connaissance, est utilisée par GPUtil.

3
Stanowczo