Bad Allocation Error looping though TTree


_ROOT Version: 6.26
_Platform: Windows 10
_Compiler: Visual Studio


I’m trying to loop through the entries in a TTree from a .root file, but I’m getting bad_alloc errors. The files are 80 - 300 MB in size. I believe I’m running out of memory, as I don’t get the error on smaller files (~ 1 MB).

I have tried both python and C++, and get the same behaviour. Is there a way of looping through the entries in a TTree that doesn’t load the whole thing into memory?

Alternatively, what I’m trying to do is take a root file containing data from several hours of an experiment and split it into several root files, based on the timestamp of each event, to check for changes in the detector response over time. So for example, get a separate root file for each hour of data. Is there a way to do this that doesn’t involve looping through the whole TTree?

That’s precisely what ROOT does.
It looks like you have some bug(s) in your code (you “allocate” space and do not “release” it).

This C++ snippet produces the bad_alloc error:

std::string filename = "C:\\runxxx.root";
TFile* tf = TFile::Open(filename.c_str(), "READ");

	TTree* tree = dynamic_cast<TTree*>(tf->Get("Board 0"));

ULong64_t timeStamp;
	UShort_t energy;
	UShort_t channel;
	tree->SetBranchAddress("timeStamp", &timeStamp);
	tree->SetBranchAddress("energy", &energy);
	tree->SetBranchAddress("channel", &channel);

	float ts = 0;
	int nEntries = tree->GetEntriesFast();
	for (int i = 0; i < nEntries; i++) {

		tree->GetEntry(i);

		if (timeStamp > ts) {
			ts = timeStamp;
		}
	}

(Sorry about the code formatting)

Could you please explain where I allocate the space and how I would release it after each iteration?

You allocate nothing in this piece, but this is not your complete routine.

Inside the “for” loop, use:
if (tree->GetEntry(i) <= 0) break; // we are done

At the end of your routine (after the “for” loop), you should:
delete tf; // automatically deletes the tree, too

BTW. Try (check branch types): tree->Print();

I tried changing my GetEntry(i) call to

 if (tree->GetEntry(i) <= 0) break; 

and adding delete tf at the end of the loop, but I’m still getting the same bad_alloc error during the loop. The error happens after around 200,000 iterations, but not always at exactly the same one.

My code that produces the error now reads

void testSnippet() {

	std::string filename = "C:\\run277_lf.root";
	TFile* tf = TFile::Open(filename.c_str(), "READ");

	TTree* tree = dynamic_cast<TTree*>(tf->Get("Board 0"));

	tree->Print();

	ULong64_t timeStamp;
	tree->SetBranchAddress("timeStamp", &timeStamp);

	float ts = 0;
	int nEntries = tree->GetEntriesFast();
	for (int i = 0; i < nEntries; i++) {

		if (tree->GetEntry(i) <= 0) {
			break;
		}

		if (timeStamp > ts) {
			ts = timeStamp;
		}
	}
	delete tf;
	std::cout << ts << std::endl;
}

The output of tree->Print() is the following:

******************************************************************************
*Tree    :Board 0   : Board 0                                                *
*Entries : 19457543 : Total =       331056491 bytes  File  Size =  111694493 *
*        :          : Tree compression factor =   2.96                       *
******************************************************************************
*Br    0 :pu        : pu/O                                                   *
*Entries : 19457543 : Total  Size=   19473930 bytes  File Size  =     131197 *
*Baskets :      175 : Basket Size=    1317888 bytes  Compression= 148.40     *
*............................................................................*
*Br    1 :satu      : satu/O                                                 *
*Entries : 19457543 : Total  Size=   19474288 bytes  File Size  =     103519 *
*Baskets :      175 : Basket Size=    1317888 bytes  Compression= 188.09     *
*............................................................................*
*Br    2 :lost      : lost/O                                                 *
*Entries : 19457543 : Total  Size=   19474288 bytes  File Size  =     103519 *
*Baskets :      175 : Basket Size=    1317888 bytes  Compression= 188.09     *
*............................................................................*
*Br    3 :channel   : channel/s                                              *
*Entries : 19457543 : Total  Size=   38948181 bytes  File Size  =     449540 *
*Baskets :      338 : Basket Size=    2635776 bytes  Compression=  86.62     *
*............................................................................*
*Br    4 :cfd       : cfd/s                                                  *
*Entries : 19457543 : Total  Size=   38946813 bytes  File Size  =     205429 *
*Baskets :      338 : Basket Size=    2635264 bytes  Compression= 189.55     *
*............................................................................*
*Br    5 :timeStamp : timeStamp/l                                            *
*Entries : 19457543 : Total  Size=  155790759 bytes  File Size  =   86452034 *
*Baskets :     1314 : Basket Size=   25600000 bytes  Compression=   1.80     *
*............................................................................*
*Br    6 :energy    : energy/s                                               *
*Entries : 19457543 : Total  Size=   38947839 bytes  File Size  =   24222836 *
*Baskets :      338 : Basket Size=    2635264 bytes  Compression=   1.61     *
*............................................................................*

I assume the /s /l /O after the branch names are the data type (short, long, boolean)?

So, the “testSnippet” looks fine (I would use “Long64_t” for “nEntries” and “i”).
Also, the types of branches are fine, see: TTree → Add a column (“branch”) holding fundamental types and arrays thereof

Maybe @pcanal will have some ideas.

BTW. Could you try to change the last line: std::cout << ts << "\n";

That’s a good point about the type for nEntries and i, I’m just so used to typing int for these things.

Is there a particular reason to use “\n” over std::endl? The code hits the bad_alloc error before completing the for loop, so it makes no difference on this occasion, but I generally prefer to use the standard library if I can.

I remember problems with “std::endl” on Windows.

Attach the full “log” from the crash.

Does Root have in-built logging? All I can see at the moment are the attached images.

If there is no in-built logging then I can log manually. What information would you like to see?
Error1

If I run with the debugger I get this:

Well, we do not have your “Analyse16O” source code for inspection.

Can you open the shell and simply run: root testSnippet.cxx

BTW. Maybe @bellenot can help with crashing executables on Windows.

If I run that from cmd I don’t get any message:

If I open a root window and run it then root just crashes to desktop without any error message.

Have you tried to replace “std::endl” with "\n"?

BTW. I can now see “6.26/04”. Can you try the latest “6.28/00”? There should appear “6.28/02” shortly.

Yes, I got the same (lack of) result with “\n”. I’m pretty sure it crashes during the for loop, before it gets to that line.

I’ll try with the latest version. It will probably be tomorrow before I can get to that, but thanks for your help so far.

Another thing to try … work in a directory whose name does not contain any “special” characters (e.g., “O16_p_y” instead of “16O(p,y)”, and maybe “O16Analysis” instead of “16OAnalysis”).

I have tried both python and C++, and get the same behaviour. Is there a way of looping through the entries in a TTree that doesn’t load the whole thing into memory?

As Wile mentioned, ROOT already does not load the data chunk-a-time. However even if it was loading all the data at once (300 MB), you should not be running out of memory (assuming your machine has several GB of RAM).

So something else is going. A straight-forward solution would be (if you can) to run your failing example on Linux and use the tool valgrind to pin point the problem; Alternatively, you can try to cut portion of your code until it stops failing and that might give you an indication of the issues. Another alternative is to build your code in debug mode and use the debugger to find out where it fails.

Something along these lines with RDataFrame should help

import ROOT

df = ROOT.RDataFrame("TTreeNameHere", "FileNameHere")

hours = [(datetime.datetime.now() + datetime.timedelta(hours=x)).timestamp() for x in range(5)]

opts = ROOT.RDF.RSnapshotOptions()
opts.fLazy = True # This avoids that Snapshot calls trigger the execution right away

# Book all different Snapshot calls in advance
snapshots = [
    df.Filter(f"timestamp >= {hour_begin} && timestamp < {hour_end}")\
      .Snapshot(treeName, FileName, listOfCols, opts)
    for hour_begin, hour_end in zip(hours[:-1],hours[1:])
]

# Trigger execution of one of the Snapshots, all others will be executed at the same time
snap_df = snapshots[0].GetValue()

Yes, I have 8GB of RAM, so I wouldn’t expect it to actually have run out of memory.

I don’t have easy access to a linux version of root, so I would like to leave that as a last resort. However I based the code on a colleague’s python script using root that runs fine on linux using

tree = f1.Get( “Board 0” )
for evt in tree:

I’m confident that it’s the

tree->GetEntry(i)

call that is the problem. If I remove that then it runs without error, and running in the debugger shows it failing at that line.

Thanks @vpadulan I’ll give that a try tomorrow. I hadn’t heard of RDataFrame, but it does look useful

Note that the 'for evt in treeshould be callingGetEntry` under the hood :).

If I remove that then it runs without error, and running in the debugger shows it failing at that line.

If you upload your code and your ROOT file we can try running on Linux and see what the problem might be.