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