Multithread filling a TTree using TThreadedObject<TTree>

Dear Rooters,

I am trying to fill a TTree using multithreading. For some reason, I want to use TThreadedObject, since the rest of the code base already used a lot TThreadedObject’s for data processing, and all worked fine.

So I tried using TThreadedObject to fill a tree multithreadingly. The procedure is basically like:

//-------------------------------------------------
    ROOT::EnableThreadSafety();
    ROOT::TThreadedObject<TTree> th_T("T", "threaded object");

    auto _fill = [&](int n)
    {
        mtx.lock();
        cout<<"thread id: "<<this_thread::get_id()<<endl;
        mtx.unlock();
        TTree* T = th_T.Get().get();
        float v;
        T->Branch("v", &v, "v/F");
        for(int i=0;i<1e6;i++)
        {
            v = 10;
            T->Fill();
        }
    };
    // save results
    TFile *f = new TFile("ss.root", "recreate");
    th_T.GetAtSlot(0)->Write();
    f->Close();

But somehow the resulted behavior is odd, I checked the output tree, about 1M entries is just 0, and around 4000 entries is 10(the expected value). I am not sure where I missed anything, I searched quite a lot and couldn’t find any solutions. I have attached a working test code, any help on this code would be highly appreciated.

Regards,

Elliot

multi_filling.cpp (937 Bytes)

I can reproduce the problem and I can also see that if I use

TTree *T = new TTree("", "");

instead of the threaded object, the individual ss$n$ files are all right.

@pcanal Do you have an idea?

Hi,
I’m not sure TTree can be used within TThreadedObject.
What TThreadedObject does is to create a certain number of copies of your object (by default it would be 64 TTrees), then it makes sure that each thread works on a different copy when you call methods on the TThreadedObject.

I’m pretty sure you have to manually call TThreadedObject::Merge when you are done, to merge all thread-local copies (including possibly the ones that have not been used by any thread), and then the object at slot 0 is the result of the merge.

Can TTree be used like this?

This does not answer directly your question, but ROOT provides an higher-level interface to get the end-result you want, RDataFrame. These two lines fill a TTree with a million entries in parallel and save it to a TFile:

ROOT::EnableImplicitMT();  // to activate implicit multi-threading
ROOT::RDataFrame(1e6).Define("v", [] { return 10; }).Snapshot("t", "ss.root");

Cheers,
Enrico

Hi,

Indeed there is currently no horizontal (automatic) merging of TTree (gather distinct branches from various TTree) (The closest concept is “Tree Friends”).

Cheers,
Philippe.

PS. Admittedly implementing horizontal merge for in-memory trees should be straightforward (just steal the branch object from one tree and put it into another).

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