Multithreading and multiprocessing

To improve the performance of the calculations, ROOT offers a tool whose work is based on distributed system parallelism - PROOF. On the other hand, decisions related to simple multi-thread and multiprocessing of data. Beginning with Version 6.08 in ROOT appeared methods that provide these properties. To make these features available, ROOT must be compiled with imt = ON flag. When working on a HybriLit cluster, the user must install the ROOT/v6-13-02-1 module.

Multithreading

ROOT can be used as a library from several streams provided that special function ROOT::EnableImplicitMT (numthreads). This function provides global inclusion of implicit multithreading in ROOT, activation of parallel execution of methods in ROOT, which provide internal parallelization.

The 'numthreads' parameter allows you to control the number of threads used by implicit multithreading. However, this parameter is only asks ROOT to set such a number of threads, and ROOT will try to satisfy the request if the execution of the script allows do it. And if the ROOT is configured to use an external scheduler, setting the value to 'numthreads' may have no effect. ROOT:: EnableImplicitMT (numthreads) is called before performing operations in multiple threads.

The ROOT::EnableThreadSafety()function provides a global mutex to make ROOT thread-safe.

Mutex (eng. mutex, from mutual exclusion "mutual exclusion") is an analogue of a single semaphore that serves in programming for synchronization simultaneously running threads. A mutex differs from a semaphore in that only the thread that owns it can release it, i.e. translate into marked condition. Mutexes are one of the variants of semaphore mechanisms for organizing mutual exclusion. They are implemented in many operating systems. their main purpose is the organization of mutual exclusion for threads from the same or from different processes. Mutexes are the simplest binary semaphores that can be in one of two states marked or unmarked (open and closed respectively). When a thread belonging to any process becomes the owner of a mutex object, the latter is transferred to the unmarked state. If the task releases the mutex, its state becomes marked. The task of the mutex is to protect an object from access to it by other threads other than the one that has taken possession of the mutex. At any given moment only one thread can own an object protected by a mutex. If another thread needs access to a variable protected by a mutex, then this thread is blocked until the mutex is released. The purpose of using mutexes is to protect data from damage due to asynchronous changes.

The ROOT::DisableImplicitMT () and ROOT::IsImplicitMTEnabled() methods are used to disable and check the state of global implicit multithreading in ROOT, respectively. It is also necessary that only one file is read or written in each stream.

Multiprocessing

A tool provided by ROOT for simple operations using multiprocessing. - this is a class TProcPool. The new interface implemented in the class TProcPool provides the ability to perform a very common set of tasks in parallel, described by macros or functions. The main methods of the class TProcPool::Map (F func, unsigned nTimes) and TProcPool::MapReduce (F func, unsigned nTimes, R redfunk) analyze trees using all kernels available.

For this link there are tutorials designed to illustrate the multi-core features of ROOT, such as thread awareness and security, multithreading, and multiprocessing.

The program below demonstrates how to activate and use implicit paralleling the TTree::GetEntry() method.

int imt001_parBranchProcessing()
{
    // First enable implicit multi-threading globally, so that the implicit parallelisation is on.
  // The parameter of the call specifies the number of threads to use.
  int nthreads = 4;
  ROOT::EnableImplicitMT(nthreads);
  // Open the file containing the tree
  TFile *file = TFile::Open("http://root.cern.ch/files/h1/dstarmb.root");
  // Get the tree
  TTree *tree = nullptr;
  file->GetObject("h42", tree);
  // Read the branches in parallel.
  // Note that the interface does not change, the parallelisation is internal
  for (Long64_t i = 0; i < tree->GetEntries(); ++i) {
     tree->GetEntry(i);  // parallel read
  }
  // IMT parallelisation can be disabled for a specific tree
  tree->SetImplicitMT(false);
  // If now GetEntry is invoked on the tree, the reading is sequential
  for (Long64_t i = 0; i < tree->GetEntries(); ++i) {
     tree->GetEntry(i);  // sequential read
  }
  // Parallel reading can be re-enabled
  tree->SetImplicitMT(true);
  // IMT can be also disabled globally.
  // As a result, no tree will run GetEntry in parallel
  ROOT::DisableImplicitMT();
  // This is still sequential: the global flag is disabled, even if the
  // flag for this particular tree is enabled
  for (Long64_t i = 0; i < tree->GetEntries(); ++i) {
     tree->GetEntry(i);  // sequential read
  }
  return 0; 
 
}			
			

Such parallelization creates one task for each branch of the top level of the analyzed tree. In this example, most of the branches are floating-point numbers that are read very quickly. However, this parallelization can also be used on large trees with many (complex) branches. In this case, the benefits of acceleration will be more obvious.