Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RPageStorageFile.cxx
Go to the documentation of this file.
1/// \file RPageStorageFile.cxx
2/// \author Jakob Blomer <jblomer@cern.ch>
3/// \date 2019-11-25
4
5/*************************************************************************
6 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13#include <ROOT/RCluster.hxx>
14#include <ROOT/RLogger.hxx>
16#include <ROOT/RNTupleModel.hxx>
18#include <ROOT/RNTupleZip.hxx>
19#include <ROOT/RPage.hxx>
21#include <ROOT/RPagePool.hxx>
23#include <ROOT/RRawFile.hxx>
25#include <ROOT/RNTupleTypes.hxx>
26#include <ROOT/RNTupleUtils.hxx>
27
28#include <RVersion.h>
29#include <TDirectory.h>
30#include <TError.h>
32
33#include <algorithm>
34#include <cstdio>
35#include <cstdlib>
36#include <cstring>
37#include <iterator>
38#include <limits>
39#include <utility>
40
41#include <functional>
42#include <mutex>
43
55
62
69
76
83
84ROOT::Internal::RPageSinkFile::RPageSinkFile(std::unique_ptr<ROOT::Internal::RNTupleFileWriter> writer,
85 const ROOT::RNTupleWriteOptions &options)
86 : RPageSinkFile(writer->GetNTupleName(), options)
87{
88 fWriter = std::move(writer);
89}
90
92
94{
96 auto szZipHeader =
97 RNTupleCompressor::Zip(serializedHeader, length, GetWriteOptions().GetCompression(), zipBuffer.get());
98 fWriter->WriteNTupleHeader(zipBuffer.get(), szZipHeader, length);
99}
100
103{
105
106 auto fnAddStreamerInfo = [this](const ROOT::RFieldBase *field) {
107 const TClass *cl = nullptr;
108 if (auto classField = dynamic_cast<const RClassField *>(field)) {
109 cl = classField->GetClass();
110 } else if (auto streamerField = dynamic_cast<const RStreamerField *>(field)) {
111 cl = streamerField->GetClass();
112 } else if (auto soaField = dynamic_cast<const ROOT::Experimental::RSoAField *>(field)) {
113 cl = soaField->GetSoAClass();
114 }
115 if (!cl)
116 return;
117
118 auto streamerInfo = cl->GetStreamerInfo(field->GetTypeVersion());
119 if (!streamerInfo) {
120 throw RException(R__FAIL(std::string("cannot get streamerInfo for ") + cl->GetName() + " [" +
121 std::to_string(field->GetTypeVersion()) + "]"));
122 }
123 fInfosOfClassFields[streamerInfo->GetNumber()] = streamerInfo;
124 };
125
126 for (const auto field : changeset.fAddedFields) {
128 for (const auto &subField : *field) {
130 }
131 }
132}
133
136{
137 std::uint64_t offsetData;
138 {
139 RNTupleAtomicTimer timer(fCounters->fTimeWallWrite, fCounters->fTimeCpuWrite);
140 offsetData = fWriter->WriteBlob(sealedPage.GetBuffer(), sealedPage.GetBufferSize(), bytesPacked);
141 }
142
144 result.SetPosition(offsetData);
145 result.SetNBytesOnStorage(sealedPage.GetDataSize());
146 fCounters->fNPageCommitted.Inc();
147 fCounters->fSzWritePayload.Add(sealedPage.GetBufferSize());
148 fNBytesCurrentCluster += sealedPage.GetBufferSize();
149 return result;
150}
151
154{
155 const auto nBits = fDescriptorBuilder.GetDescriptor().GetColumnDescriptor(physicalColumnId).GetBitsOnStorage();
156 const auto bytesPacked = (nBits * sealedPage.GetNElements() + 7) / 8;
157 return WriteSealedPage(sealedPage, bytesPacked);
158}
159
161{
162 RNTupleAtomicTimer timer(fCounters->fTimeWallWrite, fCounters->fTimeCpuWrite);
163
164 std::uint64_t offset = fWriter->ReserveBlob(batch.fSize, batch.fBytesPacked);
165
166 locators.reserve(locators.size() + batch.fSealedPages.size());
167
168 for (const auto *pagePtr : batch.fSealedPages) {
169 fWriter->WriteIntoReservedBlob(pagePtr->GetBuffer(), pagePtr->GetBufferSize(), offset);
171 locator.SetPosition(offset);
172 locator.SetNBytesOnStorage(pagePtr->GetDataSize());
173 locators.push_back(locator);
174 offset += pagePtr->GetBufferSize();
175 }
176
177 fCounters->fNPageCommitted.Add(batch.fSealedPages.size());
178 fCounters->fSzWritePayload.Add(batch.fSize);
179 fNBytesCurrentCluster += batch.fSize;
180
181 batch.fSize = 0;
182 batch.fBytesPacked = 0;
183 batch.fSealedPages.clear();
184}
185
186std::vector<ROOT::RNTupleLocator>
187ROOT::Internal::RPageSinkFile::CommitSealedPageVImpl(std::span<RPageStorage::RSealedPageGroup> ranges,
188 const std::vector<bool> &mask)
189{
190 const std::uint64_t maxKeySize = fOptions->GetMaxKeySize();
191
193 std::vector<RNTupleLocator> locators;
194
195 std::size_t iPage = 0;
196 for (auto rangeIt = ranges.begin(); rangeIt != ranges.end(); ++rangeIt) {
197 auto &range = *rangeIt;
198 if (range.fFirst == range.fLast) {
199 // Skip empty ranges, they might not have a physical column ID!
200 continue;
201 }
202
203 const auto bitsOnStorage =
204 fDescriptorBuilder.GetDescriptor().GetColumnDescriptor(range.fPhysicalColumnId).GetBitsOnStorage();
205
206 for (auto sealedPageIt = range.fFirst; sealedPageIt != range.fLast; ++sealedPageIt, ++iPage) {
207 if (!mask[iPage])
208 continue;
209
210 const auto bytesPacked = (bitsOnStorage * sealedPageIt->GetNElements() + 7) / 8;
211
212 if (batch.fSize > 0 && batch.fSize + sealedPageIt->GetBufferSize() > maxKeySize) {
213 /**
214 * Adding this page would exceed maxKeySize. Since we always want to write into a single key
215 * with vectorized writes, we commit the current set of pages before proceeding.
216 * NOTE: we do this *before* checking if sealedPageIt->GetBufferSize() > maxKeySize to guarantee that
217 * we always flush the current batch before doing an individual WriteBlob. This way we
218 * preserve the assumption that a CommitBatch always contain a sequential set of pages.
219 */
220 CommitBatchOfPages(batch, locators);
221 }
222
223 if (sealedPageIt->GetBufferSize() > maxKeySize) {
224 // This page alone is bigger than maxKeySize: save it by itself, since it will need to be
225 // split into multiple keys.
226
227 // Since this check implies the previous check on batchSize + newSize > maxSize, we should
228 // already have committed the current batch before writing this page.
229 assert(batch.fSize == 0);
230
231 std::uint64_t offset =
232 fWriter->WriteBlob(sealedPageIt->GetBuffer(), sealedPageIt->GetBufferSize(), bytesPacked);
234 locator.SetPosition(offset);
235 locator.SetNBytesOnStorage(sealedPageIt->GetDataSize());
236 locators.push_back(locator);
237
238 fCounters->fNPageCommitted.Inc();
239 fCounters->fSzWritePayload.Add(sealedPageIt->GetBufferSize());
240 fNBytesCurrentCluster += sealedPageIt->GetBufferSize();
241
242 } else {
243 batch.fSealedPages.emplace_back(&(*sealedPageIt));
244 batch.fSize += sealedPageIt->GetBufferSize();
245 batch.fBytesPacked += bytesPacked;
246 }
247 }
248 }
249
250 if (batch.fSize > 0) {
251 CommitBatchOfPages(batch, locators);
252 }
253
254 return locators;
255}
256
258{
259 auto result = fNBytesCurrentCluster;
260 fNBytesCurrentCluster = 0;
261 return result;
262}
263
266{
268 auto szPageListZip =
269 RNTupleCompressor::Zip(serializedPageList, length, GetWriteOptions().GetCompression(), bufPageListZip.get());
270
272 result.SetNBytesOnStorage(szPageListZip);
273 result.SetPosition(fWriter->WriteBlob(bufPageListZip.get(), szPageListZip, length));
274 return result;
275}
276
279{
280 // Add the streamer info records from streamer fields: because of runtime polymorphism we may need to add additional
281 // types not covered by the type names of the class fields
282 for (const auto &extraTypeInfo : fDescriptorBuilder.GetDescriptor().GetExtraTypeInfoIterable()) {
284 continue;
285 // Ideally, we would avoid deserializing the streamer info records of the streamer fields that we just serialized.
286 // However, this happens only once at the end of writing and only when streamer fields are used, so the
287 // preference here is for code simplicity.
288 fInfosOfClassFields.merge(RNTupleSerializer::DeserializeStreamerInfos(extraTypeInfo.GetContent()).Unwrap());
289 }
290 fWriter->UpdateStreamerInfos(fInfosOfClassFields);
291
293 auto szFooterZip =
294 RNTupleCompressor::Zip(serializedFooter, length, GetWriteOptions().GetCompression(), bufFooterZip.get());
295 fWriter->WriteNTupleFooter(bufFooterZip.get(), szFooterZip, length);
296 return fWriter->Commit(GetWriteOptions().GetCompression());
297}
298
299std::unique_ptr<ROOT::Internal::RPageSink>
301{
302 auto writer = fWriter->CloneAsHidden(name);
303 auto cloned = std::unique_ptr<RPageSinkFile>(new RPageSinkFile(std::move(writer), opts));
304 return cloned;
305}
306
307////////////////////////////////////////////////////////////////////////////////
308
310{
311 return source.fAnchor ? &*source.fAnchor : nullptr;
312}
313
316{
317 EnableDefaultMetrics("RPageSourceFile");
318 fFileCounters = std::make_unique<RFileCounters>(RFileCounters{
319 *fMetrics.MakeCounter<RNTupleAtomicCounter *>("szSkip", "B",
320 "cumulative seek distance (excluding header/footer reads)"),
322 "szFile", "B", "total file size", fMetrics,
323 [this](const RNTupleMetrics &) -> std::pair<bool, double> {
324 if (fFileSize > 0)
325 return {true, static_cast<double>(fFileSize)};
326 return {false, -1.};
327 }),
329 "randomness", "",
330 "ratio of seek distance to bytes read (excluding file structure reads)", fMetrics,
331 [](const RNTupleMetrics &metrics) -> std::pair<bool, double> {
332 if (const auto szSkip = metrics.GetLocalCounter("szSkip")) {
333 if (const auto szReadPayload = metrics.GetLocalCounter("szReadPayload")) {
334 if (const auto szReadOverhead = metrics.GetLocalCounter("szReadOverhead")) {
335 auto totalRead = szReadPayload->GetValueAsInt() + szReadOverhead->GetValueAsInt();
336 if (totalRead > 0) {
337 return {true, (1. * szSkip->GetValueAsInt()) / totalRead};
338 }
339 }
340 }
341 }
342 return {false, -1.};
343 }),
345 "sparseness", "",
346 "ratio of bytes read to total file size (excluding file structure reads)", fMetrics,
347 [this](const RNTupleMetrics &metrics) -> std::pair<bool, double> {
348 if (fFileSize > 0) {
349 if (const auto szReadPayload = metrics.GetLocalCounter("szReadPayload")) {
350 if (const auto szReadOverhead = metrics.GetLocalCounter("szReadOverhead")) {
351 auto totalRead = szReadPayload->GetValueAsInt() + szReadOverhead->GetValueAsInt();
352 return {true, (1. * totalRead) / fFileSize};
353 }
354 }
355 }
356 return {false, -1.};
357 })});
358}
359
361 std::unique_ptr<ROOT::Internal::RRawFile> file,
362 const ROOT::RNTupleReadOptions &options)
363 : RPageSourceFile(ntupleName, options)
364{
365 fFile = std::move(file);
368}
369
370ROOT::Internal::RPageSourceFile::RPageSourceFile(std::string_view ntupleName, std::string_view path,
371 const ROOT::RNTupleReadOptions &options)
372 : RPageSourceFile(ntupleName, ROOT::Internal::RRawFile::Create(path), options)
373{
374}
375
376std::unique_ptr<ROOT::Internal::RPageSourceFile>
378{
379 if (!anchor.fFile)
380 throw RException(R__FAIL("This RNTuple object was not streamed from a ROOT file (TFile or descendant)"));
381
382 std::unique_ptr<ROOT::Internal::RRawFile> rawFile;
383 // For local TFiles, TDavixFile, TCurlFile, and TNetXNGFile, we want to open a new RRawFile to take advantage of the
384 // faster reading. We check the exact class name to avoid classes inheriting in ROOT (for example TMemFile) or in
385 // experiment frameworks.
386 const std::string className = anchor.fFile->IsA()->GetName();
387 const auto url = anchor.fFile->GetEndpointUrl();
388 if (className == "TFile") {
390 } else if (className == "TDavixFile" || className == "TCurlFile" || className == "TNetXNGFile") {
392 } else {
394 }
395
396 auto pageSource = std::make_unique<RPageSourceFile>("", std::move(rawFile), options);
397 pageSource->fAnchor = anchor;
398 // NOTE: fNTupleName gets set only upon Attach().
399 return pageSource;
400}
401
403{
404 StopClusterPoolBackgroundThread();
405}
406
407std::unique_ptr<ROOT::Internal::RPageSource>
409 const ROOT::RNTupleReadOptions &options)
410{
411 assert(anchorLink.fLocator.GetType() == RNTupleLocator::kTypeFile);
412
413 const auto anchorPos = anchorLink.fLocator.GetPosition<std::uint64_t>();
414 auto anchor =
415 fReader.GetNTupleProperAtOffset(anchorPos, anchorLink.fLocator.GetNBytesOnStorage(), anchorLink.fLength).Unwrap();
416 auto pageSource = std::make_unique<RPageSourceFile>("", fFile->Clone(), options);
417 pageSource->fAnchor = anchor;
418 // NOTE: fNTupleName gets set only upon Attach().
419 return pageSource;
420}
421
423{
424 // If we constructed the page source with (ntuple name, path), we need to find the anchor first.
425 // Otherwise, the page source was created by OpenFromAnchor()
426 if (!fAnchor) {
427 fAnchor = fReader.GetNTuple(fNTupleName).Unwrap();
428 }
429 fReader.SetMaxKeySize(fAnchor->GetMaxKeySize());
430
431 fDescriptorBuilder.SetVersion(fAnchor->GetVersionEpoch(), fAnchor->GetVersionMajor(), fAnchor->GetVersionMinor(),
432 fAnchor->GetVersionPatch());
433 fDescriptorBuilder.SetOnDiskHeaderSize(fAnchor->GetNBytesHeader());
434 fDescriptorBuilder.AddToOnDiskFooterSize(fAnchor->GetNBytesFooter());
435
436 // Reserve enough space for the compressed and the uncompressed header/footer (see AttachImpl)
437 const auto bufSize = fAnchor->GetNBytesHeader() + fAnchor->GetNBytesFooter() +
438 std::max(fAnchor->GetLenHeader(), fAnchor->GetLenFooter());
439 fStructureBuffer.fBuffer = MakeUninitArray<unsigned char>(bufSize);
440 fStructureBuffer.fPtrHeader = fStructureBuffer.fBuffer.get();
441 fStructureBuffer.fPtrFooter = fStructureBuffer.fBuffer.get() + fAnchor->GetNBytesHeader();
442
443 auto readvLimits = fFile->GetReadVLimits();
444 // Never try to vectorize reads to a split key
445 readvLimits.fMaxSingleSize = std::min<size_t>(readvLimits.fMaxSingleSize, fAnchor->GetMaxKeySize());
446
447 if ((readvLimits.fMaxReqs < 2) ||
448 (std::max(fAnchor->GetNBytesHeader(), fAnchor->GetNBytesFooter()) > readvLimits.fMaxSingleSize) ||
449 (fAnchor->GetNBytesHeader() + fAnchor->GetNBytesFooter() > readvLimits.fMaxTotalSize)) {
450 RNTupleAtomicTimer timer(fCounters->fTimeWallRead, fCounters->fTimeCpuRead);
451 fReader.ReadBuffer(fStructureBuffer.fPtrHeader, fAnchor->GetNBytesHeader(), fAnchor->GetSeekHeader());
452 fReader.ReadBuffer(fStructureBuffer.fPtrFooter, fAnchor->GetNBytesFooter(), fAnchor->GetSeekFooter());
453 fCounters->fNRead.Add(2);
454 } else {
455 RNTupleAtomicTimer timer(fCounters->fTimeWallRead, fCounters->fTimeCpuRead);
456 R__ASSERT(fAnchor->GetNBytesHeader() < std::numeric_limits<std::size_t>::max());
457 R__ASSERT(fAnchor->GetNBytesFooter() < std::numeric_limits<std::size_t>::max());
458 ROOT::Internal::RRawFile::RIOVec readRequests[2] = {{fStructureBuffer.fPtrHeader, fAnchor->GetSeekHeader(),
459 static_cast<std::size_t>(fAnchor->GetNBytesHeader()), 0},
460 {fStructureBuffer.fPtrFooter, fAnchor->GetSeekFooter(),
461 static_cast<std::size_t>(fAnchor->GetNBytesFooter()), 0}};
462 fFile->ReadV(readRequests, 2);
463 fCounters->fNReadV.Inc();
464 }
465}
466
468{
469 auto unzipBuf = reinterpret_cast<unsigned char *>(fStructureBuffer.fPtrFooter) + fAnchor->GetNBytesFooter();
470
471 RNTupleDecompressor::Unzip(fStructureBuffer.fPtrHeader, fAnchor->GetNBytesHeader(), fAnchor->GetLenHeader(),
472 unzipBuf);
473 RNTupleSerializer::DeserializeHeader(unzipBuf, fAnchor->GetLenHeader(), fDescriptorBuilder);
474
475 RNTupleDecompressor::Unzip(fStructureBuffer.fPtrFooter, fAnchor->GetNBytesFooter(), fAnchor->GetLenFooter(),
476 unzipBuf);
477 RNTupleSerializer::DeserializeFooter(unzipBuf, fAnchor->GetLenFooter(), fDescriptorBuilder);
478
479 // fNTupleName is empty if and only if we created this source via CreateFromAnchor. If that's the case, this is the
480 // earliest we can set the name.
481 if (fNTupleName.empty())
482 fNTupleName = fDescriptorBuilder.GetDescriptor().GetName();
483
484 // For the page reads, we rely on the I/O scheduler to define the read requests
485 fFile->SetBuffering(false);
486
487 // Set file size once after buffering is turned off
488 fFileSize = fFile->GetSize();
489
490 return fDescriptorBuilder.MoveDescriptor();
491}
492
494{
495 fReader.ReadBuffer(buffer, locator.GetNBytesOnStorage(), locator.GetPosition<std::uint64_t>());
496}
497
499{
500 RNTupleAtomicTimer timer(fCounters->fTimeWallRead, fCounters->fTimeCpuRead);
501 const auto offset = locator.GetPosition<std::uint64_t>();
502 // Track seek distance (excluding file structure reads)
503 if (fLastOffset != 0) {
504 R__ASSERT(fFileCounters);
505 const auto distance = static_cast<std::uint64_t>(
506 std::abs(static_cast<std::int64_t>(offset) - static_cast<std::int64_t>(fLastOffset)));
507 fFileCounters->fSzSkip.Add(distance);
508 }
509 fReader.ReadBuffer(const_cast<void *>(sealedPage.GetBuffer()), sealedPage.GetBufferSize(),
510 locator.GetPosition<std::uint64_t>());
511 fLastOffset = offset + sealedPage.GetBufferSize();
512}
513
514std::unique_ptr<ROOT::Internal::RPageSource> ROOT::Internal::RPageSourceFile::CloneImpl() const
515{
516 auto clone = new RPageSourceFile(fNTupleName, fOptions);
517 clone->fFile = fFile->Clone();
518 clone->fReader = ROOT::Internal::RMiniFileReader(clone->fFile.get());
519 return std::unique_ptr<RPageSourceFile>(clone);
520}
521
522std::unique_ptr<ROOT::Internal::RCluster>
524 std::vector<ROOT::Internal::RRawFile::RIOVec> &readRequests)
525{
526 struct ROnDiskPageLocator {
527 ROOT::DescriptorId_t fColumnId = 0;
528 ROOT::NTupleSize_t fPageNo = 0;
529 std::uint64_t fOffset = 0;
530 std::uint64_t fSize = 0;
531 std::size_t fBufPos = 0;
532 };
533
534 std::vector<ROnDiskPageLocator> onDiskPages;
535 auto activeSize = 0;
536 auto pageZeroMap = std::make_unique<ROnDiskPageMap>();
537 PrepareLoadCluster(
541 const auto &pageLocator = pageInfo.GetLocator();
543 throw RException(R__FAIL("tried to read a page with an unknown locator"));
544 const auto nBytes = pageLocator.GetNBytesOnStorage() + pageInfo.HasChecksum() * kNBytesPageChecksum;
546 onDiskPages.push_back({physicalColumnId, pageNo, pageLocator.GetPosition<std::uint64_t>(), nBytes, 0});
547 });
548
549 // Linearize the page requests by file offset
550 std::sort(onDiskPages.begin(), onDiskPages.end(),
551 [](const ROnDiskPageLocator &a, const ROnDiskPageLocator &b) { return a.fOffset < b.fOffset; });
552
553 // In order to coalesce close-by pages, we collect the sizes of the gaps between pages on disk. We then order
554 // the gaps by size, sum them up and find a cutoff for the largest gap that we tolerate when coalescing pages.
555 // The size of the cutoff is given by the fraction of extra bytes we are willing to read in order to reduce
556 // the number of read requests. We thus schedule the lowest number of requests given a tolerable fraction
557 // of extra bytes.
558 // TODO(jblomer): Eventually we may want to select the parameter at runtime according to link latency and speed,
559 // memory consumption, device block size.
560 float maxOverhead = 0.25 * float(activeSize);
561 std::vector<std::size_t> gaps;
562 if (onDiskPages.size())
563 gaps.reserve(onDiskPages.size() - 1);
564 for (unsigned i = 1; i < onDiskPages.size(); ++i) {
565 std::int64_t gap =
566 static_cast<int64_t>(onDiskPages[i].fOffset) - (onDiskPages[i - 1].fSize + onDiskPages[i - 1].fOffset);
567 gaps.emplace_back(std::max(gap, std::int64_t(0)));
568 // If the pages overlap, substract the overlapped bytes from `activeSize`
569 activeSize += std::min(gap, std::int64_t(0));
570 }
571 std::sort(gaps.begin(), gaps.end());
572 std::size_t gapCut = 0;
573 std::size_t currentGap = 0;
574 float szExtra = 0.0;
575 for (auto g : gaps) {
576 if (g != currentGap) {
578 currentGap = g;
579 }
580 szExtra += g;
581 if (szExtra > maxOverhead)
582 break;
583 }
584
585 // In a first step, we coalesce the read requests and calculate the cluster buffer size.
586 // In a second step, we'll fix-up the memory destinations for the read calls given the
587 // address of the allocated buffer. We must not touch, however, the read requests from previous
588 // calls to PrepareSingleCluster()
589 const auto currentReadRequestIdx = readRequests.size();
590
592 // To simplify the first loop iteration, pretend an empty request starting at the first page's fOffset.
593 if (!onDiskPages.empty())
594 req.fOffset = onDiskPages[0].fOffset;
595 std::size_t szPayload = 0;
596 std::size_t szOverhead = 0;
597 const std::uint64_t maxKeySize = fReader.GetMaxKeySize();
598 for (auto &s : onDiskPages) {
599 R__ASSERT(s.fSize > 0);
600 const std::int64_t readUpTo = req.fOffset + req.fSize;
601 // Note: byte ranges of pages may overlap
602 const std::uint64_t overhead = std::max(static_cast<std::int64_t>(s.fOffset) - readUpTo, std::int64_t(0));
603 const std::uint64_t extent = std::max(static_cast<std::int64_t>(s.fOffset + s.fSize) - readUpTo, std::int64_t(0));
604 if (req.fSize + extent < maxKeySize && overhead <= gapCut) {
607 s.fBufPos = reinterpret_cast<intptr_t>(req.fBuffer) + s.fOffset - req.fOffset;
608 req.fSize += extent;
609 continue;
610 }
611
612 // close the current request and open new one
613 if (req.fSize > 0)
614 readRequests.emplace_back(req);
615
616 req.fBuffer = reinterpret_cast<unsigned char *>(req.fBuffer) + req.fSize;
617 s.fBufPos = reinterpret_cast<intptr_t>(req.fBuffer);
618
619 szPayload += s.fSize;
620 req.fOffset = s.fOffset;
621 req.fSize = s.fSize;
622 }
623 readRequests.emplace_back(req);
624 fCounters->fSzReadPayload.Add(szPayload);
625 fCounters->fSzReadOverhead.Add(szOverhead);
626
627 // Register the on disk pages in a page map
628 auto buffer = new unsigned char[reinterpret_cast<intptr_t>(req.fBuffer) + req.fSize];
629 auto pageMap = std::make_unique<ROOT::Internal::ROnDiskPageMapHeap>(std::unique_ptr<unsigned char[]>(buffer));
630 for (const auto &s : onDiskPages) {
631 ROnDiskPage::Key key(s.fColumnId, s.fPageNo);
632 pageMap->Register(key, ROnDiskPage(buffer + s.fBufPos, s.fSize));
633 }
634 fCounters->fNPageRead.Add(onDiskPages.size());
635 for (auto i = currentReadRequestIdx; i < readRequests.size(); ++i) {
636 readRequests[i].fBuffer = buffer + reinterpret_cast<intptr_t>(readRequests[i].fBuffer);
637 }
638
639 auto cluster = std::make_unique<RCluster>(clusterKey.fClusterId);
640 cluster->Adopt(std::move(pageMap));
641 cluster->Adopt(std::move(pageZeroMap));
642 for (auto colId : clusterKey.fPhysicalColumnSet)
643 cluster->SetColumnAvailable(colId);
644 return cluster;
645}
646
647std::vector<std::unique_ptr<ROOT::Internal::RCluster>>
649{
650 fCounters->fNClusterLoaded.Add(clusterKeys.size());
651
652 std::vector<std::unique_ptr<ROOT::Internal::RCluster>> clusters;
653 std::vector<ROOT::Internal::RRawFile::RIOVec> readRequests;
654
655 clusters.reserve(clusterKeys.size());
656 for (const auto &key : clusterKeys) {
657 clusters.emplace_back(PrepareSingleCluster(key, readRequests));
658 }
659
660 auto nReqs = readRequests.size();
661 auto readvLimits = fFile->GetReadVLimits();
662 // We never want to do vectorized reads of split blobs, so we limit our single size to maxKeySize.
663 readvLimits.fMaxSingleSize = std::min<size_t>(readvLimits.fMaxSingleSize, fReader.GetMaxKeySize());
664
665 int iReq = 0;
666 while (nReqs > 0) {
667 auto nBatch = std::min(nReqs, readvLimits.fMaxReqs);
668
669 if (readvLimits.HasSizeLimit()) {
670 std::uint64_t totalSize = 0;
671 for (std::size_t i = 0; i < nBatch; ++i) {
672 if (readRequests[iReq + i].fSize > readvLimits.fMaxSingleSize) {
673 nBatch = i;
674 break;
675 }
676
677 totalSize += readRequests[iReq + i].fSize;
678 if (totalSize > readvLimits.fMaxTotalSize) {
679 nBatch = i;
680 break;
681 }
682 }
683 }
684
685 // Track seek distance for each read request (excluding file structure reads)
686 R__ASSERT(fFileCounters);
687 for (std::size_t i = 0; i < nBatch; ++i) {
688 const auto offset = readRequests[iReq + i].fOffset;
689 if (fLastOffset != 0) {
690 const auto distance = static_cast<std::uint64_t>(std::abs(
691 static_cast<std::int64_t>(offset) - static_cast<std::int64_t>(fLastOffset)));
692 fFileCounters->fSzSkip.Add(distance);
693 }
694 fLastOffset = offset + readRequests[iReq + i].fSize;
695 }
696
697 if (nBatch <= 1) {
698 nBatch = 1;
699 RNTupleAtomicTimer timer(fCounters->fTimeWallRead, fCounters->fTimeCpuRead);
700 fReader.ReadBuffer(readRequests[iReq].fBuffer, readRequests[iReq].fSize, readRequests[iReq].fOffset);
701 } else {
702 RNTupleAtomicTimer timer(fCounters->fTimeWallRead, fCounters->fTimeCpuRead);
703 fFile->ReadV(&readRequests[iReq], nBatch);
704 }
705 fCounters->fNReadV.Inc();
706 fCounters->fNRead.Add(nBatch);
707
708 iReq += nBatch;
709 nReqs -= nBatch;
710 }
711
712 return clusters;
713}
714
716{
717 fReader.LoadStreamerInfo();
718}
fBuffer
dim_t fSize
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:322
#define b(i)
Definition RSha256.hxx:100
#define g(i)
Definition RSha256.hxx:105
#define a(i)
Definition RSha256.hxx:99
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t mask
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h length
char name[80]
Definition TGX11.cxx:148
A thread-safe integral performance counter.
A metric element that computes its floating point value from other counters.
A collection of Counter objects with a name, a unit, and a description.
CounterPtrT MakeCounter(const std::string &name, Args &&... args)
An interface to read from, or write to, a ROOT file, as well as performing other common operations.
Definition RFile.hxx:252
The SoA field provides I/O for an in-memory SoA layout linked to an on-disk collection of the underly...
Definition RFieldSoA.hxx:56
An in-memory subset of the packed and compressed pages of a cluster.
Definition RCluster.hxx:147
Read RNTuple data blocks from a TFile container, provided by a RRawFile.
Definition RMiniFile.hxx:60
Helper class to compress data blocks in the ROOT compression frame format.
static std::size_t Zip(const void *from, std::size_t nbytes, int compression, void *to)
Returns the size of the compressed data, written into the provided output buffer.
Helper class to uncompress data blocks in the ROOT compression frame format.
static void Unzip(const void *from, size_t nbytes, size_t dataLen, void *to)
The nbytes parameter provides the size ls of the from buffer.
Write RNTuple data blocks in a TFile or a bare file container.
static std::unique_ptr< RNTupleFileWriter > Append(std::string_view ntupleName, TDirectory &fileOrDirectory, std::uint64_t maxKeySize, bool isHidden)
The directory parameter can also be a TFile object (TFile inherits from TDirectory).
static std::unique_ptr< RNTupleFileWriter > Recreate(std::string_view ntupleName, std::string_view path, EContainerFormat containerFormat, const ROOT::RNTupleWriteOptions &options)
Create or truncate the local file given by path with the new empty RNTuple identified by ntupleName.
A helper class for serializing and deserialization of the RNTuple binary format.
static RResult< void > DeserializeFooter(const void *buffer, std::uint64_t bufSize, ROOT::Internal::RNTupleDescriptorBuilder &descBuilder)
static RResult< StreamerInfoMap_t > DeserializeStreamerInfos(const std::string &extraTypeInfoContent)
static RResult< void > DeserializeHeader(const void *buffer, std::uint64_t bufSize, ROOT::Internal::RNTupleDescriptorBuilder &descBuilder)
A memory region that contains packed and compressed pages.
Definition RCluster.hxx:98
A page as being stored on disk, that is packed and compressed.
Definition RCluster.hxx:40
Base class for a sink with a physical storage backend.
void UpdateSchema(const ROOT::Internal::RNTupleModelChangeset &changeset, ROOT::NTupleSize_t firstEntry) override
Incorporate incremental changes to the model into the ntuple descriptor.
void EnableDefaultMetrics(const std::string &prefix)
Enables the default set of metrics provided by RPageSink.
Storage provider that write ntuple pages into a file.
void CommitBatchOfPages(CommitBatch &batch, std::vector< RNTupleLocator > &locators)
Subroutine of CommitSealedPageVImpl, used to perform a vector write of the (multi-)range of pages con...
RPageSinkFile(std::string_view ntupleName, const ROOT::RNTupleWriteOptions &options)
std::unique_ptr< RPageSink > CloneAsHidden(std::string_view name, const ROOT::RNTupleWriteOptions &opts) const override
Creates a new sink with the same underlying storage as this but writing to a different RNTuple named ...
RNTupleLocator CommitSealedPageImpl(ROOT::DescriptorId_t physicalColumnId, const RPageStorage::RSealedPage &sealedPage) override
std::uint64_t StageClusterImpl() final
Returns the number of bytes written to storage (excluding metadata)
void InitImpl(unsigned char *serializedHeader, std::uint32_t length) final
RNTupleLocator WriteSealedPage(const RPageStorage::RSealedPage &sealedPage, std::size_t bytesPacked)
We pass bytesPacked so that TFile::ls() reports a reasonable value for the compression ratio of the c...
RNTupleLocator CommitClusterGroupImpl(unsigned char *serializedPageList, std::uint32_t length) final
Returns the locator of the page list envelope of the given buffer that contains the serialized page l...
RNTupleLink CommitDatasetImpl() final
std::unique_ptr< ROOT::Internal::RNTupleFileWriter > fWriter
void UpdateSchema(const ROOT::Internal::RNTupleModelChangeset &changeset, ROOT::NTupleSize_t firstEntry) final
Incorporate incremental changes to the model into the ntuple descriptor.
std::vector< RNTupleLocator > CommitSealedPageVImpl(std::span< RPageStorage::RSealedPageGroup > ranges, const std::vector< bool > &mask) final
Vector commit of preprocessed pages.
Storage provider that reads ntuple pages from a file.
ROOT::RNTupleDescriptor AttachImpl() final
LoadStructureImpl() has been called before AttachImpl() is called
std::int64_t fFileSize
Total file size, set once in AttachImpl()
std::unique_ptr< ROOT::Internal::RCluster > PrepareSingleCluster(const ROOT::Internal::RCluster::RKey &clusterKey, std::vector< RRawFile::RIOVec > &readRequests)
Helper function for LoadClusters: it prepares the memory buffer (page map) and the read requests for ...
std::unique_ptr< RPageSource > OpenWithDifferentAnchor(const ROOT::Internal::RNTupleLink &anchorLink, const ROOT::RNTupleReadOptions &options={}) final
Creates a new PageSource using the same underlying file as this but referring to a different RNTuple,...
static std::unique_ptr< RPageSourceFile > CreateFromAnchor(const RNTuple &anchor, const ROOT::RNTupleReadOptions &options=ROOT::RNTupleReadOptions())
Used from the RNTuple class to build a datasource if the anchor is already available.
void LoadPageListImpl(const RNTupleLocator &locator, unsigned char *buffer) final
std::vector< std::unique_ptr< ROOT::Internal::RCluster > > LoadClusters(std::span< ROOT::Internal::RCluster::RKey > clusterKeys) final
Populates all the pages of the given cluster ids and columns; it is possible that some columns do not...
std::unique_ptr< RFileCounters > fFileCounters
void LoadSealedPageImpl(const RNTupleLocator &locator, RSealedPage &sealedPage) final
RPageSourceFile(std::string_view ntupleName, const ROOT::RNTupleReadOptions &options)
std::unique_ptr< RPageSource > CloneImpl() const final
The cloned page source creates a new raw file and reader and opens its own file descriptor to the dat...
void LoadStructureImpl() final
Fills fStructureBuffer with the compressed header and footer.
void LoadStreamerInfo() final
Forces the loading of ROOT StreamerInfo from the underlying file.
std::unique_ptr< RRawFile > fFile
An RRawFile is used to request the necessary byte ranges from a local or a remote file.
ROOT::Internal::RMiniFileReader fReader
Takes the fFile to read ntuple blobs from it.
Abstract interface to read data from an ntuple.
void EnableDefaultMetrics(const std::string &prefix)
Enables the default set of metrics provided by RPageSource.
ROOT::Experimental::Detail::RNTupleMetrics fMetrics
The RRawFileTFile wraps an open TFile, but does not take ownership.
The RRawFile provides read-only access to local and remote files.
Definition RRawFile.hxx:43
static std::unique_ptr< RRawFile > Create(std::string_view url, ROptions options=ROptions())
Factory method that returns a suitable concrete implementation according to the transport in the url.
Definition RRawFile.cxx:64
The field for a class with dictionary.
Definition RField.hxx:135
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
A field translates read and write calls from/to underlying columns to/from tree values.
The on-storage metadata of an RNTuple.
Generic information about the physical location of data.
Common user-tunable settings for reading RNTuples.
Common user-tunable settings for storing RNTuples.
std::uint64_t GetMaxKeySize() const
Representation of an RNTuple data set in a ROOT file.
Definition RNTuple.hxx:67
const_iterator begin() const
const_iterator end() const
The field for a class using ROOT standard streaming.
Definition RField.hxx:234
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
TVirtualStreamerInfo * GetStreamerInfo(Int_t version=0, Bool_t isTransient=kFALSE) const
returns a pointer to the TVirtualStreamerInfo object for version If the object does not exist,...
Definition TClass.cxx:4657
Describe directory structure in memory.
Definition TDirectory.h:45
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
const ROOT::RNTuple * GetAnchorFromFile(const RPageSourceFile &source)
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
The identifiers that specifies the content of a (partial) cluster.
Definition RCluster.hxx:151
The incremental changes to a RNTupleModel
On-disk pages within a page source are identified by the column and page number.
Definition RCluster.hxx:50
File-specific I/O performance counters.
A sealed page contains the bytes of a page as written to storage (packed & compressed).
Used for vector reads from multiple offsets into multiple buffers.
Definition RRawFile.hxx:61
Information about a single page in the context of a cluster's page range.