Running a root macro using all cores/threads


ROOT Version: 6.18/04
Platform: Ubuntu 18.04
Compiler: gcc 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)


Dear co-rooters,

I’m looping over a TTree, doing a few calculations and then I’m trying to add the result of these calculations to the same TTree as a new TBranch.

This means that I have to loop 2 times over the TTree, once over a couple of vectors and once over these vectors while looping over the tree (this is what makes the calculation slow).

The vector will have the same size as the tree so if there are many entries it takes a ridiculous amount of time. Is there a way to minimise the execution time by running the macro on multiple cores? I ave 40 and I’m only using 1.

Thanks in advance.

The way I’m coding this is as follows (note that this is just a sample code to illustrate the 3 loops) :

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

using namespace std;

void macro(){
    vector<double> T1, T2, DT;
    double t1, t2, dt;
    int det;

    TFile *f_in = new TFile("root_file.root", "UPDATE");
    TTree *t    = (TTree*)f_in->Get("TREE");
    int entries = t->GetEntries();

    t->SetBranchAddress("tof", &tof);
    t->SetBranchAddress("det", &det);

    TBranch *bdt = t->Branch("dt",&dt);

    // First loop over TTree
    for (int i=0; i<entries; ++i){
        t->GetEntry(i);
        if(det == 1)
            T1.push_back( tof);
       if(det==2)
            T2.push_back(tof);
    }
 
    // Loop over the vector
    for (int i=0; i<T1.size(); ++i){
        dt = T1[i] - T2[i];
        DT.push_back(dt);
    }

    // Second loop over the tree
    for(int i=0; i<entries; ++i){
        t->GetEntry(i);
        
        // second loop over the vector inside the second tree loop
        for (ii=0; ii<DT.size(); ++ii){
            if (det==1)
                dt = DT[i];
            if (det==2)
                dt = -DT[i];
        }
        bdt->Fill();
    }
    
}

Hi,
it’s not trivial to split a TTree read over multiple threads. You can manually divide the work between processes (each should run over a different bunch of entries) and then merge the histograms.

But before going there, you might consider some alternative options:

  1. compile your code with optimizations as a standard C++ program instead of running it interpreted. For example, you can add a main function that calls the macro function and then compile the program with g++ -O2 -o macro macro.cpp $(root-config --libs --cflags). -O2 adds compiler optimizations (depending on the workload you might get a factor 2 or less or even an order of magnitude of speedup). root-config --libs --cflags is a helper command that outputs ROOT-specific compilation flags

  2. change the way you store the data (this might or might not be simple depending on your case): for example, a tree with det == 1 and a tree with det == 2 that you loop over in lockstep might be a better layout for your data. Also, instead of creating a new branch in the same tree, you might want to create a separate “friend tree”

  3. use RDataFrame and its implicit multi-threading feature to run the event loop on multiple threads

Also note that the (simplified, I guess) logic you reported above does not make sense:

    for(int i=0; i<entries; ++i){
        t->GetEntry(i);
        
        // second loop over the vector inside the second tree loop
        for (ii=0; ii<DT.size(); ++ii){
            if (det==1)
                dt = DT[i];
            if (det==2)
                dt = -DT[i];
        }
        bdt->Fill();
    }

The inner for loop keeps setting dt to different values, but the bdt->Fill() only ever fills bdt with the last value dt was set to. Plus, that t->GetEntry(i) seems useless. So it might be that there is a faster way to get the result you want.

Cheers,
Enrico

Ciao Enrico,

Thanks a lot for your advice.
I’ll try compiling the code as a C++ code because I can’t change the data format.
Concerning the RDataFrame I had no idea. It might seem a nice solution, but I have to study because it seems a bit confusing at first sight.

As for the code, you are right! It makes no sense. I just tried to add a simplified version which probably wasn’t a good idea. If you want to have a look in the code, I’m attaching it.

add_coincidence.C (8.7 KB)

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