The call to
gROOT->GetListOfCleanups()->RecursiveRemove(this) in TObject’s destructor seems like an O(N^2) or O(N*M) operation, where N is the number of objects in the cleanup lists and M is the number of objects destroyed through the life of a program. This consumes a lot of cycles for our code, slowing it by a factor of 2.
One stage of our collaboration’s analysis is essentially a home-grown replacement for TSelector, looping over a TTree’s entries, processing, and filling histograms with the created objects. There are many hundreds of histograms that we put in the output TFile, and we get more every day
Since the output TFile is contained in the list of Cleanups in gROOT, every time TObject is destroyed, this list of hundreds of histograms is iterated in the RecursiveRemove call. There are a few solutions I’ve come up with:
[ul][li]Turn off the cleanup with gROOT->SetMustClean(false) at the beginning of my program. This seems heavy-handed, and while our code doesn’t use any of the dangling-pointer garbage collection provided by the cleanup lists, I don’t know if ROOT internally uses these. Am I in danger of a double delete or memory leak in the ROOT libraries? Small-scale tests indicate no, and this is the solution I implemented.[/li]
[li] Remove the output TFile from the global list of Cleanups. Again, I don’t know if this has any undesirable side-effects, but it sounds dangerous to manually remove things from TROOT’s lists. [/li]
[li] Try to create the histograms without a directory, so the Cleanup lists won’t see them. Since we want to write the histograms to a file eventually, we have to move them to a TFile just before the end of the program. This is a major effort to go back through all the analysis modules and manually move histogram directories around.[/li]
[li] Don’t call RecursiveRemove whenever a TObject is deleted. You can unset the bit with
TObject::SetBit(TObject::kMustCleanup, false) but since we would have to do this in every class that inherits from TObject, it’s also a major retro-fit.[/li][/ul]
I’m mostly posting this because I had a lot of trouble finding any documentation on the List of Cleanups facility, and even once I figured out that’s where the problem is, the User’s guide page on the kMustCleanup bit wasn’t that helpful. It doesn’t mention the TROOT::SetMustClean() flag, for instance.
I should mention this is using ROOT 5.34.28, but applies across many recent versions.