TClonesArray Advice with Object Size known at Runtime

Hi there,

Previously I’ve been using TClonesArray blindly for holding objects of type “Waveform” which itself contains an std::vector of pulse samples. This TClonesArray of Waveform classes is itself stored in an Event class which is a branch of a tree. Since vectors are dynamic it seems that using a TClonesArray isn’t just helpful, it’s actually worse since all of the Waveform objects and the TClonesArray itself gets new’d and deleted in every event loop inside the containing tree.

Now, I am thinking of a better way to do this. What I want is to take advantage of TClonesArray, however I also want my Waveform class to have a defined number of pulse samples. The number of samples wouldn’t change during an execution of a script or in a file, it would only vary between different applications/experiments (ie I want to take 100 samples for todays run, 200 for tomorrow).

Is this possible, to take advantage of TClonesArray for a class, with a length that isn’t known until run time that doesn’t change in the life of the program?

I’ve been studying the examples provided by root, specifically test/Event.cxx and test/Event.h . I believe if someone could kindly explain how the following works I will have better insight into what I’m trying to do

In Event.h, the Event class contains a TClonesArray of Track objects, inside Track objects we have the following members

[code]class TDirectory;

class Track : public TObject {

private:
Float_t fPx; //X component of the momentum

Double32_t fVertex[3]; //[-30,30,16] Track vertex position

Int_t fNsp; //Number of points for this track with a special value
Double32_t* fPointValue; //[fNsp][0,3] a special quantity for some point.

[/code]

It is my understanding that to take advantage of a TClonesArray we need to have max sizes for arrays (see Rene’s answer [url=https://root-forum.cern.ch/t/tclonesarray-and-tobject-containing-a-pointer-to-std-vector/11344/1 I would have expected *fPointValue to be a fixed length array. In this example it doesn’t need to be, the fPointValue is allocated in the constructor of Event. If this true then what is the purpose of using fixed length arrays at all in root? Just let the sizes be controlled by (in this case) fNsp.

To reiterate my point, this is so I can use a TClonesArray on a class with an array size that is set once. There are lots of entries in the array (possibly over a thousand shorts) so I don’t want to set a max size of the array and pad with zeros. Additionally it is uncertain what an appropriate max size would be to cover all use cases.

Hi,

The TClonesArray is more efficient if there is no allocation/reallocation needed to handle the data member of its content. However it does function fine (as well as can be) even if the data member need to be reallocate (i.e. the contained class can have std::vector or variable size array).

Cheers,
Philippe.

Right, however is repeatedly using a class with a member array that could change but doesn’t considered a reallocation?

Basically, if there any lines such as “array = new Double_t[fNArraySize];” in the constructor of the class, the TClonesArray will not be efficient, correct?

If that’s the case I’m wondering if it’s possible to determine if the memory for the array is already allocated by the TClonesArray, and point to it directly instead of allocating it with “new”.

I know this may sound pedantic, but we are approaching files with billions of instances of this class and I want to fully understand how TClonesArray can make analyzing this data repeatedly efficient.

[quote]Right, however is repeatedly using a class with a member array that could change but doesn’t considered a reallocation?
[/quote]Yes.

[quote]Basically, if there any lines such as “array = new Double_t[fNArraySize];” in the constructor of the class, the TClonesArray will not be efficient, correct?[/quote]Yes, not as efficient as it could be without it.

[quote]If that’s the case I’m wondering if it’s possible to determine if the memory for the array is already allocated by the TClonesArray, and point to it directly instead of allocating it with “new”.[/quote]Humm … for the variable size array because of order of the setting of member, we can never know whether the already allocated memory is enough, so it will re-allocated for each object read. For std::vector, the memory will be reused as often as possible (i.e. as long as it does not need to grow).

Cheers,
Philippe.

If anyone is ever curious, I wanted to see what the overhead of using std::vector was. I created a simple Event builder script where Event is a class that contains many pulses, where each pulse contains sample data.

This makes a comparison of pulses storing the samples as a simple C-Array, a “variable” length array, and a vector. Turns out the write speeds for a vector is about 8% slower, and the read speeds are about 20% slower.

Attached is the code as a mercurial repository … it’s based on cmake so to generate the makefiles cd into the cmake directory, run cmake .. && make, and then ./rootFileTest and it will output a canvas.png in the cmake directory.

Philippe, as you pointed out there isn’t a good way of knowing whether the “variable” array has enough memory in the constructor, so for now I assumed the user would be smart enough to not change the size during a run! I couldn’t figure out how to have pulses store the variable array with a maxsize and actual size (as in this example, root.cern.ch/root/html/tutorials … ee3.C.html). If anyone knows if that’s possible, let me know!
root_test.tar.gz (42.5 KB)


Hi,

Which version of ROOT did you test with? (See release notes and read/write performance increases for std::vector).

Thanks,
Philippe.

Hmm… Previously I was using version 5.34/02 which did not include the recent improvements in std::vector. I just downloaded and ran with the latest release, 5.34/09, which includes the improvements but I don’t notice any better performance, it looks more or less the same. I didn’t configure the latest version in any special way, just a simple ./configure && make

I’m using RHEL 6.4 on a 64bit machine btw.


Hi,

Can you send me your example?

Thanks,
Philippe.

Okay, now it looks like I have it. Previously to clear the pulses I would clear and/or resize the vector of samples. Instead I only resize when building data and do nothing to the vector in the Clear() function for the pulses. Since the length of the vector doesn’t change there is no re-allocation of data. The read speed is very close to the c-array speed now, the write speed is still 8% slower, however this is very good!

Now I want to see how much overhead TClonesArrays have. In some experiments we will only have at most a few pulses (other experiments tens of pulses). So it may be faster to have the Event class simply contain all the pulses as members directly for a small number of pulses.

Attached is my code, you should simply need to cd into the cmake directory and call ‘cmake … && make’, a canvas.png will be outputted along with a statsfile.root holding the canvas.
root_test.tar.gz (43.5 KB)


I’ve finished my studies, and I’ve mostly just included this as a reference for other people. I compared having my class use a TClonesArray holding pulses versus storing the pulses directly as member variables.

Even at two entries, TClonesArray wins, so it seems to be very useful.