Skip to content

Commit a69d2a1

Browse files
committed
StepTHn: do a pre lookup for the bin using a uniform grid
Rather than doing a O(log N) binary search for the correct bin, precompute a uniform index for a O(1) lookup and find the actual bin using a short linear search.
1 parent eaf1d69 commit a69d2a1

2 files changed

Lines changed: 74 additions & 9 deletions

File tree

Framework/Core/include/Framework/StepTHn.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,22 @@ class StepTHn : public TNamed
7777

7878
THnBase** mTarget; //! target histogram
7979

80-
TAxis** mAxisCache; //! cache axis pointers (about 50% of the time in Fill is spent in GetAxis otherwise)
81-
Int_t* mNbinsCache; //! cache Nbins per axis
82-
Double_t* mLastVars; //! caching of last used bins (in many loops some vars are the same for a while)
83-
Int_t* mLastBins; //! caching of last used bins (in many loops some vars are the same for a while)
80+
TAxis** mAxisCache; //! cache axis pointers (about 50% of the time in Fill is spent in GetAxis otherwise)
81+
Int_t* mNbinsCache; //! cache Nbins per axis
82+
Double_t* mLastVars; //! caching of last used bins (in many loops some vars are the same for a while)
83+
Int_t* mLastBins; //! caching of last used bins (in many loops some vars are the same for a while)
84+
85+
// Fast bin lookup table: for each axis, maps a quantized position to an approximate bin.
86+
static constexpr Int_t kLookupSize = 1024; // number of slots per axis
87+
struct AxisLookup {
88+
Double_t invSlotWidth; // 1.0 / slot width for fast index computation
89+
Double_t xmin; // axis minimum
90+
Double_t xmax; // axis maximum
91+
const Double_t* edges; // pointer to bin edges array (nBins+1 entries)
92+
Int_t nBins; // number of bins
93+
Int_t table[kLookupSize]; // slot -> bin index (1-based, TAxis convention)
94+
};
95+
AxisLookup* mLookup; //! per-axis lookup tables
8496

8597
THnSparse* mPrototype; // not filled used as prototype histogram for axis functionality etc.
8698

Framework/Core/src/StepTHn.cxx

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ StepTHn::StepTHn() : mNBins(0),
3939
mNbinsCache(nullptr),
4040
mLastVars(nullptr),
4141
mLastBins(nullptr),
42+
mLookup(nullptr),
4243
mPrototype(nullptr)
4344
{
4445
// Default constructor (for streaming)
@@ -55,6 +56,7 @@ StepTHn::StepTHn(const Char_t* name, const Char_t* title, const Int_t nSteps, co
5556
mNbinsCache(nullptr),
5657
mLastVars(nullptr),
5758
mLastBins(nullptr),
59+
mLookup(nullptr),
5860
mPrototype(nullptr)
5961
{
6062
// Constructor
@@ -130,6 +132,7 @@ StepTHn::StepTHn(const StepTHn& c) : mNBins(c.mNBins),
130132
mNbinsCache(nullptr),
131133
mLastVars(nullptr),
132134
mLastBins(nullptr),
135+
mLookup(nullptr),
133136
mPrototype(nullptr)
134137
{
135138
//
@@ -152,6 +155,7 @@ StepTHn::~StepTHn()
152155
delete[] mNbinsCache;
153156
delete[] mLastVars;
154157
delete[] mLastBins;
158+
delete[] mLookup;
155159
delete mPrototype;
156160
}
157161

@@ -392,13 +396,35 @@ void StepTHn::Fill(int iStep, int nParams, double positionAndWeight[])
392396
LOGF(fatal, "Fill called with invalid number of parameters (%d vs %d)", mNVars, nParams);
393397
}
394398

395-
// fill axis cache
399+
// fill axis cache and build lookup tables on first call
396400
if (!mAxisCache) {
397401
mAxisCache = new TAxis*[mNVars];
398402
mNbinsCache = new Int_t[mNVars];
403+
mLookup = new AxisLookup[mNVars];
399404
for (Int_t i = 0; i < mNVars; i++) {
400405
mAxisCache[i] = mPrototype->GetAxis(i);
401406
mNbinsCache[i] = mAxisCache[i]->GetNbins();
407+
408+
// Build lookup table for this axis
409+
auto& lut = mLookup[i];
410+
lut.nBins = mNbinsCache[i];
411+
lut.edges = mAxisCache[i]->GetXbins()->GetArray();
412+
lut.xmin = mAxisCache[i]->GetXmin();
413+
lut.xmax = mAxisCache[i]->GetXmax();
414+
415+
if (lut.edges && lut.xmax > lut.xmin) {
416+
// Variable-width binning: build slot->bin mapping
417+
Double_t slotWidth = (lut.xmax - lut.xmin) / kLookupSize;
418+
lut.invSlotWidth = 1.0 / slotWidth;
419+
for (Int_t s = 0; s < kLookupSize; s++) {
420+
Double_t x = lut.xmin + (s + 0.5) * slotWidth;
421+
lut.table[s] = mAxisCache[i]->FindBin(x);
422+
}
423+
} else {
424+
// Uniform binning or degenerate axis: direct formula, no table needed
425+
lut.edges = nullptr;
426+
lut.invSlotWidth = lut.xmax > lut.xmin ? lut.nBins / (lut.xmax - lut.xmin) : 0;
427+
}
402428
}
403429

404430
mLastVars = new Double_t[mNVars];
@@ -420,11 +446,39 @@ void StepTHn::Fill(int iStep, int nParams, double positionAndWeight[])
420446
if (mLastVars[i] == positionAndWeight[i]) {
421447
tmpBin = mLastBins[i];
422448
} else {
423-
tmpBin = mAxisCache[i]->FindBin(positionAndWeight[i]);
449+
const auto& lut = mLookup[i];
450+
Double_t x = positionAndWeight[i];
451+
452+
if (!lut.edges) {
453+
// Uniform binning: direct computation
454+
tmpBin = 1 + static_cast<Int_t>((x - lut.xmin) * lut.invSlotWidth);
455+
if (tmpBin < 1) {
456+
tmpBin = 0; // underflow
457+
} else if (tmpBin > lut.nBins) {
458+
tmpBin = lut.nBins + 1; // overflow
459+
}
460+
} else {
461+
// Variable-width binning: lookup table + refine
462+
Int_t slot = static_cast<Int_t>((x - lut.xmin) * lut.invSlotWidth);
463+
if (slot < 0 || slot >= kLookupSize) {
464+
tmpBin = (slot < 0) ? 0 : lut.nBins + 1; // under/overflow
465+
} else {
466+
tmpBin = lut.table[slot];
467+
// Refine: the lookup gives an approximate bin, check edges
468+
// Move left if x is below the lower edge of tmpBin
469+
while (tmpBin > 1 && x < lut.edges[tmpBin - 1]) {
470+
tmpBin--;
471+
}
472+
// Move right if x is at or above the upper edge of tmpBin
473+
while (tmpBin < lut.nBins && x >= lut.edges[tmpBin]) {
474+
tmpBin++;
475+
}
476+
}
477+
}
478+
424479
mLastBins[i] = tmpBin;
425-
mLastVars[i] = positionAndWeight[i];
480+
mLastVars[i] = x;
426481
}
427-
// Printf("%d", tmpBin);
428482

429483
// under/overflow not supported
430484
if (tmpBin < 1 || tmpBin > mNbinsCache[i]) {
@@ -433,7 +487,6 @@ void StepTHn::Fill(int iStep, int nParams, double positionAndWeight[])
433487

434488
// bins start from 0 here
435489
bin += tmpBin - 1;
436-
// Printf("%lld", bin);
437490
}
438491

439492
updateBin(iStep, bin, weight);

0 commit comments

Comments
 (0)