15
$\begingroup$

Probably this is a simple question, however I really don't understand this behavior in Mathematica. It particularly leads to a memory leak in a huge program I have to do processing of 3D images. I broke it down which took me quite a time and had to figure out that the problem occured in a simple function that includes a GaussianFilter in Block. The following simple code increases the memory each time I execute it and I can't figure out how to stop this:

image = Import["ExampleData/CTengine.tiff", "Image3D"];

oldMem = MemoryInUse[];
GaussianFilter[image, 1];
N[(MemoryInUse[] - oldMem)/1000/1000]

58.6569

Meaning that the consumed RAM increases by about 58 MB when the filter is applied even though nothing is returned. I checked for the data type that is produced by the GaussianFilter. The data type changes from Byte to Real32. I know that Mathematica stores output internally but nothing is returned. Especially, I don't understand why the memory is never freed even if I put the stuff into scoping structures like Block. By the way, setting $HistoryLength to 0 doesn't help either.

ImageType@image
ImageType@GaussianFilter[image, 1]

Byte

Real32

fun[image_] := Block[
   {},
   GaussianFilter[image, 1];
   ];

oldMem = MemoryInUse[];
fun[image];
N[(MemoryInUse[] - oldMem)/1000/1000]

58.6571

$HistoryLength = 0;
oldMem = MemoryInUse[];
fun[image];
N[(MemoryInUse[] - oldMem)/1000/1000]

58.6571

The same problem also occurs when using Map:

oldMem = MemoryInUse[];
$HistoryLength = 0;
Map[GaussianFilter[img3d, 1] &, Range[1, 10]];
oldMem/1000/1000 // N
MemoryInUse[]/1000/1000 // N
(MemoryInUse[] - oldMem)/1000/1000 // N

28.9377

620.817

591.878

Without saving anything into a variable, the memory has increased by 590 MB. Besides, setting $HistoryLength to zero shows no effect. I also tried to Clear In and Out, again with no effect.

Unprotect[In, Out]
Clear[In, Out]

System specifictation

  • OS: Windows 7 Professional, Service Pack 1 (64-bit)
  • Processor: Intel Xeon CPU E5630 @ 2.53 GHz (2 Processors)
  • Installed memory: 96 GB
  • Mathematica 10.0.1
$\endgroup$
  • 5
    $\begingroup$ Wow, quite a bit of RAM available there ;-) Does changing $HistoryLength=0 show any effect? $\endgroup$ – Yves Klett Nov 10 '14 at 14:56
  • $\begingroup$ Thanks for your fast reply. No doesn't have any effect. I will add it directly. It seems that the filter just soaks up my memory ;) $\endgroup$ – g3kk0 Nov 10 '14 at 15:02
  • $\begingroup$ quite a bit of RAM available, yes. But at some point even 96 GB are gone ... ;) $\endgroup$ – g3kk0 Nov 10 '14 at 15:05
  • $\begingroup$ Does this continue indeterminately, or is there a cap after which use of memory doesn't raise any more? Nothing really guarantees that garbage collectors return conceptually vacant memory right away, and there are many use cases where this makes sense (especially if the collection is not based on reference counting). It may be a bug, but it may also be an internal implementation detail of the collector, for instance considering garbage collection only every N object allocations. $\endgroup$ – kirma Nov 10 '14 at 15:08
  • 1
    $\begingroup$ I applied it several times using Map and Table and the memory consumption raises in each iteration. If i would run it long enough it would certainly kill my computer (which it did already in the real program I use this line). So it never stops or returns the memory... $\endgroup$ – g3kk0 Nov 10 '14 at 15:13
9
+100
$\begingroup$

Here are some debugging ideas and a possible workaround. Note that for v10, I am using the Wolfram Programming Cloud, which as far as I can tell, is reproducing your results.

Version specific specific problem

Using this code:

$HistoryLength = 0;
i = Import["ExampleData/CTengine.tiff", "Image3D"];
oldMem = MemoryInUse[];
GaussianFilter[i, 5];
N[(MemoryInUse[] - oldMem)/1000/1000]

A fresh WPC session returns 62.8 and a fresh v9 (Windows 7, 64-bit) returns 2.11. Subsequent executions of the same cell in WPC always to 62.8 until such point that I have reached the maximum memory allocated to my plan. On v9, however, four executions of the same cell results in -0.000952 being returned.

Independent of filter

Note that the same problem occurs when GaussianFilter is replaced with LaplacianFilter, LaplacianGaussianFilter, but not GradientFilter. For the latter, I obtain similar values for the memory difference on the WPC and v9. For the Laplacian and Gaussian filters, memory differences are on the order of 60 MB.

Possible workaround

The problem appears to lie in how the filters are using 3D image files. If we extract ImageData, the problem seems to be mitigated:

$HistoryLength = 0;
i = Import["ExampleData/CTengine.tiff", "Image3D"];
oldMem = MemoryInUse[];
Image3D@GaussianFilter[ImageData@i, 5];
N[(MemoryInUse[] - oldMem)/1000/1000]

The output of the above code block is 1.5 on the WPC and 2.1 on v9. Both platforms return a negative value after 4 executions of the same cell.

It appears that converting the image to data prior to filtering may be a suitable workaround.

i2 = GaussianFilter[i, 5]
i3 = Image3D[GaussianFilter[ImageData@i, 5]]

i2===i3 returns false; however ImageDifference[i2,i3] returns:

enter image description here

A bit of spelunking

@SimonWoods traced the problem to Image`FilteringDump`image3DConvolve and came to a similar conclusion with respect to a possible workaround. He graciously allowed me to copy his answer into this one for completeness.

Using Trace and Spelunk I tracked the evaluation of your GaussianFilter through to Image`FilteringDump`image3DConvolve. The memory leak occurs somewhere inside that function, though I was unable to pin it down any further. I couldn't find any sign of leaked temporary symbols in the Image context, so I guess the memory leak is somewhere inside the kernel code.

For a workaround you can apply the filter to the image data directly, which will use ListConvolve internally rather than Image`FilteringDump`image3DConvolve:

$HistoryLength = 0;
image = Import["ExampleData/CTengine.tiff", "Image3D"];

oldMem = MemoryInUse[];
Image3D @ GaussianFilter[ImageData @ image, 1];
N[(MemoryInUse[] - oldMem)/1000/1000]

(* 0.000984 *)
$\endgroup$
  • $\begingroup$ I couldn't think of this straightforward solution, thanks :) However, I will report this memory leak as a bug to Wolfram support. $\endgroup$ – g3kk0 Nov 18 '14 at 15:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.