java-topology/defects/root-cern-0003/TICKET.md
russell@unturf.com 09012e7ec1 openfoam+root-cern: 5-MOAD scan; openfoam-0003 CWE-407 DSMCCloud typeIdList O(C*T*M*K), root-cern-0003 CWE-407 TTree::InitializeBranchLists fSeqBranches O(B^2)
openfoam-0003: DSMCCloud::initialise calls findIndex(typeIdList_, moleculeName)
inside triple-nested forAll(cells) * forAll(tets) * forAll(molecules) loop.
Fix: pre-build HashTable<label, word> before cell loop for O(1) lookups.
3-10x speedup depending on type count. 5/5 PASS.

root-cern-0003: TTree::InitializeBranchLists calls std::find on fSeqBranches
(std::vector<TBranch*>) inside two O(B) loops, yielding O(B^2) total.
Fix: mirror fSeqBranches in std::unordered_set<TBranch*> for O(1) lookup.
~500x speedup at B=500, ~1000x at B=1000 (CMS NanoAOD scale). 6/6 PASS.

MOAD-0002: OpenFOAM objectRegistry god-object structural, ROOT gROOT intertangle
structural (gROOTMutex inconsistently applied). Both documented.
MOAD-0003: ROOT TTHREAD_TLS method-scoped only, CLEAN. OpenFOAM no thread-local, CLEAN.
MOAD-0004: OpenFOAM CLEAN. ROOT TWebFile auth logging pre-existing root-cern-0002.
MOAD-0005: Both CLEAN.
2026-04-03 15:31:30 -04:00

2.3 KiB

root-cern-0003: TTree::InitializeBranchLists fSeqBranches std::find O(B^2) MOAD-0001

Target

ROOT (CERN data analysis framework) -- https://github.com/root-project/root

File

tree/tree/src/TTree.cxx

Function

TTree::InitializeBranchLists(bool checkLeafCount)

MOAD

0001 -- CWE-407 Algorithmic Complexity

Severity

MEDIUM

Pattern

// fSeqBranches is std::vector<TBranch*>

// First loop: O(B * S) where S grows toward B
for (Int_t i = 0; i < nbranches; i++) {
    ...
    if (std::find(fSeqBranches.begin(), fSeqBranches.end(), countBranch)
        == fSeqBranches.end()) {          // O(S) linear scan per branch
        fSeqBranches.push_back(countBranch);
    }
}

// Second loop: O(B * S) again
for (Int_t i = 0; i < nbranches; i++) {
    ...
    if (std::find(fSeqBranches.begin(), fSeqBranches.end(), branch)
        == fSeqBranches.end()) {          // O(S) linear scan per branch
        fSortedBranches.emplace_back(bbytes, branch);
    }
}

fSeqBranches is a std::vector<TBranch*>. Each call to std::find on it is O(S) where S is the current size of fSeqBranches. In a TTree where all branches have a count leaf (common in variable-length array TTrees), S grows to B, making both loops O(B^2) total.

Complexity

Variable Meaning Typical
B top-level branches 10-1200
S sequential branches up to B

Total comparisons (both loops): O(B^2). CMS NanoAOD has ~1000 branches. In worst case: 2 * 1000^2 = 2,000,000 pointer comparisons vs. 2 * 1000 = 2,000 with an unordered_set. Speedup: ~1000x at B=1000.

When Called

InitializeBranchLists(true) is called lazily on first GetEntry() when ROOT Implicit MultiThreading (IMT) is enabled (ROOT::EnableImplicitMT()). It is also called when branches are added during writing.

For users running RDataFrame or parallel GetEntry loops over large TTrees with many count branches, this function executes once per TTree activation and dominates startup latency.

Fix

Mirror fSeqBranches in a std::unordered_set<TBranch*> for O(1) pointer lookup. Maintain both in sync as branches are inserted.

Header Change Required

#include <unordered_set> must be added to TTree.cxx.

Patch

patch/root-cern-0003-ttree-seqbranches-find.patch

Test

test/test_root_cern_0003.py

Date

2026-04-03