{ "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 }