Change:
for module in (nn.RNN, nn.LSTM, nn.GRU): for bias in (True, False): for bidirectional in (False, True): <del> for dropout in (0, 1):  # Because of dropout randomness, can only compare 0 and 1 for batch_first in (False, True): num_directions = 2 if bidirectional else 1 if batch_first: input_val = torch.randn(batch, seq_length, input_size) else: input_val = torch.randn(seq_length, batch, input_size) hx_val = torch.randn(num_layers * num_directions, batch, hidden_size) rnn = module(input_size, </del> <ins> for batch_first in (False, True): num_directions = 2 if bidirectional else 1 if batch_first: input_val = torch.randn(batch, seq_length, input_size) else: input_val = torch.randn(seq_length, batch, input_size) hx_val = torch.randn(num_layers * num_directions, batch, hidden_size) rnn = module(input_size, hidden_size, num_layers, bias=bias, dropout=dropout, bidirectional=bidirectional, batch_first=batch_first) outputs_cpu = forward_backward( False, rnn, input_val, hx_val, rnn.all_weights) rnn_gpu = module(input_size, </ins> hidden_size, num_layers, bias=bias,

Discussion:
cuDNN backend for RNN stabilizes at large mem usage. The following code stabilizes for me at 14+GiB reported by nvidia-smi. The same RNN without cuDNN uses <2.5GiB.
Oops... any idea what's going on with the garbage collector in this case?
are you running this on a P100 GPU?
Yes, I observe similar behavior on Titan X Pascal
hmmm, do cudnn RNNs have weird memory-oversubscribing properties? If it wasn't for CuDNN RNN, then it's not unreasonable. we use a custom CUDA allocator that will oversubscribe if needed, but will free the memory blocks it's holding onto if it hits OOM / failed allocations.
Tried the same RNN in torch and am getting ~2.6GiB on nvidia-smi.
With cuDNN I'm getting a constant usage of 2241MB on my machine using PyTorch (M40).
are you using cudnnv5, or v6?
v5
no, cudnn does not do memory oversubscribing, and already tried the same network in lua torch with cudnn, where it gets expected memory usage. Also, he is getting OOMs on slightly bigger networks, where apparently caching allocator should have tried to free previous blocks. Interesting that can not repro this.
Just to make sure did you set when you ran? I left it as False in the repro.
I can repro in the docker image built from the current version on P100, and I go OOM after 8 batches with the same image on 1080, which generally should not happen - if there is enough memory to go through 1 batch, memory usage should not increase afterwards.
No, I just copied your script and ran it. Weird thing is that it goes OOM after 18 batches once I enable . Without it memory remains stable.
A lot of cudaMalloc allocations go not through THCudaMalloc, and never get freed:
So not going through THCudaMalloc is actually expected for storages allocated with THCStorage_(newWithSize), but what's not expected is to every time cudaMalloc bytes of rnn workspace, instead of reusing earlier allocations. (~784 MB allocations are for workspace, ~156MB allocations happen somewhere in criterion).
My guess is that the bug is at the Python level.

Either: Something is holding on to a Tensor longer than it should Something is creating a circular reference which prevents quick clean-up
The issue is that in creates cyclic references. It's made worse by the fact that it holds on to more memory than it should (like the workspace). You can see this by adding a as the first line in your for loop. With the explicit call to the GC every iteration, the memory usage doesn't explode.
how can these cyclic references be fixed?
Not sure yet, but I will fix it tomorrow with