diff --git a/tree/treeplayer/inc/ROOT/TTreeProcessorMT.hxx b/tree/treeplayer/inc/ROOT/TTreeProcessorMT.hxx index fa7ec2e6f95ae3af42ed09bc7ec10c87610e51f9..be0e679ba9e2cbf184c0a022cd2a8e4412bd78bb 100644 --- a/tree/treeplayer/inc/ROOT/TTreeProcessorMT.hxx +++ b/tree/treeplayer/inc/ROOT/TTreeProcessorMT.hxx @@ -98,12 +98,13 @@ namespace ROOT { //////////////////////////////////////////////////////////////////////////////// /// Construct fChain, also adding friends if needed and injecting knowledge of offsets if available. - void MakeChain(const std::vector<Long64_t> &nEntries, const std::vector<std::vector<Long64_t>> &friendEntries) + void MakeChain(const std::vector<std::string> &fileNames, const std::vector<Long64_t> &nEntries, + const std::vector<std::vector<Long64_t>> &friendEntries) { fChain.reset(new TChain(fTreeName.c_str())); - const auto nFiles = fFileNames.size(); + const auto nFiles = fileNames.size(); for (auto i = 0u; i < nFiles; ++i) { - fChain->Add(fFileNames[i].c_str(), nEntries[i]); + fChain->Add(fileNames[i].c_str(), nEntries[i]); } fChain->ResetBit(TObject::kMustCleanup); @@ -282,10 +283,11 @@ namespace ROOT { ////////////////////////////////////////////////////////////////////////// /// Get a TTreeReader for the current tree of this view. - TreeReaderEntryListPair GetTreeReader(Long64_t start, Long64_t end, const std::vector<Long64_t> &nEntries, + TreeReaderEntryListPair GetTreeReader(Long64_t start, Long64_t end, const std::vector<std::string> &fileNames, + const std::vector<Long64_t> &nEntries, const std::vector<std::vector<Long64_t>> &friendEntries) { - MakeChain(nEntries, friendEntries); + MakeChain(fileNames, nEntries, friendEntries); std::unique_ptr<TTreeReader> reader; std::unique_ptr<TEntryList> elist; @@ -336,6 +338,11 @@ namespace ROOT { { return fFriendFileNames; } + + const TEntryList &GetEntryList() const + { + return fEntryList; + } }; } // End of namespace Internal diff --git a/tree/treeplayer/src/TTreeProcessorMT.cxx b/tree/treeplayer/src/TTreeProcessorMT.cxx index 73676cc4d2a40618c267034fb43390398197c7ba..f9262a9388b089fa740e9c4a2b1feaf2bb4aadb7 100644 --- a/tree/treeplayer/src/TTreeProcessorMT.cxx +++ b/tree/treeplayer/src/TTreeProcessorMT.cxx @@ -140,34 +140,62 @@ void TTreeProcessorMT::Process(std::function<void(TTreeReader &)> func) // Enable this IMT use case (activate its locks) Internal::TParTreeProcessingRAII ptpRAII; - // Retrieve cluster boundaries and number of entries for each file - const auto clustersAndEntries = ROOT::Internal::MakeClusters(treeView->GetTreeName(), treeView->GetFileNames()); - const auto &clustersPerFile = clustersAndEntries.first; + // If an entry list or friend trees are present, we need to generate clusters with global entry numbers, + // so we do it here for all files. + const bool hasFriends = !treeView->GetFriendNames().empty(); + const bool hasEntryList = treeView->GetEntryList().GetN() > 0; + const bool shouldRetrieveAllClusters = hasFriends || hasEntryList; + const auto clustersAndEntries = shouldRetrieveAllClusters + ? ROOT::Internal::MakeClusters(treeView->GetTreeName(), treeView->GetFileNames()) + : ROOT::Internal::ClustersAndEntries{}; + const auto &clusters = clustersAndEntries.first; const auto &entries = clustersAndEntries.second; + // Retrieve number of entries for each file for each friend tree const auto friendEntries = - ROOT::Internal::GetFriendEntries(treeView->GetFriendNames(), treeView->GetFriendFileNames()); + hasFriends ? ROOT::Internal::GetFriendEntries(treeView->GetFriendNames(), treeView->GetFriendFileNames()) + : std::vector<std::vector<Long64_t>>{}; TThreadExecutor pool; // Parent task, spawns tasks that process each of the entry clusters for each input file using ROOT::Internal::EntryCluster; - auto processFile = [this, &func, &entries, &friendEntries, &pool](const std::vector<EntryCluster> &clusters) { - - auto processCluster = [this, &func, &entries, &friendEntries](const ROOT::Internal::EntryCluster &c) { + auto processFile = [&](std::size_t fileIdx) { + + // If cluster information is already present, build TChains with all input files and use global entry numbers + // Otherwise get cluster information only for the file we need to process and use local entry numbers + const bool shouldUseGlobalEntries = hasFriends || hasEntryList; + // theseFiles contains either all files or just the single file to process + const auto &theseFiles = shouldUseGlobalEntries ? treeView->GetFileNames() + : std::vector<std::string>({treeView->GetFileNames()[fileIdx]}); + // Evaluate clusters (with local entry numbers) and number of entries for this file, if needed + const auto theseClustersAndEntries = shouldUseGlobalEntries + ? Internal::ClustersAndEntries{} + : Internal::MakeClusters(treeView->GetTreeName(), theseFiles); + + // All clusters for the file to process, either with global or local entry numbers + const auto &thisFileClusters = shouldUseGlobalEntries ? clusters[fileIdx] : theseClustersAndEntries.first[0]; + + // Either all number of entries or just the ones for this file + const auto &theseEntries = + shouldUseGlobalEntries ? entries : std::vector<Long64_t>({theseClustersAndEntries.second[0]}); + + auto processCluster = [&](const ROOT::Internal::EntryCluster &c) { // This task will operate with the tree that contains start treeView->PushTaskFirstEntry(c.start); std::unique_ptr<TTreeReader> reader; std::unique_ptr<TEntryList> elist; - std::tie(reader, elist) = treeView->GetTreeReader(c.start, c.end, entries, friendEntries); + std::tie(reader, elist) = treeView->GetTreeReader(c.start, c.end, theseFiles, theseEntries, friendEntries); func(*reader); // In case of task interleaving, we need to load here the tree of the parent task treeView->PopTaskFirstEntry(); }; - pool.Foreach(processCluster, clusters); + pool.Foreach(processCluster, thisFileClusters); }; - pool.Foreach(processFile, clustersPerFile); + std::vector<std::size_t> fileIdxs(treeView->GetFileNames().size()); + std::iota(fileIdxs.begin(), fileIdxs.end(), 0u); + pool.Foreach(processFile, fileIdxs); }