diff --git a/core/cont/inc/TClonesArray.h b/core/cont/inc/TClonesArray.h index fb0a4a93cb13216f20fde6ad24b1463f441d2523..e6b8b0f5a0604bbfab78362e9a71f11c719af9e6 100644 --- a/core/cont/inc/TClonesArray.h +++ b/core/cont/inc/TClonesArray.h @@ -18,7 +18,7 @@ // TClonesArray // // // // An array of clone TObjects. The array expands automatically when // -// adding elements (shrinking can be done by hand). // +// adding elements (shrinking can be done explicitly). // // // ////////////////////////////////////////////////////////////////////////// @@ -66,6 +66,8 @@ public: void AddBefore(const TObject *, TObject *) { MayNotUse("AddBefore"); } void BypassStreamer(Bool_t bypass=kTRUE); Bool_t CanBypassStreamer() const { return TestBit(kBypassStreamer); } + TObject *ConstructedAt(Int_t idx); + TObject *ConstructedAt(Int_t idx, Option_t *clear_options); void SetClass(const char *classname,Int_t size=1000); void SetClass(const TClass *cl,Int_t size=1000); diff --git a/core/cont/src/TClonesArray.cxx b/core/cont/src/TClonesArray.cxx index d719ba569ca4bacfadc9caeaeb3586f5572a7c23..f9ed65c251c04ab8319357960859de81bea19e1c 100644 --- a/core/cont/src/TClonesArray.cxx +++ b/core/cont/src/TClonesArray.cxx @@ -41,12 +41,30 @@ // ... // // } // // ... // -// a.Delete(); // +// a.Delete(); // or a.Clear() or a.Clear("C") // +// } // +// // +// To reduce the number of call to the constructor (especially useful // +// if the user class requires memory allocation), the object can be // +// added (and constructed when needed) using ConstructedAt which only // +// calls the constructor once per slot. // +// // +// TClonesArray a("TTrack", 10000); // +// while (TEvent *ev = (TEvent *)next()) { // O(100000) events // +// for (int i = 0; i < ev->Ntracks; i++) { // O(10000) tracks // +// TTrack *track = (TTrack*)a.ConstructedAt(i); // +// track->Set(x,y,z,....); // +// ... // +// ... // +// } // +// ... // +// a.Clear(); // or a.Clear("C"); // // } // // // // Note: the only supported way to add objects to a TClonesArray is // -// via the new with placement method. The diffrent Add() methods of // -// TObjArray and its base classes are not allowed. // +// via the new with placement method or the ConstructedAt method. // +// The other Add() methods ofTObjArray and its base classes are not // +// allowed. // // // // Considering that a new/delete costs about 70 mus on a 300 MHz HP, // // O(10^9) new/deletes will save about 19 hours. // @@ -317,6 +335,52 @@ void TClonesArray::Compress() R__ASSERT(je == jf); } +//______________________________________________________________________________ +TObject *TClonesArray::ConstructedAt(Int_t idx) +{ + // Get an object at index 'idx' that is guaranteed to have been constructed. + // It might be either a freshly allocated object or one that had already been + // allocated (and assumingly used). In the later case, it is the callers + // responsability to insure that the object is returned to a known state, + // usually by calling the Clear method on the TClonesArray. + // + // Tests to see if the destructor has been called on the object. + // If so, or if the object has never been constructed the class constructor is called using + // New(). If not, return a pointer to the correct memory location. + // This explicitly to deal with TObject classes that allocate memory + // which will be reset (but not deallocated) in their Clear() + // functions. + + TObject *obj = (*this)[idx]; + if ( obj && obj->TestBit(TObject::kNotDeleted) ) { + return obj; + } + return (fClass) ? static_cast<TObject*>(fClass->New(obj)) : 0; +} + +//______________________________________________________________________________ +TObject *TClonesArray::ConstructedAt(Int_t idx, Option_t *clear_options) +{ + // Get an object at index 'idx' that is guaranteed to have been constructed. + // It might be either a freshly allocated object or one that had already been + // allocated (and assumingly used). In the later case, the function Clear + // will be called and passed the value of 'clear_options' + // + // Tests to see if the destructor has been called on the object. + // If so, or if the object has never been constructed the class constructor is called using + // New(). If not, return a pointer to the correct memory location. + // This explicitly to deal with TObject classes that allocate memory + // which will be reset (but not deallocated) in their Clear() + // functions. + + TObject *obj = (*this)[idx]; + if ( obj && obj->TestBit(TObject::kNotDeleted) ) { + obj->Clear(clear_options); + return obj; + } + return (fClass) ? static_cast<TObject*>(fClass->New(obj)) : 0; +} + //______________________________________________________________________________ void TClonesArray::Clear(Option_t *option) { @@ -332,12 +396,19 @@ void TClonesArray::Clear(Option_t *option) if (option && option[0] == 'C') { const char *cplus = strstr(option,"+"); + if (cplus) { + cplus = cplus + 1; + } else { + cplus = ""; + } Int_t n = GetEntriesFast(); for (Int_t i = 0; i < n; i++) { TObject *obj = UncheckedAt(i); if (obj) { - if (cplus) obj->Clear(cplus+1); - else obj->Clear(); + obj->Clear(cplus); + obj->ResetBit( kHasUUID ); + obj->ResetBit( kIsReferenced ); + obj->SetUniqueID( 0 ); } } } @@ -829,9 +900,14 @@ TObject *&TClonesArray::operator[](Int_t idx) if (idx >= fSize) Expand(TMath::Max(idx+1, GrowBy(fSize))); - if (!fKeep->fCont[idx]) + if (!fKeep->fCont[idx]) { fKeep->fCont[idx] = (TObject*) TStorage::ObjectAlloc(fClass->Size()); - + // Reset the bit so that: + // obj = myClonesArray[i]; + // obj->TestBit(TObject::kNotDeleted) + // will behave correctly. + memset(fKeep->fCont[idx], 0, sizeof(TObject)); // TClonesArray requires the class to start with the TObject part. + } fCont[idx] = fKeep->fCont[idx]; fLast = TMath::Max(idx, GetAbsLast()); diff --git a/docbook/users-guide/CollectionClasses.xml b/docbook/users-guide/CollectionClasses.xml index 49b587b70df8a1db329c18cf3c100c3761444680..ca8afa71725ba8f66cc7952fda41e25cc9a426ba 100644 --- a/docbook/users-guide/CollectionClasses.xml +++ b/docbook/users-guide/CollectionClasses.xml @@ -41,7 +41,7 @@ Collections act as flexible alternatives to traditional data structures of compu <programlisting language="c++"> if (myObject->InheritsFrom("TParticle") { -printf("myObject is a TParticlen"); + printf("myObject is a TParticlen"); } </programlisting> @@ -118,17 +118,17 @@ These include:</para> <programlisting language="c++"> class TEvent : public TObject { -private: -TList *fTracks; <code>//list of all tracks</code> -TList *fVertex1; <code>//subset of tracks part of vertex1</code> -TList *fVertex2; <code>//subset of tracks part of vertex2</code> + private: + TList *fTracks; <code>//list of all tracks</code> + TList *fVertex1; <code>//subset of tracks part of vertex1</code> + TList *fVertex2; <code>//subset of tracks part of vertex2</code> }; TEvent::~TEvent() { -fTracks->Delete(); -delete fTracks; -delete fVertex1; -delete fVertex2; + fTracks->Delete(); + delete fTracks; + delete fVertex1; + delete fVertex2; } </programlisting> @@ -152,23 +152,23 @@ delete fVertex2; <programlisting language="c++"> class TObjNum : public TObject { -private: -Int_t num; <code> // TObjNum is a simple container for an integer.</code> + private: + Int_t num; <code> // TObjNum is a simple container for an integer.</code> public: -TObjNum(Int_t i = 0) : num(i) { } -~TObjNum() { } -void SetNum(Int_t i) { num = i; } -Int_t GetNum() const { return num; } -void Print(Option_t *) const -{ printf("num = %dn", num); } -Bool_t IsEqual(TObject *obj) const -{ return num == ((TObjNum*)obj)->num; } -Bool_t IsSortable() const { return kTRUE; } -Int_t Compare(const TObject *obj) const -{ if (num < ((TObjNum*)obj)->num) return -1; -else if (num > ((TObjNum*)obj)->num) return 1; -else return 0; } -ULong_t Hash() const { return num; } + TObjNum(Int_t i = 0) : num(i) { } + ~TObjNum() { } + void SetNum(Int_t i) { num = i; } + Int_t GetNum() const { return num; } + void Print(Option_t *) const + { printf("num = %dn", num); } + Bool_t IsEqual(TObject *obj) const + { return num == ((TObjNum*)obj)->num; } + Bool_t IsSortable() const { return kTRUE; } + Int_t Compare(const TObject *obj) const + { if (num < ((TObjNum*)obj)->num) return -1; + else if (num > ((TObjNum*)obj)->num) return 1; + else return 0; } + ULong_t Hash() const { return num; } }; </programlisting> @@ -264,7 +264,7 @@ GetListOfPrimitives()->ForEach(TObject,Draw)(); <programlisting language="c++"> TIter next(GetListOfTracks()); while ((TTrack *obj = (TTrack *)next())) -obj->Draw(); + obj->Draw(); </programlisting> <itemizedlist> <listitem><para>Using the <emphasis role="bold"><code>TObjLink</code></emphasis> list entries (that wrap the <emphasis role="bold"><code>TObject</code></emphasis>*):</para></listitem> @@ -272,8 +272,8 @@ obj->Draw(); <programlisting language="c++"> TObjLink *lnk = GetListOfPrimitives()->FirstLink(); while (lnk) { -lnk->GetObject()->Draw(); -lnk = lnk->Next(); + lnk->GetObject()->Draw(); + lnk = lnk->Next(); } </programlisting> <itemizedlist> @@ -282,8 +282,8 @@ lnk = lnk->Next(); <programlisting language="c++"> TFree *idcur = this; while (idcur) { -... -idcur = (TFree*)GetListOfFree()->After(idcur); + ... + idcur = (TFree*)GetListOfFree()->After(idcur); } </programlisting> @@ -309,7 +309,7 @@ idcur = (TFree*)GetListOfFree()->After(idcur); <programlisting language="c++"> for (int i = 0; i <= fArr.GetLast(); i++) if ((track = (TTrack*)fArr[i])) <emphasis role="italic"><code>// or fArr.At(i)</code></emphasis> -track->Draw(); + track->Draw(); </programlisting> <para>Main features of <emphasis role="bold"><code>TObjArray</code></emphasis> are simple, well-known array semantics. <emphasis role="bold">Overhead per element</emphasis>: none, except possible over sizing of <code>fCont</code>.</para> @@ -334,12 +334,12 @@ track->Draw(); <programlisting language="c++"> TObjArray a(10000); while (TEvent *ev = (TEvent *)next()) { 聽聽 <emphasis role="italic"><code>// O(100000)</code></emphasis> -for (int i = 0; i < ev->Ntracks; i++) {聽 <emphasis role="italic"><code>// O(10000)</code></emphasis> -a[i] = new TTrack(x,y,z,...); -... -} -... -a.Delete(); + for (int i = 0; i < ev->Ntracks; i++) {聽 <emphasis role="italic"><code>// O(10000)</code></emphasis> + a[i] = new TTrack(x,y,z,...); + ... + } + ... + a.Delete(); } </programlisting> @@ -349,12 +349,13 @@ a.Delete(); <programlisting language="c++"> TClonesArray a("TTrack", 10000); while (TEvent *ev = (TEvent *)next()) { <code>// O(100000</code><emphasis role="italic">)</emphasis> -for (int i = 0; i < ev->Ntracks; i++) { <code>// O(10000)</code> -new(a[i]) TTrack(x,y,z,...); -... -} -... -a.Delete(); + for (int i = 0; i < ev->Ntracks; i++) { <code>// O(10000)</code> + TTrack *track = (Track*)a.ConstructedAt(i); + track->Set(x,y,z,...); + ... + } + ... + a.Clear(); // Or Clear("C") if the track objects must be returned (via Track::Clear) to a default state. } </programlisting> @@ -370,9 +371,9 @@ a.Delete(); <programlisting language="c++"> template<class T> class ArrayContainer { -private: -T *member[10]; -... + private: + T *member[10]; + ... }; </programlisting> diff --git a/docbook/users-guide/InputOutput.xml b/docbook/users-guide/InputOutput.xml index 9094f07363a3aaaa22f60a70478fe98bbd8f766b..03608b69d47e764bf5e7e6fbcb070d2d983541dd 100644 --- a/docbook/users-guide/InputOutput.xml +++ b/docbook/users-guide/InputOutput.xml @@ -1467,7 +1467,8 @@ fTracks->BypassStreamer(kFALSE); <emphasis role="italic"><code>// use the <programlisting language="c++"> <code>TRef fLastTrack; </code> <emphasis role="italic"><code>//pointer to last track</code></emphasis> <code>鈥�</code> -<code>Track *track = new(tracks[fNtrack++])Track(random);</code> +<code>Track *track = (Track*)fTracks->ConstructedAt(fNtrack++); +track->Set(random);</code> <emphasis role="italic"><code>// Save reference to last Track in the collection of Tracks</code></emphasis> <code>fLastTrack = track;</code> </programlisting> diff --git a/test/Event.cxx b/test/Event.cxx index def16822501f4e3d7a71586267d4c10ff11a985f..f7fd4ac1a36ae2b007c13bfcdc4363fb062b7837 100644 --- a/test/Event.cxx +++ b/test/Event.cxx @@ -192,8 +192,8 @@ Track *Event::AddTrack(Float_t random, Float_t ptmin) // is called. If tracks[i] is 0, a new Track object will be created // otherwise the previous Track[i] will be overwritten. - TClonesArray &tracks = *fTracks; - Track *track = new(tracks[fNtrack++]) Track(random); + Track *track = (Track*)fTracks->ConstructedAt(fNtrack++); + track->Set(random); //Save reference to last Track in the collection of Tracks fLastTrack = track; //Save reference in fHighPt if track is a high Pt track @@ -252,7 +252,7 @@ void Event::SetRandomVertex() { } //______________________________________________________________________________ -Track::Track(const Track &orig) : TObject(orig) +Track::Track(const Track &orig) : TObject(orig),fTriggerBits(orig.fTriggerBits) { // Copy a track object @@ -286,9 +286,6 @@ Track::Track(const Track &orig) : TObject(orig) fPointValue = 0; } fValid = orig.fValid; - - fTriggerBits = orig.fTriggerBits; - } //______________________________________________________________________________ @@ -373,12 +370,16 @@ Track &Track::operator=(const Track &orig) fNsp = orig.fNsp; if (fNsp == 0) { delete [] fPointValue; + fPointValue = 0; } else { for(int i=0; i<fNsp; i++) { fPointValue[i] = orig.fPointValue[i]; } } } else { + if (fNsp) { + delete [] fPointValue; + } fNsp = orig.fNsp; if (fNsp) { fPointValue = new Double32_t[fNsp]; @@ -399,9 +400,79 @@ Track &Track::operator=(const Track &orig) //______________________________________________________________________________ void Track::Clear(Option_t * /*option*/) { + // Note that we intend on using TClonesArray::ConstructedAt, so we do not + // need to delete any of the arrays. + + TObject::Clear(); fTriggerBits.Clear(); - delete [] fPointValue; - fPointValue = 0; +} + +//______________________________________________________________________________ +void Track::Set(Float_t random) +{ + // Set the values of the Track data members. + + Float_t a,b,px,py; + gRandom->Rannor(px,py); + fPx = px; + fPy = py; + fPz = TMath::Sqrt(px*px+py*py); + fRandom = 1000*random; + if (fRandom < 10) fMass2 = 0.106; + else if (fRandom < 100) fMass2 = 0.8; + else if (fRandom < 500) fMass2 = 4.5; + else if (fRandom < 900) fMass2 = 8.9; + else fMass2 = 9.8; + gRandom->Rannor(a,b); + fBx = 0.1*a; + fBy = 0.1*b; + fMeanCharge = 0.01*gRandom->Rndm(1); + gRandom->Rannor(a,b); + fXfirst = a*10; + fXlast = b*10; + gRandom->Rannor(a,b); + fYfirst = a*12; + fYlast = b*16; + gRandom->Rannor(a,b); + fZfirst = 50 + 5*a; + fZlast = 200 + 10*b; + fCharge = Double32_t(Int_t(3*gRandom->Rndm(1)) - 1); + + fTriggerBits.SetBitNumber((UInt_t)(64*gRandom->Rndm(1))); + fTriggerBits.SetBitNumber((UInt_t)(64*gRandom->Rndm(1))); + fTriggerBits.SetBitNumber((UInt_t)(64*gRandom->Rndm(1))); + + fVertex[0] = gRandom->Gaus(0,0.1); + fVertex[1] = gRandom->Gaus(0,0.2); + fVertex[2] = gRandom->Gaus(0,10); + fNpoint = Int_t(60+10*gRandom->Rndm(1)); + Int_t newNsp = Int_t(3*gRandom->Rndm(1)); + if (fNsp > newNsp) { + fNsp = newNsp; + if (fNsp == 0) { + delete [] fPointValue; + fPointValue = 0; + } else { + for(int i=0; i<fNsp; i++) { + fPointValue[i] = i+1; + } + } + + } else { + if (fNsp) { + delete [] fPointValue; + } + fNsp = newNsp; + if (fNsp) { + fPointValue = new Double32_t[fNsp]; + for(int i=0; i<fNsp; i++) { + fPointValue[i] = i+1; + } + } else { + fPointValue = 0; + } + } + fValid = Int_t(0.6+gRandom->Rndm(1)); } //______________________________________________________________________________ diff --git a/test/Event.h b/test/Event.h index d03e83736741b2edb8281962285b53135378233b..3b34bb132addb20744e2a440dbad3fbe180625be 100644 --- a/test/Event.h +++ b/test/Event.h @@ -46,12 +46,13 @@ private: TBits fTriggerBits; //Bits triggered by this track. public: - Track() { fPointValue = 0; } + Track() : fTriggerBits(64) { fNsp = 0; fPointValue = 0; } Track(const Track& orig); Track(Float_t random); virtual ~Track() {Clear();} Track &operator=(const Track &orig); + void Set(Float_t random); void Clear(Option_t *option=""); Float_t GetPx() const { return fPx; } Float_t GetPy() const { return fPy; } diff --git a/test/EventMT.cxx b/test/EventMT.cxx index f74d53455ab1b121d45c09b4b01ea78446529328..6173925872400d6f2a6d73a5952b01fff5637897 100644 --- a/test/EventMT.cxx +++ b/test/EventMT.cxx @@ -232,7 +232,7 @@ void Event::SetRandomVertex() { } //______________________________________________________________________________ -Track::Track(const Track &orig) : TObject(orig) +Track::Track(const Track &orig) : TObject(orig),fTriggerBits(orig.fTriggerBits) { // Copy a track object @@ -266,9 +266,6 @@ Track::Track(const Track &orig) : TObject(orig) fPointValue = 0; } fValid = orig.fValid; - - fTriggerBits = orig.fTriggerBits; - } //______________________________________________________________________________ @@ -353,12 +350,16 @@ Track &Track::operator=(const Track &orig) fNsp = orig.fNsp; if (fNsp == 0) { delete [] fPointValue; + fPointValue = 0; } else { for(int i=0; i<fNsp; i++) { fPointValue[i] = orig.fPointValue[i]; } } } else { + if (fNsp) { + delete [] fPointValue; + } fNsp = orig.fNsp; if (fNsp) { fPointValue = new Double32_t[fNsp];