TkN 2.7
Toolkit for Nuclei
Loading...
Searching...
No Matches
tkn-thread-example.cpp

Simple example using TkN with multi-threading

TkN can be used in a multi-threaded read-only program. In this example, a large number of nuclei are randomly produced and one state is selected for each nucleus. If the example is linked with ROOT, every worker fills private histograms; they are merged only after all workers have joined.

By default, the example preloads all default level schemes before starting the workers:

gmanager->preload_level_schemes(true);

This preloads only the default dataset, normally ADOPTED LEVELS from ENSDF. Use --no-preload to exercise thread-safe lazy loading instead. First loads are serialised because the current low-level database cursor is shared; once a scheme has been published, read access is concurrent.

The following indicative wall times were measured with the ROOT-free Release build on an Apple M4 Pro (14 cores), using the TkN 2.7 database and 107 events. They are one representative run, not a performance guarantee. Initialisation took about 0.07 s without preloading; full preloading took about 2.3 s.

workers event time, preloaded (s) total time, preloaded (s) event time, lazy (s) total time, lazy (s)
1 7.26 9.51 9.29 9.37
2 3.77 6.14 5.80 5.87
4 1.92 4.19 4.23 4.30
8 1.21 3.49 3.42 3.49
16 0.96 3.24 3.24 3.32

Preloading moves the serial database work before the timed event loop. Lazy loading avoids loading unused nuclei, but the first access to each encountered nucleus is included in the event time.

user-guide

To compile this example, use:

g++ tkn-thread-example.cpp -o tkn-thread-example `tkn-config --cflags --linklibs` -lpthread

use tkn-thread-example --help

*******************************************
*** tkn-thread-example user-guide ***
*******************************************
this utility allows to test the multi thread compatibility
supported options:
--evts N number of random nuclei to process (default 1e6)
--workers N number of workers to be used (default 1)
--no-preload do not preload level schemes before starting workers

For example, to process 107 events on 8 workers with lazy loading, use:

./tkn-thread-example --workers 8 --evts 1e7 --no-preload

When linked with ROOT, the merged histograms are stored in tkn-thread.root. ROOT file writing is performed after the event-processing timer stops.

Source code

#include <algorithm>
#include <chrono>
#include <getopt.h>
#include <iomanip>
#include <iostream>
#include <memory>
#include <random>
#include <thread>
#include <vector>
#include "tkmanager.h"
#ifdef HAS_ROOT
#include "TH2F.h"
#include "TFile.h"
#include "TROOT.h"
#endif
#define no_argument 0
#define required_argument 1
#define optional_argument 2
using namespace tkn;
using namespace std;
struct thread_results {
#ifdef HAS_ROOT
explicit thread_results(int worker)
: hNucChart(new TH2F(tkstring::Form("NucChart_worker_%d", worker).data(), "NucChart", 90, 0, 180, 60, 0, 120)),
hNucChartBetaminus(new TH2F(tkstring::Form("NucChartBetaminus_worker_%d", worker).data(), "NucChartBetaminus", 90, 0, 180, 60, 0, 120)),
hNucChartBetaplus(new TH2F(tkstring::Form("NucChartBetaplus_worker_%d", worker).data(), "NucChartBetaplus", 90, 0, 180, 60, 0, 120)),
hNucChartStable(new TH2F(tkstring::Form("NucChartStable_worker_%d", worker).data(), "NucChartStable", 90, 0, 180, 60, 0, 120)),
hRandExcitedState(new TH1F(tkstring::Form("RandExcitedState_worker_%d", worker).data(), "RandExcitedState", 20000, 0, 20000))
{
hNucChart->SetDirectory(nullptr);
hNucChartBetaminus->SetDirectory(nullptr);
hNucChartBetaplus->SetDirectory(nullptr);
hNucChartStable->SetDirectory(nullptr);
hRandExcitedState->SetDirectory(nullptr);
}
unique_ptr<TH2F> hNucChart;
unique_ptr<TH2F> hNucChartBetaminus;
unique_ptr<TH2F> hNucChartBetaplus;
unique_ptr<TH2F> hNucChartStable;
unique_ptr<TH1F> hRandExcitedState;
#else
explicit thread_results(int) {}
#endif
};
void a_thread(int ithread, long NEvents, thread_results *results);
void print_help();
// To compile this code, use: g++ tkn-thread-example.cpp -o tkn-thread `tkn-config --cflags --linklibs`
void print_help() {
glog << blue << high_intensity << " *******************************************" << do_endl;
glog << blue << high_intensity << " *** tkn-thread-example user-guide ***" << do_endl;
glog << blue << high_intensity << " *******************************************" << do_endl;
glog << do_endl;
glog << "this utility allows to test the multi thread compatibility" << do_endl;
glog << do_endl;
glog << "supported options:" << do_endl;
glog << " --evts N number of random nuclei to process (default 1e6)" << do_endl;
glog << " --workers N number of workers to be used (default 1)" << do_endl;
glog << " --no-preload do not preload level schemes before starting workers" << do_endl;
glog << do_endl;
}
int main(int argc, char **argv) {
int option_index = 0;
static struct option long_options[] = {
{"evts", required_argument, nullptr, 'e'},
{"workers", required_argument, nullptr, 'w'},
{"no-preload", no_argument, nullptr, 'n'},
{"help", no_argument, nullptr, 'h'},
{nullptr, 0, nullptr, 0}};
long NEvents=1e6;
int NWorkers = 1;
bool preload = true;
while (true) {
int c = getopt_long (argc, argv, "e:w:nh", long_options, &option_index);
if (c == -1) break;
switch (c) {
case 'e':
NEvents = atof(optarg);
break;
case 'w':
NWorkers = atoi(optarg);
break;
case 'n':
preload = false;
break;
case 'h':
print_help();
exit(EXIT_SUCCESS);
}
}
if (NEvents < 0 || NWorkers < 1) {
glog << error << "--evts must be non-negative and --workers must be positive" << do_endl;
return EXIT_FAILURE;
}
cout << "***********************************************************************************" << endl;
cout << "* Randomly produce a nucleus and extract a random excited state in multi thread *" << endl;
cout << "***********************************************************************************" << endl;
cout<<endl;
cout << "Nevents : " << NEvents << endl;
cout << "N workers: " << NWorkers << endl;
cout << "Preload : " << (preload ? "yes" : "no") << endl;
// to remove printouts due to empty level schemes
glog.set_warnings(false);
auto tsload = std::chrono::high_resolution_clock::now();
// Initialise the manager before starting workers. Level schemes are
// either preloaded here or loaded lazily on first use by the workers.
(void)gmanager->get_nuclei();
if (preload) gmanager->preload_level_schemes(true);
cout<<endl;
auto tstart = std::chrono::high_resolution_clock::now();
vector<thread> threads;
vector<unique_ptr<thread_results>> results;
threads.reserve(NWorkers);
results.reserve(NWorkers);
for(int i=0 ; i<NWorkers ; i++) {
results.emplace_back(make_unique<thread_results>(i));
const long worker_events = NEvents / NWorkers + (i < NEvents % NWorkers ? 1 : 0);
threads.emplace_back(thread(a_thread, i, worker_events, results.back().get()));
}
for(int i=0 ; i<NWorkers ; i++) {
threads.at(i).join();
}
auto tstop = std::chrono::high_resolution_clock::now();
#ifdef HAS_ROOT
TFile fout("tkn-thread.root", "recreate");
TH2F hNucChart("NucChart", "NucChart", 90, 0, 180, 60, 0, 120);
TH2F hNucChartBetaminus("NucChartBetaminus", "NucChartBetaminus", 90, 0, 180, 60, 0, 120);
TH2F hNucChartBetaplus("NucChartBetaplus", "NucChartBetaplus", 90, 0, 180, 60, 0, 120);
TH2F hNucChartStable("NucChartStable", "NucChartStable", 90, 0, 180, 60, 0, 120);
TH1F hRandExcitedState("RandExcitedState", "RandExcitedState", 20000, 0, 20000);
for (const auto &worker : results) {
hNucChart.Add(worker->hNucChart.get());
hNucChartBetaminus.Add(worker->hNucChartBetaminus.get());
hNucChartBetaplus.Add(worker->hNucChartBetaplus.get());
hNucChartStable.Add(worker->hNucChartStable.get());
hRandExcitedState.Add(worker->hRandExcitedState.get());
}
hNucChart.Write();
hNucChartBetaminus.Write();
hNucChartBetaplus.Write();
hNucChartStable.Write();
hRandExcitedState.Write();
glog << info << "tkn-thread.root created with content: " << do_endl;
fout.ls();
fout.Close();
#endif
auto dtload = std::chrono::duration<double, std::milli>(tstart-tsload).count() * 0.001;
auto dt_process = std::chrono::duration<double, std::milli>(tstop-tstart).count() * 0.001;
auto dt = std::chrono::duration<double, std::milli>(tstop-tsload).count() * 0.001;
cout<<endl;
cout << fixed << setprecision(4) << "Initialization/preload wall time: " << dtload << " s\n";
cout << fixed << setprecision(4) << "Event processing wall time : " << dt_process << " s\n";
cout << fixed << setprecision(4) << "Total measured wall time : " << dt << " s\n";
}
void a_thread(int ithread, long NEvents, thread_results *results) {
// Create a random number generator engine
std::random_device rd;
std::mt19937 gen(rd());
// Create a uniform integer distribution on the number of nuclei
std::uniform_int_distribution<> dist_nuc(0, gmanager->get_nuclei().size()-1);
const long Fact = std::max(1L, NEvents / 100);
for(long entry=0 ; entry<NEvents ; entry++) {
if(ithread==0 && entry%Fact==0) {
cout<<"\r"<<"Analysis progress : "<<setw(3)<<setprecision(3)<<(double)entry/(double)NEvents*100<<" %"<<flush;
}
long inuc = dist_nuc(gen);
auto nuc = gmanager->get_nuclei().at(inuc);
const auto level_scheme = nuc->get_level_scheme();
const auto &levels = level_scheme->get_levels();
if(levels.size()>1) {
// Create a uniform integer distribution on the number of levels
std::uniform_int_distribution<> dist_lev(0, levels.size()-1);
long ilvl = dist_lev(gen);
double elev = levels.at(ilvl)->get_energy(tkn::tkunit_manager::keV,true);
#ifdef HAS_ROOT
results->hNucChart->Fill(nuc->get_n(),nuc->get_z());
if(nuc->has_property("QbetaMinus") && nuc->get("QbetaMinus")->get_value()>0) results->hNucChartBetaminus->Fill(nuc->get_n(),nuc->get_z());
if(nuc->has_property("QpositronEmission") && nuc->get("QpositronEmission")->get_value()>0) results->hNucChartBetaplus->Fill(nuc->get_n(),nuc->get_z());
if(nuc->is_stable()) results->hNucChartStable->Fill(nuc->get_n(),nuc->get_z());
if(elev>0) results->hRandExcitedState->Fill(elev);
#endif
}
}
}
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
Definition tkstring.h:33
Definition tklog.cpp:16
tklog & info(tklog &log)
Definition tklog.h:313
tklog & error(tklog &log)
Definition tklog.h:344
tklog & do_endl(tklog &log)
Definition tklog.h:212
tklog & blue(tklog &log)
Definition tklog.h:247
tklog & high_intensity(tklog &log)
Colors.
Definition tklog.h:232