CMS 3D CMS Logo

RootOutputTree.cc
Go to the documentation of this file.
1 
2 #include "RootOutputTree.h"
3 
14 
15 #include "TBranch.h"
16 #include "TBranchElement.h"
17 #include "TCollection.h"
18 #include "TFile.h"
19 #include "TTreeCloner.h"
20 #include "Rtypes.h"
21 #include "RVersion.h"
22 
23 #include <limits>
24 
25 #include "tbb/task_arena.h"
26 
27 namespace edm {
28 
42  {
43  public:
45  : tree_(tree)
46  {
47  dup();
48  }
49 
50  TTree *tree() const {return mytree_ ? mytree_.get() : tree_;}
51 
52  private:
53  DuplicateTreeSentry(DuplicateTreeSentry const&) = delete; // Disallow copying and moving
55  struct CloseBeforeDelete { void operator()(TFile* iFile) const { if( iFile) { iFile->Close(); } delete iFile; } };
56 
57  void dup()
58  {
60  if (!pSLC.isAvailable()) {return;}
61  if (pSLC->sourceCacheHint() && *(pSLC->sourceCacheHint()) == "lazy-download") {return;}
62  if (!pSLC->sourceCloneCacheHint() || *(pSLC->sourceCloneCacheHint()) != "lazy-download") {return;}
63  edm::LogWarning("DuplicateTreeSentry") << "Re-opening file for fast-cloning";
64 
65  TFile *file = tree_->GetCurrentFile();
66  const TUrl *url = file->GetEndpointUrl();
67  if (!url)
68  {
69  return;
70  }
71  file_.reset(TFile::Open(url->GetUrl(), "READWRAP")); // May throw an exception.
72  if (!file_)
73  {
74  return;
75  }
76  mytree_.reset(dynamic_cast<TTree*>(file_->Get(tree_->GetName())));
77  if (!mytree_) {return;}
78  }
79 
84  std::unique_ptr<TFile, CloseBeforeDelete> file_;
85  TTree *tree_ = nullptr;
86  std::unique_ptr<TTree> mytree_ = nullptr;
87  };
88 
90  std::shared_ptr<TFile> filePtr,
91  BranchType const& branchType,
92  int splitLevel,
93  int treeMaxVirtualSize) :
94  filePtr_(filePtr),
95  tree_(makeTTree(filePtr.get(), BranchTypeToProductTreeName(branchType), splitLevel)),
96  producedBranches_(),
97  readBranches_(),
98  auxBranches_(),
99  unclonedReadBranches_(),
100  clonedReadBranchNames_(),
101  currentlyFastCloning_(),
102  fastCloneAuxBranches_(false) {
103 
104  if(treeMaxVirtualSize >= 0) tree_->SetMaxVirtualSize(treeMaxVirtualSize);
105  }
106 
107  TTree*
108  RootOutputTree::assignTTree(TFile* filePtr, TTree* tree) {
109  tree->SetDirectory(filePtr);
110  // Turn off autosaving because it is such a memory hog and we are not using
111  // this check-pointing feature anyway.
112  tree->SetAutoSave(std::numeric_limits<Long64_t>::max());
113  return tree;
114  }
115 
116  TTree*
117  RootOutputTree::makeTTree(TFile* filePtr, std::string const& name, int splitLevel) {
118  TTree* tree = new TTree(name.c_str(), "", splitLevel);
119  if(!tree) throw edm::Exception(errors::FatalRootError)
120  << "Failed to create the tree: " << name << "\n";
121  if(tree->IsZombie())
123  << "Tree: " << name << " is a zombie." << "\n";
124 
125  return assignTTree(filePtr, tree);
126  }
127 
128  bool
130 
131  assert(inputTree != nullptr);
132 
133  // Do the split level and basket size match in the input and output?
134  for(auto const& outputBranch : readBranches_) {
135  if(outputBranch != nullptr) {
136  TBranch* inputBranch = inputTree->GetBranch(outputBranch->GetName());
137 
138  if(inputBranch != nullptr) {
139  if(inputBranch->GetSplitLevel() != outputBranch->GetSplitLevel() ||
140  inputBranch->GetBasketSize() != outputBranch->GetBasketSize()) {
141  return false;
142  }
143  }
144  }
145  }
146  return true;
147  }
148 
149  namespace {
150  bool checkMatchingBranches(TBranchElement* inputBranch, TBranchElement* outputBranch) {
151  if(inputBranch->GetStreamerType() != outputBranch->GetStreamerType()) {
152  return false;
153  }
154  TObjArray* inputArray = inputBranch->GetListOfBranches();
155  TObjArray* outputArray = outputBranch->GetListOfBranches();
156 
157  if(outputArray->GetSize() < inputArray->GetSize()) {
158  return false;
159  }
160  TIter iter(outputArray);
161  TObject* obj = nullptr;
162  while((obj = iter.Next()) != nullptr) {
163  TBranchElement* outBranch = dynamic_cast<TBranchElement*>(obj);
164  if(outBranch) {
165  TBranchElement* inBranch = dynamic_cast<TBranchElement*>(inputArray->FindObject(outBranch->GetName()));
166  if(!inBranch) {
167  return false;
168  }
169  if(!checkMatchingBranches(inBranch, outBranch)) {
170  return false;
171  }
172  }
173  }
174  return true;
175  }
176  }
177 
178  bool RootOutputTree::checkIfFastClonable(TTree* inputTree) const {
179 
180  if(inputTree == nullptr) return false;
181 
182  // Do the sub-branches match in the input and output. Extra sub-branches in the input are OK for fast cloning, but not in the output.
183  for(auto const& outputBr : readBranches_) {
184  TBranchElement* outputBranch = dynamic_cast<TBranchElement*>(outputBr);
185  if(outputBranch != nullptr) {
186  TBranchElement* inputBranch = dynamic_cast<TBranchElement*>(inputTree->GetBranch(outputBranch->GetName()));
187  if(inputBranch != nullptr) {
188  // We have a matching top level branch. Do the recursive check on subbranches.
189  if(!checkMatchingBranches(inputBranch, outputBranch)) {
190  LogInfo("FastCloning")
191  << "Fast Cloning disabled because a data member has been added to split branch: " << inputBranch->GetName() << "\n.";
192  return false;
193  }
194  }
195  }
196  }
197  return true;
198  }
199 
200  bool RootOutputTree::checkEntriesInReadBranches(Long64_t expectedNumberOfEntries) const {
201  for(auto const& readBranch : readBranches_) {
202  if(readBranch->GetEntries() != expectedNumberOfEntries) {
203  return false;
204  }
205  }
206  return true;
207  }
208 
209  void
211  if(in->GetEntries() != 0) {
212  TObjArray* branches = tree_->GetListOfBranches();
213  // If any products were produced (not just event products), the EventAuxiliary will be modified.
214  // In that case, don't fast copy auxiliary branches. Remove them, and add back after fast copying.
215  std::map<Int_t, TBranch *> auxIndexes;
216  bool mustRemoveSomeAuxs = false;
217  if(!fastCloneAuxBranches_) {
218  for(auto const& auxBranch : auxBranches_) {
219  int auxIndex = branches->IndexOf(auxBranch);
220  assert (auxIndex >= 0);
221  auxIndexes.insert(std::make_pair(auxIndex, auxBranch));
222  branches->RemoveAt(auxIndex);
223  }
224  mustRemoveSomeAuxs = true;
225  }
226 
227  //Deal with any aux branches which can never be cloned
228  for(auto const& auxBranch : unclonedAuxBranches_) {
229  int auxIndex = branches->IndexOf(auxBranch);
230  assert (auxIndex >= 0);
231  auxIndexes.insert(std::make_pair(auxIndex, auxBranch));
232  branches->RemoveAt(auxIndex);
233  mustRemoveSomeAuxs = true;
234  }
235 
236  if(mustRemoveSomeAuxs) {
237  branches->Compress();
238  }
239 
240  DuplicateTreeSentry dupTree(in);
241  TTreeCloner cloner(dupTree.tree(), tree_, option.c_str(), TTreeCloner::kNoWarnings|TTreeCloner::kIgnoreMissingTopLevel);
242 
243  if(!cloner.IsValid()) {
244  // Let's check why
245  static const char* okerror = "One of the export branch";
246  if(strncmp(cloner.GetWarning(), okerror, strlen(okerror)) == 0) {
247  // That's fine we will handle it;
248  } else {
250  << "invalid TTreeCloner (" << cloner.GetWarning() << ")\n";
251  }
252  }
253  tree_->SetEntries(tree_->GetEntries() + in->GetEntries());
254  Service<RootHandlers> rootHandler;
255  rootHandler->ignoreWarningsWhileDoing([&cloner] { cloner.Exec(); });
256 
257  if(mustRemoveSomeAuxs) {
258  for(auto const& auxIndex : auxIndexes) {
259  // Add the auxiliary branches back after fast copying the rest of the tree.
260  Int_t last = branches->GetLast();
261  if(last >= 0) {
262  branches->AddAtAndExpand(branches->At(last), last+1);
263  for(Int_t ind = last-1; ind >= auxIndex.first; --ind) {
264  branches->AddAt(branches->At(ind), ind+1);
265  };
266  branches->AddAt(auxIndex.second, auxIndex.first);
267  } else {
268  branches->Add(auxIndex.second);
269  }
270  }
271  }
272  }
273  }
274 
275  void
277  if(tree->GetNbranches() != 0) {
278  // This is required when Fill is called on individual branches
279  // in the TTree instead of calling Fill once for the entire TTree.
280  tree->SetEntries(-1);
281  }
282  setRefCoreStreamer(true);
283  tree->AutoSave("FlushBaskets");
284  }
285 
286  void
287  RootOutputTree::fillTTree(std::vector<TBranch*> const& branches) {
288  for_all(branches, std::bind(&TBranch::Fill, std::placeholders::_1));
289  }
290 
291  void
293  writeTTree(tree());
294  }
295 
296  void
297  RootOutputTree::maybeFastCloneTree(bool canFastClone, bool canFastCloneAux, TTree* tree, std::string const& option) {
298  unclonedReadBranches_.clear();
299  clonedReadBranchNames_.clear();
300  currentlyFastCloning_ = canFastClone && !readBranches_.empty();
302  fastCloneAuxBranches_ = canFastCloneAux;
303  fastCloneTTree(tree, option);
304  for(auto const& branch : readBranches_) {
305  if(branch->GetEntries() == tree_->GetEntries()) {
306  clonedReadBranchNames_.insert(std::string(branch->GetName()));
307  } else {
308  unclonedReadBranches_.push_back(branch);
309  }
310  }
311  Service<JobReport> reportSvc;
312  reportSvc->reportFastClonedBranches(clonedReadBranchNames_, tree_->GetEntries());
313  }
314  }
315 
316  void
323  } else {
324  // Isolate the fill operation so that IMT doesn't grab other large tasks
325  // that could lead to PoolOutputModule stalling
326  tbb::this_task_arena::isolate( [&]{ tree_->Fill(); } );
327  }
328  }
329 
330  void
332  std::string const& className,
333  void const*& pProd,
334  int splitLevel,
335  int basketSize,
336  bool produced) {
337  assert(splitLevel != BranchDescription::invalidSplitLevel);
338  assert(basketSize != BranchDescription::invalidBasketSize);
339  TBranch* branch = tree_->Branch(branchName.c_str(),
340  className.c_str(),
341  &pProd,
342  basketSize,
343  splitLevel);
344  assert(branch != nullptr);
345 /*
346  if(pProd != nullptr) {
347  // Delete the product that ROOT has allocated.
348  WrapperBase const* edp = static_cast<WrapperBase const *>(pProd);
349  delete edp;
350  pProd = nullptr;
351  }
352 */
353  if(produced) {
354  producedBranches_.push_back(branch);
355  } else {
356  readBranches_.push_back(branch);
357  }
358  }
359 
360  void
362  // The TFile was just closed.
363  // Just to play it safe, zero all pointers to quantities in the file.
364  auxBranches_.clear();
365  unclonedAuxBranches_.clear();
366  producedBranches_.clear();
367  readBranches_.clear();
368  unclonedReadBranches_.clear();
369  tree_ = nullptr; // propagate_const<T> has no reset() function
370  filePtr_ = nullptr; // propagate_const<T> has no reset() function
371  }
372 }
virtual std::string const * sourceCacheHint() const =0
std::set< std::string > clonedReadBranchNames_
edm::propagate_const< TTree * > tree_
static int const invalidSplitLevel
DuplicateTreeSentry(TTree *tree)
static int const invalidBasketSize
static void fillTTree(std::vector< TBranch * > const &branches)
bool checkSplitLevelsAndBasketSizes(TTree *inputTree) const
TTree const * tree() const
void setRefCoreStreamer(bool resetAll=false)
std::vector< TBranch * > unclonedAuxBranches_
edm::propagate_const< std::shared_ptr< TFile > > filePtr_
bool checkIfFastClonable(TTree *inputTree) const
BranchType
Definition: BranchType.h:11
std::vector< TBranch * > producedBranches_
std::vector< TBranch * > auxBranches_
Func for_all(ForwardSequence &s, Func f)
wrapper for std::for_each
Definition: Algorithms.h:16
std::unique_ptr< TTree > mytree_
RootOutputTree(std::shared_ptr< TFile > filePtr, BranchType const &branchType, int splitLevel, int treeMaxVirtualSize)
static TTree * assignTTree(TFile *file, TTree *tree)
void addBranch(std::string const &branchName, std::string const &className, void const *&pProd, int splitLevel, int basketSize, bool produced)
void Fill(HcalDetId &id, double val, std::vector< TH2F > &depth)
T const & get(Event const &event, InputTag const &tag)
Definition: Event.h:690
bool isAvailable() const
Definition: Service.h:46
virtual std::string const * sourceCloneCacheHint() const =0
std::vector< TBranch * > unclonedReadBranches_
bool checkEntriesInReadBranches(Long64_t expectedNumberOfEntries) const
std::string const & BranchTypeToProductTreeName(BranchType const &branchType)
Definition: BranchType.cc:104
static TTree * makeTTree(TFile *filePtr, std::string const &name, int splitLevel)
DuplicateTreeSentry & operator=(DuplicateTreeSentry const &)=delete
static void writeTTree(TTree *tree)
std::vector< TBranch * > readBranches_
HLT enums.
Definition: tree.py:1
void maybeFastCloneTree(bool canFastClone, bool canFastCloneAux, TTree *tree, std::string const &option)
def branchType(schema, name)
Definition: revisionDML.py:113
std::string className(const T &t)
Definition: ClassName.h:30
void fastCloneTTree(TTree *in, std::string const &option)
std::unique_ptr< TFile, CloseBeforeDelete > file_