-
Notifications
You must be signed in to change notification settings - Fork 1.4k
First-pass IMT implementation of FlushBaskets. #277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
c247dcc
0be50e3
88def72
9235428
249cb14
e14e674
71b2161
cca9e30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,6 +70,8 @@ | |
| #include "TVirtualTreePlayer.h" | ||
| #endif | ||
|
|
||
| #include <atomic> | ||
|
|
||
| class TBranch; | ||
| class TBrowser; | ||
| class TFile; | ||
|
|
@@ -99,6 +101,7 @@ class TTree : public TNamed, public TAttLine, public TAttFill, public TAttMarker | |
|
|
||
| protected: | ||
| Long64_t fEntries; ///< Number of entries | ||
| // NOTE: cannot use std::atomic for these counters as it cannot be serialized. | ||
| Long64_t fTotBytes; ///< Total number of bytes in all branches before compression | ||
| Long64_t fZipBytes; ///< Total number of bytes in all branches after compression | ||
| Long64_t fSavedBytes; ///< Number of autosaved bytes | ||
|
|
@@ -160,6 +163,11 @@ class TTree : public TNamed, public TAttLine, public TAttFill, public TAttMarker | |
| TTree(const TTree& tt); // not implemented | ||
| TTree& operator=(const TTree& tt); // not implemented | ||
|
|
||
| #ifdef R__USE_IMT | ||
| mutable Bool_t fIMTFlush{false}; ///<! True if we are doing a multithreaded flush. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Odd to see a mutable non-atomic. Is that really the intention? (i.e. is it really never possible to modify/read it in parallel?)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that's the intention. Well, the real problem here is that The I wanted to avoid making it atomic to fulfill the request that there is no performance penalty when ROOT is used in non-IMT mode.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair enough, a comment here or at the use place along the line of this answer ought to be added. |
||
| mutable std::atomic<Long64_t> fIMTTotBytes; ///<! Total bytes for the IMT flush baskets | ||
| mutable std::atomic<Long64_t> fIMTZipBytes; ///<! Zip bytes for the IMT flush baskets. | ||
| #endif | ||
| void InitializeSortedBranches(); | ||
| void SortBranchesByTime(); | ||
|
|
||
|
|
@@ -303,8 +311,10 @@ class TTree : public TNamed, public TAttLine, public TAttFill, public TAttMarker | |
| virtual TFriendElement *AddFriend(const char* treename, const char* filename = ""); | ||
| virtual TFriendElement *AddFriend(const char* treename, TFile* file); | ||
| virtual TFriendElement *AddFriend(TTree* tree, const char* alias = "", Bool_t warn = kFALSE); | ||
| virtual void AddTotBytes(Int_t tot) { fTotBytes += tot; } | ||
| virtual void AddZipBytes(Int_t zip) { fZipBytes += zip; } | ||
| // As the TBasket invokes Add{Tot,Zip}Bytes on its parent tree, we must do these updates in a thread-safe | ||
| // manner only when we are flushing multiple baskets in parallel. | ||
| virtual void AddTotBytes(Int_t tot) { if (fIMTFlush) { fIMTTotBytes += tot; } else { fTotBytes += tot; } } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will fail to compile if R__USE_IMT is off. |
||
| virtual void AddZipBytes(Int_t zip) { if (fIMTFlush) { fIMTZipBytes += zip; } else { fZipBytes += zip; } } | ||
| virtual Long64_t AutoSave(Option_t* option = ""); | ||
| virtual Int_t Branch(TCollection* list, Int_t bufsize = 32000, Int_t splitlevel = 99, const char* name = ""); | ||
| virtual Int_t Branch(TList* list, Int_t bufsize = 32000, Int_t splitlevel = 99); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -932,6 +932,9 @@ Int_t TBasket::WriteBuffer() | |
| return -1; | ||
| } | ||
| fMotherDir = file; // fBranch->GetDirectory(); | ||
| #ifdef R__USE_IMT | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the slightly unusual use pattern (take the lock then explicit unlock then lock). Can you add comment on what this is propecting (and why it will not provoke a dead lock). |
||
| std::unique_lock<std::mutex> sentry(file->fWriteMutex); | ||
| #endif // R__USE_IMT | ||
|
|
||
| if (R__unlikely(fBufferRef->TestBit(TBufferFile::kNotDecompressed))) { | ||
| // Read the basket information that was saved inside the buffer. | ||
|
|
@@ -998,7 +1001,16 @@ Int_t TBasket::WriteBuffer() | |
| if (i == nbuffers - 1) bufmax = fObjlen - nzip; | ||
| else bufmax = kMAXZIPBUF; | ||
| //compress the buffer | ||
| #ifdef R__USE_IMT | ||
| sentry.unlock(); | ||
| #endif // R__USE_IMT | ||
| // NOTE this is declared with C linkage, so it shouldn't except. Also, when | ||
| // USE_IMT is defined, we are guaranteed that the compression buffer is unique per-branch. | ||
| // (see fCompressedBufferRef in constructor). | ||
| R__zipMultipleAlgorithm(cxlevel, &bufmax, objbuf, &bufmax, bufcur, &nout, cxAlgorithm); | ||
| #ifdef R__USE_IMT | ||
| sentry.lock(); | ||
| #endif // R__USE_IMT | ||
|
|
||
| // test if buffer has really been compressed. In case of small buffers | ||
| // when the buffer contains random data, it may happen that the compressed | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1336,17 +1336,17 @@ Long64_t TTree::AutoSave(Option_t* option) | |
| { | ||
| if (!fDirectory || fDirectory == gROOT || !fDirectory->IsWritable()) return 0; | ||
| if (gDebug > 0) { | ||
| printf("AutoSave Tree:%s after %lld bytes written\n",GetName(),fTotBytes); | ||
| Info("AutoSave", "Tree:%s after %lld bytes written\n",GetName(),GetTotBytes()); | ||
| } | ||
| TString opt = option; | ||
| opt.ToLower(); | ||
|
|
||
| if (opt.Contains("flushbaskets")) { | ||
| if (gDebug > 0) printf("AutoSave: calling FlushBaskets \n"); | ||
| if (gDebug > 0) Info("AutoSave", "calling FlushBaskets \n"); | ||
| FlushBaskets(); | ||
| } | ||
|
|
||
| fSavedBytes = fZipBytes; | ||
| fSavedBytes = GetZipBytes(); | ||
|
|
||
| TKey *key = (TKey*)fDirectory->GetListOfKeys()->FindObject(GetName()); | ||
| Long64_t nbytes; | ||
|
|
@@ -3608,7 +3608,7 @@ void TTree::Delete(Option_t* option /* = "" */) | |
| key = fDirectory->GetKey(GetName()); | ||
| } | ||
| if (dirsav) dirsav->cd(); | ||
| if (gDebug) printf(" Deleting Tree: %s: %d baskets deleted. Total space freed = %d bytes\n",GetName(),nbask,ntot); | ||
| if (gDebug) Info("TTree::Delete", "Deleting Tree: %s: %d baskets deleted. Total space freed = %d bytes\n",GetName(),nbask,ntot); | ||
| } | ||
|
|
||
| if (fDirectory) { | ||
|
|
@@ -4417,35 +4417,37 @@ Int_t TTree::Fill() | |
| if (fEntries > fMaxEntries) { | ||
| KeepCircular(); | ||
| } | ||
| if (gDebug > 0) printf("TTree::Fill - A: %d %lld %lld %lld %lld %lld %lld \n", | ||
| nbytes, fEntries, fAutoFlush,fAutoSave,fZipBytes,fFlushedBytes,fSavedBytes); | ||
| if (gDebug > 0) Info("TTree::Fill", " - A: %d %lld %lld %lld %lld %lld %lld \n", | ||
| nbytes, fEntries, fAutoFlush,fAutoSave,GetZipBytes(),fFlushedBytes,fSavedBytes); | ||
|
|
||
| if (fAutoFlush != 0 || fAutoSave != 0) { | ||
| // Is it time to flush or autosave baskets? | ||
| if (fFlushedBytes == 0) { | ||
| // Decision can be based initially either on the number of bytes | ||
| // or the number of entries written. | ||
| if ((fAutoFlush<0 && fZipBytes > -fAutoFlush) || | ||
| (fAutoSave <0 && fZipBytes > -fAutoSave ) || | ||
| Long64_t zipBytes = GetZipBytes(); | ||
| if ((fAutoFlush<0 && zipBytes > -fAutoFlush) || | ||
| (fAutoSave <0 && zipBytes > -fAutoSave ) || | ||
| (fAutoFlush>0 && fEntries%TMath::Max((Long64_t)1,fAutoFlush) == 0) || | ||
| (fAutoSave >0 && fEntries%TMath::Max((Long64_t)1,fAutoSave) == 0) ) { | ||
|
|
||
| //First call FlushBasket to make sure that fTotBytes is up to date. | ||
| FlushBaskets(); | ||
| OptimizeBaskets(fTotBytes,1,""); | ||
| if (gDebug > 0) Info("TTree::Fill","OptimizeBaskets called at entry %lld, fZipBytes=%lld, fFlushedBytes=%lld\n",fEntries,fZipBytes,fFlushedBytes); | ||
| fFlushedBytes = fZipBytes; | ||
| OptimizeBaskets(GetTotBytes(),1,""); | ||
| if (gDebug > 0) Info("TTree::Fill","OptimizeBaskets called at entry %lld, fZipBytes=%lld, fFlushedBytes=%lld\n",fEntries,GetZipBytes(),fFlushedBytes); | ||
| fFlushedBytes = GetZipBytes(); | ||
| fAutoFlush = fEntries; // Use test on entries rather than bytes | ||
|
|
||
| // subsequently in run | ||
| if (fAutoSave < 0) { | ||
| // Set fAutoSave to the largest integer multiple of | ||
| // fAutoFlush events such that fAutoSave*fFlushedBytes | ||
| // < (minus the input value of fAutoSave) | ||
| if (fZipBytes != 0) { | ||
| fAutoSave = TMath::Max( fAutoFlush, fEntries*((-fAutoSave/fZipBytes)/fEntries)); | ||
| } else if (fTotBytes != 0) { | ||
| fAutoSave = TMath::Max( fAutoFlush, fEntries*((-fAutoSave/fTotBytes)/fEntries)); | ||
| Long64_t totBytes = GetTotBytes(); | ||
| if (zipBytes != 0) { | ||
| fAutoSave = TMath::Max( fAutoFlush, fEntries*((-fAutoSave/zipBytes)/fEntries)); | ||
| } else if (totBytes != 0) { | ||
| fAutoSave = TMath::Max( fAutoFlush, fEntries*((-fAutoSave/totBytes)/fEntries)); | ||
| } else { | ||
| TBufferFile b(TBuffer::kWrite, 10000); | ||
| TTree::Class()->WriteBuffer(b, (TTree*) this); | ||
|
|
@@ -4462,24 +4464,24 @@ Int_t TTree::Fill() | |
| if (fAutoSave != 0 && fEntries%fAutoSave == 0) { | ||
| //We are at an AutoSave point. AutoSave flushes baskets and saves the Tree header | ||
| AutoSave("flushbaskets"); | ||
| if (gDebug > 0) Info("TTree::Fill","AutoSave called at entry %lld, fZipBytes=%lld, fSavedBytes=%lld\n",fEntries,fZipBytes,fSavedBytes); | ||
| if (gDebug > 0) Info("TTree::Fill","AutoSave called at entry %lld, fZipBytes=%lld, fSavedBytes=%lld\n",fEntries,GetZipBytes(),fSavedBytes); | ||
| } else { | ||
| //We only FlushBaskets | ||
| FlushBaskets(); | ||
| if (gDebug > 0) Info("TTree::Fill","FlushBasket called at entry %lld, fZipBytes=%lld, fFlushedBytes=%lld\n",fEntries,fZipBytes,fFlushedBytes); | ||
| if (gDebug > 0) Info("TTree::Fill","FlushBasket called at entry %lld, fZipBytes=%lld, fFlushedBytes=%lld\n",fEntries,GetZipBytes(),fFlushedBytes); | ||
| } | ||
| fFlushedBytes = fZipBytes; | ||
| fFlushedBytes = GetZipBytes(); | ||
| } else if (fNClusterRange == 0 && fEntries > 1 && fAutoFlush && fEntries%fAutoFlush == 0) { | ||
| if (fAutoSave != 0 && fEntries%fAutoSave == 0) { | ||
| //We are at an AutoSave point. AutoSave flushes baskets and saves the Tree header | ||
| AutoSave("flushbaskets"); | ||
| if (gDebug > 0) Info("TTree::Fill","AutoSave called at entry %lld, fZipBytes=%lld, fSavedBytes=%lld\n",fEntries,fZipBytes,fSavedBytes); | ||
| if (gDebug > 0) Info("TTree::Fill","AutoSave called at entry %lld, fZipBytes=%lld, fSavedBytes=%lld\n",fEntries,GetZipBytes(),fSavedBytes); | ||
| } else { | ||
| //We only FlushBaskets | ||
| FlushBaskets(); | ||
| if (gDebug > 0) Info("TTree::Fill","FlushBasket called at entry %lld, fZipBytes=%lld, fFlushedBytes=%lld\n",fEntries,fZipBytes,fFlushedBytes); | ||
| if (gDebug > 0) Info("TTree::Fill","FlushBasket called at entry %lld, fZipBytes=%lld, fFlushedBytes=%lld\n",fEntries,GetZipBytes(),fFlushedBytes); | ||
| } | ||
| fFlushedBytes = fZipBytes; | ||
| fFlushedBytes = GetZipBytes(); | ||
| } | ||
| } | ||
| // Check that output file is still below the maximum size. | ||
|
|
@@ -4790,6 +4792,16 @@ Int_t TTree::Fit(const char* funcname, const char* varexp, const char* selection | |
| return -1; | ||
| } | ||
|
|
||
| struct BoolRAIIToggle { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider putting this class in the anonymous namespace. |
||
|
|
||
| Bool_t &m_val; | ||
|
|
||
| BoolRAIIToggle(Bool_t &val) : m_val(val) { m_val = true; } | ||
|
|
||
| ~BoolRAIIToggle() { m_val = false; } | ||
| }; | ||
|
|
||
|
|
||
| //////////////////////////////////////////////////////////////////////////////// | ||
| /// Write to disk all the basket that have not yet been individually written. | ||
| /// | ||
|
|
@@ -4802,6 +4814,53 @@ Int_t TTree::FlushBaskets() const | |
| Int_t nerror = 0; | ||
| TObjArray *lb = const_cast<TTree*>(this)->GetListOfBranches(); | ||
| Int_t nb = lb->GetEntriesFast(); | ||
|
|
||
| #ifdef R__USE_IMT | ||
| if (ROOT::IsImplicitMTEnabled() && fIMTEnabled) { | ||
| if (fSortedBranches.empty()) { const_cast<TTree*>(this)->InitializeSortedBranches(); } | ||
|
|
||
| BoolRAIIToggle sentry(fIMTFlush); | ||
| fIMTZipBytes.store(0); | ||
| fIMTTotBytes.store(0); | ||
| std::atomic<Int_t> nerrpar(0); | ||
| std::atomic<Int_t> nbpar(0); | ||
| std::atomic<Int_t> pos(0); | ||
| tbb::task_group g; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We must be extremely careful when embedding calls to TBB into legacy code. Holding any shared resource (which a C++ object itself may be viewed as such a resource) can lead to deadlocks. See https://software.intel.com/en-us/forums/intel-threading-building-blocks/topic/401006 for details. This is one reason why the CMS threaded-framework is built using explicit tasks rather than implicit ones. For the case of writing, I can't think of anyone being dependent on the TFile, although we must be certain that no ROOT locks are being held by the thread calling
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi Chris, Indeed. My logic here is:
So, the remaining danger is if the caller (CMSSW) is holding a mutex of its own that other CMSSW tasks might need. I will write these concerns up into a comment, provide an example of dangerous behavior, and add them to the function's documentation. Brian
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's an example of a deadlock that could have happened in older versions of CMSSW:
Now, Chris tells me that CMSSW has switched away from the mutex for output module access - but this might be a good example. I pushed a long comment for
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This of course depend on whether the mutex that is used is recursive or not (TMutex is, std::mutex is not).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Certainly! The point is that working code with IMT-disabled may stop working when IMT is enabled. One would hope that issues would be fixable once the user understands the underlying issue. Hence the extensive comment in the code. |
||
|
|
||
| for (Int_t i = 0; i < nb; i++) { | ||
| g.run([&]() { | ||
| // The branch to process is obtained when the task starts to run. | ||
| // This way, since branches are sorted, we make sure that branches | ||
| // leading to big tasks are processed first. If we assigned the | ||
| // branch at task creation time, the scheduler would not necessarily | ||
| // respect our sorting. | ||
| Int_t j = pos.fetch_add(1); | ||
|
|
||
| auto branch = fSortedBranches[j].second; | ||
| if (R__unlikely(!branch)) { return; } | ||
|
|
||
| if (R__unlikely(gDebug > 0)) { | ||
| std::stringstream ss; | ||
| ss << std::this_thread::get_id(); | ||
| Info("FlushBaskets", "[IMT] Thread %s", ss.str().c_str()); | ||
| Info("FlushBaskets", "[IMT] Running task for branch #%d: %s", j, branch->GetName()); | ||
| } | ||
|
|
||
| Int_t nbtask = branch->FlushBaskets(); | ||
|
|
||
| if (nbtask < 0) { nerrpar++; } | ||
| else { nbpar += nbtask; } | ||
| }); | ||
| } | ||
| g.wait(); | ||
|
|
||
| fIMTFlush = false; | ||
| const_cast<TTree*>(this)->AddTotBytes(fIMTTotBytes); | ||
| const_cast<TTree*>(this)->AddZipBytes(fIMTZipBytes); | ||
|
|
||
| return nerrpar ? -1 : nbpar.load(); | ||
| } | ||
| #endif | ||
| for (Int_t j = 0; j < nb; j++) { | ||
| TBranch* branch = (TBranch*) lb->UncheckedAt(j); | ||
| if (branch) { | ||
|
|
@@ -5006,7 +5065,7 @@ Long64_t TTree::GetCacheAutoSize(Bool_t withDefault /* = kFALSE */ ) const | |
|
|
||
| if (fAutoFlush < 0) cacheSize = Long64_t(-cacheFactor*fAutoFlush); | ||
| else if (fAutoFlush == 0) cacheSize = 0; | ||
| else cacheSize = Long64_t(cacheFactor*1.5*fAutoFlush*fZipBytes/(fEntries+1)); | ||
| else cacheSize = Long64_t(cacheFactor*1.5*fAutoFlush*GetZipBytes()/(fEntries+1)); | ||
|
|
||
| if (cacheSize >= (INT_MAX / 4)) { | ||
| cacheSize = INT_MAX / 4; | ||
|
|
@@ -5019,7 +5078,7 @@ Long64_t TTree::GetCacheAutoSize(Bool_t withDefault /* = kFALSE */ ) const | |
| if (cacheSize == 0 && withDefault) { | ||
| if (fAutoFlush < 0) cacheSize = -fAutoFlush; | ||
| else if (fAutoFlush == 0) cacheSize = 0; | ||
| else cacheSize = Long64_t(1.5*fAutoFlush*fZipBytes/(fEntries+1)); | ||
| else cacheSize = Long64_t(1.5*fAutoFlush*GetZipBytes()/(fEntries+1)); | ||
| } | ||
|
|
||
| return cacheSize; | ||
|
|
@@ -6600,7 +6659,7 @@ void TTree::OptimizeBaskets(ULong64_t maxMemory, Float_t minComp, Option_t *opti | |
| if (newBsize < bmin) newBsize = bmin; | ||
| if (newBsize > 10000000) newBsize = bmax; | ||
| if (pass) { | ||
| if (pDebug) printf("Changing buffer size from %6d to %6d bytes for %s\n",oldBsize,newBsize,branch->GetName()); | ||
| if (pDebug) Info("OptimizeBaskets", "Changing buffer size from %6d to %6d bytes for %s\n",oldBsize,newBsize,branch->GetName()); | ||
| branch->SetBasketSize(newBsize); | ||
| } | ||
| newMemsize += newBsize; | ||
|
|
@@ -6613,7 +6672,7 @@ void TTree::OptimizeBaskets(ULong64_t maxMemory, Float_t minComp, Option_t *opti | |
| Double_t comp = 1; | ||
| if (branch->GetZipBytes() > 0) comp = totBytes/Double_t(branch->GetZipBytes()); | ||
| if (comp > 1 && comp < minComp) { | ||
| if (pDebug) printf("Disabling compression for branch : %s\n",branch->GetName()); | ||
| if (pDebug) Info("OptimizeBaskets", "Disabling compression for branch : %s\n",branch->GetName()); | ||
| branch->SetCompressionSettings(0); | ||
| } | ||
| } | ||
|
|
@@ -6634,8 +6693,8 @@ void TTree::OptimizeBaskets(ULong64_t maxMemory, Float_t minComp, Option_t *opti | |
| bmax = (bmax_new > hardmax) ? bmin : (UInt_t)bmax_new; | ||
| } | ||
| if (pDebug) { | ||
| printf("oldMemsize = %d, newMemsize = %d\n",oldMemsize, newMemsize); | ||
| printf("oldBaskets = %d, newBaskets = %d\n",oldBaskets, newBaskets); | ||
| Info("OptimizeBaskets", "oldMemsize = %d, newMemsize = %d\n",oldMemsize, newMemsize); | ||
| Info("OptimizeBaskets", "oldBaskets = %d, newBaskets = %d\n",oldBaskets, newBaskets); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -6702,16 +6761,17 @@ void TTree::Print(Option_t* option) const | |
| } | ||
| } | ||
| Long64_t total = skey; | ||
| if (fZipBytes > 0) { | ||
| total += fTotBytes; | ||
| Long64_t zipBytes = GetZipBytes(); | ||
| if (zipBytes > 0) { | ||
| total += GetTotBytes(); | ||
| } | ||
| TBufferFile b(TBuffer::kWrite, 10000); | ||
| TTree::Class()->WriteBuffer(b, (TTree*) this); | ||
| total += b.Length(); | ||
| Long64_t file = fZipBytes + s; | ||
| Long64_t file = zipBytes + s; | ||
| Float_t cx = 1; | ||
| if (fZipBytes) { | ||
| cx = (fTotBytes + 0.00001) / fZipBytes; | ||
| if (zipBytes) { | ||
| cx = (GetTotBytes() + 0.00001) / zipBytes; | ||
| } | ||
| Printf("******************************************************************************"); | ||
| Printf("*Tree :%-10s: %-54s *", GetName(), GetTitle()); | ||
|
|
@@ -6758,7 +6818,7 @@ void TTree::Print(Option_t* option) const | |
| if (count[l] < 0) continue; | ||
| leaf = (TLeaf *)const_cast<TTree*>(this)->GetListOfLeaves()->At(l); | ||
| br = leaf->GetBranch(); | ||
| printf("branch: %-20s %9lld\n",br->GetName(),count[l]); | ||
| Printf("branch: %-20s %9lld\n",br->GetName(),count[l]); | ||
| } | ||
| delete [] count; | ||
| } else { | ||
|
|
@@ -7352,8 +7412,8 @@ void TTree::Refresh() | |
|
|
||
| fAutoSave = tree->fAutoSave; | ||
| fEntries = tree->fEntries; | ||
| fTotBytes = tree->fTotBytes; | ||
| fZipBytes = tree->fZipBytes; | ||
| fTotBytes = tree->GetTotBytes(); | ||
| fZipBytes = tree->GetZipBytes(); | ||
| fSavedBytes = tree->fSavedBytes; | ||
| fTotalBuffers = tree->fTotalBuffers.load(); | ||
|
|
||
|
|
@@ -8823,10 +8883,12 @@ void TTree::Streamer(TBuffer& b) | |
| } else if (fAutoFlush != 0) { | ||
| // Estimate the cluster size. | ||
| // This will allow TTree::Process to enable the cache. | ||
| if (fZipBytes != 0) { | ||
| fCacheSize = fAutoFlush*(fZipBytes/fEntries); | ||
| } else if (fTotBytes != 0) { | ||
| fCacheSize = fAutoFlush*(fTotBytes/fEntries); | ||
| Long64_t zipBytes = GetZipBytes(); | ||
| Long64_t totBytes = GetTotBytes(); | ||
| if (zipBytes != 0) { | ||
| fCacheSize = fAutoFlush*(zipBytes/fEntries); | ||
| } else if (totBytes != 0) { | ||
| fCacheSize = fAutoFlush*(totBytes/fEntries); | ||
| } else { | ||
| fCacheSize = 30000000; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see
#include <mutex>?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.