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