diff --git a/tree/treeplayer/inc/ROOT/TTreeProcessorMT.hxx b/tree/treeplayer/inc/ROOT/TTreeProcessorMT.hxx index 15d05068b89acdae6f87c870d5a70142975c93e7..b3fdc5fcf34e1d2cae344f6e342dd500dbb3bc04 100644 --- a/tree/treeplayer/inc/ROOT/TTreeProcessorMT.hxx +++ b/tree/treeplayer/inc/ROOT/TTreeProcessorMT.hxx @@ -170,7 +170,7 @@ namespace ROOT { const TEntryList fEntryList; // const to be sure to avoid race conditions among TTreeViews const Internal::FriendInfo fFriendInfo; - ROOT::TThreadedObject<ROOT::Internal::TTreeView> treeView; ///<! Thread-local TreeViews + ROOT::TThreadedObject<ROOT::Internal::TTreeView> fTreeView; ///<! Thread-local TreeViews Internal::FriendInfo GetFriendInfo(TTree &tree); std::string FindTreeName(); diff --git a/tree/treeplayer/src/TTreeProcessorMT.cxx b/tree/treeplayer/src/TTreeProcessorMT.cxx index 025565dc37b28b3fd8daf4823bcd991a4d190146..27493a0042439807484fbb32faad1d80a004cceb 100644 --- a/tree/treeplayer/src/TTreeProcessorMT.cxx +++ b/tree/treeplayer/src/TTreeProcessorMT.cxx @@ -35,6 +35,119 @@ namespace ROOT { unsigned int TTreeProcessorMT::fgMaxTasksPerFilePerWorker = 24U; namespace Internal { + +/// A cluster of entries +struct EntryCluster { + Long64_t start; + Long64_t end; +}; + +/// Names, aliases, and file names of a TTree's or TChain's friends +using NameAlias = std::pair<std::string, std::string>; +struct FriendInfo { + /// Pairs of names and aliases of friend trees/chains + std::vector<Internal::NameAlias> fFriendNames; + /// Names of the files where each friend is stored. fFriendFileNames[i] is the list of files for friend with + /// name fFriendNames[i] + std::vector<std::vector<std::string>> fFriendFileNames; +}; + +class TTreeView { +private: + using TreeReaderEntryListPair = std::pair<std::unique_ptr<TTreeReader>, std::unique_ptr<TEntryList>>; + + // NOTE: fFriends must come before fChain to be deleted after it, see ROOT-9281 for more details + std::vector<std::unique_ptr<TChain>> fFriends; ///< Friends of the tree/chain + std::unique_ptr<TChain> fChain; ///< Chain on which to operate + + //////////////////////////////////////////////////////////////////////////////// + /// Construct fChain, also adding friends if needed and injecting knowledge of offsets if available. + void MakeChain(const std::string &treeName, const std::vector<std::string> &fileNames, const FriendInfo &friendInfo, + const std::vector<Long64_t> &nEntries, const std::vector<std::vector<Long64_t>> &friendEntries) + { + const std::vector<NameAlias> &friendNames = friendInfo.fFriendNames; + const std::vector<std::vector<std::string>> &friendFileNames = friendInfo.fFriendFileNames; + + fChain.reset(new TChain(treeName.c_str())); + const auto nFiles = fileNames.size(); + for (auto i = 0u; i < nFiles; ++i) { + fChain->Add(fileNames[i].c_str(), nEntries[i]); + } + fChain->ResetBit(TObject::kMustCleanup); + + fFriends.clear(); + const auto nFriends = friendNames.size(); + for (auto i = 0u; i < nFriends; ++i) { + const auto &friendName = friendNames[i]; + const auto &name = friendName.first; + const auto &alias = friendName.second; + + // Build a friend chain + auto frChain = std::make_unique<TChain>(name.c_str()); + const auto nFileNames = friendFileNames[i].size(); + for (auto j = 0u; j < nFileNames; ++j) + frChain->Add(friendFileNames[i][j].c_str(), friendEntries[i][j]); + + // Make it friends with the main chain + fChain->AddFriend(frChain.get(), alias.c_str()); + fFriends.emplace_back(std::move(frChain)); + } + } + + TreeReaderEntryListPair MakeReaderWithEntryList(TEntryList &globalList, Long64_t start, Long64_t end) + { + // TEntryList and SetEntriesRange do not work together (the former has precedence). + // We need to construct a TEntryList that contains only those entry numbers in our desired range. + auto localList = std::make_unique<TEntryList>(); + Long64_t entry = globalList.GetEntry(0); + do { + if (entry >= end) + break; + else if (entry >= start) + localList->Enter(entry); + } while ((entry = globalList.Next()) >= 0); + + auto reader = std::make_unique<TTreeReader>(fChain.get(), localList.get()); + return std::make_pair(std::move(reader), std::move(localList)); + } + + std::unique_ptr<TTreeReader> MakeReader(Long64_t start, Long64_t end) + { + auto reader = std::make_unique<TTreeReader>(fChain.get()); + reader->SetEntriesRange(start, end); + return reader; + } + +public: + TTreeView() {} + + // no-op, we don't want to copy the local TChains + TTreeView(const TTreeView &) {} + + ////////////////////////////////////////////////////////////////////////// + /// Get a TTreeReader for the current tree of this view. + TreeReaderEntryListPair GetTreeReader(Long64_t start, Long64_t end, const std::string &treeName, + const std::vector<std::string> &fileNames, const FriendInfo &friendInfo, + TEntryList entryList, const std::vector<Long64_t> &nEntries, + const std::vector<std::vector<Long64_t>> &friendEntries) + { + const bool usingLocalEntries = friendInfo.fFriendNames.empty() && entryList.GetN() == 0; + if (fChain == nullptr || (usingLocalEntries && fileNames[0] != fChain->GetListOfFiles()->At(0)->GetTitle())) + MakeChain(treeName, fileNames, friendInfo, nEntries, friendEntries); + + std::unique_ptr<TTreeReader> reader; + std::unique_ptr<TEntryList> localList; + if (entryList.GetN() > 0) { + std::tie(reader, localList) = MakeReaderWithEntryList(entryList, start, end); + } else { + reader = MakeReader(start, end); + } + + // we need to return the entry list too, as it needs to be in scope as long as the reader is + return std::make_pair(std::move(reader), std::move(localList)); + } +}; + //////////////////////////////////////////////////////////////////////// /// Return a vector of cluster boundaries for the given tree and files. // EntryClusters and number of entries per file @@ -388,8 +501,8 @@ void TTreeProcessorMT::Process(std::function<void(TTreeReader &)> func) auto processCluster = [&](const Internal::EntryCluster &c) { std::unique_ptr<TTreeReader> reader; std::unique_ptr<TEntryList> elist; - std::tie(reader, elist) = treeView->GetTreeReader(c.start, c.end, fTreeName, theseFiles, fFriendInfo, - fEntryList, theseEntries, friendEntries); + std::tie(reader, elist) = fTreeView->GetTreeReader(c.start, c.end, fTreeName, theseFiles, fFriendInfo, + fEntryList, theseEntries, friendEntries); func(*reader); };