{
"cells": [
{
"cell_type": "markdown",
"id": "912f83a4",
"metadata": {},
"source": [
"# mt_parallelHistoFill\n",
"Fill histogram in parallel with a multithreaded approach\n",
"using TThreadedObject and TThreadedObject::SnapshotMerge.\n",
"\n",
"The difference with the multiprocessing case,\n",
"see mp_parallelHistoFill, is that we cannot count on \n",
"the copy-on-write mechanism here. Instead, we need to protect \n",
"the histogram resource with a TThreadedObject class. \n",
"The result of the filling is monitored with the *SnapshotMerge*\n",
"method. This method is not thread safe: in the presence of \n",
"ROOT histograms, the system will not crash but the result \n",
"is not uniquely defined.\n",
"\n",
"\n",
"\n",
"\n",
"**Author:** Danilo Piparo \n",
"This notebook tutorial was automatically generated with ROOTBOOK-izer from the macro found in the ROOT repository on Tuesday, May 19, 2026 at 08:11 PM."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "754da06c",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:11:12.117293Z",
"iopub.status.busy": "2026-05-19T20:11:12.117164Z",
"iopub.status.idle": "2026-05-19T20:11:12.764443Z",
"shell.execute_reply": "2026-05-19T20:11:12.763687Z"
}
},
"outputs": [],
"source": [
"const UInt_t poolSize = 4U;"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "88b38676",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:11:12.788625Z",
"iopub.status.busy": "2026-05-19T20:11:12.788447Z",
"iopub.status.idle": "2026-05-19T20:11:13.016838Z",
"shell.execute_reply": "2026-05-19T20:11:13.006252Z"
}
},
"outputs": [],
"source": [
"ROOT::EnableThreadSafety();"
]
},
{
"cell_type": "markdown",
"id": "5e2dc2c1",
"metadata": {},
"source": [
"The concrete histogram instances are created in each thread\n",
"lazily, i.e. only if a method is invoked."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "1755dea1",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:11:13.036293Z",
"iopub.status.busy": "2026-05-19T20:11:13.036142Z",
"iopub.status.idle": "2026-05-19T20:11:13.415286Z",
"shell.execute_reply": "2026-05-19T20:11:13.414571Z"
}
},
"outputs": [],
"source": [
"ROOT::TThreadedObject ts_h(\"myHist\", \"Filled in parallel\", 128, -8, 8);"
]
},
{
"cell_type": "markdown",
"id": "5e14b472",
"metadata": {},
"source": [
"The function used to fill the histograms in each thread."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "865fd72b",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:11:13.430325Z",
"iopub.status.busy": "2026-05-19T20:11:13.430176Z",
"iopub.status.idle": "2026-05-19T20:11:13.635000Z",
"shell.execute_reply": "2026-05-19T20:11:13.634495Z"
}
},
"outputs": [],
"source": [
"auto fillRandomHisto = [&](int seed = 0) {\n",
" TRandom3 rndm(seed);\n",
" // IMPORTANT!\n",
" // It is important to realise that a copy on the stack of the object we\n",
" // would like to perform operations on is the most efficient way of\n",
" // accessing it, in particular in presence of a tight loop like the one\n",
" // below where any overhead put on top of the Fill function call would\n",
" // have an impact.\n",
" auto histogram = ts_h.Get();\n",
" for (auto i : ROOT::TSeqI(1000000)) {\n",
" histogram->Fill(rndm.Gaus(0, 1));\n",
" }\n",
"};"
]
},
{
"cell_type": "markdown",
"id": "c00907a6",
"metadata": {},
"source": [
"The seeds for the random number generators."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "ef4cda02",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:11:13.642236Z",
"iopub.status.busy": "2026-05-19T20:11:13.642099Z",
"iopub.status.idle": "2026-05-19T20:11:13.849045Z",
"shell.execute_reply": "2026-05-19T20:11:13.848662Z"
}
},
"outputs": [],
"source": [
"auto seeds = ROOT::TSeqI(1, poolSize + 1);\n",
"\n",
"std::vector pool;"
]
},
{
"cell_type": "markdown",
"id": "13cf1336",
"metadata": {},
"source": [
"A monitoring thread. This is here only to illustrate the functionality of\n",
"the SnapshotMerge method.\n",
"It allows \"to spy\" the multithreaded calculation without the need\n",
"of interrupting it."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "212cb2d3",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:11:13.851012Z",
"iopub.status.busy": "2026-05-19T20:11:13.850892Z",
"iopub.status.idle": "2026-05-19T20:11:14.240434Z",
"shell.execute_reply": "2026-05-19T20:11:14.239954Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Entries for the snapshot 0\n",
"Entries for the snapshot 0\n",
"Entries for the snapshot 0\n",
"Entries for the snapshot 0\n",
"Entries for the snapshot 0\n"
]
}
],
"source": [
"auto monitor = [&]() {\n",
" for (auto i : ROOT::TSeqI(5)) {\n",
" std::this_thread::sleep_for(std::chrono::duration(500));\n",
" auto h = ts_h.SnapshotMerge();\n",
" std::cout << \"Entries for the snapshot \" << h->GetEntries() << std::endl;\n",
" }\n",
"};\n",
"pool.emplace_back(monitor);"
]
},
{
"cell_type": "markdown",
"id": "3efba69a",
"metadata": {},
"source": [
"The threads filling the histograms"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "0582328b",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:11:14.242122Z",
"iopub.status.busy": "2026-05-19T20:11:14.241984Z",
"iopub.status.idle": "2026-05-19T20:11:14.475824Z",
"shell.execute_reply": "2026-05-19T20:11:14.460695Z"
}
},
"outputs": [],
"source": [
"for (auto seed : ROOT::TSeqI(seeds)) {\n",
" pool.emplace_back(fillRandomHisto, seed);\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "ebc34e1e",
"metadata": {},
"source": [
"Wait for the threads to finish"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "55dd1cee",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:11:14.498433Z",
"iopub.status.busy": "2026-05-19T20:11:14.498277Z",
"iopub.status.idle": "2026-05-19T20:11:14.722719Z",
"shell.execute_reply": "2026-05-19T20:11:14.713661Z"
}
},
"outputs": [],
"source": [
"for (auto &&t : pool)\n",
" t.join();"
]
},
{
"cell_type": "markdown",
"id": "84310a76",
"metadata": {},
"source": [
"Merge the final result"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "59ca2a9e",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:11:14.724639Z",
"iopub.status.busy": "2026-05-19T20:11:14.724480Z",
"iopub.status.idle": "2026-05-19T20:11:14.942795Z",
"shell.execute_reply": "2026-05-19T20:11:14.942268Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Entries for the total sum 4e+06\n"
]
}
],
"source": [
"auto sumRandomHisto = ts_h.Merge();\n",
"\n",
"std::cout << \"Entries for the total sum \" << sumRandomHisto->GetEntries() << std::endl;\n",
"\n",
"auto c = new TCanvas();\n",
"sumRandomHisto->DrawClone();\n",
"return 0;"
]
},
{
"cell_type": "markdown",
"id": "8f648a7f",
"metadata": {},
"source": [
"Draw all canvases "
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "df517527",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:11:14.952340Z",
"iopub.status.busy": "2026-05-19T20:11:14.952202Z",
"iopub.status.idle": "2026-05-19T20:11:15.163562Z",
"shell.execute_reply": "2026-05-19T20:11:15.163161Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"\n",
"
\n",
"\n",
"\n",
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"gROOT->GetListOfCanvases()->Draw()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "ROOT C++",
"language": "c++",
"name": "root"
},
"language_info": {
"codemirror_mode": "text/x-c++src",
"file_extension": ".C",
"mimetype": " text/x-c++src",
"name": "c++"
}
},
"nbformat": 4,
"nbformat_minor": 5
}