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.
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