TTree::Draw vs loop

I am concerned that TTree::Draw performance is significantly faster than a manual iteration over the tree (see below).

Is there a way to make it on-par with TTree::Draw?

#include <iostream>
#include <TFile.h>
#include <TTree.h>

using namespace std;

int main(int argc, char *argv[]) {

    TFile *f = new TFile("/afs/cern.ch/work/y/yuraic/410000_v4.root");
    TTree *tree = (TTree*)f->Get("nominal");
    tree->SetBranchStatus("*", 0);
    tree->SetBranchStatus("mcWeightOrg", 1);

    Double_t mcWeightOrg = 0;
    tree->SetBranchAddress("mcWeightOrg",&mcWeightOrg);

    Double_t total = 0;
    Long64_t nentries = tree->GetEntries();
    for (Long64_t i=0;i<nentries;i++) {
        if (i % 100000 == 0)
            cout << "Event number " << i << endl;
        tree->GetEntry(i);
        total = total + mcWeightOrg;

    }

    cout << "Total mcWeightOrg: "<< total << endl;
    return 0;
}

Hi,

for reading just a single branch which is a double it should not be the case.
I see that you are reading a file on afs: is the measurement fair (e.g. no glitches, warm caches and so on)?

D

Beware also that TTree::Draw implicitly converts all data to doubles. In your case it does not matter because you have doubles, but If you had large integer values (ULong64_t) in your branch, this can lead to rounding errors. So the loop is more exact than TTree::Draw (and thus not completely equivalent).

See also:
sft.its.cern.ch/jira/browse/ROOT-8009

I ran these two simple scripts on both - lxplus and my laptop hard drive. The results are very similar. The execution time is 3 seconds with TTree::Draw vs 2-4 minutes with the loop. This suggests that TTree::Draw is faster than the loop by x100!

Thank you for your help!
get_weights.cxx (822 Bytes)
get_TTree_Draw.cxx (524 Bytes)

(hit twice by mistake; need to delete this post)

Hi,

for completeness, could you provide the input file too?

D

Try the attached code (I assume that you use it as a “compiled executable”, not as an “interpreted macro”).
get_weights.cxx (779 Bytes)

I can confirm, the code example from Pepe Le Pew works just as fast as compiled TTree::Draw - both take 2 seconds to run.

Do we know why in my original code it’s not that fast? I would think that disabling all branches except the one we are interested in is essentially the same as in Pepe’s example with TBranch::GetEntry in the loop?

In case, the input ROOT file is still needed it can be found here, /afs/cern.ch/work/y/yuraic/public/410000_v4.root

Many thanks, guys!!!

Thanks Pepe for the link! It explains difference between the two approaches.

Meanwhile I also discovered that your way of getting a pointer to the branch, i.e.

    TBranch *b_mcWeightOrg;
    tree->SetBranchAddress("mcWeightOrg", &mcWeightOrg, &b_mcWeightOrg);

leads to about 3 times faster iteration in the loop, than this one

    TBranch *b_mcWeightOrg = tree->GetBranch("mcWeightOrg");
    b_mcWeightOrg->SetAddress(&mcWeightOrg);

leads to about 3 times faster iteration in the loop, than this one

Where do you place the code in this case? [Note that the SetBranchAddress branch is superior as its supports TChain while the GetBranch/SetAddress does not or need to be called wastefully within the loop).

Cheers,
Philippe.

Thanks Philippe! I am attaching the code below for completeness. However, apparently there was some cache issues because I cannot confirm that the approach with TBranch::SetAddress() is slower than TBranch::SetBranchAddress().

Do you know by any chance how to implement SetBranchAddress code in Python? Namely, how to pass pointer-to-pointer in PyROOT.

  Double_t mcWeightOrg = 0; 
  TBranch *b_mcWeightOrg;
  tree->SetBranchAddress("mcWeightOrg", &mcWeightOrg, &b_mcWeightOrg ); 
  
  // 3rd parameter is pointer-to-pointer to TBranch (i.e. &b_mcWeightOrg)

get_weights_branch.cxx (706 Bytes)

See wlav.web.cern.ch/wlav/pyroot/tpytree.html (towards the end of the page, which also applies to SetBranchAddress) and SetBranchAddress in PyROOT .

Cheers,
Philippe.