Logo ROOT   6.14/05
Reference Guide
TTreeCache.cxx
Go to the documentation of this file.
1 // @(#)root/tree:$Id$
2 // Author: Rene Brun 04/06/2006
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /** \class TTreeCache
13 \ingroup tree
14 
15 A specialized TFileCacheRead object for a TTree.
16 
17 This class acts as a file cache, registering automatically the baskets from
18 the branches being processed (TTree::Draw or TTree::Process and TSelectors)
19 when in the learning phase. The learning phase is by default 100 entries.
20 It can be changed via TTreeCache::SetLearnEntries.
21 
22 This cache speeds-up considerably the performance, in particular
23 when the Tree is accessed remotely via a high latency network.
24 
25 The default cache size (10 Mbytes) may be changed via the function
26 TTree::SetCacheSize
27 
28 Only the baskets for the requested entry range are put in the cache
29 
30 For each Tree being processed a TTreeCache object is created.
31 This object is automatically deleted when the Tree is deleted or
32 when the file is deleted.
33 
34 - Special case of a TChain
35  Once the training is done on the first Tree, the list of branches
36  in the cache is kept for the following files.
37 
38 - Special case of a TEventlist
39  if the Tree or TChain has a TEventlist, only the buffers
40  referenced by the list are put in the cache.
41 
42 The learning period is started or restarted when:
43  - TTree automatically creates a cache. This feature can be
44  controlled with an env. variable or the TTreeCache.Size option.
45  - TTree::SetCacheSize is called with a non-zero size and a cache
46  did not previously exist
47  - TTreeCache::StartLearningPhase is called.
48  - TTreeCache::SetEntryRange is called
49  * and the learning is not yet finished
50  * and has not been set to manual
51  * and the new minimun entry is different.
52 
53 The learning period is stopped (and prefetching is actually started) when:
54  - TTreeCache::StopLearningPhase is called.
55  - An entry outside the 'learning' range is requested
56  The 'learning range is from fEntryMin (default to 0) to
57  fEntryMin + fgLearnEntries (default to 100).
58  - A 'cached' TChain switches over to a new file.
59 
60 Further, the TreeCache can optimize its behavior on a cache miss. When
61 miss optimization is enabled, it will track all branches utilized after
62 the learning phase (those that cause a cache miss). When one cache miss
63 occurs, then all the utilized branches will be prefetched for that event.
64 This optimization utilizes the observation that infrequently accessed
65 branches are often accessed together. For example, this will greatly speed
66 up an analysis where the results of a trigger are read out for every branch,
67 but the majority of event collections are read only when the trigger results
68 pass a set of filters. NOTE - when this mode is enabled, the memory dedicated
69 to the cache will up to double in the case of cache miss. Additionally, on
70 the first miss of an event, we must iterate through all the "active branches"
71 for the miss cache and find the correct basket. This can be potentially a
72 CPU-expensive operation compared to, e.g., the latency of a SSD. This is why
73 the miss cache is currently disabled by default.
74 
75 ## WHY DO WE NEED the TreeCache when doing data analysis?
76 
77 When writing a TTree, the branch buffers are kept in memory.
78 A typical branch buffersize (before compression) is typically 32 KBytes.
79 After compression, the zipped buffer may be just a few Kbytes.
80 The branch buffers cannot be much larger in case of Trees with several
81 hundred or thousand branches.
82 
83 When writing, this does not generate a performance problem because branch
84 buffers are always written sequentially and the OS is in general clever enough
85 to flush the data to the output file when a few MBytes of data have to be written.
86 When reading at the contrary, one may hit a performance problem when reading
87 across a network (LAN or WAN) and the network latency is high.
88 For example in a WAN with 10ms latency, reading 1000 buffers of 10 KBytes each
89 with no cache will imply 10s penalty where a local read of the 10 MBytes would
90 take about 1 second.
91 
92 The TreeCache will try to prefetch all the buffers for the selected branches
93 such that instead of transferring 1000 buffers of 10 Kbytes, it will be able
94 to transfer one single large buffer of 10 Mbytes in one single transaction.
95 Not only the TreeCache minimizes the number of transfers, but in addition
96 it can sort the blocks to be read in increasing order such that the file
97 is read sequentially.
98 
99 Systems like xrootd, dCache or httpd take advantage of the TreeCache in
100 reading ahead as much data as they can and return to the application
101 the maximum data specified in the cache and have the next chunk of data ready
102 when the next request comes.
103 
104 ## HOW TO USE the TreeCache
105 
106 A few use cases are discussed below. A cache may be created with automatic sizing
107 when a TTree is used:
108 
109 Caches are created and automatically sized for TTrees when TTreeCache.Size or
110 the environment variable ROOT_TTREECACHE_SIZE is set to a sizing factor.
111 
112 But there are many possible configurations where manual control may be wanted.
113 In some applications you know a priori the list of branches to read. In other
114 applications the analysis loop calls several layers of user functions where it
115 is impossible to predict a priori which branches will be used. This
116 is probably the most frequent case. In this case ROOT I/O will flag used
117 branches automatically when a branch buffer is read during the learning phase.
118 The TreeCache interface provides functions to instruct the cache about the used
119 branches if they are known a priori. In the examples below, portions of analysis
120 code are shown. The few statements involving the TreeCache are marked with `//<<<`
121 
122 ### 1. with TTree::Draw
123 
124 the TreeCache is automatically used by TTree::Draw. The function knows
125 which branches are used in the query and it puts automatically these branches
126 in the cache. The entry range is also known automatically.
127 
128 ### 2. with TTree::Process and TSelectors
129 
130 You must enable the cache and tell the system which branches to cache
131 and also specify the entry range. It is important to specify the entry range
132 in case you process only a subset of the events, otherwise you run the risk
133 to store in the cache entries that you do not need.
134 
135 #### example 2a
136 ~~~ {.cpp}
137  TTree *T = (TTree*)f->Get("mytree");
138  Long64_t nentries = T->GetEntries();
139  Int_t cachesize = 10000000; //10 MBytes
140  T->SetCacheSize(cachesize); //<<<
141  T->AddBranchToCache("*",kTRUE); //<<< add all branches to the cache
142  T->Process('myselector.C+");
143  //in the TSelector::Process function we read all branches
144  T->GetEntry(i);
145  ... here you process your entry
146 ~~~
147 #### example 2b
148 
149 in the Process function we read a subset of the branches.
150 Only the branches used in the first entry will be put in the cache
151 ~~~ {.cpp}
152  TTree *T = (TTree*)f->Get("mytree");
153  //we want to process only the 200 first entries
154  Long64_t nentries=200;
155  int efirst= 0;
156  int elast = efirst+nentries;
157  Int_t cachesize = 10000000; //10 MBytes
158  TTreeCache::SetLearnEntries(1); //<<< we can take the decision after 1 entry
159  T->SetCacheSize(cachesize); //<<<
160  T->SetCacheEntryRange(efirst,elast); //<<<
161  T->Process('myselector.C+","",nentries,efirst);
162  // in the TSelector::Process we read only 2 branches
163  TBranch *b1 = T->GetBranch("branch1");
164  b1->GetEntry(i);
165  if (somecondition) return;
166  TBranch *b2 = T->GetBranch("branch2");
167  b2->GetEntry(i);
168  ... here you process your entry
169 ~~~
170 ### 3. with your own event loop
171 
172 #### example 3a
173 
174 in your analysis loop, you always use 2 branches. You want to prefetch
175 the branch buffers for these 2 branches only.
176 ~~~ {.cpp}
177  TTree *T = (TTree*)f->Get("mytree");
178  TBranch *b1 = T->GetBranch("branch1");
179  TBranch *b2 = T->GetBranch("branch2");
180  Long64_t nentries = T->GetEntries();
181  Int_t cachesize = 10000000; //10 MBytes
182  T->SetCacheSize(cachesize); //<<<
183  T->AddBranchToCache(b1,kTRUE); //<<<add branch1 and branch2 to the cache
184  T->AddBranchToCache(b2,kTRUE); //<<<
185  T->StopCacheLearningPhase(); //<<<
186  for (Long64_t i=0;i<nentries;i++) {
187  T->LoadTree(i); //<<< important call when calling TBranch::GetEntry after
188  b1->GetEntry(i);
189  if (some condition not met) continue;
190  b2->GetEntry(i);
191  if (some condition not met) continue;
192  //here we read the full event only in some rare cases.
193  //there is no point in caching the other branches as it might be
194  //more economical to read only the branch buffers really used.
195  T->GetEntry(i);
196  .. process the rare but interesting cases.
197  ... here you process your entry
198  }
199 ~~~
200 #### example 3b
201 
202 in your analysis loop, you always use 2 branches in the main loop.
203 you also call some analysis functions where a few more branches will be read.
204 but you do not know a priori which ones. There is no point in prefetching
205 branches that will be used very rarely.
206 ~~~ {.cpp}
207  TTree *T = (TTree*)f->Get("mytree");
208  Long64_t nentries = T->GetEntries();
209  Int_t cachesize = 10000000; //10 MBytes
210  T->SetCacheSize(cachesize); //<<<
211  T->SetCacheLearnEntries(5); //<<< we can take the decision after 5 entries
212  TBranch *b1 = T->GetBranch("branch1");
213  TBranch *b2 = T->GetBranch("branch2");
214  for (Long64_t i=0;i<nentries;i++) {
215  T->LoadTree(i);
216  b1->GetEntry(i);
217  if (some condition not met) continue;
218  b2->GetEntry(i);
219  //at this point we may call a user function where a few more branches
220  //will be read conditionally. These branches will be put in the cache
221  //if they have been used in the first 10 entries
222  if (some condition not met) continue;
223  //here we read the full event only in some rare cases.
224  //there is no point in caching the other branches as it might be
225  //more economical to read only the branch buffers really used.
226  T->GetEntry(i);
227  .. process the rare but interesting cases.
228  ... here you process your entry
229  }
230 ~~~
231 ## SPECIAL CASES WHERE TreeCache should not be activated
232 
233 When reading only a small fraction of all entries such that not all branch
234 buffers are read, it might be faster to run without a cache.
235 
236 ## HOW TO VERIFY That the TreeCache has been used and check its performance
237 
238 Once your analysis loop has terminated, you can access/print the number
239 of effective system reads for a given file with a code like
240 (where TFile* f is a pointer to your file)
241 ~~~ {.cpp}
242  printf("Reading %lld bytes in %d transactions\n",f->GetBytesRead(), f->GetReadCalls());
243 ~~~
244 */
245 
246 #include "TSystem.h"
247 #include "TEnv.h"
248 #include "TTreeCache.h"
249 #include "TChain.h"
250 #include "TList.h"
251 #include "TBranch.h"
252 #include "TBranchElement.h"
253 #include "TEventList.h"
254 #include "TObjString.h"
255 #include "TRegexp.h"
256 #include "TLeaf.h"
257 #include "TFriendElement.h"
258 #include "TFile.h"
259 #include "TMath.h"
260 #include "TBranchCacheInfo.h"
261 #include "TVirtualPerfStats.h"
262 #include <limits.h>
263 
265 
267 
268 ////////////////////////////////////////////////////////////////////////////////
269 /// Default Constructor.
270 
271 TTreeCache::TTreeCache() : TFileCacheRead(), fPrefillType(GetConfiguredPrefillType())
272 {
273 }
274 
275 ////////////////////////////////////////////////////////////////////////////////
276 /// Constructor.
277 
279  : TFileCacheRead(tree->GetCurrentFile(), buffersize, tree), fEntryMax(tree->GetEntriesFast()), fEntryNext(0),
281 {
283  Int_t nleaves = tree->GetListOfLeaves()->GetEntries();
284  fBranches = new TObjArray(nleaves);
285 }
286 
287 ////////////////////////////////////////////////////////////////////////////////
288 /// Destructor. (in general called by the TFile destructor)
289 
291 {
292  // Informe the TFile that we have been deleted (in case
293  // we are deleted explicitly by legacy user code).
294  if (fFile) fFile->SetCacheRead(0, fTree);
295 
296  delete fBranches;
297  if (fBrNames) {fBrNames->Delete(); delete fBrNames; fBrNames=0;}
298 }
299 
300 ////////////////////////////////////////////////////////////////////////////////
301 /// Add a branch to the list of branches to be stored in the cache
302 /// this function is called by TBranch::GetBasket
303 /// Returns:
304 /// - 0 branch added or already included
305 /// - -1 on error
306 
307 Int_t TTreeCache::AddBranch(TBranch *b, Bool_t subbranches /*= kFALSE*/)
308 {
309  if (!fIsLearning) {
310  return -1;
311  }
312 
313  // Reject branch that are not from the cached tree.
314  if (!b || fTree->GetTree() != b->GetTree()) return -1;
315 
316  // Is this the first addition of a branch (and we are learning and we are in
317  // the expected TTree), then prefill the cache. (We expect that in future
318  // release the Prefill-ing will be the default so we test for that inside the
319  // LearnPrefill call).
320  if (fNbranches == 0 && fEntryMin >= 0 && b->GetReadEntry() == fEntryMin) LearnPrefill();
321 
322  //Is branch already in the cache?
323  Bool_t isNew = kTRUE;
324  for (int i=0;i<fNbranches;i++) {
325  if (fBranches->UncheckedAt(i) == b) {isNew = kFALSE; break;}
326  }
327  if (isNew) {
328  fTree = b->GetTree();
329  fBranches->AddAtAndExpand(b, fNbranches);
330  const char *bname = b->GetName();
331  if (fTree->IsA() == TChain::Class()) {
332  // If we have a TChain, we will need to use the branch name
333  // and we better disambiguate them (see atlasFlushed.root for example)
334  // in order to cache all the requested branches.
335  // We do not do this all the time as GetMother is slow (it contains
336  // a linear search from list of top level branch).
337  TString build;
338  const char *mothername = b->GetMother()->GetName();
339  if (b != b->GetMother() && mothername[strlen(mothername)-1] != '.') {
340  // Maybe we ought to prefix the name to avoid ambiguity.
341  auto bem = dynamic_cast<TBranchElement*>(b->GetMother());
342  if (bem->GetType() < 3) {
343  // Not a collection.
344  build = mothername;
345  build.Append(".");
346  if (strncmp(bname,build.Data(),build.Length()) != 0) {
347  build.Append(bname);
348  bname = build.Data();
349  }
350  }
351  }
352  }
353  fBrNames->Add(new TObjString(bname));
354  fNbranches++;
355  if (gDebug > 0) printf("Entry: %lld, registering branch: %s\n",b->GetTree()->GetReadEntry(),b->GetName());
356  }
357 
358  // process subbranches
359  Int_t res = 0;
360  if (subbranches) {
361  TObjArray *lb = b->GetListOfBranches();
362  Int_t nb = lb->GetEntriesFast();
363  for (Int_t j = 0; j < nb; j++) {
364  TBranch* branch = (TBranch*) lb->UncheckedAt(j);
365  if (!branch) continue;
366  if (AddBranch(branch, subbranches)<0) {
367  res = -1;
368  }
369  }
370  }
371  return res;
372 }
373 
374 ////////////////////////////////////////////////////////////////////////////////
375 /// Add a branch to the list of branches to be stored in the cache
376 /// this is to be used by user (thats why we pass the name of the branch).
377 /// It works in exactly the same way as TTree::SetBranchStatus so you
378 /// probably want to look over there for details about the use of bname
379 /// with regular expressions.
380 /// The branches are taken with respect to the Owner of this TTreeCache
381 /// (i.e. the original Tree)
382 /// NB: if bname="*" all branches are put in the cache and the learning phase stopped
383 /// Returns:
384 /// - 0 branch added or already included
385 /// - -1 on error
386 
387 Int_t TTreeCache::AddBranch(const char *bname, Bool_t subbranches /*= kFALSE*/)
388 {
389  TBranch *branch, *bcount;
390  TLeaf *leaf, *leafcount;
391 
392  Int_t i;
393  Int_t nleaves = (fTree->GetListOfLeaves())->GetEntriesFast();
394  TRegexp re(bname,kTRUE);
395  Int_t nb = 0;
396  Int_t res = 0;
397 
398  // first pass, loop on all branches
399  // for leafcount branches activate/deactivate in function of status
400  Bool_t all = kFALSE;
401  if (!strcmp(bname,"*")) all = kTRUE;
402  for (i=0;i<nleaves;i++) {
403  leaf = (TLeaf*)(fTree->GetListOfLeaves())->UncheckedAt(i);
404  branch = (TBranch*)leaf->GetBranch();
405  TString s = branch->GetName();
406  if (!all) { //Regexp gives wrong result for [] in name
407  TString longname;
408  longname.Form("%s.%s",fTree->GetName(),branch->GetName());
409  if (strcmp(bname,branch->GetName())
410  && longname != bname
411  && s.Index(re) == kNPOS) continue;
412  }
413  nb++;
414  if (AddBranch(branch, subbranches)<0) {
415  res = -1;
416  }
417  leafcount = leaf->GetLeafCount();
418  if (leafcount && !all) {
419  bcount = leafcount->GetBranch();
420  if (AddBranch(bcount, subbranches)<0) {
421  res = -1;
422  }
423  }
424  }
425  if (nb==0 && strchr(bname,'*')==0) {
426  branch = fTree->GetBranch(bname);
427  if (branch) {
428  if (AddBranch(branch, subbranches)<0) {
429  res = -1;
430  }
431  ++nb;
432  }
433  }
434 
435  //search in list of friends
436  UInt_t foundInFriend = 0;
437  if (fTree->GetListOfFriends()) {
438  TIter nextf(fTree->GetListOfFriends());
439  TFriendElement *fe;
440  TString name;
441  while ((fe = (TFriendElement*)nextf())) {
442  TTree *t = fe->GetTree();
443  if (t==0) continue;
444 
445  // If the alias is present replace it with the real name.
446  char *subbranch = (char*)strstr(bname,fe->GetName());
447  if (subbranch!=bname) subbranch = 0;
448  if (subbranch) {
449  subbranch += strlen(fe->GetName());
450  if ( *subbranch != '.' ) subbranch = 0;
451  else subbranch ++;
452  }
453  if (subbranch) {
454  name.Form("%s.%s",t->GetName(),subbranch);
455  if (AddBranch(name, subbranches)<0) {
456  res = -1;
457  }
458  ++foundInFriend;
459  }
460  }
461  }
462  if (!nb && !foundInFriend) {
463  if (gDebug > 0) printf("AddBranch: unknown branch -> %s \n", bname);
464  Error("AddBranch", "unknown branch -> %s", bname);
465  return -1;
466  }
467  //if all branches are selected stop the learning phase
468  if (*bname == '*') {
469  fEntryNext = -1; // We are likely to have change the set of branches, so for the [re-]reading of the cluster.
471  }
472  return res;
473 }
474 
475 ////////////////////////////////////////////////////////////////////////////////
476 /// Remove a branch to the list of branches to be stored in the cache
477 /// this function is called by TBranch::GetBasket.
478 /// Returns:
479 /// - 0 branch dropped or not in cache
480 /// - -1 on error
481 
482 Int_t TTreeCache::DropBranch(TBranch *b, Bool_t subbranches /*= kFALSE*/)
483 {
484  if (!fIsLearning) {
485  return -1;
486  }
487 
488  // Reject branch that are not from the cached tree.
489  if (!b || fTree->GetTree() != b->GetTree()) return -1;
490 
491  //Is branch already in the cache?
492  if (fBranches->Remove(b)) {
493  --fNbranches;
494  if (gDebug > 0) printf("Entry: %lld, un-registering branch: %s\n",b->GetTree()->GetReadEntry(),b->GetName());
495  }
496  delete fBrNames->Remove(fBrNames->FindObject(b->GetName()));
497 
498  // process subbranches
499  Int_t res = 0;
500  if (subbranches) {
501  TObjArray *lb = b->GetListOfBranches();
502  Int_t nb = lb->GetEntriesFast();
503  for (Int_t j = 0; j < nb; j++) {
504  TBranch* branch = (TBranch*) lb->UncheckedAt(j);
505  if (!branch) continue;
506  if (DropBranch(branch, subbranches)<0) {
507  res = -1;
508  }
509  }
510  }
511  return res;
512 }
513 
514 ////////////////////////////////////////////////////////////////////////////////
515 /// Remove a branch to the list of branches to be stored in the cache
516 /// this is to be used by user (thats why we pass the name of the branch).
517 /// It works in exactly the same way as TTree::SetBranchStatus so you
518 /// probably want to look over there for details about the use of bname
519 /// with regular expressions.
520 /// The branches are taken with respect to the Owner of this TTreeCache
521 /// (i.e. the original Tree)
522 /// NB: if bname="*" all branches are put in the cache and the learning phase stopped
523 /// Returns:
524 /// - 0 branch dropped or not in cache
525 /// - -1 on error
526 
527 Int_t TTreeCache::DropBranch(const char *bname, Bool_t subbranches /*= kFALSE*/)
528 {
529  TBranch *branch, *bcount;
530  TLeaf *leaf, *leafcount;
531 
532  Int_t i;
533  Int_t nleaves = (fTree->GetListOfLeaves())->GetEntriesFast();
534  TRegexp re(bname,kTRUE);
535  Int_t nb = 0;
536  Int_t res = 0;
537 
538  // first pass, loop on all branches
539  // for leafcount branches activate/deactivate in function of status
540  Bool_t all = kFALSE;
541  if (!strcmp(bname,"*")) all = kTRUE;
542  for (i=0;i<nleaves;i++) {
543  leaf = (TLeaf*)(fTree->GetListOfLeaves())->UncheckedAt(i);
544  branch = (TBranch*)leaf->GetBranch();
545  TString s = branch->GetName();
546  if (!all) { //Regexp gives wrong result for [] in name
547  TString longname;
548  longname.Form("%s.%s",fTree->GetName(),branch->GetName());
549  if (strcmp(bname,branch->GetName())
550  && longname != bname
551  && s.Index(re) == kNPOS) continue;
552  }
553  nb++;
554  if (DropBranch(branch, subbranches)<0) {
555  res = -1;
556  }
557  leafcount = leaf->GetLeafCount();
558  if (leafcount && !all) {
559  bcount = leafcount->GetBranch();
560  if (DropBranch(bcount, subbranches)<0) {
561  res = -1;
562  }
563  }
564  }
565  if (nb==0 && strchr(bname,'*')==0) {
566  branch = fTree->GetBranch(bname);
567  if (branch) {
568  if (DropBranch(branch, subbranches)<0) {
569  res = -1;
570  }
571  ++nb;
572  }
573  }
574 
575  //search in list of friends
576  UInt_t foundInFriend = 0;
577  if (fTree->GetListOfFriends()) {
578  TIter nextf(fTree->GetListOfFriends());
579  TFriendElement *fe;
580  TString name;
581  while ((fe = (TFriendElement*)nextf())) {
582  TTree *t = fe->GetTree();
583  if (t==0) continue;
584 
585  // If the alias is present replace it with the real name.
586  char *subbranch = (char*)strstr(bname,fe->GetName());
587  if (subbranch!=bname) subbranch = 0;
588  if (subbranch) {
589  subbranch += strlen(fe->GetName());
590  if ( *subbranch != '.' ) subbranch = 0;
591  else subbranch ++;
592  }
593  if (subbranch) {
594  name.Form("%s.%s",t->GetName(),subbranch);
595  if (DropBranch(name, subbranches)<0) {
596  res = -1;
597  }
598  ++foundInFriend;
599  }
600  }
601  }
602  if (!nb && !foundInFriend) {
603  if (gDebug > 0) printf("DropBranch: unknown branch -> %s \n", bname);
604  Error("DropBranch", "unknown branch -> %s", bname);
605  return -1;
606  }
607  //if all branches are selected stop the learning phase
608  if (*bname == '*') {
609  fEntryNext = -1; // We are likely to have change the set of branches, so for the [re-]reading of the cluster.
610  }
611  return res;
612 }
613 
614 ////////////////////////////////////////////////////////////////////////////////
615 /// Start of methods for the miss cache.
616 ////////////////////////////////////////////////////////////////////////////////
617 
618 ////////////////////////////////////////////////////////////////////////////////
619 /// Enable / disable the miss cache.
620 ///
621 /// The first time this is called on a TTreeCache object, the corresponding
622 /// data structures will be allocated. Subsequent enable / disables will
623 /// simply turn the functionality on/off.
625 {
626 
627  if (opt && !fMissCache) {
628  ResetMissCache();
629  }
630  fOptimizeMisses = opt;
631 }
632 
633 ////////////////////////////////////////////////////////////////////////////////
634 /// Reset all the miss cache training.
635 ///
636 /// The contents of the miss cache will be emptied as well as the list of
637 /// branches used.
639 {
640 
641  fLastMiss = -1;
642  fFirstMiss = -1;
643 
644  if (!fMissCache) {
645  fMissCache.reset(new MissCache());
646  }
647  fMissCache->clear();
648 }
649 
650 ////////////////////////////////////////////////////////////////////////////////
651 /// For the event currently being fetched into the miss cache, find the IO
652 /// (offset / length tuple) to pull in the current basket for a given branch.
653 ///
654 /// Returns:
655 /// - IOPos describing the IO operation necessary for the basket on this branch
656 /// - On failure, IOPos.length will be set to 0.
658 {
659  if (R__unlikely(b.GetDirectory() == 0)) {
660  // printf("Branch at %p has no valid directory.\n", &b);
661  return IOPos{0, 0};
662  }
663  if (R__unlikely(b.GetDirectory()->GetFile() != fFile)) {
664  // printf("Branch at %p is in wrong file (branch file %p, my file %p).\n", &b, b.GetDirectory()->GetFile(),
665  // fFile);
666  return IOPos{0, 0};
667  }
668 
669  // printf("Trying to find a basket for branch %p\n", &b);
670  // Pull in metadata about branch; make sure it is valid
671  Int_t *lbaskets = b.GetBasketBytes();
672  Long64_t *entries = b.GetBasketEntry();
673  if (R__unlikely(!lbaskets || !entries)) {
674  // printf("No baskets or entries.\n");
675  return IOPos{0, 0};
676  }
677  // Int_t blistsize = b.GetListOfBaskets()->GetSize();
678  Int_t blistsize = b.GetWriteBasket();
679  if (R__unlikely(blistsize <= 0)) {
680  // printf("Basket list is size 0.\n");
681  return IOPos{0, 0};
682  }
683 
684  // Search for the basket that contains the event of interest. Unlike the primary cache, we
685  // are only interested in a single basket per branch - we don't try to fill the cache.
686  Long64_t basketOffset = TMath::BinarySearch(blistsize, entries, entry);
687  if (basketOffset < 0) { // No entry found.
688  // printf("No entry offset found for entry %ld\n", fTree->GetReadEntry());
689  return IOPos{0, 0};
690  }
691 
692  // Check to see if there's already a copy of this basket in memory. If so, don't fetch it
693  if ((basketOffset < blistsize) && b.GetListOfBaskets()->UncheckedAt(basketOffset)) {
694 
695  // printf("Basket is already in memory.\n");
696  return IOPos{0, 0};
697  }
698 
699  Long64_t pos = b.GetBasketSeek(basketOffset);
700  Int_t len = lbaskets[basketOffset];
701  if (R__unlikely(pos <= 0 || len <= 0)) {
702  /*printf("Basket returned was invalid (basketOffset=%ld, pos=%ld, len=%d).\n", basketOffset, pos, len);
703  for (int idx=0; idx<blistsize; idx++) {
704  printf("Basket entry %d, first event %d, pos %ld\n", idx, entries[idx], b.GetBasketSeek(idx));
705  }*/
706  return IOPos{0, 0};
707  } // Sanity check
708  // Do not cache a basket if it is bigger than the cache size!
709  if (R__unlikely(len > fBufferSizeMin)) {
710  // printf("Basket size is greater than the cache size.\n");
711  return IOPos{0, 0};
712  }
713 
714  return {pos, len};
715 }
716 
717 ////////////////////////////////////////////////////////////////////////////////
718 /// Given a particular IO description (offset / length) representing a 'miss' of
719 /// the TTreeCache's primary cache, calculate all the corresponding IO that
720 /// should be performed.
721 ///
722 /// `all` indicates that this function should search the set of _all_ branches
723 /// in this TTree. When set to false, we only search through branches that
724 /// have previously incurred a miss.
725 ///
726 /// Returns:
727 /// - TBranch pointer corresponding to the basket that will be retrieved by
728 /// this IO operation.
729 /// - If no corresponding branch could be found (or an error occurs), this
730 /// returns nullptr.
732 {
733  if (R__unlikely((pos < 0) || (len < 0))) {
734  return nullptr;
735  }
736 
737  int count = all ? (fTree->GetListOfLeaves())->GetEntriesFast() : fMissCache->fBranches.size();
738  fMissCache->fEntries.reserve(count);
739  fMissCache->fEntries.clear();
740  Bool_t found_request = kFALSE;
741  TBranch *resultBranch = nullptr;
742  Long64_t entry = fTree->GetReadEntry();
743 
744  std::vector<std::pair<size_t, Int_t>> basketsInfo;
745  auto perfStats = GetTree()->GetPerfStats();
746 
747  // printf("Will search %d branches for basket at %ld.\n", count, pos);
748  for (int i = 0; i < count; i++) {
749  TBranch *b =
750  all ? static_cast<TBranch *>(static_cast<TLeaf *>((fTree->GetListOfLeaves())->UncheckedAt(i))->GetBranch())
751  : fMissCache->fBranches[i];
752  IOPos iopos = FindBranchBasketPos(*b, entry);
753  if (iopos.fLen == 0) { // Error indicator
754  continue;
755  }
756  if (iopos.fPos == pos && iopos.fLen == len) {
757  found_request = kTRUE;
758  resultBranch = b;
759  // Note that we continue to iterate; fills up the rest of the entries in the cache.
760  }
761  // At this point, we are ready to push back a new offset
762  fMissCache->fEntries.emplace_back(std::move(iopos));
763 
764  if (R__unlikely(perfStats)) {
765  Int_t blistsize = b->GetWriteBasket();
766  Int_t basketNumber = -1;
767  for (Int_t bn = 0; bn < blistsize; ++bn) {
768  if (iopos.fPos == b->GetBasketSeek(bn)) {
769  basketNumber = bn;
770  break;
771  }
772  }
773  if (basketNumber >= 0)
774  basketsInfo.emplace_back((size_t)i, basketNumber);
775  }
776  }
777  if (R__unlikely(!found_request)) {
778  // We have gone through all the branches in this file and the requested basket
779  // doesn't appear to be in any of them. Likely a logic error / bug.
780  fMissCache->fEntries.clear();
781  }
782  if (R__unlikely(perfStats)) {
783  for (auto &info : basketsInfo) {
784  perfStats->SetLoadedMiss(info.first, info.second);
785  }
786  }
787  return resultBranch;
788 }
789 
790 ////////////////////////////////////////////////////////////////////////////////
791 ///
792 /// Process a cache miss; (pos, len) isn't in the buffer.
793 ///
794 /// The first time we have a miss, we buffer as many baskets we can (up to the
795 /// maximum size of the TTreeCache) in memory from all branches that are not in
796 /// the prefetch list.
797 ///
798 /// Subsequent times, we fetch all the buffers corresponding to branches that
799 /// had previously seen misses. If it turns out the (pos, len) isn't in the
800 /// list of branches, we treat this as if it was the first miss.
801 ///
802 /// Returns true if we were able to pull the data into the miss cache.
803 ///
805 {
806 
807  Bool_t firstMiss = kFALSE;
808  if (fFirstMiss == -1) {
810  firstMiss = kTRUE;
811  }
813  // The first time this is executed, we try to pull in as much data as we can.
814  TBranch *b = CalculateMissEntries(pos, len, firstMiss);
815  if (!b) {
816  if (!firstMiss) {
817  // TODO: this recalculates for *all* branches, throwing away the above work.
818  b = CalculateMissEntries(pos, len, kTRUE);
819  }
820  if (!b) {
821  // printf("ProcessMiss: pos %ld does not appear to correspond to a buffer in this file.\n", pos);
822  // We have gone through all the branches in this file and the requested basket
823  // doesn't appear to be in any of them. Likely a logic error / bug.
824  fMissCache->fEntries.clear();
825  return kFALSE;
826  }
827  }
828  // TODO: this should be a set.
829  fMissCache->fBranches.push_back(b);
830 
831  // OK, sort the entries
832  std::sort(fMissCache->fEntries.begin(), fMissCache->fEntries.end());
833 
834  // Now, fetch the buffer.
835  std::vector<Long64_t> positions;
836  positions.reserve(fMissCache->fEntries.size());
837  std::vector<Int_t> lengths;
838  lengths.reserve(fMissCache->fEntries.size());
839  ULong64_t cumulative = 0;
840  for (auto &mcentry : fMissCache->fEntries) {
841  positions.push_back(mcentry.fIO.fPos);
842  lengths.push_back(mcentry.fIO.fLen);
843  mcentry.fIndex = cumulative;
844  cumulative += mcentry.fIO.fLen;
845  }
846  fMissCache->fData.reserve(cumulative);
847  // printf("Reading %lu bytes into miss cache for %lu entries.\n", cumulative, fEntries->size());
848  fNMissReadPref += fMissCache->fEntries.size();
849  fFile->ReadBuffers(&(fMissCache->fData[0]), &(positions[0]), &(lengths[0]), fMissCache->fEntries.size());
851 
852  return kTRUE;
853 }
854 
855 ////////////////////////////////////////////////////////////////////////////////
856 /// Given an IO operation (pos, len) that was a cache miss in the primary TTC,
857 /// try the operation again with the miss cache.
858 ///
859 /// Returns true if the IO operation was successful and the contents of buf
860 /// were populated with the requested data.
861 ///
863 {
864 
865  if (!fOptimizeMisses) {
866  return kFALSE;
867  }
868  if (R__unlikely((pos < 0) || (len < 0))) {
869  return kFALSE;
870  }
871 
872  // printf("Checking the miss cache for offset=%ld, length=%d\n", pos, len);
873 
874  // First, binary search to see if the desired basket is already cached.
875  MissCache::Entry mcentry{IOPos{pos, len}};
876  auto iter = std::lower_bound(fMissCache->fEntries.begin(), fMissCache->fEntries.end(), mcentry);
877 
878  if (iter != fMissCache->fEntries.end()) {
879  if (len > iter->fIO.fLen) {
880  ++fNMissReadMiss;
881  return kFALSE;
882  }
883  auto offset = iter->fIndex;
884  memcpy(buf, &(fMissCache->fData[offset]), len);
885  // printf("Returning data from pos=%ld in miss cache.\n", offset);
886  ++fNMissReadOk;
887  return kTRUE;
888  }
889 
890  // printf("Data not in miss cache.\n");
891 
892  // Update the cache, looking for this (pos, len).
893  if (!ProcessMiss(pos, len)) {
894  // printf("Unable to pull data into miss cache.\n");
895  ++fNMissReadMiss;
896  return kFALSE;
897  }
898 
899  // OK, we updated the cache with as much information as possible. Seach again for
900  // the entry we want.
901  iter = std::lower_bound(fMissCache->fEntries.begin(), fMissCache->fEntries.end(), mcentry);
902 
903  if (iter != fMissCache->fEntries.end()) {
904  auto offset = iter->fIndex;
905  // printf("Expecting data at offset %ld in miss cache.\n", offset);
906  memcpy(buf, &(fMissCache->fData[offset]), len);
907  ++fNMissReadOk;
908  return kTRUE;
909  }
910 
911  // This must be a logic bug. ProcessMiss should return false if (pos, len)
912  // wasn't put into fEntries.
913  ++fNMissReadMiss;
914  return kFALSE;
915 }
916 
917 ////////////////////////////////////////////////////////////////////////////////
918 /// End of methods for miss cache.
919 ////////////////////////////////////////////////////////////////////////////////
920 
921 namespace {
922 struct BasketRanges {
923  struct Range {
924  Long64_t fMin; ///< Inclusive minimum
925  Long64_t fMax; ///< Inclusive maximum
926 
927  Range() : fMin(-1), fMax(-1) {}
928 
929  void UpdateMin(Long64_t min)
930  {
931  if (fMin == -1 || min < fMin)
932  fMin = min;
933  }
934 
935  void UpdateMax(Long64_t max)
936  {
937  if (fMax == -1 || fMax < max)
938  fMax = max;
939  }
940 
941  Bool_t Contains(Long64_t entry) { return (fMin <= entry && entry <= fMax); }
942  };
943 
944  std::vector<Range> fRanges;
945  std::map<Long64_t,size_t> fMinimums;
946  std::map<Long64_t,size_t> fMaximums;
947 
948  BasketRanges(size_t nBranches) { fRanges.resize(nBranches); }
949 
950  void Update(size_t branchNumber, Long64_t min, Long64_t max)
951  {
952  Range &range = fRanges.at(branchNumber);
953  auto old(range);
954 
955  range.UpdateMin(min);
956  range.UpdateMax(max);
957 
958  if (old.fMax != range.fMax) {
959  if (old.fMax != -1) {
960  auto maxIter = fMaximums.find(old.fMax);
961  if (maxIter != fMaximums.end()) {
962  if (maxIter->second == 1) {
963  fMaximums.erase(maxIter);
964  } else {
965  --(maxIter->second);
966  }
967  }
968  }
969  ++(fMaximums[max]);
970  }
971  }
972 
973  void Update(size_t branchNumber, size_t basketNumber, Long64_t *entries, size_t nb, size_t max)
974  {
975  Update(branchNumber, entries[basketNumber],
976  (basketNumber < (nb - 1)) ? (entries[basketNumber + 1] - 1) : max - 1);
977  }
978 
979  // Check that fMaximums and fMinimums are properly set
980  bool CheckAllIncludeRange()
981  {
982  Range result;
983  for (const auto &r : fRanges) {
984  if (result.fMin == -1 || result.fMin < r.fMin) {
985  if (r.fMin != -1)
986  result.fMin = r.fMin;
987  }
988  if (result.fMax == -1 || r.fMax < result.fMax) {
989  if (r.fMax != -1)
990  result.fMax = r.fMax;
991  }
992  }
993  // if (result.fMax < result.fMin) {
994  // // No overlapping range.
995  // }
996 
997  Range allIncludedRange(AllIncludedRange());
998 
999  return (result.fMin == allIncludedRange.fMin && result.fMax == allIncludedRange.fMax);
1000  }
1001 
1002  // This returns a Range object where fMin is the maximum of all the minimun entry
1003  // number loaded for each branch and fMax is the minimum of all the maximum entry
1004  // number loaded for each branch.
1005  // As such it is valid to have fMin > fMax, this is the case where there
1006  // are no overlap between the branch's range. For example for 2 branches
1007  // where we have for one the entry [50,99] and for the other [0,49] then
1008  // we will have fMin = max(50,0) = 50 and fMax = min(99,49) = 49
1009  Range AllIncludedRange()
1010  {
1011  Range result;
1012  if (!fMinimums.empty())
1013  result.fMin = fMinimums.rbegin()->first;
1014  if (!fMaximums.empty())
1015  result.fMax = fMaximums.begin()->first;
1016  return result;
1017  }
1018 
1019  // Returns the number of branches with at least one baskets registered.
1020  UInt_t BranchesRegistered()
1021  {
1022  UInt_t result = 0;
1023  for (const auto &r : fRanges) {
1024  if (r.fMin != -1 && r.fMax != -1)
1025  ++result;
1026  }
1027  return result;
1028  }
1029 
1030  // Returns true if at least one of the branch's range contains
1031  // the entry.
1032  Bool_t Contains(Long64_t entry)
1033  {
1034  for (const auto &r : fRanges) {
1035  if (r.fMin != -1 && r.fMax != -1)
1036  if (r.fMin <= entry && entry <= r.fMax)
1037  return kTRUE;
1038  }
1039  return kFALSE;
1040  }
1041 
1042  void Print()
1043  {
1044  for (size_t i = 0; i < fRanges.size(); ++i) {
1045  if (fRanges[i].fMin != -1 || fRanges[i].fMax != -1)
1046  Printf("Range #%zu : %lld to %lld", i, fRanges[i].fMin, fRanges[i].fMax);
1047  }
1048  }
1049 };
1050 } // Anonymous namespace.
1051 
1052 ////////////////////////////////////////////////////////////////////////////////
1053 /// Fill the cache buffer with the branches in the cache.
1054 
1056 {
1057 
1058  if (fNbranches <= 0) return kFALSE;
1060  Long64_t entry = tree->GetReadEntry();
1061  Long64_t fEntryCurrentMax = 0;
1062 
1063  if (entry != -1 && (entry < fEntryMin || fEntryMax < entry))
1064  return kFALSE;
1065 
1066  if (fEnablePrefetching) { // Prefetching mode
1067  if (fIsLearning) { // Learning mode
1068  if (fEntryNext >= 0 && entry >= fEntryNext) {
1069  // entry is outside the learn range, need to stop the learning
1070  // phase. Doing so may trigger a recursive call to FillBuffer in
1071  // the process of filling both prefetching buffers
1073  fIsManual = kFALSE;
1074  }
1075  }
1076  if (fIsLearning) { // Learning mode
1077  entry = 0;
1078  }
1079  if (fFirstTime) {
1080  //try to detect if it is normal or reverse read
1081  fFirstEntry = entry;
1082  }
1083  else {
1084  if (fFirstEntry == entry) return kFALSE;
1085  // Set the read direction
1086  if (!fReadDirectionSet) {
1087  if (entry < fFirstEntry) {
1088  fReverseRead = kTRUE;
1090  }
1091  else if (entry > fFirstEntry) {
1094  }
1095  }
1096 
1097  if (fReverseRead) {
1098  // Reverse reading with prefetching
1099  if (fEntryCurrent >0 && entry < fEntryNext) {
1100  // We can prefetch the next buffer
1101  if (entry >= fEntryCurrent) {
1102  entry = fEntryCurrent - tree->GetAutoFlush() * fFillTimes;
1103  }
1104  if (entry < 0) entry = 0;
1105  }
1106  else if (fEntryCurrent >= 0) {
1107  // We are still reading from the oldest buffer, no need to prefetch a new one
1108  return kFALSE;
1109  }
1110  if (entry < 0) return kFALSE;
1112  }
1113  else {
1114  // Normal reading with prefetching
1115  if (fEnablePrefetching) {
1116  if (entry < 0 && fEntryNext > 0) {
1117  entry = fEntryCurrent;
1118  } else if (entry >= fEntryCurrent) {
1119  if (entry < fEntryNext) {
1120  entry = fEntryNext;
1121  }
1122  }
1123  else {
1124  // We are still reading from the oldest buffer,
1125  // no need to prefetch a new one
1126  return kFALSE;
1127  }
1129  }
1130  }
1131  }
1132  }
1133 
1134  // Set to true to enable all debug output without having to set gDebug
1135  // Replace this once we have a per module and/or per class debuging level/setting.
1136  static constexpr bool showMore = kFALSE;
1137 
1138  static const auto PrintAllCacheInfo = [](TObjArray *branches) {
1139  for (Int_t i = 0; i < branches->GetEntries(); i++) {
1140  TBranch *b = (TBranch *)branches->UncheckedAt(i);
1141  b->PrintCacheInfo();
1142  }
1143  };
1144 
1145  if (showMore || gDebug > 6)
1146  Info("FillBuffer", "***** Called for entry %lld", entry);
1147 
1148  if (!fIsLearning && fEntryCurrent <= entry && entry < fEntryNext) {
1149  // Check if all the basket in the cache have already be used and
1150  // thus we can reuse the cache.
1151  Bool_t allUsed = kTRUE;
1152  for (Int_t i = 0; i < fNbranches; ++i) {
1154  if (!b->fCacheInfo.AllUsed()) {
1155  allUsed = kFALSE;
1156  break;
1157  }
1158  }
1159  if (allUsed) {
1160  fEntryNext = entry;
1161  if (showMore || gDebug > 5)
1162  Info("FillBuffer", "All baskets used already, so refresh the cache early at entry %lld", entry);
1163  }
1164  if (gDebug > 8)
1165  PrintAllCacheInfo(fBranches);
1166  }
1167 
1168  // If the entry is in the range we previously prefetched, there is
1169  // no point in retrying. Note that this will also return false
1170  // during the training phase (fEntryNext is then set intentional to
1171  // the end of the training phase).
1172  if (fEntryCurrent <= entry && entry < fEntryNext) return kFALSE;
1173 
1174  // Triggered by the user, not the learning phase
1175  if (entry == -1)
1176  entry = 0;
1177 
1178  Bool_t resetBranchInfo = kFALSE;
1179  if (entry < fCurrentClusterStart || fNextClusterStart <= entry) {
1180  // We are moving on to another set of clusters.
1181  resetBranchInfo = kTRUE;
1182  if (showMore || gDebug > 6)
1183  Info("FillBuffer", "*** Will reset the branch information about baskets");
1184  } else if (showMore || gDebug > 6) {
1185  Info("FillBuffer", "*** Info we have on the set of baskets");
1186  PrintAllCacheInfo(fBranches);
1187  }
1188 
1189  fEntryCurrentMax = fEntryCurrent;
1190  TTree::TClusterIterator clusterIter = tree->GetClusterIterator(entry);
1191 
1192  auto entryCurrent = clusterIter();
1193  auto entryNext = clusterIter.GetNextEntry();
1194 
1195  if (entryNext < fEntryMin || fEntryMax < entryCurrent) {
1196  // There is no overlap betweent the cluster we found [entryCurrent, entryNext[
1197  // and the authorized range [fEntryMin, fEntryMax]
1198  // so we have nothing to do
1199  return kFALSE;
1200  }
1201 
1202  fEntryCurrent = entryCurrent;
1203  fEntryNext = entryNext;
1204 
1205 
1206  auto firstClusterEnd = fEntryNext;
1207  if (showMore || gDebug > 6)
1208  Info("FillBuffer", "Looking at cluster spanning from %lld to %lld", fEntryCurrent, fEntryNext);
1209 
1211  if (fEntryMax <= 0) fEntryMax = tree->GetEntries();
1213 
1214  if ( fEnablePrefetching ) {
1215  if ( entry == fEntryMax ) {
1216  // We are at the end, no need to do anything else
1217  return kFALSE;
1218  }
1219  }
1220 
1221  if (resetBranchInfo) {
1222  // We earlier thought we were onto the next set of clusters.
1223  if (fCurrentClusterStart != -1 || fNextClusterStart != -1) {
1224  if (!(fEntryCurrent < fCurrentClusterStart || fEntryCurrent >= fNextClusterStart)) {
1225  Error("FillBuffer", "Inconsistency: fCurrentClusterStart=%lld fEntryCurrent=%lld fNextClusterStart=%lld "
1226  "but fEntryCurrent should not be in between the two",
1228  }
1229  }
1230 
1231  // Start the next cluster set.
1233  fNextClusterStart = firstClusterEnd;
1234  }
1235 
1236  // Check if owner has a TEventList set. If yes we optimize for this
1237  // Special case reading only the baskets containing entries in the
1238  // list.
1239  TEventList *elist = fTree->GetEventList();
1240  Long64_t chainOffset = 0;
1241  if (elist) {
1242  if (fTree->IsA() ==TChain::Class()) {
1243  TChain *chain = (TChain*)fTree;
1244  Int_t t = chain->GetTreeNumber();
1245  chainOffset = chain->GetTreeOffset()[t];
1246  }
1247  }
1248 
1249  //clear cache buffer
1250  Int_t ntotCurrentBuf = 0;
1251  if (fEnablePrefetching){ //prefetching mode
1252  if (fFirstBuffer) {
1254  ntotCurrentBuf = fNtot;
1255  }
1256  else {
1258  ntotCurrentBuf = fBNtot;
1259  }
1260  }
1261  else {
1263  ntotCurrentBuf = fNtot;
1264  }
1265 
1266  //store baskets
1267  BasketRanges ranges((showMore || gDebug > 6) ? fNbranches : 0);
1268  BasketRanges reqRanges(fNbranches);
1269  BasketRanges memRanges((showMore || gDebug > 6) ? fNbranches : 0);
1270  Int_t clusterIterations = 0;
1271  Long64_t minEntry = fEntryCurrent;
1272  Int_t prevNtot;
1273  Long64_t maxReadEntry = minEntry; // If we are stopped before the end of the 2nd pass, this marker will where we need to start next time.
1274  Int_t nReadPrefRequest = 0;
1275  auto perfStats = GetTree()->GetPerfStats();
1276  do {
1277  prevNtot = ntotCurrentBuf;
1278  Long64_t lowestMaxEntry = fEntryMax; // The lowest maximum entry in the TTreeCache for each branch for each pass.
1279 
1280  struct collectionInfo {
1281  Int_t fClusterStart{-1}; // First basket belonging to the current cluster
1282  Int_t fCurrent{0}; // Currently visited basket
1283  Bool_t fLoadedOnce{kFALSE};
1284 
1285  void Rewind() { fCurrent = (fClusterStart >= 0) ? fClusterStart : 0; }
1286  };
1287  std::vector<collectionInfo> cursor(fNbranches);
1288  Bool_t reachedEnd = kFALSE;
1289  Bool_t skippedFirst = kFALSE;
1290  Bool_t oncePerBranch = kFALSE;
1291  Int_t nDistinctLoad = 0;
1292  Bool_t progress = kTRUE;
1293  enum ENarrow {
1294  kFull = 0,
1295  kNarrow = 1
1296  };
1297  enum EPass {
1298  kStart = 1,
1299  kRegular = 2,
1300  kRewind = 3
1301  };
1302 
1303  auto CollectBaskets = [this, elist, chainOffset, entry, clusterIterations, resetBranchInfo, perfStats,
1304  &cursor, &lowestMaxEntry, &maxReadEntry, &minEntry,
1305  &reachedEnd, &skippedFirst, &oncePerBranch, &nDistinctLoad, &progress,
1306  &ranges, &memRanges, &reqRanges,
1307  &ntotCurrentBuf, &nReadPrefRequest](EPass pass, ENarrow narrow, Long64_t maxCollectEntry) {
1308  // The first pass we add one basket per branches around the requested entry
1309  // then in the second pass we add the other baskets of the cluster.
1310  // This is to support the case where the cache is too small to hold a full cluster.
1311  Int_t nReachedEnd = 0;
1312  Int_t nSkipped = 0;
1313  auto oldnReadPrefRequest = nReadPrefRequest;
1314  std::vector<Int_t> potentialVetoes;
1315 
1316  if (showMore || gDebug > 7)
1317  Info("CollectBaskets", "Called with pass=%d narrow=%d maxCollectEntry=%lld", pass, narrow, maxCollectEntry);
1318 
1319  Bool_t filled = kFALSE;
1320  for (Int_t i = 0; i < fNbranches; ++i) {
1322  if (b->GetDirectory()==0)
1323  continue;
1324  if (b->GetDirectory()->GetFile() != fFile)
1325  continue;
1326  potentialVetoes.clear();
1327  if (pass == kStart && !cursor[i].fLoadedOnce && resetBranchInfo) {
1328  // First check if we have any cluster that is currently in the
1329  // cache but was not used and would be reloaded in the next
1330  // cluster.
1331  b->fCacheInfo.GetUnused(potentialVetoes);
1332  if (showMore || gDebug > 7) {
1333  TString vetolist;
1334  for(auto v : potentialVetoes) {
1335  vetolist += v;
1336  vetolist.Append(' ');
1337  }
1338  if (!potentialVetoes.empty())
1339  Info("FillBuffer", "*** Potential Vetos for branch #%d: %s", i, vetolist.Data());
1340  }
1341  b->fCacheInfo.Reset();
1342  }
1343  Int_t nb = b->GetMaxBaskets();
1344  Int_t *lbaskets = b->GetBasketBytes();
1345  Long64_t *entries = b->GetBasketEntry();
1346  if (!lbaskets || !entries)
1347  continue;
1348  //we have found the branch. We now register all its baskets
1349  // from the requested offset to the basket below fEntryMax
1350  Int_t blistsize = b->GetListOfBaskets()->GetSize();
1351 
1352  auto maxOfBasket = [this, nb, entries](int j) {
1353  return ((j < (nb - 1)) ? (entries[j + 1] - 1) : fEntryMax - 1);
1354  };
1355 
1356  if (pass == kRewind)
1357  cursor[i].Rewind();
1358  for (auto &j = cursor[i].fCurrent; j < nb; j++) {
1359  // This basket has already been read, skip it
1360 
1361  if (j < blistsize && b->GetListOfBaskets()->UncheckedAt(j)) {
1362 
1363  if (showMore || gDebug > 6) {
1364  ranges.Update(i, entries[j], maxOfBasket(j));
1365  memRanges.Update(i, entries[j], maxOfBasket(j));
1366  }
1367  if (entries[j] <= entry && entry <= maxOfBasket(j)) {
1368  b->fCacheInfo.SetIsInCache(j);
1369  b->fCacheInfo.SetUsed(j);
1370  if (narrow) {
1371  // In narrow mode, we would select 'only' this basket,
1372  // so we are done for this round, let's 'consume' this
1373  // basket and go.
1374  ++nReachedEnd;
1375  ++j;
1376  break;
1377  }
1378  }
1379  continue;
1380  }
1381 
1382  // Important: do not try to read maxCollectEntry, otherwise we might jump to the next autoflush
1383  if (entries[j] >= maxCollectEntry) {
1384  ++nReachedEnd;
1385  break; // break out of the for each branch loop.
1386  }
1387 
1388  Long64_t pos = b->GetBasketSeek(j);
1389  Int_t len = lbaskets[j];
1390  if (pos <= 0 || len <= 0)
1391  continue;
1392  if (len > fBufferSizeMin) {
1393  // Do not cache a basket if it is bigger than the cache size!
1394  if ((showMore || gDebug > 7) &&
1395  (!(entries[j] < minEntry && (j < nb - 1 && entries[j + 1] <= minEntry))))
1396  Info("FillBuffer", "Skipping branch %s basket %d is too large for the cache: %d > %d",
1397  b->GetName(), j, len, fBufferSizeMin);
1398  continue;
1399  }
1400 
1401  if (nReadPrefRequest && entries[j] > (reqRanges.AllIncludedRange().fMax + 1)) {
1402  // There is a gap between this basket and the max of the 'lowest' already loaded basket
1403  // If we are tight in memory, reading this basket may prevent reading the basket (for the other branches)
1404  // that covers this gap, forcing those baskets to be read uncached (because the cache wont be reloaded
1405  // until we use this basket).
1406  // eg. We could end up with the cache containg
1407  // b1: [428, 514[ // 'this' basket and we can assume [321 to 428[ is already in memory
1408  // b2: [400, 424[
1409  // and when reading entry 425 we will read b2's basket uncached.
1410 
1411  if (showMore || gDebug > 8)
1412  Info("FillBuffer", "Skipping for now due to gap %d/%d with %lld > %lld", i, j, entries[j],
1413  (reqRanges.AllIncludedRange().fMax + 1));
1414  break; // Without consuming the basket.
1415  }
1416 
1417  if (entries[j] < minEntry && (j<nb-1 && entries[j+1] <= minEntry))
1418  continue;
1419 
1420  // We are within the range
1421  if (cursor[i].fClusterStart == -1)
1422  cursor[i].fClusterStart = j;
1423 
1424  if (elist) {
1425  Long64_t emax = fEntryMax;
1426  if (j<nb-1)
1427  emax = entries[j + 1] - 1;
1428  if (!elist->ContainsRange(entries[j]+chainOffset,emax+chainOffset))
1429  continue;
1430  }
1431 
1432  if (b->fCacheInfo.HasBeenUsed(j) || b->fCacheInfo.IsInCache(j) || b->fCacheInfo.IsVetoed(j)) {
1433  // We already cached and used this basket during this cluster range,
1434  // let's not redo it
1435  if (showMore || gDebug > 7)
1436  Info("FillBuffer", "Skipping basket to avoid redo: %d/%d veto: %d", i, j, b->fCacheInfo.IsVetoed(j));
1437  continue;
1438  }
1439 
1440  if (std::find(std::begin(potentialVetoes), std::end(potentialVetoes), j) != std::end(potentialVetoes)) {
1441  // This basket was in the previous cache/cluster and was not used,
1442  // let's not read it again. I.e. we bet that it will continue to not
1443  // be used. At worst it will be used and thus read by itself.
1444  // Usually in this situation the basket is large so the penalty for
1445  // (re)reading it uselessly is high and the penatly to read it by
1446  // itself is 'small' (i.e. size bigger than latency).
1447  b->fCacheInfo.Veto(j);
1448  if (showMore || gDebug > 7)
1449  Info("FillBuffer", "Veto-ing cluster %d [%lld,%lld[ in branch %s #%d", j, entries[j],
1450  maxOfBasket(j) + 1, b->GetName(), i);
1451  continue;
1452  }
1453 
1454  if (narrow) {
1455  if ((((entries[j] > entry)) || (j < nb - 1 && entries[j + 1] <= entry))) {
1456  // Keep only the basket that contains the entry
1457  if (j == cursor[i].fClusterStart && entry > entries[j])
1458  ++nSkipped;
1459  if (entries[j] > entry)
1460  break;
1461  else
1462  continue;
1463  }
1464  }
1465 
1466  if ((ntotCurrentBuf + len) > fBufferSizeMin) {
1467  // Humm ... we are going to go over the requested size.
1468  if (clusterIterations > 0 && cursor[i].fLoadedOnce) {
1469  // We already have a full cluster and now we would go over the requested
1470  // size, let's stop caching (and make sure we start next time from the
1471  // end of the previous cluster).
1472  if (showMore || gDebug > 5) {
1473  Info(
1474  "FillBuffer",
1475  "Breaking early because %d is greater than %d at cluster iteration %d will restart at %lld",
1476  (ntotCurrentBuf + len), fBufferSizeMin, clusterIterations, minEntry);
1477  }
1478  fEntryNext = minEntry;
1479  filled = kTRUE;
1480  break;
1481  } else {
1482  if (pass == kStart || !cursor[i].fLoadedOnce) {
1483  if ((ntotCurrentBuf + len) > 4 * fBufferSizeMin) {
1484  // Okay, so we have not even made one pass and we already have
1485  // accumulated request for more than twice the memory size ...
1486  // So stop for now, and will restart at the same point, hoping
1487  // that the basket will still be in memory and not asked again ..
1488  fEntryNext = maxReadEntry;
1489 
1490  if (showMore || gDebug > 5) {
1491  Info("FillBuffer", "Breaking early because %d is greater than 4*%d at cluster iteration "
1492  "%d pass %d will restart at %lld",
1493  (ntotCurrentBuf + len), fBufferSizeMin, clusterIterations, pass, fEntryNext);
1494  }
1495  filled = kTRUE;
1496  break;
1497  }
1498  } else {
1499  // We have made one pass through the branches and thus already
1500  // requested one basket per branch, let's stop prefetching
1501  // now.
1502  if ((ntotCurrentBuf + len) > 2 * fBufferSizeMin) {
1503  fEntryNext = maxReadEntry;
1504  if (showMore || gDebug > 5) {
1505  Info("FillBuffer", "Breaking early because %d is greater than 2*%d at cluster iteration "
1506  "%d pass %d will restart at %lld",
1507  (ntotCurrentBuf + len), fBufferSizeMin, clusterIterations, pass, fEntryNext);
1508  }
1509  filled = kTRUE;
1510  break;
1511  }
1512  }
1513  }
1514  }
1515 
1516  ++nReadPrefRequest;
1517 
1518  reqRanges.Update(i, j, entries, nb, fEntryMax);
1519  if (showMore || gDebug > 6)
1520  ranges.Update(i, j, entries, nb, fEntryMax);
1521 
1522  b->fCacheInfo.SetIsInCache(j);
1523 
1524  if (showMore || gDebug > 6)
1525  Info("FillBuffer", "*** Registering branch %d basket %d %s", i, j, b->GetName());
1526 
1527  if (!cursor[i].fLoadedOnce) {
1528  cursor[i].fLoadedOnce = kTRUE;
1529  ++nDistinctLoad;
1530  }
1531  if (R__unlikely(perfStats)) {
1532  perfStats->SetLoaded(i, j);
1533  }
1534 
1535  // Actual registering the basket for loading from the file.
1536  if (fEnablePrefetching){
1537  if (fFirstBuffer) {
1538  TFileCacheRead::Prefetch(pos,len);
1539  ntotCurrentBuf = fNtot;
1540  }
1541  else {
1543  ntotCurrentBuf = fBNtot;
1544  }
1545  }
1546  else {
1547  TFileCacheRead::Prefetch(pos,len);
1548  ntotCurrentBuf = fNtot;
1549  }
1550 
1551  if ( ( j < (nb-1) ) && entries[j+1] > maxReadEntry ) {
1552  // Info("FillBuffer","maxCollectEntry incremented from %lld to %lld", maxReadEntry, entries[j+1]);
1553  maxReadEntry = entries[j+1];
1554  }
1555  if (ntotCurrentBuf > 4 * fBufferSizeMin) {
1556  // Humm something wrong happened.
1557  Warning("FillBuffer", "There is more data in this cluster (starting at entry %lld to %lld, "
1558  "current=%lld) than usual ... with %d %.3f%% of the branches we already have "
1559  "%d bytes (instead of %d)",
1560  fEntryCurrent, fEntryNext, entries[j], i, (100.0 * i) / ((float)fNbranches), ntotCurrentBuf,
1561  fBufferSizeMin);
1562  }
1563  if (pass == kStart) {
1564  // In the first pass, we record one basket per branch and move on to the next branch.
1565  auto high = maxOfBasket(j);
1566  if (high < lowestMaxEntry)
1567  lowestMaxEntry = high;
1568  // 'Consume' the baskets (i.e. avoid looking at it during a subsequent pass)
1569  ++j;
1570  break;
1571  } else if ((j + 1) == nb || entries[j + 1] >= maxReadEntry || entries[j + 1] >= lowestMaxEntry) {
1572  // In the other pass, load the baskets until we get to the maximum loaded so far.
1573  auto high = maxOfBasket(j);
1574  if (high < lowestMaxEntry)
1575  lowestMaxEntry = high;
1576  // 'Consume' the baskets (i.e. avoid looking at it during a subsequent pass)
1577  ++j;
1578  break;
1579  }
1580  }
1581 
1582  if (cursor[i].fCurrent == nb) {
1583  ++nReachedEnd;
1584  }
1585 
1586  if (gDebug > 0)
1587  Info("CollectBaskets",
1588  "Entry: %lld, registering baskets branch %s, fEntryNext=%lld, fNseek=%d, ntotCurrentBuf=%d",
1589  minEntry, ((TBranch *)fBranches->UncheckedAt(i))->GetName(), fEntryNext, fNseek, ntotCurrentBuf);
1590  }
1591  reachedEnd = (nReachedEnd == fNbranches);
1592  skippedFirst = (nSkipped > 0);
1593  oncePerBranch = (nDistinctLoad == fNbranches);
1594  progress = nReadPrefRequest - oldnReadPrefRequest;
1595  return filled;
1596  };
1597 
1598  // First collect all the basket containing the request entry.
1599  bool full = kFALSE;
1600 
1601  full = CollectBaskets(kStart, kNarrow, fEntryNext);
1602 
1603  // Then fill out from all but the 'largest' branch to even out
1604  // the range across branches;
1605  while (!full && !reachedEnd && progress) { // used to be restricted to !oncePerBranch
1606  full = CollectBaskets(kStart, kFull, std::min(maxReadEntry, fEntryNext));
1607  }
1608 
1609  resetBranchInfo = kFALSE; // Make sure the 2nd cluster iteration does not erase the info.
1610 
1611  // Then fill out to the end of the cluster.
1612  if (!full && !fReverseRead) {
1613  do {
1614  full = CollectBaskets(kRegular, kFull, fEntryNext);
1615  } while (!full && !reachedEnd && progress);
1616  }
1617 
1618  // The restart from the start of the cluster.
1619  if (!full && skippedFirst) {
1620  full = CollectBaskets(kRewind, kFull, fEntryNext);
1621  while (!full && !reachedEnd && progress) {
1622  full = CollectBaskets(kRegular, kFull, fEntryNext);
1623  }
1624  }
1625 
1626  clusterIterations++;
1627 
1628  minEntry = clusterIter.Next();
1629  if (fIsLearning) {
1630  fFillTimes++;
1631  }
1632 
1633  // Continue as long as we still make progress (prevNtot < ntotCurrentBuf), that the next entry range to be looked
1634  // at,
1635  // which start at 'minEntry', is not past the end of the requested range (minEntry < fEntryMax)
1636  // and we guess that we not going to go over the requested amount of memory by asking for another set
1637  // of entries (fBufferSizeMin > ((Long64_t)ntotCurrentBuf*(clusterIterations+1))/clusterIterations).
1638  // ntotCurrentBuf / clusterIterations is the average size we are accumulated so far at each loop.
1639  // and thus (ntotCurrentBuf / clusterIterations) * (clusterIterations+1) is a good guess at what the next total
1640  // size
1641  // would be if we run the loop one more time. ntotCurrentBuf and clusterIterations are Int_t but can sometimes
1642  // be 'large' (i.e. 30Mb * 300 intervals) and can overflow the numerical limit of Int_t (i.e. become
1643  // artificially negative). To avoid this issue we promote ntotCurrentBuf to a long long (64 bits rather than 32
1644  // bits)
1645  if (!((fBufferSizeMin > ((Long64_t)ntotCurrentBuf * (clusterIterations + 1)) / clusterIterations) &&
1646  (prevNtot < ntotCurrentBuf) && (minEntry < fEntryMax))) {
1647  if (showMore || gDebug > 6)
1648  Info("FillBuffer", "Breaking because %d <= %lld || (%d >= %d) || %lld >= %lld", fBufferSizeMin,
1649  ((Long64_t)ntotCurrentBuf * (clusterIterations + 1)) / clusterIterations, prevNtot, ntotCurrentBuf,
1650  minEntry, fEntryMax);
1651  break;
1652  }
1653 
1654  //for the reverse reading case
1655  if (!fIsLearning && fReverseRead) {
1656  if (clusterIterations >= fFillTimes)
1657  break;
1658  if (minEntry >= fEntryCurrentMax && fEntryCurrentMax > 0)
1659  break;
1660  }
1661  fEntryNext = clusterIter.GetNextEntry();
1664  } while (kTRUE);
1665 
1666  if (showMore || gDebug > 6) {
1667  Info("FillBuffer", "Mem ranges");
1668  memRanges.Print();
1669  Info("FillBuffer", "Combined ranges");
1670  ranges.Print();
1671  Info("FillBuffer", "Requested ranges");
1672  reqRanges.Print();
1673  PrintAllCacheInfo(fBranches);
1674  }
1675 
1676  if (nReadPrefRequest == 0) {
1677  // Nothing was added in the cache. This usually indicates that the baskets
1678  // contains the requested entry are either already in memory or are too large
1679  // on their own to fit in the cache.
1680  if (showMore || gDebug > 5) {
1681  Info("FillBuffer", "For entry %lld, nothing was added to the cache.", entry);
1682  }
1683  } else if (fEntryNext < firstClusterEnd && !reqRanges.Contains(entry)) {
1684  // Something went very wrong and even-though we searched for the baskets
1685  // holding 'entry' we somehow ended up with a range of entries that does
1686  // validate. So we must have been unable to find or fit the needed basket.
1687  // And thus even-though, we know the corresponding baskets wont be in the cache,
1688  // Let's make it official that 'entry' is within the range of this TTreeCache ('s search.)
1689 
1690  // Without this, the next read will be flagged as 'out-of-range' and then we start at
1691  // the exact same point as this FillBuffer execution resulting in both the requested
1692  // entry still not being part of the cache **and** the beginning of the cluster being
1693  // read **again**.
1694 
1695  if (showMore || gDebug > 5) {
1696  Error("FillBuffer", "Reset the next entry because the currently loaded range does not contains the request "
1697  "entry: %lld. fEntryNext updated from %lld to %lld. %d",
1698  entry, fEntryNext, firstClusterEnd, nReadPrefRequest);
1699  reqRanges.Print();
1700  }
1701 
1702  fEntryNext = firstClusterEnd;
1703  } else {
1704  if (showMore || gDebug > 5) {
1705  Info("FillBuffer", "Complete adding %d baskets from %d branches taking in memory %d out of %d",
1706  nReadPrefRequest, reqRanges.BranchesRegistered(), ntotCurrentBuf, fBufferSizeMin);
1707  }
1708  }
1709 
1710  fNReadPref += nReadPrefRequest;
1711  if (fEnablePrefetching) {
1712  if (fIsLearning) {
1714  }
1715  if (!fIsLearning && fFirstTime){
1716  // First time we add autoFlush entries , after fFillTimes * autoFlush
1717  // only in reverse prefetching mode
1718  fFirstTime = kFALSE;
1719  }
1720  }
1721  fIsLearning = kFALSE;
1722  return kTRUE;
1723 }
1724 
1725 ////////////////////////////////////////////////////////////////////////////////
1726 /// Return the desired prefill type from the environment or resource variable
1727 /// - 0 - No prefill
1728 /// - 1 - All branches
1729 
1731 {
1732  const char *stcp;
1733  Int_t s = 0;
1734 
1735  if (!(stcp = gSystem->Getenv("ROOT_TTREECACHE_PREFILL")) || !*stcp) {
1736  s = gEnv->GetValue("TTreeCache.Prefill", 1);
1737  } else {
1738  s = TString(stcp).Atoi();
1739  }
1740 
1741  return static_cast<TTreeCache::EPrefillType>(s);
1742 }
1743 
1744 ////////////////////////////////////////////////////////////////////////////////
1745 /// Give the total efficiency of the primary cache... defined as the ratio
1746 /// of blocks found in the cache vs. the number of blocks prefetched
1747 /// ( it could be more than 1 if we read the same block from the cache more
1748 /// than once )
1749 ///
1750 /// Note: This should eb used at the end of the processing or we will
1751 /// get incomplete stats
1752 
1754 {
1755  if ( !fNReadPref )
1756  return 0;
1757 
1758  return ((Double_t)fNReadOk / (Double_t)fNReadPref);
1759 }
1760 
1761 ////////////////////////////////////////////////////////////////////////////////
1762 /// The total efficiency of the 'miss cache' - defined as the ratio
1763 /// of blocks found in the cache versus the number of blocks prefetched
1764 
1766 {
1767  if (!fNMissReadPref) {
1768  return 0;
1769  }
1770  return static_cast<double>(fNMissReadOk) / static_cast<double>(fNMissReadPref);
1771 }
1772 
1773 ////////////////////////////////////////////////////////////////////////////////
1774 /// This will indicate a sort of relative efficiency... a ratio of the
1775 /// reads found in the cache to the number of reads so far
1776 
1778 {
1779  if ( !fNReadOk && !fNReadMiss )
1780  return 0;
1781 
1782  return ((Double_t)fNReadOk / (Double_t)(fNReadOk + fNReadMiss));
1783 }
1784 
1785 ////////////////////////////////////////////////////////////////////////////////
1786 /// Relative efficiency of the 'miss cache' - ratio of the reads found in cache
1787 /// to the number of reads so far.
1788 
1790 {
1791  if (!fNMissReadOk && !fNMissReadMiss) {
1792  return 0;
1793  }
1794 
1795  return static_cast<double>(fNMissReadOk) / static_cast<double>(fNMissReadOk + fNMissReadMiss);
1796 }
1797 
1798 ////////////////////////////////////////////////////////////////////////////////
1799 /// Static function returning the number of entries used to train the cache
1800 /// see SetLearnEntries
1801 
1803 {
1804  return fgLearnEntries;
1805 }
1806 
1807 ////////////////////////////////////////////////////////////////////////////////
1808 /// Print cache statistics. Like:
1809 ///
1810 /// ~~~ {.cpp}
1811 /// ******TreeCache statistics for file: cms2.root ******
1812 /// Number of branches in the cache ...: 1093
1813 /// Cache Efficiency ..................: 0.997372
1814 /// Cache Efficiency Rel...............: 1.000000
1815 /// Learn entries......................: 100
1816 /// Reading............................: 72761843 bytes in 7 transactions
1817 /// Readahead..........................: 256000 bytes with overhead = 0 bytes
1818 /// Average transaction................: 10394.549000 Kbytes
1819 /// Number of blocks in current cache..: 210, total size: 6280352
1820 /// ~~~
1821 ///
1822 /// - if option = "a" the list of blocks in the cache is printed
1823 /// see also class TTreePerfStats.
1824 /// - if option contains 'cachedbranches', the list of branches being
1825 /// cached is printed.
1826 
1827 void TTreeCache::Print(Option_t *option) const
1828 {
1829  TString opt = option;
1830  opt.ToLower();
1831  printf("******TreeCache statistics for tree: %s in file: %s ******\n",fTree ? fTree->GetName() : "no tree set",fFile ? fFile->GetName() : "no file set");
1832  if (fNbranches <= 0) return;
1833  printf("Number of branches in the cache ...: %d\n",fNbranches);
1834  printf("Cache Efficiency ..................: %f\n",GetEfficiency());
1835  printf("Cache Efficiency Rel...............: %f\n",GetEfficiencyRel());
1836  printf("Secondary Efficiency ..............: %f\n", GetMissEfficiency());
1837  printf("Secondary Efficiency Rel ..........: %f\n", GetMissEfficiencyRel());
1838  printf("Learn entries......................: %d\n",TTreeCache::GetLearnEntries());
1839  if ( opt.Contains("cachedbranches") ) {
1840  opt.ReplaceAll("cachedbranches","");
1841  printf("Cached branches....................:\n");
1842  const TObjArray *cachedBranches = this->GetCachedBranches();
1843  Int_t nbranches = cachedBranches->GetEntriesFast();
1844  for (Int_t i = 0; i < nbranches; ++i) {
1845  TBranch* branch = (TBranch*) cachedBranches->UncheckedAt(i);
1846  printf("Branch name........................: %s\n",branch->GetName());
1847  }
1848  }
1849  TFileCacheRead::Print(opt);
1850 }
1851 
1852 ////////////////////////////////////////////////////////////////////////////////
1853 /// Old method ReadBuffer before the addition of the prefetch mechanism.
1854 
1856  //Is request already in the cache?
1857  if (TFileCacheRead::ReadBuffer(buf,pos,len) == 1){
1858  fNReadOk++;
1859  return 1;
1860  }
1861 
1862  static const auto recordMiss = [](TVirtualPerfStats *perfStats, TObjArray *branches, Bool_t bufferFilled,
1863  Long64_t basketpos) {
1864  if (gDebug > 6)
1865  ::Info("TTreeCache::ReadBufferNormal", "Cache miss after an %s FillBuffer: pos=%lld",
1866  bufferFilled ? "active" : "inactive", basketpos);
1867  for (Int_t i = 0; i < branches->GetEntries(); ++i) {
1868  TBranch *b = (TBranch *)branches->UncheckedAt(i);
1869  Int_t blistsize = b->GetListOfBaskets()->GetSize();
1870  for (Int_t j = 0; j < blistsize; ++j) {
1871  if (basketpos == b->GetBasketSeek(j)) {
1872  if (gDebug > 6)
1873  ::Info("TTreeCache::ReadBufferNormal", " Missing basket: %d for %s", j, b->GetName());
1874  perfStats->SetMissed(i, j);
1875  }
1876  }
1877  }
1878  };
1879 
1880  //not found in cache. Do we need to fill the cache?
1881  Bool_t bufferFilled = FillBuffer();
1882  if (bufferFilled) {
1883  Int_t res = TFileCacheRead::ReadBuffer(buf,pos,len);
1884 
1885  if (res == 1)
1886  fNReadOk++;
1887  else if (res == 0) {
1888  fNReadMiss++;
1889  auto perfStats = GetTree()->GetPerfStats();
1890  if (perfStats)
1891  recordMiss(perfStats, fBranches, bufferFilled, pos);
1892  }
1893 
1894  return res;
1895  }
1896 
1897  if (CheckMissCache(buf, pos, len)) {
1898  return 1;
1899  }
1900 
1901  fNReadMiss++;
1902  auto perfStats = GetTree()->GetPerfStats();
1903  if (perfStats)
1904  recordMiss(perfStats, fBranches, bufferFilled, pos);
1905 
1906  return 0;
1907 }
1908 
1909 ////////////////////////////////////////////////////////////////////////////////
1910 /// Used to read a chunk from a block previously fetched. It will call FillBuffer
1911 /// even if the cache lookup succeeds, because it will try to prefetch the next block
1912 /// as soon as we start reading from the current block.
1913 
1915 {
1916  if (TFileCacheRead::ReadBuffer(buf, pos, len) == 1){
1917  //call FillBuffer to prefetch next block if necessary
1918  //(if we are currently reading from the last block available)
1919  FillBuffer();
1920  fNReadOk++;
1921  return 1;
1922  }
1923 
1924  //keep on prefetching until request is satisfied
1925  // try to prefetch a couple of times and if request is still not satisfied then
1926  // fall back to normal reading without prefetching for the current request
1927  Int_t counter = 0;
1928  while (1) {
1929  if(TFileCacheRead::ReadBuffer(buf, pos, len)) {
1930  break;
1931  }
1932  FillBuffer();
1933  fNReadMiss++;
1934  counter++;
1935  if (counter>1) {
1936  return 0;
1937  }
1938  }
1939 
1940  fNReadOk++;
1941  return 1;
1942 }
1943 
1944 ////////////////////////////////////////////////////////////////////////////////
1945 /// Read buffer at position pos if the request is in the list of
1946 /// prefetched blocks read from fBuffer.
1947 /// Otherwise try to fill the cache from the list of selected branches,
1948 /// and recheck if pos is now in the list.
1949 /// Returns:
1950 /// - -1 in case of read failure,
1951 /// - 0 in case not in cache,
1952 /// - 1 in case read from cache.
1953 /// This function overloads TFileCacheRead::ReadBuffer.
1954 
1956 {
1957  if (!fEnabled) return 0;
1958 
1959  if (fEnablePrefetching)
1960  return TTreeCache::ReadBufferPrefetch(buf, pos, len);
1961  else
1962  return TTreeCache::ReadBufferNormal(buf, pos, len);
1963 }
1964 
1965 ////////////////////////////////////////////////////////////////////////////////
1966 /// This will simply clear the cache
1967 
1969 {
1971 
1972  if (fEnablePrefetching) {
1973  fFirstTime = kTRUE;
1975  }
1976 }
1977 
1978 ////////////////////////////////////////////////////////////////////////////////
1979 /// Change the underlying buffer size of the cache.
1980 /// If the change of size means some cache content is lost, or if the buffer
1981 /// is now larger, setup for a cache refill the next time there is a read
1982 /// Returns:
1983 /// - 0 if the buffer content is still available
1984 /// - 1 if some or all of the buffer content has been made unavailable
1985 /// - -1 on error
1986 
1988 {
1989  Int_t prevsize = GetBufferSize();
1990  Int_t res = TFileCacheRead::SetBufferSize(buffersize);
1991  if (res < 0) {
1992  return res;
1993  }
1994 
1995  if (res == 0 && buffersize <= prevsize) {
1996  return res;
1997  }
1998 
1999  // if content was removed from the buffer, or the buffer was enlarged then
2000  // empty the prefetch lists and prime to fill the cache again
2001 
2003  if (fEnablePrefetching) {
2005  }
2006 
2007  fEntryCurrent = -1;
2008  if (!fIsLearning) {
2009  fEntryNext = -1;
2010  }
2011 
2012  return 1;
2013 }
2014 
2015 ////////////////////////////////////////////////////////////////////////////////
2016 /// Set the minimum and maximum entry number to be processed
2017 /// this information helps to optimize the number of baskets to read
2018 /// when prefetching the branch buffers.
2019 
2021 {
2022  // This is called by TTreePlayer::Process in an automatic way...
2023  // don't restart it if the user has specified the branches.
2024  Bool_t needLearningStart = (fEntryMin != emin) && fIsLearning && !fIsManual;
2025 
2026  fEntryMin = emin;
2027  fEntryMax = emax;
2029  if (gDebug > 0)
2030  Info("SetEntryRange", "fEntryMin=%lld, fEntryMax=%lld, fEntryNext=%lld",
2032 
2033  if (needLearningStart) {
2034  // Restart learning
2036  }
2037 }
2038 
2039 ////////////////////////////////////////////////////////////////////////////////
2040 /// Overload to make sure that the object specific
2041 
2043 {
2044  // The infinite recursion is 'broken' by the fact that
2045  // TFile::SetCacheRead remove the entry from fCacheReadMap _before_
2046  // calling SetFile (and also by setting fFile to zero before the calling).
2047  if (fFile) {
2048  TFile *prevFile = fFile;
2049  fFile = 0;
2050  prevFile->SetCacheRead(0, fTree, action);
2051  }
2052  TFileCacheRead::SetFile(file, action);
2053 }
2054 
2055 ////////////////////////////////////////////////////////////////////////////////
2056 /// Static function to set the number of entries to be used in learning mode
2057 /// The default value for n is 10. n must be >= 1
2058 
2060 {
2061  if (n < 1) n = 1;
2062  fgLearnEntries = n;
2063 }
2064 
2065 ////////////////////////////////////////////////////////////////////////////////
2066 /// Set whether the learning period is started with a prefilling of the
2067 /// cache and which type of prefilling is used.
2068 /// The two value currently supported are:
2069 /// - TTreeCache::kNoPrefill disable the prefilling
2070 /// - TTreeCache::kAllBranches fill the cache with baskets from all branches.
2071 /// The default prefilling behavior can be controlled by setting
2072 /// TTreeCache.Prefill or the environment variable ROOT_TTREECACHE_PREFILL.
2073 
2075 {
2076  fPrefillType = type;
2077 }
2078 
2079 ////////////////////////////////////////////////////////////////////////////////
2080 /// The name should be enough to explain the method.
2081 /// The only additional comments is that the cache is cleaned before
2082 /// the new learning phase.
2083 
2085 {
2086  fIsLearning = kTRUE;
2087  fIsManual = kFALSE;
2088  fNbranches = 0;
2089  if (fBrNames) fBrNames->Delete();
2091  fEntryCurrent = -1;
2092 }
2093 
2094 ////////////////////////////////////////////////////////////////////////////////
2095 /// This is the counterpart of StartLearningPhase() and can be used to stop
2096 /// the learning phase. It's useful when the user knows exactly what branches
2097 /// they are going to use.
2098 /// For the moment it's just a call to FillBuffer() since that method
2099 /// will create the buffer lists from the specified branches.
2100 
2102 {
2103  if (fIsLearning) {
2104  // This will force FillBuffer to read the buffers.
2105  fEntryNext = -1;
2106  fIsLearning = kFALSE;
2107  }
2108  fIsManual = kTRUE;
2109 
2110  auto perfStats = GetTree()->GetPerfStats();
2111  if (perfStats)
2112  perfStats->UpdateBranchIndices(fBranches);
2113 
2114  //fill the buffers only once during learning
2115  if (fEnablePrefetching && !fOneTime) {
2116  fIsLearning = kTRUE;
2117  FillBuffer();
2118  fOneTime = kTRUE;
2119  }
2120 }
2121 
2122 ////////////////////////////////////////////////////////////////////////////////
2123 /// Update pointer to current Tree and recompute pointers to the branches in the cache.
2124 
2126 {
2127 
2128  fTree = tree;
2129 
2130  fEntryMin = 0;
2131  fEntryMax = fTree->GetEntries();
2132 
2133  fEntryCurrent = -1;
2134 
2135  if (fBrNames->GetEntries() == 0 && fIsLearning) {
2136  // We still need to learn.
2138  } else {
2139  // We learnt from a previous file.
2140  fIsLearning = kFALSE;
2141  fEntryNext = -1;
2142  }
2143  fNbranches = 0;
2144 
2145  TIter next(fBrNames);
2146  TObjString *os;
2147  while ((os = (TObjString*)next())) {
2148  TBranch *b = fTree->GetBranch(os->GetName());
2149  if (!b) {
2150  continue;
2151  }
2152  fBranches->AddAt(b, fNbranches);
2153  fNbranches++;
2154  }
2155 
2156  auto perfStats = GetTree()->GetPerfStats();
2157  if (perfStats)
2158  perfStats->UpdateBranchIndices(fBranches);
2159 }
2160 
2161 ////////////////////////////////////////////////////////////////////////////////
2162 /// Perform an initial prefetch, attempting to read as much of the learning
2163 /// phase baskets for all branches at once
2164 
2166 {
2167  // This is meant for the learning phase
2168  if (!fIsLearning) return;
2169 
2170  // This should be called before reading entries, otherwise we'll
2171  // always exit here, since TBranch adds itself before reading
2172  if (fNbranches > 0) return;
2173 
2174  // Is the LearnPrefill enabled (using an Int_t here to allow for future
2175  // extension to alternative Prefilling).
2176  if (fPrefillType == kNoPrefill) return;
2177 
2178  // Force only the learn entries to be cached by temporarily setting min/max
2179  // to the learning phase entry range
2180  // But save all the old values, so we can restore everything to how it was
2181  Long64_t eminOld = fEntryMin;
2182  Long64_t emaxOld = fEntryMax;
2183  Long64_t ecurrentOld = fEntryCurrent;
2184  Long64_t enextOld = fEntryNext;
2185  auto currentClusterStartOld = fCurrentClusterStart;
2186  auto nextClusterStartOld = fNextClusterStart;
2187 
2190 
2191  // Add all branches to be cached. This also sets fIsManual, stops learning,
2192  // and makes fEntryNext = -1 (which forces a cache fill, which is good)
2193  AddBranch("*");
2194  fIsManual = kFALSE; // AddBranch sets fIsManual, so we reset it
2195 
2196  // Now, fill the buffer with the learning phase entry range
2197  FillBuffer();
2198 
2199  // Leave everything the way we found it
2200  fIsLearning = kTRUE;
2201  DropBranch("*"); // This doesn't work unless we're already learning
2202 
2203  // Restore entry values
2204  fEntryMin = eminOld;
2205  fEntryMax = emaxOld;
2206  fEntryCurrent = ecurrentOld;
2207  fEntryNext = enextOld;
2208  fCurrentClusterStart = currentClusterStartOld;
2209  fNextClusterStart = nextClusterStartOld;
2210 }
Bool_t CheckMissCache(char *buf, Long64_t pos, int len)
Check the miss cache for a particular buffer, fetching if deemed necessary.
Definition: TTreeCache.cxx:862
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
Int_t fNtot
Total size of prefetched blocks.
A TLeaf describes individual elements of a TBranch See TBranch structure in TTree.
Definition: TLeaf.h:32
Long64_t fEntryMax
! last entry in the cache
Definition: TTreeCache.h:42
An array of TObjects.
Definition: TObjArray.h:37
Long64_t * GetBasketEntry() const
Definition: TBranch.h:172
Double_t GetMissEfficiencyRel() const
Relative efficiency of the &#39;miss cache&#39; - ratio of the reads found in cache to the number of reads so...
Long64_t GetNextEntry()
Definition: TTree.h:273
virtual void Delete(Option_t *option="")
Remove all objects from the list AND delete all heap based objects.
Definition: TList.cxx:467
Int_t fNReadOk
Number of blocks read and found in the cache.
Definition: TTreeCache.h:48
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:854
TFile * fFile
Pointer to file.
long long Long64_t
Definition: RtypesCore.h:69
virtual void SetMissed(TBranch *b, size_t basketNumber)=0
const TObjArray * GetCachedBranches() const
Definition: TTreeCache.h:140
#define R__unlikely(expr)
Definition: RConfig.h:578
TObjArray * GetListOfBaskets()
Definition: TBranch.h:200
Collectable string class.
Definition: TObjString.h:28
Provides the interface for the PROOF internal performance measurement and event tracing.
TObjArray * fBranches
! List of branches to be stored in the cache
Definition: TTreeCache.h:54
A cache when reading files over the network.
const char Option_t
Definition: RtypesCore.h:62
Long64_t fLastMiss
! set to the event # of the last miss.
Definition: TTreeCache.h:75
Bool_t fOneTime
! used in the learning phase
Definition: TTreeCache.h:60
Int_t fNMissReadMiss
Number of blocks read and not found in either cache.
Definition: TTreeCache.h:51
virtual void SetCacheRead(TFileCacheRead *cache, TObject *tree=0, ECacheAction action=kDisconnect)
Set a pointer to the read cache.
Definition: TFile.cxx:2265
const Ssiz_t kNPOS
Definition: RtypesCore.h:111
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition: TString.h:687
virtual void SetLearnPrefill(EPrefillType type=kNoPrefill)
Set whether the learning period is started with a prefilling of the cache and which type of prefillin...
A specialized TFileCacheRead object for a TTree.
Definition: TTreeCache.h:35
A ROOT file is a suite of consecutive data records (TKey instances) with a well defined format...
Definition: TFile.h:47
virtual Int_t GetEntries() const
Definition: TCollection.h:177
virtual TList * GetListOfFriends() const
Definition: TTree.h:411
virtual Long64_t GetAutoFlush() const
Definition: TTree.h:368
Regular expression class.
Definition: TRegexp.h:31
TDirectory * GetDirectory() const
Definition: TBranch.h:180
Bool_t IsInCache(Int_t basketNumber) const
virtual TObject * Remove(TObject *obj)
Remove object from array.
Definition: TObjArray.cxx:703
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition: TString.h:634
Long64_t fEntryMin
! first entry in the cache
Definition: TTreeCache.h:41
Basic string class.
Definition: TString.h:131
void ToLower()
Change string to lower-case.
Definition: TString.cxx:1100
virtual void Prefetch(Long64_t pos, Int_t len)
Add block of length len at position pos in the list of blocks to be prefetched.
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
virtual void StopLearningPhase()
This is the counterpart of StartLearningPhase() and can be used to stop the learning phase...
virtual void LearnPrefill()
Perform an initial prefetch, attempting to read as much of the learning phase baskets for all branche...
void StartLearningPhase()
The name should be enough to explain the method.
virtual Int_t AddBranch(TBranch *b, Bool_t subgbranches=kFALSE)
Add a branch to the list of branches to be stored in the cache this function is called by TBranch::Ge...
Definition: TTreeCache.cxx:307
Long64_t * GetTreeOffset() const
Definition: TChain.h:117
Bool_t IsVetoed(Int_t basketNumber) const
virtual TObject * FindObject(const char *name) const
Delete a TObjLink object.
Definition: TList.cxx:574
Int_t fNMissReadPref
Number of blocks read into the secondary ("miss") cache.
Definition: TTreeCache.h:53
Long64_t fNextClusterStart
! End+1 of the cluster(s) where the current content was picked out
Definition: TTreeCache.h:46
virtual Int_t ReadBufferNormal(char *buf, Long64_t pos, Int_t len)
Old method ReadBuffer before the addition of the prefetch mechanism.
IOPos FindBranchBasketPos(TBranch &, Long64_t entry)
Given a branch and an entry, determine the file location (offset / size) of the corresponding basket...
Definition: TTreeCache.cxx:657
Helper class to iterate over cluster of baskets.
Definition: TTree.h:235
Int_t * GetBasketBytes() const
Definition: TBranch.h:171
void Veto(Int_t basketNumber)
virtual void SecondPrefetch(Long64_t, Int_t)
virtual void Print(Option_t *option="") const
Print cache statistics.
void Class()
Definition: Class.C:29
Bool_t fOptimizeMisses
! true if we should optimize cache misses.
Definition: TTreeCache.h:73
virtual Long64_t GetReadEntry() const
Definition: TTree.h:430
Bool_t fIsTransferred
True when fBuffer contains something valid.
virtual Bool_t ContainsRange(Long64_t entrymin, Long64_t entrymax)
Return TRUE if list contains entries from entrymin to entrymax included.
Definition: TEventList.cxx:171
virtual TVirtualPerfStats * GetPerfStats() const
Definition: TTree.h:427
virtual const char * Getenv(const char *env)
Get environment variable.
Definition: TSystem.cxx:1638
virtual TClusterIterator GetClusterIterator(Long64_t firstentry)
Return an iterator over the cluster of baskets starting at firstentry.
Definition: TTree.cxx:5193
TString & Append(const char *cs)
Definition: TString.h:559
virtual Int_t ReadBuffer(char *buf, Long64_t pos, Int_t len)
Read buffer at position pos if the request is in the list of prefetched blocks read from fBuffer...
virtual void ResetCache()
This will simply clear the cache.
Bool_t fEnabled
! cache enabled for cached reading
Definition: TTreeCache.h:66
TBranch * CalculateMissEntries(Long64_t, int, bool)
Given an file read, try to determine the corresponding branch.
Definition: TTreeCache.cxx:731
TObjArray * GetListOfBranches()
Definition: TBranch.h:201
virtual TFile * GetFile() const
Definition: TDirectory.h:147
Double_t GetEfficiencyRel() const
This will indicate a sort of relative efficiency...
virtual TTree * GetTree() const
Definition: TTree.h:438
A doubly linked list.
Definition: TList.h:44
virtual TBranch * GetBranch(const char *name)
Return pointer to the branch with the given name in this tree or its friends.
Definition: TTree.cxx:5017
TTree * fTree
! pointer to the current Tree
Definition: TTreeCache.h:56
void PrintCacheInfo() const
Print the information we have about which basket is currently cached and whether they have been &#39;used...
Definition: TBranch.cxx:2021
const char * GetName() const
Returns name of object.
Definition: TObjString.h:39
EPrefillType GetConfiguredPrefillType() const
Return the desired prefill type from the environment or resource variable.
virtual void SetEntryRange(Long64_t emin, Long64_t emax)
Set the minimum and maximum entry number to be processed this information helps to optimize the numbe...
virtual void AddAtAndExpand(TObject *obj, Int_t idx)
Add object at position idx.
Definition: TObjArray.cxx:234
ROOT::R::TRInterface & r
Definition: Object.C:4
virtual Int_t DropBranch(TBranch *b, Bool_t subbranches=kFALSE)
Remove a branch to the list of branches to be stored in the cache this function is called by TBranch:...
Definition: TTreeCache.cxx:482
CacheInfo_t fCacheInfo
! Hold info about which basket are in the cache and if they have been retrieved from the cache...
Definition: TBranch.h:122
R__EXTERN TSystem * gSystem
Definition: TSystem.h:540
SVector< double, 2 > v
Definition: Dict.h:5
TTree * GetTree() const
Definition: TTreeCache.h:150
virtual TObject * Remove(TObject *obj)
Remove object from the list.
Definition: TList.cxx:818
virtual Int_t ReadBufferPrefetch(char *buf, Long64_t pos, Int_t len)
Used to read a chunk from a block previously fetched.
Int_t GetWriteBasket() const
Definition: TBranch.h:193
virtual ~TTreeCache()
Destructor. (in general called by the TFile destructor)
Definition: TTreeCache.cxx:290
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2264
unsigned int UInt_t
Definition: RtypesCore.h:42
Int_t GetEntriesFast() const
Definition: TObjArray.h:64
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:880
Ssiz_t Length() const
Definition: TString.h:405
virtual TLeaf * GetLeafCount() const
If this leaf stores a variable-sized array or a multi-dimensional array whose last dimension has vari...
Definition: TLeaf.h:74
virtual void SetFile(TFile *file, TFile::ECacheAction action=TFile::kDisconnect)
Set the file using this cache and reset the current blocks (if any).
Int_t GetMaxBaskets() const
Definition: TBranch.h:203
A TEventList object is a list of selected events (entries) in a TTree.
Definition: TEventList.h:31
Bool_t fReadDirectionSet
! read direction established
Definition: TTreeCache.h:65
Int_t fNReadMiss
Number of blocks read and not found in the cache.
Definition: TTreeCache.h:50
static void SetLearnEntries(Int_t n=10)
Static function to set the number of entries to be used in learning mode The default value for n is 1...
std::unique_ptr< MissCache > fMissCache
! Cache contents for misses
Definition: TTreeCache.h:106
Long64_t fCurrentClusterStart
! Start of the cluster(s) where the current content was picked out
Definition: TTreeCache.h:45
virtual Int_t SetBufferSize(Int_t buffersize)
Sets the buffer size.
virtual Bool_t FillBuffer()
Fill the cache buffer with the branches in the cache.
Bool_t HasBeenUsed(Int_t basketNumber) const
virtual void AddAt(TObject *obj, Int_t idx)
Add object at position ids.
Definition: TObjArray.cxx:253
void SetIsInCache(Int_t basketNumber)
Int_t fNReadPref
Number of blocks that were prefetched.
Definition: TTreeCache.h:52
A Branch for the case of an object.
virtual Long64_t GetBasketSeek(Int_t basket) const
Return address of basket in the file.
Definition: TBranch.cxx:1253
#define Printf
Definition: TGeoToOCC.h:18
const Bool_t kFALSE
Definition: RtypesCore.h:88
void SetUsed(Int_t basketNumber)
void SetOptimizeMisses(Bool_t opt)
Start of methods for the miss cache.
Definition: TTreeCache.cxx:624
Bool_t fReverseRead
! reading in reverse mode
Definition: TTreeCache.h:61
virtual void UpdateBranchIndices(TObjArray *branches)=0
Bool_t fFirstTime
! save the fact that we processes the first entry
Definition: TTreeCache.h:63
Long64_t fEntryCurrent
! current lowest entry number in the cache
Definition: TTreeCache.h:43
Int_t fBufferSizeMin
Original size of fBuffer.
Long64_t Next()
Move on to the next cluster and return the starting entry of this next cluster.
Definition: TTree.cxx:614
TObject * UncheckedAt(Int_t i) const
Definition: TObjArray.h:89
TTreeCache()
Default Constructor.
Definition: TTreeCache.cxx:271
virtual void Print(Option_t *option="") const
Print cache statistics.
#define ClassImp(name)
Definition: Rtypes.h:359
double Double_t
Definition: RtypesCore.h:55
Bool_t fIsLearning
! true if cache is in learning mode
Definition: TTreeCache.h:57
virtual Int_t GetTreeNumber() const
Definition: TChain.h:116
int type
Definition: TGX11.cxx:120
R__EXTERN TEnv * gEnv
Definition: TEnv.h:171
virtual Int_t GetBufferSize() const
unsigned long long ULong64_t
Definition: RtypesCore.h:70
void ResetMissCache()
Reset all the miss cache training.
Definition: TTreeCache.cxx:638
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition: TString.h:619
virtual void SetFile(TFile *file, TFile::ECacheAction action=TFile::kDisconnect)
Overload to make sure that the object specific.
static constexpr double s
Int_t fNMissReadOk
Number of blocks read, not found in the primary cache, and found in the secondary cache...
Definition: TTreeCache.h:49
EPrefillType fPrefillType
Whether a pre-filling is enabled (and if applicable which type)
Definition: TTreeCache.h:67
Long64_t fFirstMiss
! set to the event # of the first miss.
Definition: TTreeCache.h:74
Int_t fFillTimes
! how many times we can fill the current buffer
Definition: TTreeCache.h:62
virtual Int_t SetBufferSize(Int_t buffersize)
Change the underlying buffer size of the cache.
virtual Long64_t GetEntries() const
Definition: TTree.h:384
Int_t fNbranches
! Number of branches in the cache
Definition: TTreeCache.h:47
Bool_t fEnablePrefetching
reading by prefetching asynchronously
Bool_t ProcessMiss(Long64_t pos, int len)
! Given a file read not in the miss cache, handle (possibly) loading the data.
Definition: TTreeCache.cxx:804
Ta Range(0, 0, 1, 1)
virtual void Add(TObject *obj)
Definition: TList.h:87
Long64_t fFirstEntry
! save the value of the first entry
Definition: TTreeCache.h:64
Definition: file.py:1
virtual Bool_t ReadBuffers(char *buf, Long64_t *pos, Int_t *len, Int_t nbuf)
Read the nbuf blocks described in arrays pos and len.
Definition: TFile.cxx:1721
A TFriendElement TF describes a TTree object TF in a file.
A chain is a collection of files containing TTree objects.
Definition: TChain.h:33
TList * fBrNames
! list of branch names in the cache
Definition: TTreeCache.h:55
Long64_t GetReadEntry() const
Definition: TBranch.h:192
you should not use this method at all Int_t Int_t Double_t Double_t Double_t Int_t Double_t Double_t Double_t Double_t b
Definition: TRolke.cxx:630
Bool_t fFirstBuffer
! true if first buffer is used for prefetching
Definition: TTreeCache.h:59
static Int_t fgLearnEntries
number of entries used for learning mode
Definition: TTreeCache.h:68
TTree * GetTree() const
Definition: TBranch.h:207
Double_t GetMissEfficiency() const
The total efficiency of the &#39;miss cache&#39; - defined as the ratio of blocks found in the cache versus t...
Int_t GetEntries() const
Return the number of objects in array (i.e.
Definition: TObjArray.cxx:522
R__EXTERN Int_t gDebug
Definition: Rtypes.h:86
void GetUnused(std::vector< Int_t > &unused)
Int_t Atoi() const
Return integer value of string.
Definition: TString.cxx:1896
virtual Int_t ReadBuffer(char *buf, Long64_t pos, Int_t len)
Read buffer at position pos.
Definition: tree.py:1
A TTree object has a header with a name and a title.
Definition: TTree.h:70
TEventList * GetEventList() const
Definition: TTree.h:394
TBranch * GetBranch() const
Definition: TLeaf.h:71
Bool_t fIsManual
! true if cache is StopLearningPhase was used
Definition: TTreeCache.h:58
virtual const char * GetName() const
Returns name of object.
Definition: TObject.cxx:357
virtual Int_t GetSize() const
Return the capacity of the collection, i.e.
Definition: TCollection.h:182
A TTree is a list of TBranches.
Definition: TBranch.h:62
Long64_t fEntryNext
! next entry number where cache must be filled
Definition: TTreeCache.h:44
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition: TEnv.cxx:491
virtual void UpdateBranches(TTree *tree)
Update pointer to current Tree and recompute pointers to the branches in the cache.
const Bool_t kTRUE
Definition: RtypesCore.h:87
const Int_t n
Definition: legend1.C:16
Long64_t BinarySearch(Long64_t n, const T *array, T value)
Binary search in an array of n values to locate value.
Definition: TMath.h:1221
Double_t GetEfficiency() const
Give the total efficiency of the primary cache...
TBranch * GetMother() const
Get our top-level parent branch in the tree.
Definition: TBranch.cxx:1718
char name[80]
Definition: TGX11.cxx:109
static Int_t GetLearnEntries()
Static function returning the number of entries used to train the cache see SetLearnEntries.
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition: TObject.cxx:866
Int_t fNseek
Number of blocks to be prefetched.
virtual TObjArray * GetListOfLeaves()
Definition: TTree.h:410
const char * Data() const
Definition: TString.h:364
ECacheAction
TTreeCache flushing semantics.
Definition: TFile.h:64