diff --git a/tree/doc/v520/index.html b/tree/doc/v520/index.html index ecd6fc80957b152698a6f1f472393cbdf641e381..7aedd4d0b3b19817d2429d4b727f37991b9e383c 100644 --- a/tree/doc/v520/index.html +++ b/tree/doc/v520/index.html @@ -129,6 +129,7 @@ where reco_ee_et is a vector<vector<double> > See http://root.cern. <li>Insure that the formula that are used as indices or as argument to special functions have their branch(es) loaded once. This fixes http://root.cern.ch/phpBB2/viewtopic.php?p=27080#27080 <li>Correct the drawing of "X[1]:X[5]" when X is a vector< vector<float> > and X[1].size()!=X[5].size(). (reported at http://root.cern.ch/phpBB2/viewtopic.php?p=27070) +<li>Correct the passing of NaN to function being called by TTree::Draw.</li> </ul> <h4>Splitting STL collections of pointers</h4> @@ -156,8 +157,37 @@ The size of this unzipping cache is 20% the size of the TTreeCache and can be mo This experimental feature is disabled by default, to activate it use the static function <pre>TTreeCache::SetParallelUnzip(TTreeCacheUnzip::EParUnzipMode option = TTreeCacheUnzip::kEnable).</pre> The possible values to pass are: <ul><li>TTreeCacheUnzip::kEnable to enable it</li><li>TTreeCacheUnzip::kDisable to disable it</li><li>TTreeCacheUnzip::kForce to force it.</li></ul>The TTreeCacheUnzip is actived only if you have more than one core. To activate it with only one core useTTreeCacheUnzip::kForce option (for example to measure the overhead). +<h4>Disk and Memory Space Gain</h4> + +In ROOT older than v5.20/00, the branches' last basket, also known as the <i>write basket</i>, was always saved in the same "key" as the TTree object and was always present in memory when reading or writing. +When reading this write basket was always present in memory even if the branch was never accessed. +<p> +Starting in v5.20/00, TTree::Write closes out, compresses (when requested) and writes to disk in their own file record the <i>write baskets</i> of all the branches. +(This is implemented via the new function TTree::FlushBaskets, TBranch::FlushBaskets, TBranch::FlushOneBaskets) +<p> +TTree::AutoSave supports a new option "FlushBaskets" which will call FlushBaskets before saving the TTree object. + +<h5><b><u>Benefits</u></b></h5> + +Flushing the write baskets has several advantages: +<ul> +<li>Reduce the file size of the TTree object (it not longer contains the last basket), improving read time of the TTree object</li> +<li>Reduce memory footprint of the TTree object. +<ul><li>In a TTree which "flushed" buffer, there is now usually only zero or one buffer in memory.</li> +<li>Previously each branch always had at least one basket in memory and usually 2 (the write basket and one read basket).</li> +<li>Now only the basket of the branches actually read are loaded in memory.</li> +</ul> +</li> +<li>allow for the basket to be compressed and stored separated, increasing the compression factor.</li> +</ul> + +Note: Calling FlushBaskets too often (either directly of via AutoSave("FlushBaskets")) can lead to unnecessary fragmentation of the ROOT file, +since it write the baskets to disk (and a new basket will be started at the next fill) whether or not the content was close to filling the basket or not. + <h4>Others</h4> <ul> +<li>The fast tree cloning (TTreeCloner) was enhanced to support copying in-memory TTrees (that have been save as a single key on file). This issue was preventing <b>hadd</b> to fast clone files containing any 'in-memory' tree. +</li> <li>Re-enabled the splitting of TVector3 and of any classes starting by TVector that is not a TVectorT.</li> <li>Fix the list of StreamerInfo stored in the TFile in the case of a slow @@ -193,5 +223,6 @@ This omission meant that slow CloneTree was (fataly) missing in some cases the copy of the TStreamerInfo for class that are part part of the TTree but had only a base and no member or in some cases where it had only object data members.</li> +<li>Fix the return value of the lookup in TChainIndex +when the value searched for does not exist.</li> </ul> - diff --git a/tree/tree/inc/TBranch.h b/tree/tree/inc/TBranch.h index 5159411d51de22ce82fabadce7a5887dbfa1a0db..b6571da5f3bdd2439deeb3fb835e540230b1b994 100644 --- a/tree/tree/inc/TBranch.h +++ b/tree/tree/inc/TBranch.h @@ -93,6 +93,8 @@ protected: void SetSkipZip(Bool_t skip = kTRUE) { fSkipZip = skip; } void Init(const char *name, const char *leaflist, Int_t compress); + Int_t WriteBasket(TBasket* basket, Int_t where); + private: TBranch(const TBranch&); // not implemented TBranch& operator=(const TBranch&); // not implemented @@ -104,6 +106,7 @@ public: virtual ~TBranch(); virtual void AddBasket(TBasket &b, Bool_t ondisk, Long64_t startEntry); + virtual void AddLastBasket(Long64_t startEntry); virtual void Browse(TBrowser *b); virtual void DeleteBaskets(Option_t* option=""); virtual void DropBaskets(Option_t *option = ""); @@ -112,6 +115,9 @@ public: virtual void FillLeaves(TBuffer &b); virtual TBranch *FindBranch(const char *name); virtual TLeaf *FindLeaf(const char *name); + Int_t FlushBaskets(); + Int_t FlushOneBasket(UInt_t which); + virtual char *GetAddress() const {return fAddress;} virtual Int_t GetBasketSize() const {return fBasketSize;} virtual const char* GetClassName() const { return ""; } @@ -175,7 +181,6 @@ public: virtual void SetOffset(Int_t offset=0) {fOffset=offset;} virtual void SetTree(TTree *tree) { fTree = tree;} virtual void UpdateAddress() {;} - void WriteBasket(TBasket* basket); static void ResetCount(); diff --git a/tree/tree/inc/TTree.h b/tree/tree/inc/TTree.h index 66b42ebecaac9a3abf4dd1175499ca7ded2c5d0e..17ee9b373e012f5eb0bd81e6debd0aea494fcf0f 100644 --- a/tree/tree/inc/TTree.h +++ b/tree/tree/inc/TTree.h @@ -247,6 +247,7 @@ public: virtual TBranch *FindBranch(const char* name); virtual TLeaf *FindLeaf(const char* name); virtual Int_t Fit(const char* funcname, const char* varexp, const char* selection = "", Option_t* option = "", Option_t* goption = "", Long64_t nentries = 1000000000, Long64_t firstentry = 0); // *MENU* + virtual Int_t FlushBaskets() const; virtual const char *GetAlias(const char* aliasName) const; virtual TBranch *GetBranch(const char* name); virtual TBranchRef *GetBranchRef() const { return fBranchRef; }; @@ -400,6 +401,8 @@ public: virtual void StartViewer(); // *MENU* virtual Int_t UnbinnedFit(const char* funcname, const char* varexp, const char* selection = "", Option_t* option = "", Long64_t nentries = 1000000000, Long64_t firstentry = 0); void UseCurrentStyle(); + virtual Int_t Write(const char *name=0, Int_t option=0, Int_t bufsize=0); + virtual Int_t Write(const char *name=0, Int_t option=0, Int_t bufsize=0) const; ClassDef(TTree,16) //Tree descriptor (the main ROOT I/O class) }; diff --git a/tree/tree/src/TBasket.cxx b/tree/tree/src/TBasket.cxx index fb013adb12fe6b6fa488198b34b25b6504317278..541019f09c30abbbcd2c95d6a60fd593d45330ff 100644 --- a/tree/tree/src/TBasket.cxx +++ b/tree/tree/src/TBasket.cxx @@ -53,7 +53,7 @@ TBasket::TBasket() //_______________________________________________________________________ TBasket::TBasket(TDirectory *motherDir) : TKey(motherDir) { - // Simple Constructor. + // Constructor used during reading. fDisplacement = 0; fEntryOffset = 0; @@ -71,7 +71,7 @@ TBasket::TBasket(TDirectory *motherDir) : TKey(motherDir) TBasket::TBasket(const char *name, const char *title, TBranch *branch) : TKey(branch->GetDirectory()) { - // Basket normal constructor. + // Basket normal constructor, used during writing. SetName(name); SetTitle(title); diff --git a/tree/tree/src/TBranch.cxx b/tree/tree/src/TBranch.cxx index ddef833c89cc8ae1fd38d29227cb4d2b3aa5f068..489aab5319a12982167d5d695b4b6c3c0cd043f1 100644 --- a/tree/tree/src/TBranch.cxx +++ b/tree/tree/src/TBranch.cxx @@ -85,7 +85,7 @@ TBranch::TBranch() , fZipBytes(0) , fBranches() , fLeaves() -, fBaskets() +, fBaskets(fMaxBaskets) , fNBasketRAM(kMaxRAM+1) , fBasketRAM(0) , fBasketBytes(0) @@ -129,7 +129,7 @@ TBranch::TBranch(TTree *tree, const char* name, void* address, const char* leafl , fZipBytes(0) , fBranches() , fLeaves() -, fBaskets() +, fBaskets(fMaxBaskets) , fNBasketRAM(kMaxRAM+1) , fBasketRAM(0) , fBasketBytes(0) @@ -211,7 +211,7 @@ TBranch::TBranch(TBranch *parent, const char* name, void* address, const char* l , fZipBytes(0) , fBranches() , fLeaves() -, fBaskets() +, fBaskets(fMaxBaskets) , fNBasketRAM(kMaxRAM+1) , fBasketRAM(0) , fBasketBytes(0) @@ -403,15 +403,6 @@ void TBranch::Init(const char* name, const char* leaflist, Int_t compress) delete[] leaftype; leaftype = 0; - // Create the first basket. - // - // Note: We cannot do this until we have created the leaves, - // otherwise we do not know the fEntryOffsetLen which - // compensates for variable-sized leaves, like varying - // length arrays. - // - TBasket* basket = fTree->CreateBasket(this); - fBaskets.AddAt(basket, 0); } //______________________________________________________________________________ @@ -530,12 +521,10 @@ void TBranch::AddBasket(TBasket& b, Bool_t ondisk, Long64_t startEntry) if (ondisk) { fBasketBytes[where] = basket->GetNbytes(); // not for in mem fBasketSeek[where] = basket->GetSeekKey(); // not for in mem + fBaskets.AddAtAndExpand(0,fWriteBasket); ++fWriteBasket; } else { fBaskets.AddAtAndExpand(basket,fWriteBasket); - if (fWriteBasket >= fMaxBaskets) { - ExpandBasketArrays(); - } fTree->IncrementTotalBuffers(basket->GetBufferSize()); } @@ -549,6 +538,26 @@ void TBranch::AddBasket(TBasket& b, Bool_t ondisk, Long64_t startEntry) } } +//______________________________________________________________________________ +void TBranch::AddLastBasket(Long64_t startEntry) +{ + // Add the start entry of the write basket (not yet create) + + if (fWriteBasket >= fMaxBaskets) { + ExpandBasketArrays(); + } + Int_t where = fWriteBasket; + + if (where && startEntry < fBasketEntry[where-1]) { + // Need to find the right location and move the possible baskets + + Fatal("AddBasket","The last basket must have the highest entry number (%s/%d/%d).",GetName(),startEntry,fWriteBasket); + + } + fBasketEntry[where] = startEntry; + fBaskets.AddAtAndExpand(0,fWriteBasket); +} + //______________________________________________________________________________ void TBranch::Browse(TBrowser* b) { @@ -688,8 +697,11 @@ void TBranch::ExpandBasketArrays() newsize*sizeof(Long64_t),fMaxBaskets*sizeof(Long64_t)); fBasketSeek = (Long64_t*)TStorage::ReAlloc(fBasketSeek, newsize*sizeof(Long64_t),fMaxBaskets*sizeof(Long64_t)); + fMaxBaskets = newsize; + fBaskets.Expand(newsize); + for (Int_t i=fWriteBasket;i<fMaxBaskets;i++) { fBasketBytes[i] = 0; fBasketEntry[i] = 0; @@ -714,7 +726,9 @@ Int_t TBranch::Fill() TBasket* basket = GetBasket(fWriteBasket); if (!basket) { - return 0; + basket = fTree->CreateBasket(this); // create a new basket + if (!basket) return 0; + fBaskets.AddAtAndExpand(basket,fWriteBasket); } TBuffer* buf = basket->GetBufferRef(); @@ -744,7 +758,7 @@ Int_t TBranch::Fill() // If the basket already contains entry we need to close it // out. (This is because we can only transfer full compressed // buffer) - WriteBasket(basket); + WriteBasket(basket,fWriteBasket); // And restart from scratch return Fill(); } @@ -836,26 +850,7 @@ Int_t TBranch::Fill() if (fTree->TestBit(TTree::kCircular)) { return nbytes; } - Int_t nout = basket->WriteBuffer(); - fBasketBytes[fWriteBasket] = basket->GetNbytes(); - fBasketSeek[fWriteBasket] = basket->GetSeekKey(); - Int_t addbytes = basket->GetObjlen() + basket->GetKeylen(); - if (fDirectory && (fDirectory != gROOT) && fDirectory->IsWritable()) { - delete basket; - basket = 0; - fBaskets[fWriteBasket] = 0; - } - fZipBytes += nout; - fTotBytes += addbytes; - fTree->AddTotBytes(addbytes); - fTree->AddZipBytes(nout); - basket = fTree->CreateBasket(this); - ++fWriteBasket; - fBaskets.AddAtAndExpand(basket, fWriteBasket); - if (fWriteBasket >= fMaxBaskets) { - ExpandBasketArrays(); - } - fBasketEntry[fWriteBasket] = fEntryNumber; + Int_t nout = WriteBasket(basket,fWriteBasket); return (nout >= 0) ? nbytes : -1; } return nbytes; @@ -959,6 +954,79 @@ TLeaf* TBranch::FindLeaf(const char* searchname) return 0; } + +//______________________________________________________________________________ +Int_t TBranch::FlushBaskets() +{ + // Flush to disk all the baskets of this branch and any of subbranches. + // Return the number of bytes written or -1 in case of write error. + + UInt_t nerror = 0; + Int_t nbytes = 0; + + for(Int_t i=0; i<=fWriteBasket; ++i) { + if (fBaskets.UncheckedAt(i)) { + Int_t nwrite = FlushOneBasket(i); + if (nwrite<0) { + ++nerror; + } else { + nbytes += nwrite; + } + } + } + Int_t len = fBranches.GetEntriesFast(); + for (Int_t i = 0; i < len; ++i) { + TBranch* branch = (TBranch*) fBranches.UncheckedAt(i); + if (!branch) { + continue; + } + Int_t nwrite = branch->FlushBaskets(); + if (nwrite<0) { + ++nerror; + } else { + nbytes += nwrite; + } + } + if (nerror) { + return -1; + } else { + return nbytes; + } +} + +//______________________________________________________________________________ +Int_t TBranch::FlushOneBasket(UInt_t ibasket) +{ + // If we have a write basket in memory and it contains some entries and + // has not yet been written to disk, we write it and delete it from memory. + // Return the number of bytes written; + + Int_t nbytes = 0; + if (fBaskets.GetEntries()) { + TBasket *basket = (TBasket*)fBaskets.UncheckedAt(ibasket); + + if (basket) { + if (basket->GetNevBuf() + && fBasketSeek[ibasket]==0) { + // If the basket already contains entry we need to close it out. + // (This is because we can only transfer full compressed buffer) + + if (basket->GetBufferRef()->IsReading()) { + basket->SetWriteMode(); + } + nbytes = WriteBasket(basket,ibasket); // WriteBasket will delete the existing basket. + + } else { + // If the basket is empty or has already been written. + basket->DropBuffers(); + delete basket; + fBaskets[ibasket] = 0; + } + } + } + return nbytes; +} + //______________________________________________________________________________ TBasket* TBranch::GetBasket(Int_t basketnumber) { @@ -971,8 +1039,9 @@ TBasket* TBranch::GetBasket(Int_t basketnumber) if (basketnumber <0 || basketnumber > fWriteBasket) return 0; TBasket *basket = (TBasket*)fBaskets.UncheckedAt(basketnumber); if (basket) return basket; + if (basketnumber == fWriteBasket) return 0; - // create/decode basket parameters from buffer + // create/decode basket parameters from buffer TFile *file = GetFile(0); basket = new TBasket(file); if (fSkipZip) basket->SetBit(TBufferFile::kNotDecompressed); @@ -1574,7 +1643,7 @@ void TBranch::Refresh(TBranch* b) Int_t nbaskets = b->fBaskets.GetSize(); fBaskets.Expand(nbaskets); //The current fWritebasket must always be in memory. - //Take it (just s swap) from the Tree being read + //Take it (just swap) from the Tree being read TBasket *basket = (TBasket*)b->fBaskets.UncheckedAt(fWriteBasket); fBaskets.AddAt(basket,fWriteBasket); b->fBaskets.RemoveAt(fWriteBasket); @@ -1598,8 +1667,6 @@ void TBranch::Reset(Option_t*) fZipBytes = 0; fEntryNumber = 0; - Int_t nbaskets = fBaskets.GetEntries(); - if (fBasketBytes) { for (Int_t i = 0; i < fMaxBaskets; ++i) { fBasketBytes[i] = 0; @@ -1619,11 +1686,6 @@ void TBranch::Reset(Option_t*) } fBaskets.Delete(); - - if (nbaskets) { - TBasket* basket = fTree->CreateBasket(this); - fBaskets.AddAt(basket, 0); - } } //______________________________________________________________________________ @@ -1854,6 +1916,7 @@ void TBranch::Streamer(TBuffer& b) TLeaf *leaf = (TLeaf*)fLeaves.UncheckedAt(i); leaf->SetBranch(this); } + Int_t nbaskets = fBaskets.GetEntries(); for (Int_t j=fWriteBasket,n=0;j>=0 && n<nbaskets;--j) { TBasket *bk = (TBasket*)fBaskets.UncheckedAt(j); @@ -1991,7 +2054,7 @@ void TBranch::Streamer(TBuffer& b) } else { Int_t maxBaskets = fMaxBaskets; - fMaxBaskets = fBaskets.GetEntriesFast(); + fMaxBaskets = fWriteBasket+1; if (fMaxBaskets < 10) fMaxBaskets=10; b.WriteClassBuffer(TBranch::Class(),this); fMaxBaskets = maxBaskets; @@ -1999,30 +2062,35 @@ void TBranch::Streamer(TBuffer& b) } //_______________________________________________________________________ -void TBranch::WriteBasket(TBasket* basket) +Int_t TBranch::WriteBasket(TBasket* basket, Int_t where) { - // Write the current basket to disk + // Write the current basket to disk and return the number of bytes + // written to the file. Int_t nout = basket->WriteBuffer(); // Write buffer - fBasketBytes[fWriteBasket] = basket->GetNbytes(); - fBasketSeek[fWriteBasket] = basket->GetSeekKey(); + fBasketBytes[where] = basket->GetNbytes(); + fBasketSeek[where] = basket->GetSeekKey(); Int_t addbytes = basket->GetObjlen() + basket->GetKeylen() ; - if (fDirectory != gROOT && fDirectory->IsWritable()) { + if (fDirectory && (fDirectory != gROOT) && fDirectory->IsWritable()) { + basket->DropBuffers(); delete basket; - fBaskets[fWriteBasket] = 0; + fBaskets[where] = 0; } fZipBytes += nout; fTotBytes += addbytes; fTree->AddTotBytes(addbytes); fTree->AddZipBytes(nout); - basket = fTree->CreateBasket(this); // create a new basket - fWriteBasket++; - fBaskets.AddAtAndExpand(basket,fWriteBasket); - if (fWriteBasket >= fMaxBaskets) { - ExpandBasketArrays(); + if (where==fWriteBasket) { + ++fWriteBasket; + if (fWriteBasket >= fMaxBaskets) { + ExpandBasketArrays(); + } + fBaskets.AddAtAndExpand(0,fWriteBasket); + fBasketEntry[fWriteBasket] = fEntryNumber; } - fBasketEntry[fWriteBasket] = fEntryNumber; + + return nout; } //------------------------------------------------------------------------------ diff --git a/tree/tree/src/TBranchClones.cxx b/tree/tree/src/TBranchClones.cxx index 5c28f666649be74257ec3dcf2d8d9ed3834687e1..9b1f187dabbbfd05f858042a86707454e2289e60 100644 --- a/tree/tree/src/TBranchClones.cxx +++ b/tree/tree/src/TBranchClones.cxx @@ -123,9 +123,6 @@ void TBranchClones::Init(TTree *tree, TBranch *parent, const char* name, void* p fDirectory = fTree->GetDirectory(); fFileName = ""; - // Create the first basket. - TBasket* basket = new TBasket(branchcount, fTree->GetName(), this); - fBaskets.Add(basket); // Loop on all public data members of the class and its base classes. const char* itype = 0; TRealData* rd = 0; diff --git a/tree/tree/src/TBranchElement.cxx b/tree/tree/src/TBranchElement.cxx index a8520be26f5be85d53ae36656369e1fefd17532e..57c327644121973aed9a75155cc5dd5fd3851044 100644 --- a/tree/tree/src/TBranchElement.cxx +++ b/tree/tree/src/TBranchElement.cxx @@ -262,10 +262,6 @@ void TBranchElement::Init(TTree *tree, TBranch *parent,const char* bname, TStrea fBasketSeek[i] = 0; } - // Create a basket for the branch. - TBasket* basket = new TBasket(name, fTree->GetName(), this); - fBaskets.Add(basket); - // We need to keep track of the counter branch if we have // one, since we cannot set it until we have created our // leaf, which we do last. @@ -393,7 +389,7 @@ void TBranchElement::Init(TTree *tree, TBranch *parent,const char* bname, TStrea } else { clones = (TClonesArray*)pointer; } - basket->DeleteEntryOffset(); //entryoffset not required for the clonesarray counter +// basket->DeleteEntryOffset(); //entryoffset not required for the clonesarray counter fEntryOffsetLen = 0; // ===> Create a leafcount TLeaf* leaf = new TLeafElement(this, name, fID, fStreamerType); @@ -603,10 +599,6 @@ void TBranchElement::Init(TTree *tree, TBranch *parent, const char* bname, TClon fBasketSeek[i] = 0; } - // Create a basket for the terminal branch - TBasket *basket = new TBasket(name, fTree->GetName(), this); - fBaskets.Add(basket); - // Reset the bit kAutoDelete to specify that when reading // the object should not be deleted before calling the streamer. SetAutoDelete(kFALSE); @@ -734,10 +726,7 @@ void TBranchElement::Init(TTree *tree, TBranch *parent, const char* bname, TVirt fBasketEntry[0] = fEntryNumber; fBasketBytes[0] = 0; - - // Create a basket for the terminal branch - TBasket* basket = new TBasket(name, fTree->GetName(), this); - fBaskets.Add(basket); + fBasketSeek[0] = 0; // Reset the bit kAutoDelete to specify that, when reading, // the object should not be deleted before calling the streamer. diff --git a/tree/tree/src/TBranchObject.cxx b/tree/tree/src/TBranchObject.cxx index 836114772e475aed3166d20fb0873a6b985039d5..ee90082848b3c6da7ed1526753f2186b56fdde0b 100644 --- a/tree/tree/src/TBranchObject.cxx +++ b/tree/tree/src/TBranchObject.cxx @@ -142,11 +142,6 @@ void TBranchObject::Init(TTree *tree, TBranch *parent, const char* name, const c fDirectory = fTree->GetDirectory(); fFileName = ""; - // Create the first basket. - if (!splitlevel) { - TBasket* basket = new TBasket(name, fTree->GetName(), this); - fBaskets.Add(basket); - } } //______________________________________________________________________________ diff --git a/tree/tree/src/TBranchRef.cxx b/tree/tree/src/TBranchRef.cxx index 5805f34a379ab862ad5c5fd6e46f586a29c5e8a6..da92f6d9a0a36dce4c3226c4052c240b292f6aba 100644 --- a/tree/tree/src/TBranchRef.cxx +++ b/tree/tree/src/TBranchRef.cxx @@ -78,9 +78,6 @@ TBranchRef::TBranchRef(TTree *tree) fDirectory = fTree->GetDirectory(); fFileName = ""; - // Create the first basket - TBasket *basket = new TBasket("TRefTable",fTree->GetName(),this); - fBaskets.Add(basket); } diff --git a/tree/tree/src/TBranchSTL.cxx b/tree/tree/src/TBranchSTL.cxx index 92d1d1bbd092c26a484f2c3d2aa0c604e3a2640e..12405267b7b4b52e90aa6a213c23420642e10df6 100644 --- a/tree/tree/src/TBranchSTL.cxx +++ b/tree/tree/src/TBranchSTL.cxx @@ -75,11 +75,6 @@ TBranchSTL::TBranchSTL( TTree *tree, const char *name, fBasketSeek[i] = 0; } - //--------------------------------------------------------------------------- - // Create a basket for the branch. - //--------------------------------------------------------------------------- - TBasket* basket = new TBasket(name, fTree->GetName(), this); - fBaskets.Add(basket); } //------------------------------------------------------------------------------ @@ -123,11 +118,6 @@ TBranchSTL::TBranchSTL( TBranch* parent, const char* name, fBasketSeek[i] = 0; } - //--------------------------------------------------------------------------- - // Create a basket for the branch. - //--------------------------------------------------------------------------- - TBasket* basket = new TBasket(name, fTree->GetName(), this); - fBaskets.Add(basket); } //------------------------------------------------------------------------------ diff --git a/tree/tree/src/TTree.cxx b/tree/tree/src/TTree.cxx index 4aef1c5bdcef9b3346989bc7271f82be8f2243d2..089e394e183201ddc7f3a768ac69f2a8da8adb85 100644 --- a/tree/tree/src/TTree.cxx +++ b/tree/tree/src/TTree.cxx @@ -899,6 +899,9 @@ Long64_t TTree::AutoSave(Option_t* option) // if option contains "SaveSelf", gDirectory->SaveSelf() is called. // This allows another process to analyze the Tree while the Tree is being filled. // + // if option contains "FlushBaskets", TTree::FlushBaskets is called and all + // the current basket are closed-out and written to disk individually. + // // By default the previous header is deleted after having written the new header. // if option contains "Overwrite", the previous Tree header is deleted // before written the new header. This option is slightly faster, but @@ -950,6 +953,9 @@ Long64_t TTree::AutoSave(Option_t* option) } TString opt = option; opt.ToLower(); + + if (opt.Contains("flushbaskets")) FlushBaskets(); + fSavedBytes = fTotBytes; TKey *key = (TKey*)fDirectory->GetListOfKeys()->FindObject(GetName()); @@ -3651,6 +3657,35 @@ Int_t TTree::Fit(const char* funcname, const char* varexp, const char* selection return -1; } +//______________________________________________________________________________ +Int_t TTree::FlushBaskets() const +{ + // Write to disk all the basket that have not yet been individually written. + // + // Return the number of bytes written or -1 in case of write error. + + Int_t nbytes = 0; + Int_t nerror = 0; + TObjArray *lb = const_cast<TTree*>(this)->GetListOfBranches(); + Int_t nb = lb->GetEntriesFast(); + for (Int_t j = 0; j < nb; j++) { + TBranch* branch = (TBranch*) lb->UncheckedAt(j); + if (branch) { + Int_t nwrite = branch->FlushBaskets(); + if (nwrite<0) { + ++nerror; + } else { + nbytes += nwrite; + } + } + } + if (nerror) { + return -1; + } else { + return nbytes; + } +} + //______________________________________________________________________________ const char* TTree::GetAlias(const char* aliasName) const { @@ -6327,6 +6362,25 @@ void TTree::UseCurrentStyle() } } +//______________________________________________________________________________ +Int_t TTree::Write(const char *name, Int_t option, Int_t bufsize) const +{ + // Write this object to the current directory. For more see TObject::Write + // Write calls TTree::FlushBaskets before writing the tree. + + FlushBaskets(); + return TObject::Write(name, option, bufsize); +} + +//______________________________________________________________________________ +Int_t TTree::Write(const char *name, Int_t option, Int_t bufsize) +{ + // Write this object to the current directory. For more see TObject::Write + // If option & kFlushBasket, call FlushBasket before writing the tree. + + return ((const TTree*)this)->Write(name, option, bufsize); +} + ////////////////////////////////////////////////////////////////////////// // // // TTreeFriendLeafIter // diff --git a/tree/tree/src/TTreeCloner.cxx b/tree/tree/src/TTreeCloner.cxx index 66de7373755db2f4993b178aa633b4446bc927fd..8de44e0cf490f506cb698c124f0fac1003ce1347 100644 --- a/tree/tree/src/TTreeCloner.cxx +++ b/tree/tree/src/TTreeCloner.cxx @@ -136,29 +136,7 @@ void TTreeCloner::CloseOutWriteBaskets() for(Int_t i=0; i<fToBranches.GetEntries(); ++i) { TBranch *to = (TBranch*)fToBranches.UncheckedAt(i); - - TObjArray *array = to->GetListOfBaskets(); - if (array->GetEntries()) { - TBasket *basket = to->GetBasket(to->GetWriteBasket()); - if (basket) { - if (basket->GetNevBuf()) { - // If the basket already contains entry we need to close it - // out. (This is because we can only transfer full compressed - // buffer) - - if (basket->GetBufferRef()->IsReading()) { - basket->SetWriteMode(); - } - to->WriteBasket(basket); - basket = to->GetBasket(to->GetWriteBasket()); // WriteBasket create an empty basket - } - if (basket) { - basket->DropBuffers(); - to->GetListOfBaskets()->RemoveAt(to->GetWriteBasket()); - delete basket; - } - } - } + to->FlushOneBasket(to->GetWriteBasket()); } } @@ -352,10 +330,12 @@ void TTreeCloner::CopyMemoryBaskets() basket = (TBasket*)basket->Clone(); basket->SetBranch(to); to->AddBasket(*basket, kFALSE, fToStartEntries+from->GetBasketEntry()[from->GetWriteBasket()]); + } else { + to->AddLastBasket( fToStartEntries+from->GetBasketEntry()[from->GetWriteBasket()] ); } - // If the branch is a TBranchElement non-terminal 'object' branch, it's basket will contain 0 - // events. - if (from->GetEntries()!=0 && from->GetWriteBasket()==0 && basket->GetNevBuf()==0) { + // In older files, if the branch is a TBranchElement non-terminal 'object' branch, it's basket will contain 0 + // events, in newer file in the same case, the write basket will be missing. + if (from->GetEntries()!=0 && from->GetWriteBasket()==0 && (basket==0 || basket->GetNevBuf()==0)) { to->SetEntries(to->GetEntries()+from->GetEntries()); } } @@ -449,22 +429,25 @@ void TTreeCloner::WriteBaskets() Int_t index = fBasketNum[ fBasketIndex[j] ]; Long64_t pos = from->GetBasketSeek(index); - if (from->GetBasketBytes()[index] == 0) { - from->GetBasketBytes()[index] = basket->ReadBasketBytes(pos, fromfile); - } - Int_t len = from->GetBasketBytes()[index]; - Long64_t entryInBasket; - if (index == from->GetWriteBasket()) { - entryInBasket = from->GetEntries() - from->GetBasketEntry()[index]; + if (pos!=0) { + if (from->GetBasketBytes()[index] == 0) { + from->GetBasketBytes()[index] = basket->ReadBasketBytes(pos, fromfile); + } + Int_t len = from->GetBasketBytes()[index]; + + basket->LoadBasketBuffers(pos,len,fromfile); + basket->IncrementPidOffset(fPidOffset); + basket->CopyTo(tofile); + to->AddBasket(*basket,kTRUE,fToStartEntries + from->GetBasketEntry()[index]); } else { - entryInBasket = from->GetBasketEntry()[index+1] - from->GetBasketEntry()[index]; + TBasket *frombasket = from->GetBasket( index ); + if (frombasket->GetNevBuf()>0) { + TBasket *tobasket = (TBasket*)frombasket->Clone(); + tobasket->SetBranch(to); + to->AddBasket(*tobasket, kFALSE, fToStartEntries+from->GetBasketEntry()[index]); + to->FlushOneBasket(to->GetWriteBasket()); + } } - - basket->LoadBasketBuffers(pos,len,fromfile); - basket->IncrementPidOffset(fPidOffset); - basket->CopyTo(tofile); - to->AddBasket(*basket,kTRUE,fToStartEntries + from->GetBasketEntry()[index]); - } delete basket; }