298 Int_t TTreeCache::fgLearnEntries = 100;
300 ClassImp(TTreeCache);
305 TTreeCache::TTreeCache() : TFileCacheRead(), fPrefillType(GetConfiguredPrefillType())
312 TTreeCache::TTreeCache(TTree *tree, Int_t buffersize)
313 : TFileCacheRead(tree->GetCurrentFile(), buffersize, tree), fEntryMax(tree->GetEntriesFast()), fEntryNext(0),
314 fBrNames(new TList), fTree(tree), fPrefillType(GetConfiguredPrefillType())
316 fEntryNext = fEntryMin + fgLearnEntries;
317 Int_t nleaves = tree->GetListOfLeaves()->GetEntries();
318 fBranches =
new TObjArray(nleaves);
324 TTreeCache::~TTreeCache()
328 if (fFile) fFile->SetCacheRead(0, fTree);
331 if (fBrNames) {fBrNames->Delete();
delete fBrNames; fBrNames=0;}
342 Int_t TTreeCache::LearnBranch(TBranch *b, Bool_t subbranches )
349 if (!b || fTree->GetTree() != b->GetTree())
return -1;
355 if (!fLearnPrefilling && fNbranches == 0) LearnPrefill();
357 return AddBranch(b, subbranches);
368 Int_t TTreeCache::AddBranch(TBranch *b, Bool_t subbranches )
371 if (!b || fTree->GetTree() != b->GetTree())
return -1;
374 Bool_t isNew = kTRUE;
375 for (
int i=0;i<fNbranches;i++) {
376 if (fBranches->UncheckedAt(i) == b) {isNew = kFALSE;
break;}
379 fTree = b->GetTree();
380 fBranches->AddAtAndExpand(b, fNbranches);
381 const char *bname = b->GetName();
382 if (fTree->IsA() == TChain::Class()) {
389 const char *mothername = b->GetMother()->GetName();
390 if (b != b->GetMother() && mothername[strlen(mothername)-1] !=
'.') {
392 auto bem =
dynamic_cast<TBranchElement*
>(b->GetMother());
393 if (bem->GetType() < 3) {
397 if (strncmp(bname,build.Data(),build.Length()) != 0) {
399 bname = build.Data();
404 fBrNames->Add(
new TObjString(bname));
406 if (gDebug > 0) printf(
"Entry: %lld, registering branch: %s\n",b->GetTree()->GetReadEntry(),b->GetName());
412 TObjArray *lb = b->GetListOfBranches();
413 Int_t nb = lb->GetEntriesFast();
414 for (Int_t j = 0; j < nb; j++) {
415 TBranch* branch = (TBranch*) lb->UncheckedAt(j);
416 if (!branch)
continue;
417 if (AddBranch(branch, subbranches)<0) {
438 Int_t TTreeCache::AddBranch(
const char *bname, Bool_t subbranches )
440 TBranch *branch, *bcount;
441 TLeaf *leaf, *leafcount;
444 Int_t nleaves = (fTree->GetListOfLeaves())->GetEntriesFast();
445 TRegexp re(bname,kTRUE);
452 if (!strcmp(bname,
"*")) all = kTRUE;
453 for (i=0;i<nleaves;i++) {
454 leaf = (TLeaf*)(fTree->GetListOfLeaves())->UncheckedAt(i);
455 branch = (TBranch*)leaf->GetBranch();
456 TString s = branch->GetName();
459 longname.Form(
"%s.%s",fTree->GetName(),branch->GetName());
460 if (strcmp(bname,branch->GetName())
462 && s.Index(re) == kNPOS)
continue;
465 if (AddBranch(branch, subbranches)<0) {
468 leafcount = leaf->GetLeafCount();
469 if (leafcount && !all) {
470 bcount = leafcount->GetBranch();
471 if (AddBranch(bcount, subbranches)<0) {
476 if (nb==0 && strchr(bname,
'*')==0) {
477 branch = fTree->GetBranch(bname);
479 if (AddBranch(branch, subbranches)<0) {
487 UInt_t foundInFriend = 0;
488 if (fTree->GetListOfFriends()) {
489 TIter nextf(fTree->GetListOfFriends());
492 while ((fe = (TFriendElement*)nextf())) {
493 TTree *t = fe->GetTree();
497 char *subbranch = (
char*)strstr(bname,fe->GetName());
498 if (subbranch!=bname) subbranch = 0;
500 subbranch += strlen(fe->GetName());
501 if ( *subbranch !=
'.' ) subbranch = 0;
505 name.Form(
"%s.%s",t->GetName(),subbranch);
506 if (name != bname && AddBranch(name, subbranches)<0) {
513 if (!nb && !foundInFriend) {
514 if (gDebug > 0) printf(
"AddBranch: unknown branch -> %s \n", bname);
515 Error(
"AddBranch",
"unknown branch -> %s", bname);
533 Int_t TTreeCache::DropBranch(TBranch *b, Bool_t subbranches )
540 if (!b || fTree->GetTree() != b->GetTree())
return -1;
543 if (fBranches->Remove(b)) {
545 if (gDebug > 0) printf(
"Entry: %lld, un-registering branch: %s\n",b->GetTree()->GetReadEntry(),b->GetName());
547 delete fBrNames->Remove(fBrNames->FindObject(b->GetName()));
552 TObjArray *lb = b->GetListOfBranches();
553 Int_t nb = lb->GetEntriesFast();
554 for (Int_t j = 0; j < nb; j++) {
555 TBranch* branch = (TBranch*) lb->UncheckedAt(j);
556 if (!branch)
continue;
557 if (DropBranch(branch, subbranches)<0) {
578 Int_t TTreeCache::DropBranch(
const char *bname, Bool_t subbranches )
580 TBranch *branch, *bcount;
581 TLeaf *leaf, *leafcount;
584 Int_t nleaves = (fTree->GetListOfLeaves())->GetEntriesFast();
585 TRegexp re(bname,kTRUE);
592 if (!strcmp(bname,
"*")) all = kTRUE;
593 for (i=0;i<nleaves;i++) {
594 leaf = (TLeaf*)(fTree->GetListOfLeaves())->UncheckedAt(i);
595 branch = (TBranch*)leaf->GetBranch();
596 TString s = branch->GetName();
599 longname.Form(
"%s.%s",fTree->GetName(),branch->GetName());
600 if (strcmp(bname,branch->GetName())
602 && s.Index(re) == kNPOS)
continue;
605 if (DropBranch(branch, subbranches)<0) {
608 leafcount = leaf->GetLeafCount();
609 if (leafcount && !all) {
610 bcount = leafcount->GetBranch();
611 if (DropBranch(bcount, subbranches)<0) {
616 if (nb==0 && strchr(bname,
'*')==0) {
617 branch = fTree->GetBranch(bname);
619 if (DropBranch(branch, subbranches)<0) {
627 UInt_t foundInFriend = 0;
628 if (fTree->GetListOfFriends()) {
629 TIter nextf(fTree->GetListOfFriends());
632 while ((fe = (TFriendElement*)nextf())) {
633 TTree *t = fe->GetTree();
637 char *subbranch = (
char*)strstr(bname,fe->GetName());
638 if (subbranch!=bname) subbranch = 0;
640 subbranch += strlen(fe->GetName());
641 if ( *subbranch !=
'.' ) subbranch = 0;
645 name.Form(
"%s.%s",t->GetName(),subbranch);
646 if (DropBranch(name, subbranches)<0) {
653 if (!nb && !foundInFriend) {
654 if (gDebug > 0) printf(
"DropBranch: unknown branch -> %s \n", bname);
655 Error(
"DropBranch",
"unknown branch -> %s", bname);
675 void TTreeCache::SetOptimizeMisses(Bool_t opt)
678 if (opt && !fMissCache) {
681 fOptimizeMisses = opt;
689 void TTreeCache::ResetMissCache()
696 fMissCache.reset(
new MissCache());
708 TTreeCache::IOPos TTreeCache::FindBranchBasketPos(TBranch &b, Long64_t entry)
710 if (R__unlikely(b.GetDirectory() == 0)) {
714 if (R__unlikely(b.GetDirectory()->GetFile() != fFile)) {
722 Int_t *lbaskets = b.GetBasketBytes();
723 Long64_t *entries = b.GetBasketEntry();
724 if (R__unlikely(!lbaskets || !entries)) {
729 Int_t blistsize = b.GetWriteBasket();
730 if (R__unlikely(blistsize <= 0)) {
737 Long64_t basketOffset = TMath::BinarySearch(blistsize, entries, entry);
738 if (basketOffset < 0) {
744 if ((basketOffset < blistsize) && b.GetListOfBaskets()->UncheckedAt(basketOffset)) {
750 Long64_t pos = b.GetBasketSeek(basketOffset);
751 Int_t len = lbaskets[basketOffset];
752 if (R__unlikely(pos <= 0 || len <= 0)) {
760 if (R__unlikely(len > fBufferSizeMin)) {
782 TBranch *TTreeCache::CalculateMissEntries(Long64_t pos, Int_t len, Bool_t all)
784 if (R__unlikely((pos < 0) || (len < 0))) {
788 int count = all ? (fTree->GetListOfLeaves())->GetEntriesFast() : fMissCache->fBranches.size();
789 fMissCache->fEntries.reserve(count);
790 fMissCache->fEntries.clear();
791 Bool_t found_request = kFALSE;
792 TBranch *resultBranch =
nullptr;
793 Long64_t entry = fTree->GetReadEntry();
795 std::vector<std::pair<size_t, Int_t>> basketsInfo;
796 auto perfStats = GetTree()->GetPerfStats();
799 for (
int i = 0; i < count; i++) {
801 all ?
static_cast<TBranch *
>(
static_cast<TLeaf *
>((fTree->GetListOfLeaves())->UncheckedAt(i))->GetBranch())
802 : fMissCache->fBranches[i];
803 IOPos iopos = FindBranchBasketPos(*b, entry);
804 if (iopos.fLen == 0) {
807 if (iopos.fPos == pos && iopos.fLen == len) {
808 found_request = kTRUE;
813 fMissCache->fEntries.emplace_back(std::move(iopos));
815 if (R__unlikely(perfStats)) {
816 Int_t blistsize = b->GetWriteBasket();
817 Int_t basketNumber = -1;
818 for (Int_t bn = 0; bn < blistsize; ++bn) {
819 if (iopos.fPos == b->GetBasketSeek(bn)) {
824 if (basketNumber >= 0)
825 basketsInfo.emplace_back((
size_t)i, basketNumber);
828 if (R__unlikely(!found_request)) {
831 fMissCache->fEntries.clear();
833 if (R__unlikely(perfStats)) {
834 for (
auto &info : basketsInfo) {
835 perfStats->SetLoadedMiss(info.first, info.second);
855 Bool_t TTreeCache::ProcessMiss(Long64_t pos,
int len)
858 Bool_t firstMiss = kFALSE;
859 if (fFirstMiss == -1) {
860 fFirstMiss = fEntryCurrent;
863 fLastMiss = fEntryCurrent;
865 TBranch *b = CalculateMissEntries(pos, len, firstMiss);
869 b = CalculateMissEntries(pos, len, kTRUE);
875 fMissCache->fEntries.clear();
880 fMissCache->fBranches.push_back(b);
883 std::sort(fMissCache->fEntries.begin(), fMissCache->fEntries.end());
886 std::vector<Long64_t> positions;
887 positions.reserve(fMissCache->fEntries.size());
888 std::vector<Int_t> lengths;
889 lengths.reserve(fMissCache->fEntries.size());
890 ULong64_t cumulative = 0;
891 for (
auto &mcentry : fMissCache->fEntries) {
892 positions.push_back(mcentry.fIO.fPos);
893 lengths.push_back(mcentry.fIO.fLen);
894 mcentry.fIndex = cumulative;
895 cumulative += mcentry.fIO.fLen;
897 fMissCache->fData.reserve(cumulative);
899 fNMissReadPref += fMissCache->fEntries.size();
900 fFile->ReadBuffers(&(fMissCache->fData[0]), &(positions[0]), &(lengths[0]), fMissCache->fEntries.size());
901 fFirstMiss = fLastMiss = fEntryCurrent;
913 Bool_t TTreeCache::CheckMissCache(
char *buf, Long64_t pos,
int len)
916 if (!fOptimizeMisses) {
919 if (R__unlikely((pos < 0) || (len < 0))) {
926 MissCache::Entry mcentry{IOPos{pos, len}};
927 auto iter = std::lower_bound(fMissCache->fEntries.begin(), fMissCache->fEntries.end(), mcentry);
929 if (iter != fMissCache->fEntries.end()) {
930 if (len > iter->fIO.fLen) {
934 auto offset = iter->fIndex;
935 memcpy(buf, &(fMissCache->fData[offset]), len);
944 if (!ProcessMiss(pos, len)) {
952 iter = std::lower_bound(fMissCache->fEntries.begin(), fMissCache->fEntries.end(), mcentry);
954 if (iter != fMissCache->fEntries.end()) {
955 auto offset = iter->fIndex;
957 memcpy(buf, &(fMissCache->fData[offset]), len);
973 struct BasketRanges {
978 Range() : fMin(-1), fMax(-1) {}
980 void UpdateMin(Long64_t min)
982 if (fMin == -1 || min < fMin)
986 void UpdateMax(Long64_t max)
988 if (fMax == -1 || fMax < max)
992 Bool_t Contains(Long64_t entry) {
return (fMin <= entry && entry <= fMax); }
995 std::vector<Range> fRanges;
996 std::map<Long64_t,size_t> fMinimums;
997 std::map<Long64_t,size_t> fMaximums;
999 BasketRanges(
size_t nBranches) { fRanges.resize(nBranches); }
1001 void Update(
size_t branchNumber, Long64_t min, Long64_t max)
1003 Range &range = fRanges.at(branchNumber);
1006 range.UpdateMin(min);
1007 range.UpdateMax(max);
1009 if (old.fMax != range.fMax) {
1010 if (old.fMax != -1) {
1011 auto maxIter = fMaximums.find(old.fMax);
1012 if (maxIter != fMaximums.end()) {
1013 if (maxIter->second == 1) {
1014 fMaximums.erase(maxIter);
1016 --(maxIter->second);
1024 void Update(
size_t branchNumber,
size_t basketNumber, Long64_t *entries,
size_t nb,
size_t max)
1026 Update(branchNumber, entries[basketNumber],
1027 (basketNumber < (nb - 1)) ? (entries[basketNumber + 1] - 1) : max - 1);
1031 bool CheckAllIncludeRange()
1034 for (
const auto &r : fRanges) {
1035 if (result.fMin == -1 || result.fMin < r.fMin) {
1037 result.fMin = r.fMin;
1039 if (result.fMax == -1 || r.fMax < result.fMax) {
1041 result.fMax = r.fMax;
1048 Range allIncludedRange(AllIncludedRange());
1050 return (result.fMin == allIncludedRange.fMin && result.fMax == allIncludedRange.fMax);
1060 Range AllIncludedRange()
1063 if (!fMinimums.empty())
1064 result.fMin = fMinimums.rbegin()->first;
1065 if (!fMaximums.empty())
1066 result.fMax = fMaximums.begin()->first;
1071 UInt_t BranchesRegistered()
1074 for (
const auto &r : fRanges) {
1075 if (r.fMin != -1 && r.fMax != -1)
1083 Bool_t Contains(Long64_t entry)
1085 for (
const auto &r : fRanges) {
1086 if (r.fMin != -1 && r.fMax != -1)
1087 if (r.fMin <= entry && entry <= r.fMax)
1095 for (
size_t i = 0; i < fRanges.size(); ++i) {
1096 if (fRanges[i].fMin != -1 || fRanges[i].fMax != -1)
1097 Printf(
"Range #%zu : %lld to %lld", i, fRanges[i].fMin, fRanges[i].fMax);
1106 Bool_t TTreeCache::FillBuffer()
1109 if (fNbranches <= 0)
return kFALSE;
1110 TTree *tree = ((TBranch*)fBranches->UncheckedAt(0))->GetTree();
1111 Long64_t entry = tree->GetReadEntry();
1112 Long64_t fEntryCurrentMax = 0;
1114 if (entry != -1 && (entry < fEntryMin || fEntryMax < entry))
1117 if (fEnablePrefetching) {
1119 if (fEntryNext >= 0 && entry >= fEntryNext) {
1123 StopLearningPhase();
1132 fFirstEntry = entry;
1135 if (fFirstEntry == entry)
return kFALSE;
1137 if (!fReadDirectionSet) {
1138 if (entry < fFirstEntry) {
1139 fReverseRead = kTRUE;
1140 fReadDirectionSet = kTRUE;
1142 else if (entry > fFirstEntry) {
1143 fReverseRead =kFALSE;
1144 fReadDirectionSet = kTRUE;
1150 if (fEntryCurrent >0 && entry < fEntryNext) {
1152 if (entry >= fEntryCurrent) {
1153 entry = fEntryCurrent - tree->GetAutoFlush() * fFillTimes;
1155 if (entry < 0) entry = 0;
1157 else if (fEntryCurrent >= 0) {
1161 if (entry < 0)
return kFALSE;
1162 fFirstBuffer = !fFirstBuffer;
1166 if (fEnablePrefetching) {
1167 if (entry < 0 && fEntryNext > 0) {
1168 entry = fEntryCurrent;
1169 }
else if (entry >= fEntryCurrent) {
1170 if (entry < fEntryNext) {
1179 fFirstBuffer = !fFirstBuffer;
1187 static constexpr
bool showMore = kFALSE;
1189 static const auto PrintAllCacheInfo = [](TObjArray *branches) {
1190 for (Int_t i = 0; i < branches->GetEntries(); i++) {
1191 TBranch *b = (TBranch *)branches->UncheckedAt(i);
1192 b->PrintCacheInfo();
1196 if (showMore || gDebug > 6)
1197 Info(
"FillBuffer",
"***** Called for entry %lld", entry);
1199 if (!fIsLearning && fEntryCurrent <= entry && entry < fEntryNext) {
1202 Bool_t allUsed = kTRUE;
1203 for (Int_t i = 0; i < fNbranches; ++i) {
1204 TBranch *b = (TBranch *)fBranches->UncheckedAt(i);
1205 if (!b->fCacheInfo.AllUsed()) {
1212 if (showMore || gDebug > 5)
1213 Info(
"FillBuffer",
"All baskets used already, so refresh the cache early at entry %lld", entry);
1216 PrintAllCacheInfo(fBranches);
1223 if (fEntryCurrent <= entry && entry < fEntryNext)
return kFALSE;
1229 Bool_t resetBranchInfo = kFALSE;
1230 if (entry < fCurrentClusterStart || fNextClusterStart <= entry) {
1232 resetBranchInfo = kTRUE;
1233 if (showMore || gDebug > 6)
1234 Info(
"FillBuffer",
"*** Will reset the branch information about baskets");
1235 }
else if (showMore || gDebug > 6) {
1236 Info(
"FillBuffer",
"*** Info we have on the set of baskets");
1237 PrintAllCacheInfo(fBranches);
1240 fEntryCurrentMax = fEntryCurrent;
1241 TTree::TClusterIterator clusterIter = tree->GetClusterIterator(entry);
1243 auto entryCurrent = clusterIter();
1244 auto entryNext = clusterIter.GetNextEntry();
1246 if (entryNext < fEntryMin || fEntryMax < entryCurrent) {
1253 fEntryCurrent = entryCurrent;
1254 fEntryNext = entryNext;
1257 auto firstClusterEnd = fEntryNext;
1258 if (showMore || gDebug > 6)
1259 Info(
"FillBuffer",
"Looking at cluster spanning from %lld to %lld", fEntryCurrent, fEntryNext);
1261 if (fEntryCurrent < fEntryMin) fEntryCurrent = fEntryMin;
1262 if (fEntryMax <= 0) fEntryMax = tree->GetEntries();
1263 if (fEntryNext > fEntryMax) fEntryNext = fEntryMax;
1265 if ( fEnablePrefetching ) {
1266 if ( entry == fEntryMax ) {
1272 if (resetBranchInfo) {
1274 if (fCurrentClusterStart != -1 || fNextClusterStart != -1) {
1275 if (!(fEntryCurrent < fCurrentClusterStart || fEntryCurrent >= fNextClusterStart)) {
1276 Error(
"FillBuffer",
"Inconsistency: fCurrentClusterStart=%lld fEntryCurrent=%lld fNextClusterStart=%lld "
1277 "but fEntryCurrent should not be in between the two",
1278 fCurrentClusterStart, fEntryCurrent, fNextClusterStart);
1283 fCurrentClusterStart = fEntryCurrent;
1284 fNextClusterStart = firstClusterEnd;
1290 TEventList *elist = fTree->GetEventList();
1291 Long64_t chainOffset = 0;
1293 if (fTree->IsA() ==TChain::Class()) {
1294 TChain *chain = (TChain*)fTree;
1295 Int_t t = chain->GetTreeNumber();
1296 chainOffset = chain->GetTreeOffset()[t];
1301 Int_t ntotCurrentBuf = 0;
1302 if (fEnablePrefetching){
1304 TFileCacheRead::Prefetch(0,0);
1305 ntotCurrentBuf = fNtot;
1308 TFileCacheRead::SecondPrefetch(0,0);
1309 ntotCurrentBuf = fBNtot;
1313 TFileCacheRead::Prefetch(0,0);
1314 ntotCurrentBuf = fNtot;
1318 BasketRanges ranges((showMore || gDebug > 6) ? fNbranches : 0);
1319 BasketRanges reqRanges(fNbranches);
1320 BasketRanges memRanges((showMore || gDebug > 6) ? fNbranches : 0);
1321 Int_t clusterIterations = 0;
1322 Long64_t minEntry = fEntryCurrent;
1324 Long64_t maxReadEntry = minEntry;
1325 Int_t nReadPrefRequest = 0;
1326 auto perfStats = GetTree()->GetPerfStats();
1328 prevNtot = ntotCurrentBuf;
1329 Long64_t lowestMaxEntry = fEntryMax;
1331 struct collectionInfo {
1332 Int_t fClusterStart{-1};
1334 Bool_t fLoadedOnce{kFALSE};
1336 void Rewind() { fCurrent = (fClusterStart >= 0) ? fClusterStart : 0; }
1338 std::vector<collectionInfo> cursor(fNbranches);
1339 Bool_t reachedEnd = kFALSE;
1340 Bool_t skippedFirst = kFALSE;
1341 Bool_t oncePerBranch = kFALSE;
1342 Int_t nDistinctLoad = 0;
1343 Bool_t progress = kTRUE;
1354 auto CollectBaskets = [
this, elist, chainOffset, entry, clusterIterations, resetBranchInfo, perfStats,
1355 &cursor, &lowestMaxEntry, &maxReadEntry, &minEntry,
1356 &reachedEnd, &skippedFirst, &oncePerBranch, &nDistinctLoad, &progress,
1357 &ranges, &memRanges, &reqRanges,
1358 &ntotCurrentBuf, &nReadPrefRequest](EPass pass, ENarrow narrow, Long64_t maxCollectEntry) {
1362 Int_t nReachedEnd = 0;
1364 auto oldnReadPrefRequest = nReadPrefRequest;
1365 std::vector<Int_t> potentialVetoes;
1367 if (showMore || gDebug > 7)
1368 Info(
"CollectBaskets",
"Called with pass=%d narrow=%d maxCollectEntry=%lld", pass, narrow, maxCollectEntry);
1370 Bool_t filled = kFALSE;
1371 for (Int_t i = 0; i < fNbranches; ++i) {
1372 TBranch *b = (TBranch*)fBranches->UncheckedAt(i);
1373 if (b->GetDirectory()==0 || b->TestBit(TBranch::kDoNotProcess))
1375 if (b->GetDirectory()->GetFile() != fFile)
1377 potentialVetoes.clear();
1378 if (pass == kStart && !cursor[i].fLoadedOnce && resetBranchInfo) {
1382 b->fCacheInfo.GetUnused(potentialVetoes);
1383 if (showMore || gDebug > 7) {
1385 for(
auto v : potentialVetoes) {
1387 vetolist.Append(
' ');
1389 if (!potentialVetoes.empty())
1390 Info(
"FillBuffer",
"*** Potential Vetos for branch #%d: %s", i, vetolist.Data());
1392 b->fCacheInfo.Reset();
1394 Int_t nb = b->GetMaxBaskets();
1395 Int_t *lbaskets = b->GetBasketBytes();
1396 Long64_t *entries = b->GetBasketEntry();
1397 if (!lbaskets || !entries)
1401 Int_t blistsize = b->GetListOfBaskets()->GetSize();
1403 auto maxOfBasket = [
this, nb, entries](
int j) {
1404 return ((j < (nb - 1)) ? (entries[j + 1] - 1) : fEntryMax - 1);
1407 if (pass == kRewind)
1409 for (
auto &j = cursor[i].fCurrent; j < nb; j++) {
1412 if (j < blistsize && b->GetListOfBaskets()->UncheckedAt(j)) {
1414 if (showMore || gDebug > 6) {
1415 ranges.Update(i, entries[j], maxOfBasket(j));
1416 memRanges.Update(i, entries[j], maxOfBasket(j));
1418 if (entries[j] <= entry && entry <= maxOfBasket(j)) {
1419 b->fCacheInfo.SetIsInCache(j);
1420 b->fCacheInfo.SetUsed(j);
1434 if (entries[j] >= maxCollectEntry) {
1439 Long64_t pos = b->GetBasketSeek(j);
1440 Int_t len = lbaskets[j];
1441 if (pos <= 0 || len <= 0)
1443 if (len > fBufferSizeMin) {
1445 if ((showMore || gDebug > 7) &&
1446 (!(entries[j] < minEntry && (j < nb - 1 && entries[j + 1] <= minEntry))))
1447 Info(
"FillBuffer",
"Skipping branch %s basket %d is too large for the cache: %d > %d",
1448 b->GetName(), j, len, fBufferSizeMin);
1452 if (nReadPrefRequest && entries[j] > (reqRanges.AllIncludedRange().fMax + 1)) {
1462 if (showMore || gDebug > 8)
1463 Info(
"FillBuffer",
"Skipping for now due to gap %d/%d with %lld > %lld", i, j, entries[j],
1464 (reqRanges.AllIncludedRange().fMax + 1));
1468 if (entries[j] < minEntry && (j<nb-1 && entries[j+1] <= minEntry))
1472 if (cursor[i].fClusterStart == -1)
1473 cursor[i].fClusterStart = j;
1476 Long64_t emax = fEntryMax;
1478 emax = entries[j + 1] - 1;
1479 if (!elist->ContainsRange(entries[j]+chainOffset,emax+chainOffset))
1483 if (b->fCacheInfo.HasBeenUsed(j) || b->fCacheInfo.IsInCache(j) || b->fCacheInfo.IsVetoed(j)) {
1486 if (showMore || gDebug > 7)
1487 Info(
"FillBuffer",
"Skipping basket to avoid redo: %d/%d veto: %d", i, j, b->fCacheInfo.IsVetoed(j));
1491 if (std::find(std::begin(potentialVetoes), std::end(potentialVetoes), j) != std::end(potentialVetoes)) {
1498 b->fCacheInfo.Veto(j);
1499 if (showMore || gDebug > 7)
1500 Info(
"FillBuffer",
"Veto-ing cluster %d [%lld,%lld[ in branch %s #%d", j, entries[j],
1501 maxOfBasket(j) + 1, b->GetName(), i);
1506 if ((((entries[j] > entry)) || (j < nb - 1 && entries[j + 1] <= entry))) {
1508 if (j == cursor[i].fClusterStart && entry > entries[j])
1510 if (entries[j] > entry)
1517 if ((ntotCurrentBuf + len) > fBufferSizeMin) {
1519 if (clusterIterations > 0 && cursor[i].fLoadedOnce) {
1523 if (showMore || gDebug > 5) {
1526 "Breaking early because %d is greater than %d at cluster iteration %d will restart at %lld",
1527 (ntotCurrentBuf + len), fBufferSizeMin, clusterIterations, minEntry);
1529 fEntryNext = minEntry;
1533 if (pass == kStart || !cursor[i].fLoadedOnce) {
1534 if ((ntotCurrentBuf + len) > 4 * fBufferSizeMin) {
1539 fEntryNext = maxReadEntry;
1541 if (showMore || gDebug > 5) {
1542 Info(
"FillBuffer",
"Breaking early because %d is greater than 4*%d at cluster iteration "
1543 "%d pass %d will restart at %lld",
1544 (ntotCurrentBuf + len), fBufferSizeMin, clusterIterations, pass, fEntryNext);
1553 if ((ntotCurrentBuf + len) > 2 * fBufferSizeMin) {
1554 fEntryNext = maxReadEntry;
1555 if (showMore || gDebug > 5) {
1556 Info(
"FillBuffer",
"Breaking early because %d is greater than 2*%d at cluster iteration "
1557 "%d pass %d will restart at %lld",
1558 (ntotCurrentBuf + len), fBufferSizeMin, clusterIterations, pass, fEntryNext);
1569 reqRanges.Update(i, j, entries, nb, fEntryMax);
1570 if (showMore || gDebug > 6)
1571 ranges.Update(i, j, entries, nb, fEntryMax);
1573 b->fCacheInfo.SetIsInCache(j);
1575 if (showMore || gDebug > 6)
1576 Info(
"FillBuffer",
"*** Registering branch %d basket %d %s", i, j, b->GetName());
1578 if (!cursor[i].fLoadedOnce) {
1579 cursor[i].fLoadedOnce = kTRUE;
1582 if (R__unlikely(perfStats)) {
1583 perfStats->SetLoaded(i, j);
1587 if (fEnablePrefetching){
1589 TFileCacheRead::Prefetch(pos,len);
1590 ntotCurrentBuf = fNtot;
1593 TFileCacheRead::SecondPrefetch(pos,len);
1594 ntotCurrentBuf = fBNtot;
1598 TFileCacheRead::Prefetch(pos,len);
1599 ntotCurrentBuf = fNtot;
1602 if ( ( j < (nb-1) ) && entries[j+1] > maxReadEntry ) {
1604 maxReadEntry = entries[j+1];
1606 if (ntotCurrentBuf > 4 * fBufferSizeMin) {
1608 Warning(
"FillBuffer",
"There is more data in this cluster (starting at entry %lld to %lld, "
1609 "current=%lld) than usual ... with %d %.3f%% of the branches we already have "
1610 "%d bytes (instead of %d)",
1611 fEntryCurrent, fEntryNext, entries[j], i, (100.0 * i) / ((
float)fNbranches), ntotCurrentBuf,
1614 if (pass == kStart) {
1616 auto high = maxOfBasket(j);
1617 if (high < lowestMaxEntry)
1618 lowestMaxEntry = high;
1622 }
else if ((j + 1) == nb || entries[j + 1] >= maxReadEntry || entries[j + 1] >= lowestMaxEntry) {
1624 auto high = maxOfBasket(j);
1625 if (high < lowestMaxEntry)
1626 lowestMaxEntry = high;
1633 if (cursor[i].fCurrent == nb) {
1638 Info(
"CollectBaskets",
1639 "Entry: %lld, registering baskets branch %s, fEntryNext=%lld, fNseek=%d, ntotCurrentBuf=%d",
1640 minEntry, ((TBranch *)fBranches->UncheckedAt(i))->GetName(), fEntryNext, fNseek, ntotCurrentBuf);
1642 reachedEnd = (nReachedEnd == fNbranches);
1643 skippedFirst = (nSkipped > 0);
1644 oncePerBranch = (nDistinctLoad == fNbranches);
1645 progress = nReadPrefRequest - oldnReadPrefRequest;
1652 full = CollectBaskets(kStart, kNarrow, fEntryNext);
1656 while (!full && !reachedEnd && progress) {
1657 full = CollectBaskets(kStart, kFull, std::min(maxReadEntry, fEntryNext));
1660 resetBranchInfo = kFALSE;
1663 if (!full && !fReverseRead) {
1665 full = CollectBaskets(kRegular, kFull, fEntryNext);
1666 }
while (!full && !reachedEnd && progress);
1670 if (!full && skippedFirst) {
1671 full = CollectBaskets(kRewind, kFull, fEntryNext);
1672 while (!full && !reachedEnd && progress) {
1673 full = CollectBaskets(kRegular, kFull, fEntryNext);
1677 clusterIterations++;
1679 minEntry = clusterIter.Next();
1696 if (!((fBufferSizeMin > ((Long64_t)ntotCurrentBuf * (clusterIterations + 1)) / clusterIterations) &&
1697 (prevNtot < ntotCurrentBuf) && (minEntry < fEntryMax))) {
1698 if (showMore || gDebug > 6)
1699 Info(
"FillBuffer",
"Breaking because %d <= %lld || (%d >= %d) || %lld >= %lld", fBufferSizeMin,
1700 ((Long64_t)ntotCurrentBuf * (clusterIterations + 1)) / clusterIterations, prevNtot, ntotCurrentBuf,
1701 minEntry, fEntryMax);
1706 if (!fIsLearning && fReverseRead) {
1707 if (clusterIterations >= fFillTimes)
1709 if (minEntry >= fEntryCurrentMax && fEntryCurrentMax > 0)
1712 fEntryNext = clusterIter.GetNextEntry();
1713 if (fEntryNext > fEntryMax) fEntryNext = fEntryMax;
1714 fNextClusterStart = fEntryNext;
1717 if (showMore || gDebug > 6) {
1718 Info(
"FillBuffer",
"Mem ranges");
1720 Info(
"FillBuffer",
"Combined ranges");
1722 Info(
"FillBuffer",
"Requested ranges");
1724 PrintAllCacheInfo(fBranches);
1727 if (nReadPrefRequest == 0) {
1731 if (showMore || gDebug > 5) {
1732 Info(
"FillBuffer",
"For entry %lld, nothing was added to the cache.", entry);
1734 }
else if (fEntryNext < firstClusterEnd && !reqRanges.Contains(entry)) {
1746 if (showMore || gDebug > 5) {
1747 Error(
"FillBuffer",
"Reset the next entry because the currently loaded range does not contains the request "
1748 "entry: %lld. fEntryNext updated from %lld to %lld. %d",
1749 entry, fEntryNext, firstClusterEnd, nReadPrefRequest);
1753 fEntryNext = firstClusterEnd;
1755 if (showMore || gDebug > 5) {
1756 Info(
"FillBuffer",
"Complete adding %d baskets from %d branches taking in memory %d out of %d",
1757 nReadPrefRequest, reqRanges.BranchesRegistered(), ntotCurrentBuf, fBufferSizeMin);
1761 fNReadPref += nReadPrefRequest;
1762 if (fEnablePrefetching) {
1764 fFirstBuffer = !fFirstBuffer;
1766 if (!fIsLearning && fFirstTime){
1769 fFirstTime = kFALSE;
1772 fIsLearning = kFALSE;
1781 TTreeCache::EPrefillType TTreeCache::GetConfiguredPrefillType()
const
1786 if (!(stcp = gSystem->Getenv(
"ROOT_TTREECACHE_PREFILL")) || !*stcp) {
1787 s = gEnv->GetValue(
"TTreeCache.Prefill", 1);
1789 s = TString(stcp).Atoi();
1792 return static_cast<TTreeCache::EPrefillType
>(s);
1804 Double_t TTreeCache::GetEfficiency()
const
1809 return ((Double_t)fNReadOk / (Double_t)fNReadPref);
1816 Double_t TTreeCache::GetMissEfficiency()
const
1818 if (!fNMissReadPref) {
1821 return static_cast<double>(fNMissReadOk) / static_cast<double>(fNMissReadPref);
1828 Double_t TTreeCache::GetEfficiencyRel()
const
1830 if ( !fNReadOk && !fNReadMiss )
1833 return ((Double_t)fNReadOk / (Double_t)(fNReadOk + fNReadMiss));
1840 Double_t TTreeCache::GetMissEfficiencyRel()
const
1842 if (!fNMissReadOk && !fNMissReadMiss) {
1846 return static_cast<double>(fNMissReadOk) / static_cast<double>(fNMissReadOk + fNMissReadMiss);
1853 Int_t TTreeCache::GetLearnEntries()
1855 return fgLearnEntries;
1878 void TTreeCache::Print(Option_t *option)
const
1880 TString opt = option;
1882 printf(
"******TreeCache statistics for tree: %s in file: %s ******\n",fTree ? fTree->GetName() :
"no tree set",fFile ? fFile->GetName() :
"no file set");
1883 if (fNbranches <= 0)
return;
1884 printf(
"Number of branches in the cache ...: %d\n",fNbranches);
1885 printf(
"Cache Efficiency ..................: %f\n",GetEfficiency());
1886 printf(
"Cache Efficiency Rel...............: %f\n",GetEfficiencyRel());
1887 printf(
"Secondary Efficiency ..............: %f\n", GetMissEfficiency());
1888 printf(
"Secondary Efficiency Rel ..........: %f\n", GetMissEfficiencyRel());
1889 printf(
"Learn entries......................: %d\n",TTreeCache::GetLearnEntries());
1890 if ( opt.Contains(
"cachedbranches") ) {
1891 opt.ReplaceAll(
"cachedbranches",
"");
1892 printf(
"Cached branches....................:\n");
1893 const TObjArray *cachedBranches = this->GetCachedBranches();
1894 Int_t nbranches = cachedBranches->GetEntriesFast();
1895 for (Int_t i = 0; i < nbranches; ++i) {
1896 TBranch* branch = (TBranch*) cachedBranches->UncheckedAt(i);
1897 printf(
"Branch name........................: %s\n",branch->GetName());
1900 TFileCacheRead::Print(opt);
1906 Int_t TTreeCache::ReadBufferNormal(
char *buf, Long64_t pos, Int_t len){
1908 if (TFileCacheRead::ReadBuffer(buf,pos,len) == 1){
1913 static const auto recordMiss = [](TVirtualPerfStats *perfStats, TObjArray *branches, Bool_t bufferFilled,
1914 Long64_t basketpos) {
1916 ::Info(
"TTreeCache::ReadBufferNormal",
"Cache miss after an %s FillBuffer: pos=%lld",
1917 bufferFilled ?
"active" :
"inactive", basketpos);
1918 for (Int_t i = 0; i < branches->GetEntries(); ++i) {
1919 TBranch *b = (TBranch *)branches->UncheckedAt(i);
1920 Int_t blistsize = b->GetListOfBaskets()->GetSize();
1921 for (Int_t j = 0; j < blistsize; ++j) {
1922 if (basketpos == b->GetBasketSeek(j)) {
1924 ::Info(
"TTreeCache::ReadBufferNormal",
" Missing basket: %d for %s", j, b->GetName());
1925 perfStats->SetMissed(i, j);
1932 Bool_t bufferFilled = FillBuffer();
1934 Int_t res = TFileCacheRead::ReadBuffer(buf,pos,len);
1938 else if (res == 0) {
1940 auto perfStats = GetTree()->GetPerfStats();
1942 recordMiss(perfStats, fBranches, bufferFilled, pos);
1948 if (CheckMissCache(buf, pos, len)) {
1953 auto perfStats = GetTree()->GetPerfStats();
1955 recordMiss(perfStats, fBranches, bufferFilled, pos);
1965 Int_t TTreeCache::ReadBufferPrefetch(
char *buf, Long64_t pos, Int_t len)
1967 if (TFileCacheRead::ReadBuffer(buf, pos, len) == 1){
1980 if(TFileCacheRead::ReadBuffer(buf, pos, len)) {
2006 Int_t TTreeCache::ReadBuffer(
char *buf, Long64_t pos, Int_t len)
2008 if (!fEnabled)
return 0;
2010 if (fEnablePrefetching)
2011 return TTreeCache::ReadBufferPrefetch(buf, pos, len);
2013 return TTreeCache::ReadBufferNormal(buf, pos, len);
2019 void TTreeCache::ResetCache()
2021 for (Int_t i = 0; i < fNbranches; ++i) {
2022 TBranch *b = (TBranch*)fBranches->UncheckedAt(i);
2023 if (b->GetDirectory()==0 || b->TestBit(TBranch::kDoNotProcess))
2025 if (b->GetDirectory()->GetFile() != fFile)
2027 b->fCacheInfo.Reset();
2031 fCurrentClusterStart = -1;
2032 fNextClusterStart = -1;
2034 TFileCacheRead::Prefetch(0,0);
2036 if (fEnablePrefetching) {
2038 TFileCacheRead::SecondPrefetch(0, 0);
2051 Int_t TTreeCache::SetBufferSize(Int_t buffersize)
2053 Int_t prevsize = GetBufferSize();
2054 Int_t res = TFileCacheRead::SetBufferSize(buffersize);
2059 if (res == 0 && buffersize <= prevsize) {
2066 TFileCacheRead::Prefetch(0,0);
2067 if (fEnablePrefetching) {
2068 TFileCacheRead::SecondPrefetch(0, 0);
2084 void TTreeCache::SetEntryRange(Long64_t emin, Long64_t emax)
2088 Bool_t needLearningStart = (fEntryMin != emin) && fIsLearning && !fIsManual;
2092 fEntryNext = fEntryMin + fgLearnEntries * (fIsLearning && !fIsManual);
2094 Info(
"SetEntryRange",
"fEntryMin=%lld, fEntryMax=%lld, fEntryNext=%lld",
2095 fEntryMin, fEntryMax, fEntryNext);
2097 if (needLearningStart) {
2099 StartLearningPhase();
2106 void TTreeCache::SetFile(TFile *file, TFile::ECacheAction action)
2112 TFile *prevFile = fFile;
2114 prevFile->SetCacheRead(0, fTree, action);
2116 TFileCacheRead::SetFile(file, action);
2123 void TTreeCache::SetLearnEntries(Int_t n)
2138 void TTreeCache::SetLearnPrefill(TTreeCache::EPrefillType type )
2140 fPrefillType = type;
2148 void TTreeCache::StartLearningPhase()
2150 fIsLearning = kTRUE;
2153 if (fBrNames) fBrNames->Delete();
2154 fIsTransferred = kFALSE;
2165 void TTreeCache::StopLearningPhase()
2170 fIsLearning = kFALSE;
2174 auto perfStats = GetTree()->GetPerfStats();
2176 perfStats->UpdateBranchIndices(fBranches);
2179 if (fEnablePrefetching && !fOneTime) {
2180 fIsLearning = kTRUE;
2189 void TTreeCache::UpdateBranches(TTree *tree)
2195 fEntryMax = fTree->GetEntries();
2199 if (fBrNames->GetEntries() == 0 && fIsLearning) {
2201 fEntryNext = fEntryMin + fgLearnEntries;
2204 fIsLearning = kFALSE;
2209 TIter next(fBrNames);
2211 while ((os = (TObjString*)next())) {
2212 TBranch *b = fTree->GetBranch(os->GetName());
2216 fBranches->AddAt(b, fNbranches);
2220 auto perfStats = GetTree()->GetPerfStats();
2222 perfStats->UpdateBranchIndices(fBranches);
2229 void TTreeCache::LearnPrefill()
2232 if (!fIsLearning)
return;
2236 if (fNbranches > 0)
return;
2240 if (fPrefillType == kNoPrefill)
return;
2242 Long64_t entry = fTree ? fTree->GetReadEntry() : 0;
2245 if (entry < fEntryMin || entry > fEntryMax)
return;
2247 fLearnPrefilling = kTRUE;
2253 Long64_t eminOld = fEntryMin;
2254 Long64_t emaxOld = fEntryMax;
2255 Long64_t ecurrentOld = fEntryCurrent;
2256 Long64_t enextOld = fEntryNext;
2257 auto currentClusterStartOld = fCurrentClusterStart;
2258 auto nextClusterStartOld = fNextClusterStart;
2260 fEntryMin = std::max(fEntryMin, fEntryCurrent);
2261 fEntryMax = std::min(fEntryMax, fEntryNext);
2271 if (entry < fEntryMin) fEntryMin = entry;
2272 if (entry > fEntryMax) fEntryMax = entry;
2283 fIsLearning = kTRUE;
2287 fEntryMin = eminOld;
2288 fEntryMax = emaxOld;
2289 fEntryCurrent = ecurrentOld;
2290 fEntryNext = enextOld;
2291 fCurrentClusterStart = currentClusterStartOld;
2292 fNextClusterStart = nextClusterStartOld;
2294 fLearnPrefilling = kFALSE;