TChain get entry acceleration

Dear ROOT users,

I am using OpenGATE to simulate PET scans and am a novice at ROOT. I am attempting to read the output root file and perform some data processing on the data. The root file I have generated is 100+GB, containing 1.2Billion events.

I recently profiled the application and found that 70% of computation time is spent on stream_ptr->GetEntry().

    stream_ptr = new TChain(this->chain_name.c_str());
    stream_ptr->Add(fullfilename.c_str());
    // Turn off all branches
    stream_ptr->SetBranchStatus("*",0);
    // Branches are turned back on by SetBranchAddress()
    stream_ptr->SetBranchAddress("time1", &time1);
    stream_ptr->SetBranchAddress("time2", &time2);
    stream_ptr->SetBranchAddress("eventID1",&eventID1);
    stream_ptr->SetBranchAddress("eventID2",&eventID2);
    stream_ptr->SetBranchAddress("energy1", &energy1);
    stream_ptr->SetBranchAddress("energy2", &energy2);
    stream_ptr->SetBranchAddress("comptonPhantom1", &comptonphantom1);
    stream_ptr->SetBranchAddress("comptonPhantom2", &comptonphantom2);

In terms of reading the ROOT data we use:

        if (current_position == nentries)
            return Succeeded::no;


        if (stream_ptr->GetEntry(static_cast<Long64_t>(current_position), 0) == 0)
            return Succeeded::no;

        current_position ++ ;

        DoStuff();

We recently found that stream_ptr->SetBranchStatus("*",0); halved the time taken (I believe this is because there are many other branches in the root file that are not needed in the processing) but I am looking to accelerate the reading.

Are there any suggestions that might help me?

Regards,
Robert Twyman


Please read tips for efficient and successful posting and posting code

ROOT Version: 6.22/06
Platform: MacOS/Linix
Compiler: Not Provided


Hi Robert,
and welcome to the ROOT forum!

Indeed using SetBranchStatus("*", 0) is the correct thing to do: it turns off reading of all branches (aka dataset columns) by default and you can later only turn on the ones you actually need.

It’s not surprising that GetEntry takes a significant fraction of the computing time: it’s where all the reading work happens. Now: where does GetEntry itself spend time? One thing to check is whether you are simply hitting a cap in your I/O bandwidth, it can easily happen e.g. when reading from a network storage or from a spinning disk. If that’s the case, you might need faster hardware.
Otherwise, if most of the time is spent in R__unzip or similar, your bottleneck is decompression – you can consider switching to a different compression algorithm (it’s something that you can configure when writing the data out), e.g. zstd has a great balance between speed and compression factor. Another way to speed up a CPU-bound application is to run the processing in multiple threads or multiple processes. For a multi-thread approach, you can use RDataFrame, although that might require rewriting large parts of the logic. A less intrusive method could be to use TTreeProcessorMT (multi-thread) or TTreeProcessorMP (multi-process) to run your processing in parallel.

Hope this helps!
Enrico

Hi Enrico,

Thank you for your comment. This is all being performed locally, on a 2019 Macbook Pro, for this testing testing/profiling.
I have attatched the profiler results (removing set up)

I belive you are correct that the decompression is slowing the process, but it does not appear to the only limiting factor.

I have read around the RDataFrame and TTreeProcessor(MT/MP) links above. Thank you for providing them but as you mentioned, this will require large rewriting.

Regards,
Robert

Alright, @pcanal might have some suggestions to speed up many single-thread TBranch::GetEntry calls. What types are the branches? Can you post the output of tree->Print()?

In you case you can improve the single thread performance (too much time in spent in TTree::GetEntry itself) by calling TBranch::GetEntry directly.

    TBranch *br_time1 = nullptr;
    TBranch *br_time2 = nullptr;
    ... etc ...
    stream_ptr->SetBranchAddress("time1", &time1, &br_time1);
    stream_ptr->SetBranchAddress("time2", &time2, &br_time2);
   ... etc ...

and

        if (current_position == nentries)
            return Succeeded::no;

        auto brentry = stream_ptr->LoadTree(static_cast<Long64_t>(current_position));
        if (brentry < 0)
            return Succeeded::no;
        if (br_time1->GetEntry(brentry) == 0)
            return Succeeded::no;
        if (br_time2->GetEntry(brentry) == 0)
            return Succeeded::no;
        ... etc ...

        current_position ++ ;

        DoStuff();

@eguiraud The output of stream_ptr->Print() is

******************************************************************************
*Chain   :tree      : Output/bigg.root                                       *
******************************************************************************
******************************************************************************
*Tree    :tree      : tree                                                   *
*Entries : 10530000 : Total =      2369535749 bytes  File  Size = 1142040666 *
*        :          : Tree compression factor =   2.07                       *
******************************************************************************
*Br    0 :eventID1  : eventID1/I                                             *
*Entries : 10530000 : Total  Size=   44970317 bytes  File Size  =   22800000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.95     *
*............................................................................*
*Br    1 :eventID2  : eventID2/I                                             *
*Entries : 10530000 : Total  Size=   44970317 bytes  File Size  =   22800000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.95     *
*............................................................................*
*Br    2 :sourceID1 : sourceID1/I                                            *
*Entries : 10530000 : Total  Size=   45000321 bytes  File Size  =    3270000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  13.58     *
*............................................................................*
*Br    3 :sourceID2 : sourceID2/I                                            *
*Entries : 10530000 : Total  Size=   45000321 bytes  File Size  =    3270000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  13.58     *
*............................................................................*
*Br    4 :sourcePosX1 : sourcePosX1/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42420000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.05     *
*............................................................................*
*Br    5 :sourcePosX2 : sourcePosX2/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42420000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.05     *
*............................................................................*
*Br    6 :sourcePosY1 : sourcePosY1/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42390000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.05     *
*............................................................................*
*Br    7 :sourcePosY2 : sourcePosY2/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42390000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.05     *
*............................................................................*
*Br    8 :sourcePosZ1 : sourcePosZ1/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42240000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.05     *
*............................................................................*
*Br    9 :sourcePosZ2 : sourcePosZ2/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42240000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.05     *
*............................................................................*
*Br   10 :globalPosX1 : globalPosX1/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42390000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.05     *
*............................................................................*
*Br   11 :globalPosX2 : globalPosX2/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42270000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.05     *
*............................................................................*
*Br   12 :globalPosY1 : globalPosY1/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42210000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.05     *
*............................................................................*
*Br   13 :globalPosY2 : globalPosY2/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42180000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.05     *
*............................................................................*
*Br   14 :globalPosZ1 : globalPosZ1/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   43110000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.03     *
*............................................................................*
*Br   15 :globalPosZ2 : globalPosZ2/F                                        *
*Entries : 10530000 : Total  Size=   45060329 bytes  File Size  =   42990000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.03     *
*............................................................................*
*Br   16 :time1     : time1/D                                                *
*Entries : 10530000 : Total  Size=   87000313 bytes  File Size  =   82980000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.04     *
*............................................................................*
*Br   17 :time2     : time2/D                                                *
*Entries : 10530000 : Total  Size=   87000313 bytes  File Size  =   82680000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.04     *
*............................................................................*
*Br   18 :energy1   : energy1/F                                              *
*Entries : 10530000 : Total  Size=   44940313 bytes  File Size  =   40380000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.10     *
*............................................................................*
*Br   19 :energy2   : energy2/F                                              *
*Entries : 10530000 : Total  Size=   44940313 bytes  File Size  =   40260000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.10     *
*............................................................................*
*Br   20 :comptVolName1 : comptVolName1/C                                    *
*Entries : 10530000 : Total  Size=   98010337 bytes  File Size  =   20790000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   4.69     *
*............................................................................*
*Br   21 :comptVolName2 : comptVolName2/C                                    *
*Entries : 10530000 : Total  Size=   98850337 bytes  File Size  =   22650000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   4.34     *
*............................................................................*
*Br   22 :RayleighVolName1 : RayleighVolName1/C                              *
*Entries : 10530000 : Total  Size=   98100349 bytes  File Size  =   20910000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   4.66     *
*............................................................................*
*Br   23 :RayleighVolName2 : RayleighVolName2/C                              *
*Entries : 10530000 : Total  Size=   98100349 bytes  File Size  =   20910000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   4.66     *
*............................................................................*
*Br   24 :comptonPhantom1 : comptonPhantom1/I                                *
*Entries : 10530000 : Total  Size=   45180345 bytes  File Size  =    3450000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  12.92     *
*............................................................................*
*Br   25 :comptonPhantom2 : comptonPhantom2/I                                *
*Entries : 10530000 : Total  Size=   45180345 bytes  File Size  =    3660000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  12.18     *
*............................................................................*
*Br   26 :comptonCrystal1 : comptonCrystal1/I                                *
*Entries : 10530000 : Total  Size=   45180345 bytes  File Size  =    9450000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   4.72     *
*............................................................................*
*Br   27 :comptonCrystal2 : comptonCrystal2/I                                *
*Entries : 10530000 : Total  Size=   45180345 bytes  File Size  =    9570000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   4.66     *
*............................................................................*
*Br   28 :RayleighPhantom1 : RayleighPhantom1/I                              *
*Entries : 10530000 : Total  Size=   45210349 bytes  File Size  =    3480000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  12.82     *
*............................................................................*
*Br   29 :RayleighPhantom2 : RayleighPhantom2/I                              *
*Entries : 10530000 : Total  Size=   45210349 bytes  File Size  =    3480000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  12.82     *
*............................................................................*
*Br   30 :RayleighCrystal1 : RayleighCrystal1/I                              *
*Entries : 10530000 : Total  Size=   45210349 bytes  File Size  =    5370000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   8.31     *
*............................................................................*
*Br   31 :RayleighCrystal2 : RayleighCrystal2/I                              *
*Entries : 10530000 : Total  Size=   45210349 bytes  File Size  =    5550000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   8.04     *
*............................................................................*
*Br   32 :gantryID1 : gantryID1/I                                            *
*Entries : 10530000 : Total  Size=   45000321 bytes  File Size  =    3270000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  13.58     *
*............................................................................*
*Br   33 :rsectorID1 : rsectorID1/I                                          *
*Entries : 10530000 : Total  Size=   45030325 bytes  File Size  =   16050000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   2.77     *
*............................................................................*
*Br   34 :moduleID1 : moduleID1/I                                            *
*Entries : 10530000 : Total  Size=   45000321 bytes  File Size  =    3270000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  13.58     *
*............................................................................*
*Br   35 :submoduleID1 : submoduleID1/I                                      *
*Entries : 10530000 : Total  Size=   45090333 bytes  File Size  =   12120000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   3.67     *
*............................................................................*
*Br   36 :crystalID1 : crystalID1/I                                          *
*Entries : 10530000 : Total  Size=   45030325 bytes  File Size  =   17760000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   2.50     *
*............................................................................*
*Br   37 :layerID1  : layerID1/I                                             *
*Entries : 10530000 : Total  Size=   44970317 bytes  File Size  =    3240000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  13.69     *
*............................................................................*
*Br   38 :gantryID2 : gantryID2/I                                            *
*Entries : 10530000 : Total  Size=   45000321 bytes  File Size  =    3270000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  13.58     *
*............................................................................*
*Br   39 :rsectorID2 : rsectorID2/I                                          *
*Entries : 10530000 : Total  Size=   45030325 bytes  File Size  =   16110000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   2.76     *
*............................................................................*
*Br   40 :moduleID2 : moduleID2/I                                            *
*Entries : 10530000 : Total  Size=   45000321 bytes  File Size  =    3270000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  13.58     *
*............................................................................*
*Br   41 :submoduleID2 : submoduleID2/I                                      *
*Entries : 10530000 : Total  Size=   45090333 bytes  File Size  =   12120000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   3.67     *
*............................................................................*
*Br   42 :crystalID2 : crystalID2/I                                          *
*Entries : 10530000 : Total  Size=   45030325 bytes  File Size  =   17520000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   2.54     *
*............................................................................*
*Br   43 :layerID2  : layerID2/I                                             *
*Entries : 10530000 : Total  Size=   44970317 bytes  File Size  =    3240000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=  13.69     *
*............................................................................*
*Br   44 :sinogramTheta : sinogramTheta/F                                    *
*Entries : 10530000 : Total  Size=   45120337 bytes  File Size  =   42090000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.06     *
*............................................................................*
*Br   45 :sinogramS : sinogramS/F                                            *
*Entries : 10530000 : Total  Size=   45000321 bytes  File Size  =   43260000 *
*Baskets :    30000 : Basket Size=      32000 bytes  Compression=   1.03     *
*............................................................................*

Here bigg.root is ~1.1GB.

Obviously there are many branches in here that are not needed in terms of the analysis above I would prefer to keep the structure of the root file untouched, there are other application which may need this.

@pcanal I think I will attempt to incorporate the suggested branch tests into the code, there are some checks in DoStuff() that will benifit from this but it unlikely any of the branches are “missing” from each entry.

Thank you for your suggestion.

[ Note, I updated the example to fix the check on the return value of LoadTree ]

1 Like

Just for clarity the advantage is the code I propose is not in the checks (they are really ancillary), it is the unrolling and optimization of the TTree::GetEntry loop over the branch (essentially for each branch, check if enabled then call GetEntry)

Thank you for your help @pcanal.

Thanks the suggestion you presented here I have managed to accelerate the processing by appoximately a factor 2, as, for some cases, only a handful Branch GetEntry() calls are made before an event is rejected by DoStuff() due to the event failing to satisfy a condition and the code moves onto the next event.

You can find the PR discussion here

See:
TChain::LoadTree
TBranch::GetEntry
files generated by YourTChain->MakeClass();

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.