GPU memory estimation given a network

gaoking132: <p>Hi,</p>
<p>I am dealing with 3D image data. The bottleneck of network design is both GPU and CPU memory.</p>
<p>I try to estimate the GPU memory needed for a given network architecture. However, it seems that my estimation is always much lower than what the network actually consumes. In the following example,</p>
<blockquote>
<pre><code>import torch.nn as nn
import torch
from torch.autograd import Variable
</code></pre>
</blockquote>
<blockquote>
<pre><code>net = nn.Sequential(
    nn.Conv3d(1, 16, 5, 1, 2),
)
net.cuda()
</code></pre>
</blockquote>
<blockquote>
<pre><code>input = torch.FloatTensor(1, 1, 64, 128, 128).cuda()
input = Variable(input)
out = net(input)
</code></pre>
</blockquote>
<blockquote>
<pre><code>print(out.size())
</code></pre>
</blockquote>
<p>The actual GPU memory consumed is 448 MB if I add a break point in the last line and use nvidia-smi to check the GPU memory consumption. However, if I calculated manually, my understanding is that<br>
the total consumed GPU memory = GPU memory for parameters x 2 (one for value, one for gradient) + GPU memory for storing forward and backward responses.</p>
<p>So the manual calculation would be 4MB (for input) + 64 MB x 2 (for forward and backward) + &lt;&lt; 1MB (for parameters). It is roughly 132 MB. There is still a big gap from 132 MB to 448 MB. I don’t know what I am missing. Any idea on how to manually calculate the GPU memory required for a network?</p>

ngimel: <p>Couple hundred MB are usually taken just by initializing cuda. Look at your memory consumption after</p>
<pre><code class="lang-auto">a=torch.cuda.FloatTensor(1)
</code></pre>
<p>, that would give you the framework overhead.</p>

gaoking132: <p>Thank you for the suggestion.</p>
<p>I found that 273 MB was used for initialized CUDA on my side.</p>
<p>After running this script, the program consumes 277 MB, which matches well with my calculation 273MB + 4MB (for input).</p>
<blockquote>
<p>net = nn.Conv3d(1, 16, 3, 1, 1)<br>
net.cuda()</p>
</blockquote>
<blockquote>
<p>input = torch.FloatTensor(1, 1, 64, 128, 128).cuda()<br>
input = Variable(input)<br>
# out = net(input)</p>
</blockquote>
<p>However, if I remove the comment of the last line, the program consumes 436 MB. There are 159 MB more memory consumed. However, if I calculate the size of output (16x64x128x128 x 4 bytes x 2) = 128 MB. There is still a not small gap there. Does anybody know why? Where are these additional memory consumed?</p>

jekbradbury: <p>Most likely (IIRC) this is workspace used by the convolution kernel; the way PyTorch allocates memory it will continue to leave blocks marked as in use from the perspective of nvidia-smi even if it’s no longer using them internally. This is because CUDA’s malloc and free functions are quite slow, and it’s much more efficient to cache allocated blocks in a free list. When the device runs out of memory, PyTorch will call CUDA’s free function on all free blocks and the memory usage seen by nvidia-smi will fall.</p>

gaoking132: <p>Thanks for the detailed answer. So besides running a network and checking nvidia-smi, is there any other principled way to estimate the GPU memory usage without running the network?</p>

jekbradbury: <p>If you don’t use CUDNN, then likely yes (most operations won’t use any scratchpad space, and those that do will allocate a deterministic amount that you can find in the code). But CUDNN contains many different algorithms and implementations for each operation (conv, pool, RNN, etc) with different memory requirements, and which algorithm is chosen depends in a complicated way on the sizes of all the inputs and the values of CUDNN flags. The memory usage that you’ve computed is probably accurate if you don’t count the cached free blocks, so if you’re trying to fit a network in a particular device with a given amount of memory that may be all you need to do.</p>

jacobkimmel: <p>To any future readers:</p>
<p>I implemented a quick tool to automate memory size estimation based on the approach above.<br>
<aside class="onebox whitelistedgeneric">
  <header class="source">
      <img src="https://assets-cdn.github.com/favicon.ico" class="site-icon" width="32" height="32">
      <a href="https://github.com/jacobkimmel/pytorch_modelsize" target="_blank" rel="nofollow noopener">GitHub</a>
  </header>
  <article class="onebox-body">
    <img src="https://avatars2.githubusercontent.com/u/2465221?s=400&amp;v=4" class="thumbnail onebox-avatar" width="400" height="400">

<h3><a href="https://github.com/jacobkimmel/pytorch_modelsize" target="_blank" rel="nofollow noopener">jacobkimmel/pytorch_modelsize</a></h3>

<p>pytorch_modelsize - Estimates the size of a PyTorch model in memory</p>


  </article>
  <div class="onebox-metadata">
    
    
  </div>
  <div style="clear: both"></div>
</aside>
</p>
<p>As this discussion outlines, note that these size estimations are only theoretical estimates, with implementation details altering the exact model size in practice!</p>
<p>Hope someone finds it useful <img src="//discuss.pytorch.org/images/emoji/apple/smile.png?v=5" title=":smile:" class="emoji" alt=":smile:"></p>

alexgo: <aside class="quote no-group" data-username="ngimel" data-post="2" data-topic="1713" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/ngimel/48/14766_2.png" class="avatar"> ngimel:</div>
<blockquote>
<p>Couple hundred MB are usually taken just by initializing cuda. Look at your memory consumption after</p>
<pre><code class="lang-auto">a=torch.cuda.FloatTensor(1)
</code></pre>
<p>, that would give you the framework overhead.</p>
</blockquote>
</aside>
<p>Thanks for this suggestion.<br>
Any thoughts as to why this overhead might be a lot more than a couple hundred MB?<br>
I checked this now exactly as you suggested with allocating a unit sized tensor, and in my case it seems to be 1229 [MB]! This is clearly too much.<br>
I checked right before the allocation and it was close to zero, and just after allocating this unit tensor, it jumped to 1229 [MB].</p>
<p>I’m using Pytorch v1.7, Cuda 10.1 and Tesla V100 GPU.</p>