From 5e3117146e31f71f0b10064a747b78612faedf56 Mon Sep 17 00:00:00 2001 From: Florine de Geus Date: Thu, 11 Jun 2026 16:46:41 +0200 Subject: [PATCH 1/3] [ntuple] Prevent redundant processor (re-)connection in chains --- tree/ntuple/src/RNTupleProcessor.cxx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tree/ntuple/src/RNTupleProcessor.cxx b/tree/ntuple/src/RNTupleProcessor.cxx index 3a00539e0a82c..ed6d7c702b730 100644 --- a/tree/ntuple/src/RNTupleProcessor.cxx +++ b/tree/ntuple/src/RNTupleProcessor.cxx @@ -347,13 +347,21 @@ ROOT::Experimental::RNTupleChainProcessor::AddFieldToEntry(const std::string &fi ROOT::NTupleSize_t ROOT::Experimental::RNTupleChainProcessor::LoadEntry(ROOT::NTupleSize_t entryNumber) { - ROOT::NTupleSize_t localEntryNumber = entryNumber; - std::size_t currProcessorNumber = 0; + // If the requested entry number is lower than the current entry number, we have to again localise the correct local + // entry number starting from the first processor in the chain. Otherwise, we can continue looking from the inner + // processor that is currently connected, which is much faster when the chain consists of many inner processors. if (entryNumber < fCurrentEntryNumber) { fCurrentProcessorNumber = 0; ConnectInnerProcessor(fCurrentProcessorNumber); } + std::size_t currProcessorNumber = fCurrentProcessorNumber; + ROOT::NTupleSize_t entriesSeen = 0; + for (unsigned i = 0; i < currProcessorNumber; ++i) { + entriesSeen += fInnerProcessors[i]->GetNEntries(); + } + ROOT::NTupleSize_t localEntryNumber = entryNumber - entriesSeen; + // As long as the entry fails to load from the current processor, we decrement the local entry number with the number // of entries in this processor and try with the next processor until we find the correct local entry number. while (fInnerProcessors[currProcessorNumber]->LoadEntry(localEntryNumber) == kInvalidNTupleIndex) { From 0c7ea383c725c196b9ce7a0f3870d08daf92a43a Mon Sep 17 00:00:00 2001 From: Florine de Geus Date: Fri, 12 Jun 2026 13:28:00 +0200 Subject: [PATCH 2/3] [ntuple] Remove unnecessary init and connect of single processors --- tree/ntuple/inc/ROOT/RNTupleProcessor.hxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tree/ntuple/inc/ROOT/RNTupleProcessor.hxx b/tree/ntuple/inc/ROOT/RNTupleProcessor.hxx index 5598260ad3ae5..22c1d399f3ce4 100644 --- a/tree/ntuple/inc/ROOT/RNTupleProcessor.hxx +++ b/tree/ntuple/inc/ROOT/RNTupleProcessor.hxx @@ -615,9 +615,8 @@ private: /// \brief Get the total number of entries in this processor. ROOT::NTupleSize_t GetNEntries() final { - Initialize(); if (fNEntries == ROOT::kInvalidNTupleIndex) - Connect(fFieldIdxs); + Initialize(); return fNEntries; } From d7d839bff7db46ea4408a4a9047733b0eb61496f Mon Sep 17 00:00:00 2001 From: Florine de Geus Date: Fri, 12 Jun 2026 14:05:08 +0200 Subject: [PATCH 3/3] [ntuple] Use cached entry counts before calling GetNEntries --- tree/ntuple/src/RNTupleProcessor.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tree/ntuple/src/RNTupleProcessor.cxx b/tree/ntuple/src/RNTupleProcessor.cxx index ed6d7c702b730..077503fe2cc66 100644 --- a/tree/ntuple/src/RNTupleProcessor.cxx +++ b/tree/ntuple/src/RNTupleProcessor.cxx @@ -358,7 +358,10 @@ ROOT::NTupleSize_t ROOT::Experimental::RNTupleChainProcessor::LoadEntry(ROOT::NT std::size_t currProcessorNumber = fCurrentProcessorNumber; ROOT::NTupleSize_t entriesSeen = 0; for (unsigned i = 0; i < currProcessorNumber; ++i) { - entriesSeen += fInnerProcessors[i]->GetNEntries(); + if (fInnerNEntries[i] == kInvalidNTupleIndex) { + fInnerNEntries[i] = fInnerProcessors[i]->GetNEntries(); + } + entriesSeen += fInnerNEntries[i]; } ROOT::NTupleSize_t localEntryNumber = entryNumber - entriesSeen;