{ "cells": [ { "cell_type": "markdown", "id": "cfad7d0f", "metadata": {}, "source": [ "# ntpl009_parallelWriter\n", "Example of multi-threaded writes using RNTupleParallelWriter. Adapted from the ntpl007_mtFill tutorial.\n", "\n", "\n", "\n", "\n", "**Author:** The ROOT Team \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:15 PM." ] }, { "cell_type": "code", "execution_count": 1, "id": "e9805a3f", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:11.162202Z", "iopub.status.busy": "2026-05-19T20:15:11.162085Z", "iopub.status.idle": "2026-05-19T20:15:11.170096Z", "shell.execute_reply": "2026-05-19T20:15:11.169634Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "#include \n", "#include \n", "#include \n", "#include \n", "\n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "\n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "\n", "constexpr char const *kNTupleFileName = \"ntpl009_parallelWriter.root\";\n", "\n", "constexpr int kNWriterThreads = 4;\n", "\n", "constexpr int kNEventsPerThread = 25000;" ] }, { "cell_type": "markdown", "id": "8d680fcd", "metadata": {}, "source": [ " Where to store the ntuple of this example\n", "Number of parallel threads to fill the ntuple\n", "Number of events to generate is kNEventsPerThread * kNWriterThreads\n", "Thread function to generate and write events\n", " " ] }, { "cell_type": "code", "execution_count": 2, "id": "5ebb7b68", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:11.171316Z", "iopub.status.busy": "2026-05-19T20:15:11.171198Z", "iopub.status.idle": "2026-05-19T20:15:11.277188Z", "shell.execute_reply": "2026-05-19T20:15:11.276645Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "void FillData(ROOT::RNTupleParallelWriter *writer)\n", "{\n", " static std::atomic gThreadId;\n", " const auto threadId = ++gThreadId;\n", "\n", " auto prng = std::make_unique();\n", " prng->SetSeed();\n", "\n", " auto fillContext = writer->CreateFillContext();\n", " auto entry = fillContext->CreateEntry();\n", "\n", " auto id = entry->GetPtr(\"id\");\n", " *id = threadId;\n", " auto vpx = entry->GetPtr>(\"vpx\");\n", " auto vpy = entry->GetPtr>(\"vpy\");\n", " auto vpz = entry->GetPtr>(\"vpz\");\n", "\n", " for (int i = 0; i < kNEventsPerThread; i++) {\n", " vpx->clear();\n", " vpy->clear();\n", " vpz->clear();\n", "\n", " int npx = static_cast(prng->Rndm(1) * 15);\n", " // Set the field data for the current event\n", " for (int j = 0; j < npx; ++j) {\n", " float px, py, pz;\n", " prng->Rannor(px, py);\n", " pz = px * px + py * py;\n", "\n", " vpx->emplace_back(px);\n", " vpy->emplace_back(py);\n", " vpz->emplace_back(pz);\n", " }\n", "\n", " fillContext->Fill(*entry);\n", " }\n", "}" ] }, { "cell_type": "markdown", "id": "d84cace0", "metadata": {}, "source": [ " Generate kNEvents with multiple threads in kNTupleFileName\n", " " ] }, { "cell_type": "code", "execution_count": 3, "id": "69d436d5", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:11.278633Z", "iopub.status.busy": "2026-05-19T20:15:11.278497Z", "iopub.status.idle": "2026-05-19T20:15:11.522175Z", "shell.execute_reply": "2026-05-19T20:15:11.521647Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "void Write()\n", "{\n", " // Create the data model\n", " auto model = ROOT::RNTupleModel::CreateBare();\n", " model->MakeField(\"id\");\n", " model->MakeField>(\"vpx\");\n", " model->MakeField>(\"vpy\");\n", " model->MakeField>(\"vpz\");\n", "\n", " // Create RNTupleWriteOptions to make the writing commit multiple clusters (so that \"Entry Id vs Thread Id\" shows the\n", " // interleaved clusters).\n", " ROOT::RNTupleWriteOptions options;\n", " options.SetApproxZippedClusterSize(1024 * 1024);\n", "\n", " // We hand-over the data model to a newly created ntuple of name \"NTuple\", stored in kNTupleFileName\n", " auto writer = ROOT::RNTupleParallelWriter::Recreate(std::move(model), \"NTuple\", kNTupleFileName, options);\n", "\n", " std::vector threads;\n", " for (int i = 0; i < kNWriterThreads; ++i)\n", " threads.emplace_back(FillData, writer.get());\n", " for (int i = 0; i < kNWriterThreads; ++i)\n", " threads[i].join();\n", "\n", " // The writer unique pointer goes out of scope here. On destruction, the writer flushes unwritten data to disk\n", " // and closes the attached ROOT file.\n", "}" ] }, { "cell_type": "markdown", "id": "273459aa", "metadata": {}, "source": [ " For all of the events, histogram only one of the written vectors\n", " " ] }, { "cell_type": "code", "execution_count": 4, "id": "e061f97c", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:11.523872Z", "iopub.status.busy": "2026-05-19T20:15:11.523738Z", "iopub.status.idle": "2026-05-19T20:15:11.622067Z", "shell.execute_reply": "2026-05-19T20:15:11.621445Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "void Read()\n", "{\n", " auto reader = ROOT::RNTupleReader::Open(\"NTuple\", kNTupleFileName);\n", " auto viewVpx = reader->GetView(\"vpx._0\");\n", "\n", " gStyle->SetOptStat(0);\n", "\n", " TCanvas *c1 = new TCanvas(\"c2\", \"Multi-Threaded Filling Example\", 200, 10, 1500, 500);\n", " c1->Divide(2, 1);\n", "\n", " c1->cd(1);\n", " TH1F h(\"h\", \"This is the px distribution\", 100, -4, 4);\n", " h.SetFillColor(48);\n", " // Iterate through all values of vpx in all events\n", " for (auto i : viewVpx.GetFieldRange())\n", " h.Fill(viewVpx(i));\n", " // Prevent the histogram from disappearing\n", " h.DrawCopy();\n", "\n", " c1->cd(2);\n", " auto nEvents = reader->GetNEntries();\n", " auto viewId = reader->GetView(\"id\");\n", " TH2F hFillSequence(\"\", \"Entry Id vs Thread Id;Entry Sequence Number;Filling Thread\", 100, 0, nEvents, 100, 0,\n", " kNWriterThreads + 1);\n", " for (auto i : reader->GetEntryRange())\n", " hFillSequence.Fill(i, viewId(i));\n", " hFillSequence.DrawCopy();\n", "}" ] }, { "cell_type": "code", "execution_count": 5, "id": "6976c11b", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:11.623505Z", "iopub.status.busy": "2026-05-19T20:15:11.623379Z", "iopub.status.idle": "2026-05-19T20:15:12.592898Z", "shell.execute_reply": "2026-05-19T20:15:12.591771Z" } }, "outputs": [], "source": [ "Write();\n", "Read();" ] }, { "cell_type": "markdown", "id": "3ded9727", "metadata": {}, "source": [ "Draw all canvases " ] }, { "cell_type": "code", "execution_count": 6, "id": "9da1c653", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:12.594553Z", "iopub.status.busy": "2026-05-19T20:15:12.594407Z", "iopub.status.idle": "2026-05-19T20:15:12.799967Z", "shell.execute_reply": "2026-05-19T20:15:12.799068Z" } }, "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 }