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);
 }