Memory leak in pyroot

Dear Root experts,

I am encountering a memory leak while iterating over an TTree in pyRoot. Its a small local analysis, so using pretty dated versions on my apple laptop, but worked fine up until now - hope someone can help:

I commented out as much as I could to isolate the problem, the remaining code is:

#!/usr/bin/python
#
import string, numpy
import sys,os    # for exit etc
import getopt # option parsing
import ROOT
from ROOT import gROOT, TFile, TTree, TCanvas, TPad, TH1F, TH2F, TImage, TGraph
from ROOT import TProfile2D, gPad, gStyle, gDirectory, TAxis, TF1, TLegend, TLine
from ROOT import TPaveLabel, TMultiGraph, TProfile, TSpectrum
from array import array
from locale import atof, atoi
# #######################################################################
#                     PARSE ARGUMENTS
# #######################################################################

# Usage(): print the usage and quit
def Usage ():
    print " "
    print "Usage:  Plots of line shape per bin"
    print "   LSperBin.py [args]" 
    print "      Where [args] are any of the following:"
    print "         --howto               : print this help command"
    print "         --bin xval:yval       : single grid bin to look at plots"
    print " "
    sys.exit(0)

try:
    optlist,filelist = getopt.getopt(sys.argv[1:], "",  ["howto","bin="])
except getopt.GetoptError, err:
    print str(err)
    Usage()
    sys.exit()

singlebin = 0
doplots = 0
    
for o, a in optlist:
    if o == "--howto":
        Usage()
        sys.exit()
    elif o == "--bin":
        singlebin = 1
        doplots = 1
        print a
        xbin = a.split(":",)[0]
        ybin = a.split(":",)[1]        
    
# some drawing options and canvas definition for drawing in the loop below
gStyle.SetOptFit(1011);
gROOT.SetStyle("Plain")
gStyle.SetOptStat(0); 

# ####################################################################################
#                    plot line shapes per scan bin
# #####################################################################################

# open the file
rtinput = TFile("tribic2017.root")
meas_tree = gDirectory.Get('measurements')

# get the data grid histogramm
datagrid = gDirectory.Get('dataGrid')

nbx = datagrid.GetXaxis().GetNbins();
nby = datagrid.GetYaxis().GetNbins();

# pick the bin choosen by the command line, or take all
if singlebin == 1:
    xstart = int(xbin)
    xend = xstart + 1
    ystart = int(ybin)
    yend = ystart +1
else:
    xstart = 0
    xend = nbx
    ystart = 0
    yend = nby

# Define Access to plots in ntuple branches
shapeC3 = TH1F()
meas_tree.SetBranchAddress("shapeC3",shapeC3);
shapeC4 = TH1F()
meas_tree.SetBranchAddress("shapeC4",shapeC4);

# loop over all bins to find those with outliers:

for xbin in range(xstart,xend):
    for ybin in range(ystart,yend):
        
        # Event loop
        histfound = 0
        nentries = meas_tree.GetEntries()
        for jentry in xrange (nentries)
            rtn = meas_tree.GetEntry(jentry)
            if rtn <= 0: continue
            if (xbin == meas_tree.gridXbin) and (ybin == meas_tree.gridYbin): 
                print xbin , ybin, histfound

(while running this loop and printing out the bins etc, python memory usage increases fast in the mac osx memory monitor, reaching several GB in about a minute, and continuing until the laptop runs out of memory.)

The input ntuple is about 43 MB large, and has about 32k entries, two leafs are THF1 histograms.

Any hint on how I could fix this would be most welcome.

Thanks!
Thorsten

PS: I can make the script and nuple available if needed.


ROOT Version: 5.34/23
Platform: Mac OS 10.11.6
Compiler: Python 2.7.10 (default, Oct 23 2015, 19:19:21)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin


Any chance you can try with a contemporary version of ROOT? There are some nice ones e.g. at https://root.cern/content/release-61406 :slight_smile:

Let us know!

ok, I will upgrade and try again … :sunglasses:

So I tried to upgrade: First I upgraded my mac ot 10.14 Mojave, then installed XCode from the AppStore, then

OsX 10.14 clang100 root_v6.14.06.macosx64-10.14-clang100.dmg 128M

But I get the following error ( even though Xcode is in the Applications folder, and the test command works) - does this mean I need to start from the source and compile myself?

Many thanks,
Thorsten

[MBP]  /Applications/root_v6.14.06/bin $ . thisroot.sh
[MBP]  /Applications/root_v6.14.06/bin $ root
ERROR in cling::CIFactory::createCI(): cannot extract standard library include paths!
Invoking:
  LC_ALL=C /Library/Developer/CommandLineTools/usr/bin/c++   -O2 -DNDEBUG -xc++ -E -v /dev/null 2>&1 >/dev/null | awk '/^#include </,/^End of search/{if (!/^#include </ && !/^End of search/){ print }}' | GREP_OPTIONS= grep -E "(c|g)\+\+"
Results was:
With exit code 256
warning: no such sysroot directory: '/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk'
input_line_1:1:10: fatal error: 'new' file not found
#include <new>
         ^~~~~
input_line_3:37:10: fatal error: 'string' file not found
#include <string>
         ^~~~~~~~
input_line_9:1:10: fatal error: 'iostream' file not found
#include <iostream>
         ^~~~~~~~~~
root [0] .q
[MBP]  /Applications/root_v6.14.06/bin $ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -O2 -DNDEBUG -xc++ -E -v /dev/null
Apple LLVM version 10.0.0 (clang-1000.11.45.5)
Target: x86_64-apple-darwin18.0.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.14.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -E -disable-free -disable-llvm-verifier -discard-value-names -main-file-name null -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -fno-strict-return -masm-verbose -munwind-tables -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -target-linker-version 409.12 -v -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/10.0.0 -D NDEBUG -stdlib=libc++ -O2 -fdeprecated-macro -fdebug-compilation-dir /Applications/root_v6.14.06/bin -ferror-limit 19 -fmessage-length 119 -stack-protector 1 -fblocks -fencode-extended-block-signature -fobjc-runtime=macosx-10.14.0 -fcxx-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -vectorize-loops -vectorize-slp -o - -x c++ /dev/null
clang -cc1 version 10.0.0 (clang-1000.11.45.5) default target x86_64-apple-darwin18.0.0
ignoring nonexistent directory "/usr/include/c++/v1"
ignoring nonexistent directory "/usr/local/include"
ignoring nonexistent directory "/usr/include"
#include "..." search starts here:
#include <...> search starts here:
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/10.0.0/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
 /System/Library/Frameworks (framework directory)
 /Library/Frameworks (framework directory)
End of search list.
# 1 "/dev/null"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 373 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "/dev/null" 2

[MBP]  /Applications/root_v6.14.06/bin $ root
ERROR in cling::CIFactory::createCI(): cannot extract standard library include paths!
Invoking:
  LC_ALL=C /Library/Developer/CommandLineTools/usr/bin/c++   -O2 -DNDEBUG -xc++ -E -v /dev/null 2>&1 >/dev/null | awk '/^#include </,/^End of search/{if (!/^#include </ && !/^End of search/){ print }}' | GREP_OPTIONS= grep -E "(c|g)\+\+"
Results was:
With exit code 256
warning: no such sysroot directory: '/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk'
input_line_1:1:10: fatal error: 'new' file not found
#include <new>
         ^~~~~
input_line_3:37:10: fatal error: 'string' file not found
#include <string>
         ^~~~~~~~
input_line_9:1:10: fatal error: 'iostream' file not found
#include <iostream>
         ^~~~~~~~~~
root [0] 

Hi Thorsten,

Does xcode-select --install help?

Cheers, Axel.

Yes it does, in fact I was just running it when your message came in :sunglasses: … I had decided to try to install from the source package, and then came across this in the prerequisite list, that I always ignore when installing from a .dmg …

Memory leak test coming up …

So unfortunately the memory leak is still there just like in root 5 … help?!

PS: Mojave is still running Python 2.7 - should I upgrade to a later python version as well?

No all good, let’s not shake too many things at once! Thanks for reproducing with ROOT6 - @etejedor offered to help!

Hi @Thorsten

Can you share the input ntuple so that I can try to reproduce?
Thanks!

Enric

Sure:

LSperBin.py
https://cernbox.cern.ch/index.php/s/T2HwhBZoilUcddM

tribic2017.root
https://cernbox.cern.ch/index.php/s/HIMyFXFznaOauQc

For this test I just run thisroot.sh and then

python LSperBin.py

And then watch the memory in the Mac OSX activity monitor.

Thanks!
Thorsten

Hi @Thorsten

I was able to reproduce the leak with the following code:

import ROOT

rtinput = ROOT.TFile("tribic2017.root")
meas_tree = rtinput.measurements

for entry in meas_tree:
    pass

using your file as input, but I do not observe the same behaviour with other ROOT files I have tried, needs investigation.

I opened a ticket to follow the issue here:
https://sft.its.cern.ch/jira/browse/ROOT-9875

Cheers,

Enric

Looks like it is not a Python thing, if we run an equivalent C++ macro:

{

  TFile rtinput("tribic2017.root");
  auto meas_tree = (TTree*)rtinput.Get("measurements");

  auto nentries = meas_tree->GetEntries();
  for(Long64_t i = 0; i < nentries; ++i) {
    meas_tree->GetEntry(i);
  }

}

the same leak appears.

@pcanal any thoughts on this?

The leaks seems to be coming from the branch that contains an histogram.
A work-around is:

{

   TFile rtinput("tribic2017.root");
   auto meas_tree = (TTree*)rtinput.Get("measurements");

   meas_tree->SetBranchStatus("shapeC3",kFALSE);
   meas_tree->SetBranchStatus("shapeC4",kFALSE);
   auto nentries = meas_tree->GetEntries();

   for(Long64_t i = 0; i < nentries; ++i)
      meas_tree->GetEntry(i);
   }
}

What is ‘leaking’ in the histograms are the functions which the histograms thinks it is not allowed to delete … I am now checking why that is and what can be done about it.

Many thanks for the quick replies, I can confirm that this workaround also works for my python code. This solves my immediate problem, as I dont need the histograms in this case. I might need to loop over them later, so I would be very interested to learn about a more general fix as well later on.

Many thanks,
Thorsten

Hi,

A workaround to use the histogram would be something like:

{

   TFile rtinput("tribic2017.root");
   auto meas_tree = (TTree*)rtinput.Get("measurements");

   TH1F *shapeC3hist = nullptr;
   TBranch *shapeC3branch = nullptr;
   meas_tree->SetBranchAddress("shapeC3", &shapeC3hist, &shapeC3branch);
   auto nentries = meas_tree->GetEntries();

   for(Long64_t i = 0; i < nentries; ++i)
      auto localEntry = meas_tree->LoadEntry(i);
      shapeC3branch->GetEntry(localEntry);
      if (shapeC3hist) {
          ..... use histogram ....
          shapeC3hist->GetListOfFunctions()->Delete(); // Delete the functions inside the list
      }
   }
}

Cheers,
Philippe.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.