{
"cells": [
{
"cell_type": "markdown",
"id": "467628a3",
"metadata": {},
"source": [
"# ntpl007_mtFill\n",
"Example of multi-threaded writes using multiple REntry objects\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:14 PM."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "d3d91184",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:15:02.463372Z",
"iopub.status.busy": "2026-05-19T20:15:02.463263Z",
"iopub.status.idle": "2026-05-19T20:15:02.471384Z",
"shell.execute_reply": "2026-05-19T20:15:02.470592Z"
}
},
"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 = \"ntpl007_mtFill.root\";\n",
"\n",
"constexpr int kNWriterThreads = 4;\n",
"\n",
"constexpr int kNEventsPerThread = 25000;"
]
},
{
"cell_type": "markdown",
"id": "2720fbd0",
"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": "3a8c882a",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:15:02.472717Z",
"iopub.status.busy": "2026-05-19T20:15:02.472585Z",
"iopub.status.idle": "2026-05-19T20:15:02.567317Z",
"shell.execute_reply": "2026-05-19T20:15:02.566633Z"
}
},
"outputs": [],
"source": [
"%%cpp -d\n",
"void FillData(std::unique_ptr entry, ROOT::RNTupleWriter *writer)\n",
"{\n",
" // Protect the writer->Fill() call\n",
" static std::mutex gLock;\n",
"\n",
" static std::atomic gThreadId;\n",
" const auto threadId = ++gThreadId;\n",
"\n",
" auto prng = std::make_unique();\n",
" prng->SetSeed();\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",
" std::lock_guard guard(gLock);\n",
" writer->Fill(*entry);\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "d4b4ef1b",
"metadata": {},
"source": [
" Generate kNEvents with multiple threads in kNTupleFileName\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "97d9b7b3",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:15:02.568758Z",
"iopub.status.busy": "2026-05-19T20:15:02.568620Z",
"iopub.status.idle": "2026-05-19T20:15:02.842021Z",
"shell.execute_reply": "2026-05-19T20:15:02.841442Z"
}
},
"outputs": [],
"source": [
"%%cpp -d\n",
"void Write()\n",
"{\n",
" // Create the data model\n",
" auto model = ROOT::RNTupleModel::Create();\n",
" model->MakeField(\"id\");\n",
" model->MakeField>(\"vpx\");\n",
" model->MakeField>(\"vpy\");\n",
" model->MakeField>(\"vpz\");\n",
"\n",
" // We hand-over the data model to a newly created ntuple of name \"NTuple\", stored in kNTupleFileName\n",
" auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), \"NTuple\", kNTupleFileName);\n",
"\n",
" std::vector> entries;\n",
" std::vector threads;\n",
" for (int i = 0; i < kNWriterThreads; ++i)\n",
" entries.emplace_back(writer->CreateEntry());\n",
" for (int i = 0; i < kNWriterThreads; ++i)\n",
" threads.emplace_back(FillData, std::move(entries[i]), 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": "60794adb",
"metadata": {},
"source": [
" For all of the events, histogram only one of the written vectors\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "d5fe35b7",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:15:02.843623Z",
"iopub.status.busy": "2026-05-19T20:15:02.843486Z",
"iopub.status.idle": "2026-05-19T20:15:02.947946Z",
"shell.execute_reply": "2026-05-19T20:15:02.947367Z"
}
},
"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": "33153b07",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:15:02.949427Z",
"iopub.status.busy": "2026-05-19T20:15:02.949301Z",
"iopub.status.idle": "2026-05-19T20:15:04.110118Z",
"shell.execute_reply": "2026-05-19T20:15:04.109444Z"
}
},
"outputs": [],
"source": [
"Write();\n",
"Read();"
]
},
{
"cell_type": "markdown",
"id": "2d21e1c7",
"metadata": {},
"source": [
"Draw all canvases "
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "03824c59",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:15:04.112367Z",
"iopub.status.busy": "2026-05-19T20:15:04.112238Z",
"iopub.status.idle": "2026-05-19T20:15:04.316994Z",
"shell.execute_reply": "2026-05-19T20:15:04.316245Z"
}
},
"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
}