Int_t mt001_fillHistos() { // The first, fundamental operation to be performed in order to make ROOT // thread-aware. ROOT::EnableThreadSafety(); // We define our work item auto workItem = [](UInt_t workerID) { // One generator, file and ntuple per worker TRandom3 workerRndm(workerID); // Change the seed TFile f(Form("myFile_%u.root", workerID), "RECREATE"); TH1F h(Form("myHisto_%u", workerID), "The Histogram", 64, -4, 4); for (UInt_t i = 0; i < nNumbers; ++i) { h.Fill(workerRndm.Gaus()); } h.Write(); }; // Create the collection which will hold the threads, our "pool" std::vector<std::thread> workers; // Fill the "pool" with workers for (auto workerID : ROOT::TSeqI(nWorkers)) { workers.emplace_back(workItem, workerID); } // Now join them for (auto && worker : workers) worker.join(); return 0; }
Int_t mp001_fillHistos(UInt_t nWorkers = 4) { // Total amount of numbers const UInt_t nNumbers = 20000000U; // We define our work item auto workItem = [nNumbers](UInt_t workerID) { // One generator, file and ntuple per worker TRandom3 workerRndm(workerID); // Change the seed TFile f(Form("myFile_%u.root", workerID), "RECREATE"); TH1F h(Form("myHisto_%u", workerID), "The Histogram", 64, -4, 4); for (UInt_t i = 0; i < nNumbers; ++i) { h.Fill(workerRndm.Gaus()); } h.Write(); return 0; }; // Create the pool of workers TProcPool workers(nWorkers); // Fill the pool with work std::forward_list<UInt_t> workerIDs(nWorkers); std::iota(std::begin(workerIDs), std::end(workerIDs), 0); workers.Map(workItem, workerIDs); return 0; }
Int_t mtbb101_fillNtuples() { ROOT::EnableThreadSafety(); // No nuisance for batch execution gROOT->SetBatch(); // Perform the operation sequentially --------------------------------------- // Create a random generator and and Ntuple to hold the numbers TRandom3 rndm(1); TFile ofile("mp101_singleCore.root", "RECREATE"); TNtuple randomNumbers("singleCore", "Random Numbers", "r"); fillRandom(randomNumbers, rndm, nNumbers); randomNumbers.Write(); ofile.Close(); // We now go MP! ------------------------------------------------------------ // We define our work item auto workItem = [](UInt_t workerID) { // One generator, file and ntuple per worker TRandom3 workerRndm(workerID); // Change the seed TFile ofile(Form("mp101_multiCore_%u.root", workerID), "RECREATE"); TNtuple workerRandomNumbers("multiCore", "Random Numbers", "r"); fillRandom(workerRandomNumbers, workerRndm, workSize); workerRandomNumbers.Write(); return 0; }; // Create the pool of workers ThreadPool pool(nThreads); // Fill the pool with work pool.Map(workItem, ROOT::TSeqI(nThreads)); return 0; }
Int_t mt101_fillNtuples(UInt_t nWorkers = 4) { // No nuisance for batch execution gROOT->SetBatch(); // Total amount of numbers const UInt_t nNumbers = 20000000U; // A simple function to fill ntuples randomly auto fillRandom = [](TNtuple & ntuple, TRandom3 & rndm, UInt_t n) { for (UInt_t i = 0; i < n; ++i) ntuple.Fill(rndm.Gaus()); }; // Perform the operation sequentially --------------------------------------- // Create a random generator and and Ntuple to hold the numbers TRandom3 rndm(1); TFile ofile("mt101_singleCore.root", "RECREATE"); TNtuple randomNumbers("singleCore", "Random Numbers", "r"); // Now let's measure how much time we need to fill it up { TimerRAII t("Sequential execution"); fillRandom(randomNumbers, rndm, nNumbers); randomNumbers.Write(); } // We now go MT! ------------------------------------------------------------ // The first, fundamental operation to be performed in order to make ROOT // thread-aware. ROOT::EnableThreadSafety(); // We define our work item auto workItem = [&fillRandom](UInt_t workerID, UInt_t workSize) { // One generator, file and ntuple per worker TRandom3 workerRndm(workerID); // Change the seed TFile ofile(Form("mt101_multiCore_%u.root", workerID), "RECREATE"); TNtuple workerRandomNumbers("multiCore", "Random Numbers", "r"); fillRandom(workerRandomNumbers, workerRndm, workSize); workerRandomNumbers.Write(); }; // Create the collection which will hold the threads, our "pool" std::vector<std::thread> workers; // We measure time here as well { TimerRAII t("Parallel execution"); // We split the work in equal parts const auto workSize = nNumbers / nWorkers; // Fill the "pool" with workers for (UInt_t workerID = 0; workerID < nWorkers; ++workerID) { workers.emplace_back(workItem, workerID, workSize); } // Now join them for (auto && worker : workers) worker.join(); } return 0; }