You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Memory leak when running GPU broadcast in a loop #327
Works for me, I never get an OOM and it doesn't exceed 3GB of memory use. Do note that Julia uses a GC, so it's expected to see memory usage rise quite a bit until it falls again.
That still doesn't error here. What happens if you call GC.gc(true) a couple of times after the loop but before the subsequent operation that throws an OOM?
I tried running GC.gc(true) once my memory is all filled up and that doesn't seem to do anything. It doesn't change memory use and neither does it solve the error, even if I call it multiple times and give it a few seconds every time.
Running GC.gc(true) in between loops seems to hold memory use way down, but it's also extremely slow. A middle solution where the garbage collector is called every so many loops works, though:
for i in 1:5
for j in 1:1_000
gpu_array .+= 1
end
GC.gc(true)
end
This keeps memery use at reasonable levels and prevents any OOM errors later. Memory use also doesn't depend on the number of outer loops anymore.
After some experimenting I found out calling sleep(10) instead of GC.gc(true) sometimes, but not always, has the same effect. I don't know if that can give any clues as to what is causing this issue.
That's all very weird. I can't reproduce this no matter how long I tried, or how many kernels I compiled and launched. Maybe this is related to Windows? I don't currently have WSL set-up, so won't be able to try this straight away.
If you have the time, you could consider running the code for a while and writing a heap snapshot, using the new 1.9 functionality: JuliaLang/julia#46862. You can then open the snapshot in Chrome. If it's a Julia object memory leak, we should be able to spot it there. If we're leaking GPU memory however, we'll have to annotate our alloc/free functions (which should be simple enough to add some accounting to) here in oneAPI.jl. But the fact that I can't reproduce makes me think that it's the driver that somehow keeps memory alive, which is going to be much harder to pinpoint.
All right, I just ran both again (killing the REPL in between) and took a heap snapshots of both. I looked at them quickly and don't really see an obvious difference, but I don't really know what I am supposed to be looking for.
If this helps at all: I am on Windows 10, using the standard installation of WSL2 (Ubuntu). I use VS Code, but running it directly form Ubuntu doesn't change anything.
Coming back to this: every kernel launch, copy and fill used to create a Level Zero command list that was only destroyed when Julia's GC ran its finalizer. Each one holds about a megabyte of driver memory the GC doesn't know about, and the driver keeps that memory for reuse after the list is destroyed, so a GC after the fact doesn't hand it back. It reproduces on Linux too: with oneAPI.jl v2.8.2, your loop uses about 6 GB in 5000 iterations on an Iris Xe.
Since v2.9.0 (#610), work is submitted through one immediate command list per task, and the same loop stays flat. The remaining case, launching with an explicit @oneapi queue=..., is fixed in #713. Please reopen if you still see memory growing on v2.9 or later.
Activity
maleadt commentedon Jun 5, 2023
Works for me, I never get an OOM and it doesn't exceed 3GB of memory use. Do note that Julia uses a GC, so it's expected to see memory usage rise quite a bit until it falls again.
Can you post a backtrace of an OOM?
tiemvanderdeure commentedon Jun 5, 2023
When I run this, memory usage falls very little after running the loop, but stays high at around 7GB.
The loop actually completes without an error, but the OOM error arises when doing any other GPU operation afterwards.
So the above example code should have been (sorry!):
Where the final line triggers the following error:
maleadt commentedon Jun 5, 2023
That still doesn't error here. What happens if you call
GC.gc(true)a couple of times after the loop but before the subsequent operation that throws an OOM?tiemvanderdeure commentedon Jun 7, 2023
I tried running
GC.gc(true)once my memory is all filled up and that doesn't seem to do anything. It doesn't change memory use and neither does it solve the error, even if I call it multiple times and give it a few seconds every time.Running
GC.gc(true)in between loops seems to hold memory use way down, but it's also extremely slow. A middle solution where the garbage collector is called every so many loops works, though:This keeps memery use at reasonable levels and prevents any OOM errors later. Memory use also doesn't depend on the number of outer loops anymore.
After some experimenting I found out calling
sleep(10)instead ofGC.gc(true)sometimes, but not always, has the same effect. I don't know if that can give any clues as to what is causing this issue.maleadt commentedon Jun 13, 2023
That's all very weird. I can't reproduce this no matter how long I tried, or how many kernels I compiled and launched. Maybe this is related to Windows? I don't currently have WSL set-up, so won't be able to try this straight away.
If you have the time, you could consider running the code for a while and writing a heap snapshot, using the new 1.9 functionality: JuliaLang/julia#46862. You can then open the snapshot in Chrome. If it's a Julia object memory leak, we should be able to spot it there. If we're leaking GPU memory however, we'll have to annotate our
alloc/freefunctions (which should be simple enough to add some accounting to) here in oneAPI.jl. But the fact that I can't reproduce makes me think that it's the driver that somehow keeps memory alive, which is going to be much harder to pinpoint.tiemvanderdeure commentedon Jun 15, 2023
All right, I just ran both again (killing the REPL in between) and took a heap snapshots of both. I looked at them quickly and don't really see an obvious difference, but I don't really know what I am supposed to be looking for.
If this helps at all: I am on Windows 10, using the standard installation of WSL2 (Ubuntu). I use VS Code, but running it directly form Ubuntu doesn't change anything.
The files are too big to upload here, so I uploaded them here instead: https://file.io/xgRlcuitBSPP
maleadt commentedon Oct 19, 2026
Coming back to this: every kernel launch, copy and fill used to create a Level Zero command list that was only destroyed when Julia's GC ran its finalizer. Each one holds about a megabyte of driver memory the GC doesn't know about, and the driver keeps that memory for reuse after the list is destroyed, so a GC after the fact doesn't hand it back. It reproduces on Linux too: with oneAPI.jl v2.8.2, your loop uses about 6 GB in 5000 iterations on an Iris Xe.
Since v2.9.0 (#610), work is submitted through one immediate command list per task, and the same loop stays flat. The remaining case, launching with an explicit
@oneapi queue=..., is fixed in #713. Please reopen if you still see memory growing on v2.9 or later.