Hi John, You are not drawing the right conclusion from your observation with MyNuc::GetNbObj. Let me explain. The TClonesArray is based on the standard C++ "new with placement". When calling new(a[i]) myclass(), the following happens: -if a[i] is null, an object of myclass is allocated space and the myclass constructor is called. - if a[i] is not null, your myclass constructor is called. No need to allocate space (time consuming). The object is simply initialized by the statements in your constructor, overwriting the values of the data members created by a previous call to the constructor. The address of the object is the one previously stored at a[i] (new with placement). In your case your class MyNuc records only the number of calls to your myclass constructor. It does not record the number of times a new object has been allocated. When using a TClonesArray with Trees, we assume that there are no gaps in the array. You only add/remove members at the end of the array. We provide a special function ExpandCreateFast for this purpose. If your algorithm requires gaps temporarily, you must make sure to call the Compress function before calling Ttree::Fill to eliminate all the gaps. The TClonesArray is initially created with a number of objects corresponding to the optional second argument in the TClonesArray constructor. When calling new(a[i]), the array will be automatically expanded if i is greater than the current allocation (by doubling the current size). Rene Brun On Mon, 7 Jul 2003, Frankland John wrote: > Hello ROOTpeople > > I think I have found a bug in TClonesArray, but probably I just still > haven't > understood how this thing works... > > I have attached two small classes which I have been using to test > TClonesArray-based event classes. The behaviour I am trying to get from > these classes is > > 1. several different "events" can co-exist, including events of > classes derived from some base "event" class, so no static > TClonesArray a la $ROOTSYS/test/Event.cxx > 2. from one event to the next, the number of objects in the > TClonesArray (i.e. the multiplicity of nuclei in the event) can > vary from 1 to 100 (rough order of magnitude). The TClonesArray > therefore has to change size and adapt to the current event, in > order to optimize the size of the resulting TTree. > > After some work (about 6 months ... :-D ) the working solution I have come > up with (see MyEvt.cxx) is, roughly speaking, the following: > > * initialise TClonesArray with an arbitrary number of slots: > TClonesArray("MyNuc",50) (BTW, does the number of slots - 50 here > - have any importance whatsoever ?) > * before looping over events, call ExpandCreate(1) in order to > create 1 object in the array > * for each new particle in each event, call ExpandCreate(mult) with > "mult" the new multiplicity if "mult" is larger than the > multiplicity of any previous event, otherwise just use the > existing array > * once the event is set up (all nuclei created) call > ExpandCreate(mult) with "mult" the final multiplicity in order to > set the size of the array i.e. for writing in a TTree. If the > current event is smaller than the previous one, this will > "liberate" the extra nuclei, otherwise it should have no effect > * before starting a new event, call Clear("C") to wipe all nuclei > (liberate any allocated memory if necessary) and then call > ExpandCreate(1) again in order to have 1 nucleus in the array > ready for a new event. > > This seems to work fine (see Main.cxx for example of use), the low > compression factor of the TTree branch for the events tells me that > empty slots were not written (i.e. for low multiplicity events), as does > the fact that if I use the TTree to plot, e.g., data.fParticles.GetZ() > then I do not see a huge peak at Z=0 corresponding to the empty slots. > > However I am a little worried by the following behaviour. In an > interactive session I load libPhysics.so and do ".L yNuc.cxx+". Notice > that MyNuc has a static data member to count the number of existing > objects. So if I do: > > root [3] MyNuc a > root [4] a.GetNbObj() > > I get the answer: > > (Int_t)1 > > Now I create a TClonesArray and initialise it with 5 MyNuc objects: > > root [5] TClonesArray tca("MyNuc",50) > root [6] tca.ExpandCreate(5) > > If I now type > > root [7] a.GetNbObj() > (Int_t)6 > > I see I have 6 MyNuc objects, i.e. "a" plus the 5 in the array. OK. Here > are the objects: > > root [8] (MyNuc*)tca.At(0) > (class MyNuc*)0x894da88 > root [9] (MyNuc*)tca.At(1) > (class MyNuc*)0x89f6668 > root [10] (MyNuc*)tca.At(2) > (class MyNuc*)0x89f6488 > root [11] (MyNuc*)tca.At(3) > (class MyNuc*)0x89f6598 > root [12] (MyNuc*)tca.At(4) > (class MyNuc*)0x89f66f0 > root [13] (MyNuc*)tca.At(5) > (class MyNuc*)0x0 > > (the last line was just to make sure that there are only 5 objects in > the array ;-) ). > Now let's suppose that the next event has only 1 nucleus. I do: > > root [14] tca.Clear("C") > > and I see that all the slots are empty: > > root [15] (MyNuc*)tca.At(0) > (class MyNuc*)0x0 > root [16] (MyNuc*)tca.At(4) > (class MyNuc*)0x0 > > but the objects still exist (this took me a long time to understand): > > root [17] a.GetNbObj() > (Int_t)6 > > Now I set the size of the event to 1: > > root [18] tca.ExpandCreate(1) > root [19] a.GetNbObj() > (Int_t)6 > root [20] (MyNuc*)tca.At(0) > (class MyNuc*)0x894da88 > root [21] (MyNuc*)tca.At(1) > (class MyNuc*)0x0 > > I see that no new events have been created (good !), that the first slot > of the array has been filled, > and that the object in the slot is the same one that was occupying this > slot in the previous event (good again !). > > Now let's suppose that the next event is multiplicity 3: > > root [22] tca.Clear("C") > root [23] tca.ExpandCreate(3) > root [24] (MyNuc*)tca.At(0) > (class MyNuc*)0x894da88 > root [25] (MyNuc*)tca.At(1) > (class MyNuc*)0x89f66f0 > root [26] (MyNuc*)tca.At(2) > (class MyNuc*)0x89f6598 > root [27] a.GetNbObj() > (Int_t)8 > > Two new objects have been created (the difference between the old > multiplicity - 1 - and the new - 3 - ?)!! However, the slots in the > array are filled with the old objects, although not in the same order as > before: slot "0" has not changed, but slots "1" and "2" now contain the > objects from the old slots "4" and "3" respectively.... I was > expecting/hoping that no new objects would be created (no need), and > that the 3 slots in this event would be the same as the first three > slots of the event with multiplicity 5 (this isn't essential, but it > seems a little strange to mix them up like this). > > If I try to get back to the initial event size, i.e. multiplicity 5, the > following occurs: > > root [29] tca.Clear("C") > root [30] tca.ExpandCreate(5) > root [31] a.GetNbObj() > (Int_t)10 > root [32] (MyNuc*)tca.At(0) > (class MyNuc*)0x894da88 > root [33] (MyNuc*)tca.At(1) > (class MyNuc*)0x89f66f0 > root [34] (MyNuc*)tca.At(2) > (class MyNuc*)0x89f6598 > root [35] (MyNuc*)tca.At(3) > (class MyNuc*)0x89f6488 > root [36] (MyNuc*)tca.At(4) > (class MyNuc*)0x89f6668 > root [37] (MyNuc*)tca.At(5) > (class MyNuc*)0x0 > > Once again, the fact of using ExpandCreate(n) followed by > ExpandCreate(n+i) creates i objects, even if (n+i) is smaller or the > same as the number of objects created in a previous call. Either this > behaviour is wrong or I haven't understood. However, there is no sign of > the new objects in the array slots : all slots are filled with > pre-existing objects (albeit in a different order). > > Now, if I execute the MainTest() example in Main.cxx I do not see the > number of objects increasing up to infinity (or segmentation fault, > whichever is sooner ;-) ) - rather the number of objects simply > increases up to the size of the largest event treated and no greater. > This is the "correct" behaviour - at least, it's the one I want. I then > realised that in MyEvt I use not "ExpandCreate" but "ExpandCreateFast", > and indeed if I repeat all the previous instructions replacing > ExpandCreate by ExpandCreateFast there is no more memory leak. > > Is this difference normal ? > > Best regards > John > >
This archive was generated by hypermail 2b29 : Thu Jan 01 2004 - 17:50:13 MET