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
52
59
66
73
80
81ROOT::Internal::RPageSinkFile::RPageSinkFile(std::unique_ptr<ROOT::Internal::RNTupleFileWriter> writer,
82 const ROOT::RNTupleWriteOptions &options)
83 : RPageSinkFile(writer->GetNTupleName(), options)
84{
85 fWriter = std::move(writer);
86}
87
89
91{
93 auto szZipHeader =
94 RNTupleCompressor::Zip(serializedHeader, length, GetWriteOptions().GetCompression(), zipBuffer.get());
95 fWriter->WriteNTupleHeader(zipBuffer.get(), szZipHeader, length);
96}
97
100{
102
103 auto fnAddStreamerInfo = [this](const ROOT::RFieldBase *field) {
104 const TClass *cl = nullptr;
105 if (auto classField = dynamic_cast<const RClassField *>(field)) {
106 cl = classField->GetClass();
107 } else if (auto streamerField = dynamic_cast<const RStreamerField *>(field)) {
108 cl = streamerField->GetClass();
109 } else if (auto soaField = dynamic_cast<const ROOT::Experimental::RSoAField *>(field)) {
110 cl = soaField->GetSoAClass();
111 }
112 if (!cl)
113 return;
114
115 auto streamerInfo = cl->GetStreamerInfo(field->GetTypeVersion());
116 if (!streamerInfo) {
117 throw RException(R__FAIL(std::string("cannot get streamerInfo for ") + cl->GetName() + " [" +
118 std::to_string(field->GetTypeVersion()) + "]"));
119 }
120 fInfosOfClassFields[streamerInfo->GetNumber()] = streamerInfo;
121 };
122
123 for (const auto field : changeset.fAddedFields) {
125 for (const auto &subField : *field) {
127 }
128 }
129}
130
133{
134 std::uint64_t offsetData;
135 {
136 RNTupleAtomicTimer timer(fCounters->fTimeWallWrite, fCounters->fTimeCpuWrite);
137 offsetData = fWriter->WriteBlob(sealedPage.GetBuffer(), sealedPage.GetBufferSize(), bytesPacked);
138 }
139
141 result.SetPosition(offsetData);
142 result.SetNBytesOnStorage(sealedPage.GetDataSize());
143 fCounters->fNPageCommitted.Inc();
144 fCounters->fSzWritePayload.Add(sealedPage.GetBufferSize());
145 fNBytesCurrentCluster += sealedPage.GetBufferSize();
146 return result;
147}
148
151{
152 const auto nBits = fDescriptorBuilder.GetDescriptor().GetColumnDescriptor(physicalColumnId).GetBitsOnStorage();
153 const auto bytesPacked = (nBits * sealedPage.GetNElements() + 7) / 8;
154 return WriteSealedPage(sealedPage, bytesPacked);
155}
156
158{
159 RNTupleAtomicTimer timer(fCounters->fTimeWallWrite, fCounters->fTimeCpuWrite);
160
161 std::uint64_t offset = fWriter->ReserveBlob(batch.fSize, batch.fBytesPacked);
162
163 locators.reserve(locators.size() + batch.fSealedPages.size());
164
165 for (const auto *pagePtr : batch.fSealedPages) {
166 fWriter->WriteIntoReservedBlob(pagePtr->GetBuffer(), pagePtr->GetBufferSize(), offset);
168 locator.SetPosition(offset);
169 locator.SetNBytesOnStorage(pagePtr->GetDataSize());
170 locators.push_back(locator);
171 offset += pagePtr->GetBufferSize();
172 }
173
174 fCounters->fNPageCommitted.Add(batch.fSealedPages.size());
175 fCounters->fSzWritePayload.Add(batch.fSize);
176 fNBytesCurrentCluster += batch.fSize;
177
178 batch.fSize = 0;
179 batch.fBytesPacked = 0;
180 batch.fSealedPages.clear();
181}
182
183std::vector<ROOT::RNTupleLocator>
184ROOT::Internal::RPageSinkFile::CommitSealedPageVImpl(std::span<RPageStorage::RSealedPageGroup> ranges,
185 const std::vector<bool> &mask)
186{
187 const std::uint64_t maxKeySize = fOptions->GetMaxKeySize();
188
190 std::vector<RNTupleLocator> locators;
191
192 std::size_t iPage = 0;
193 for (auto rangeIt = ranges.begin(); rangeIt != ranges.end(); ++rangeIt) {
194 auto &range = *rangeIt;
195 if (range.fFirst == range.fLast) {
196 // Skip empty ranges, they might not have a physical column ID!
197 continue;
198 }
199
200 const auto bitsOnStorage =
201 fDescriptorBuilder.GetDescriptor().GetColumnDescriptor(range.fPhysicalColumnId).GetBitsOnStorage();
202
203 for (auto sealedPageIt = range.fFirst; sealedPageIt != range.fLast; ++sealedPageIt, ++iPage) {
204 if (!mask[iPage])
205 continue;
206
207 const auto bytesPacked = (bitsOnStorage * sealedPageIt->GetNElements() + 7) / 8;
208
209 if (batch.fSize > 0 && batch.fSize + sealedPageIt->GetBufferSize() > maxKeySize) {
210 /**
211 * Adding this page would exceed maxKeySize. Since we always want to write into a single key
212 * with vectorized writes, we commit the current set of pages before proceeding.
213 * NOTE: we do this *before* checking if sealedPageIt->GetBufferSize() > maxKeySize to guarantee that
214 * we always flush the current batch before doing an individual WriteBlob. This way we
215 * preserve the assumption that a CommitBatch always contain a sequential set of pages.
216 */
217 CommitBatchOfPages(batch, locators);
218 }
219
220 if (sealedPageIt->GetBufferSize() > maxKeySize) {
221 // This page alone is bigger than maxKeySize: save it by itself, since it will need to be
222 // split into multiple keys.
223
224 // Since this check implies the previous check on batchSize + newSize > maxSize, we should
225 // already have committed the current batch before writing this page.
226 assert(batch.fSize == 0);
227
228 std::uint64_t offset =
229 fWriter->WriteBlob(sealedPageIt->GetBuffer(), sealedPageIt->GetBufferSize(), bytesPacked);
231 locator.SetPosition(offset);
232 locator.SetNBytesOnStorage(sealedPageIt->GetDataSize());
233 locators.push_back(locator);
234
235 fCounters->fNPageCommitted.Inc();
236 fCounters->fSzWritePayload.Add(sealedPageIt->GetBufferSize());
237 fNBytesCurrentCluster += sealedPageIt->GetBufferSize();
238
239 } else {
240 batch.fSealedPages.emplace_back(&(*sealedPageIt));
241 batch.fSize += sealedPageIt->GetBufferSize();
242 batch.fBytesPacked += bytesPacked;
243 }
244 }
245 }
246
247 if (batch.fSize > 0) {
248 CommitBatchOfPages(batch, locators);
249 }
250
251 return locators;
252}
253
255{
256 auto result = fNBytesCurrentCluster;
257 fNBytesCurrentCluster = 0;
258 return result;
259}
260
263{
265 auto szPageListZip =
266 RNTupleCompressor::Zip(serializedPageList, length, GetWriteOptions().GetCompression(), bufPageListZip.get());
267
269 result.SetNBytesOnStorage(szPageListZip);
270 result.SetPosition(fWriter->WriteBlob(bufPageListZip.get(), szPageListZip, length));
271 return result;
272}
273
276{
277 // Add the streamer info records from streamer fields: because of runtime polymorphism we may need to add additional
278 // types not covered by the type names of the class fields
279 for (const auto &extraTypeInfo : fDescriptorBuilder.GetDescriptor().GetExtraTypeInfoIterable()) {
281 continue;
282 // Ideally, we would avoid deserializing the streamer info records of the streamer fields that we just serialized.
283 // However, this happens only once at the end of writing and only when streamer fields are used, so the
284 // preference here is for code simplicity.
285 fInfosOfClassFields.merge(RNTupleSerializer::DeserializeStreamerInfos(extraTypeInfo.GetContent()).Unwrap());
286 }
287 fWriter->UpdateStreamerInfos(fInfosOfClassFields);
288
290 auto szFooterZip =
291 RNTupleCompressor::Zip(serializedFooter, length, GetWriteOptions().GetCompression(), bufFooterZip.get());
292 fWriter->WriteNTupleFooter(bufFooterZip.get(), szFooterZip, length);
293 return fWriter->Commit(GetWriteOptions().GetCompression());
294}
295
296std::unique_ptr<ROOT::Internal::RPageSink>
298{
299 auto writer = fWriter->CloneAsHidden(name);
300 auto cloned = std::unique_ptr<RPageSinkFile>(new RPageSinkFile(std::move(writer), opts));
301 return cloned;
302}
303
304////////////////////////////////////////////////////////////////////////////////
305
307{
308 return source.fAnchor ? &*source.fAnchor : nullptr;
309}
310
313{
314 EnableDefaultMetrics("RPageSourceFile");
315 fFileCounters = std::make_unique<RFileCounters>(RFileCounters{
316 *fMetrics.MakeCounter<RNTupleAtomicCounter *>("szSkip", "B",
317 "cumulative seek distance (excluding header/footer reads)"),
319 "szFile", "B", "total file size", fMetrics,
320 [this](const RNTupleMetrics &) -> std::pair<bool, double> {
321 if (fFileSize > 0)
322 return {true, static_cast<double>(fFileSize)};
323 return {false, -1.};
324 }),
326 "randomness", "",
327 "ratio of seek distance to bytes read (excluding file structure reads)", fMetrics,
328 [](const RNTupleMetrics &metrics) -> std::pair<bool, double> {
329 if (const auto szSkip = metrics.GetLocalCounter("szSkip")) {
330 if (const auto szReadPayload = metrics.GetLocalCounter("szReadPayload")) {
331 if (const auto szReadOverhead = metrics.GetLocalCounter("szReadOverhead")) {
332 auto totalRead = szReadPayload->GetValueAsInt() + szReadOverhead->GetValueAsInt();
333 if (totalRead > 0) {
334 return {true, (1. * szSkip->GetValueAsInt()) / totalRead};
335 }
336 }
337 }
338 }
339 return {false, -1.};
340 }),
342 "sparseness", "",
343 "ratio of bytes read to total file size (excluding file structure reads)", fMetrics,
344 [this](const RNTupleMetrics &metrics) -> std::pair<bool, double> {
345 if (fFileSize > 0) {
346 if (const auto szReadPayload = metrics.GetLocalCounter("szReadPayload")) {
347 if (const auto szReadOverhead = metrics.GetLocalCounter("szReadOverhead")) {
348 auto totalRead = szReadPayload->GetValueAsInt() + szReadOverhead->GetValueAsInt();
349 return {true, (1. * totalRead) / fFileSize};
350 }
351 }
352 }
353 return {false, -1.};
354 })});
355}
356
358 std::unique_ptr<ROOT::Internal::RRawFile> file,
359 const ROOT::RNTupleReadOptions &options)
360 : RPageSourceFile(ntupleName, options)
361{
362 fFile = std::move(file);
365}
366
367ROOT::Internal::RPageSourceFile::RPageSourceFile(std::string_view ntupleName, std::string_view path,
368 const ROOT::RNTupleReadOptions &options)
369 : RPageSourceFile(ntupleName, ROOT::Internal::RRawFile::Create(path), options)
370{
371}
372
373std::unique_ptr<ROOT::Internal::RPageSourceFile>
375{
376 if (!anchor.fFile)
377 throw RException(R__FAIL("This RNTuple object was not streamed from a ROOT file (TFile or descendant)"));
378
379 std::unique_ptr<ROOT::Internal::RRawFile> rawFile;
380 // For local TFiles, TDavixFile, TCurlFile, and TNetXNGFile, we want to open a new RRawFile to take advantage of the
381 // faster reading. We check the exact class name to avoid classes inheriting in ROOT (for example TMemFile) or in
382 // experiment frameworks.
383 const std::string className = anchor.fFile->IsA()->GetName();
384 const auto url = anchor.fFile->GetEndpointUrl();
385 if (className == "TFile") {
387 } else if (className == "TDavixFile" || className == "TCurlFile" || className == "TNetXNGFile") {
389 } else {
391 }
392
393 auto pageSource = std::make_unique<RPageSourceFile>("", std::move(rawFile), options);
394 pageSource->fAnchor = anchor;
395 // NOTE: fNTupleName gets set only upon Attach().
396 return pageSource;
397}
398
400{
401 StopClusterPoolBackgroundThread();
402}
403
404std::unique_ptr<ROOT::Internal::RPageSource>
406 const ROOT::RNTupleReadOptions &options)
407{
408 assert(anchorLink.fLocator.GetType() == RNTupleLocator::kTypeFile);
409
410 const auto anchorPos = anchorLink.fLocator.GetPosition<std::uint64_t>();
411 auto anchor =
412 fReader.GetNTupleProperAtOffset(anchorPos, anchorLink.fLocator.GetNBytesOnStorage(), anchorLink.fLength).Unwrap();
413 auto pageSource = std::make_unique<RPageSourceFile>("", fFile->Clone(), options);
414 pageSource->fAnchor = anchor;
415 // NOTE: fNTupleName gets set only upon Attach().
416 return pageSource;
417}
418
420{
421 // If we constructed the page source with (ntuple name, path), we need to find the anchor first.
422 // Otherwise, the page source was created by OpenFromAnchor()
423 if (!fAnchor) {
424 fAnchor = fReader.GetNTuple(fNTupleName).Unwrap();
425 }
426 fReader.SetMaxKeySize(fAnchor->GetMaxKeySize());
427
428 fDescriptorBuilder.SetVersion(fAnchor->GetVersionEpoch(), fAnchor->GetVersionMajor(), fAnchor->GetVersionMinor(),
429 fAnchor->GetVersionPatch());
430 fDescriptorBuilder.SetOnDiskHeaderSize(fAnchor->GetNBytesHeader());
431 fDescriptorBuilder.AddToOnDiskFooterSize(fAnchor->GetNBytesFooter());
432
433 // Reserve enough space for the compressed and the uncompressed header/footer (see AttachImpl)
434 const auto bufSize = fAnchor->GetNBytesHeader() + fAnchor->GetNBytesFooter() +
435 std::max(fAnchor->GetLenHeader(), fAnchor->GetLenFooter());
436 fStructureBuffer.fBuffer = MakeUninitArray<unsigned char>(bufSize);
437 fStructureBuffer.fPtrHeader = fStructureBuffer.fBuffer.get();
438 fStructureBuffer.fPtrFooter = fStructureBuffer.fBuffer.get() + fAnchor->GetNBytesHeader();
439
440 auto readvLimits = fFile->GetReadVLimits();
441 // Never try to vectorize reads to a split key
442 readvLimits.fMaxSingleSize = std::min<size_t>(readvLimits.fMaxSingleSize, fAnchor->GetMaxKeySize());
443
444 if ((readvLimits.fMaxReqs < 2) ||
445 (std::max(fAnchor->GetNBytesHeader(), fAnchor->GetNBytesFooter()) > readvLimits.fMaxSingleSize) ||
446 (fAnchor->GetNBytesHeader() + fAnchor->GetNBytesFooter() > readvLimits.fMaxTotalSize)) {
447 RNTupleAtomicTimer timer(fCounters->fTimeWallRead, fCounters->fTimeCpuRead);
448 fReader.ReadBuffer(fStructureBuffer.fPtrHeader, fAnchor->GetNBytesHeader(), fAnchor->GetSeekHeader());
449 fReader.ReadBuffer(fStructureBuffer.fPtrFooter, fAnchor->GetNBytesFooter(), fAnchor->GetSeekFooter());
450 fCounters->fNRead.Add(2);
451 } else {
452 RNTupleAtomicTimer timer(fCounters->fTimeWallRead, fCounters->fTimeCpuRead);
453 R__ASSERT(fAnchor->GetNBytesHeader() < std::numeric_limits<std::size_t>::max());
454 R__ASSERT(fAnchor->GetNBytesFooter() < std::numeric_limits<std::size_t>::max());
455 ROOT::Internal::RRawFile::RIOVec readRequests[2] = {{fStructureBuffer.fPtrHeader, fAnchor->GetSeekHeader(),
456 static_cast<std::size_t>(fAnchor->GetNBytesHeader()), 0},
457 {fStructureBuffer.fPtrFooter, fAnchor->GetSeekFooter(),
458 static_cast<std::size_t>(fAnchor->GetNBytesFooter()), 0}};
459 fFile->ReadV(readRequests, 2);
460 fCounters->fNReadV.Inc();
461 }
462}
463
465{
466 auto unzipBuf = reinterpret_cast<unsigned char *>(fStructureBuffer.fPtrFooter) + fAnchor->GetNBytesFooter();
467
468 RNTupleDecompressor::Unzip(fStructureBuffer.fPtrHeader, fAnchor->GetNBytesHeader(), fAnchor->GetLenHeader(),
469 unzipBuf);
470 RNTupleSerializer::DeserializeHeader(unzipBuf, fAnchor->GetLenHeader(), fDescriptorBuilder);
471
472 RNTupleDecompressor::Unzip(fStructureBuffer.fPtrFooter, fAnchor->GetNBytesFooter(), fAnchor->GetLenFooter(),
473 unzipBuf);
474 RNTupleSerializer::DeserializeFooter(unzipBuf, fAnchor->GetLenFooter(), fDescriptorBuilder);
475
476 // fNTupleName is empty if and only if we created this source via CreateFromAnchor. If that's the case, this is the
477 // earliest we can set the name.
478 if (fNTupleName.empty())
479 fNTupleName = fDescriptorBuilder.GetDescriptor().GetName();
480
481 // For the page reads, we rely on the I/O scheduler to define the read requests
482 fFile->SetBuffering(false);
483
484 // Set file size once after buffering is turned off
485 fFileSize = fFile->GetSize();
486
487 return fDescriptorBuilder.MoveDescriptor();
488}
489
491{
492 fReader.ReadBuffer(buffer, locator.GetNBytesOnStorage(), locator.GetPosition<std::uint64_t>());
493}
494
496{
497 RNTupleAtomicTimer timer(fCounters->fTimeWallRead, fCounters->fTimeCpuRead);
498 const auto offset = locator.GetPosition<std::uint64_t>();
499 // Track seek distance (excluding file structure reads)
500 if (fLastOffset != 0) {
501 R__ASSERT(fFileCounters);
502 const auto distance = static_cast<std::uint64_t>(
503 std::abs(static_cast<std::int64_t>(offset) - static_cast<std::int64_t>(fLastOffset)));
504 fFileCounters->fSzSkip.Add(distance);
505 }
506 fReader.ReadBuffer(const_cast<void *>(sealedPage.GetBuffer()), sealedPage.GetBufferSize(),
507 locator.GetPosition<std::uint64_t>());
508 fLastOffset = offset + sealedPage.GetBufferSize();
509}
510
511std::unique_ptr<ROOT::Internal::RPageSource> ROOT::Internal::RPageSourceFile::CloneImpl() const
512{
513 auto clone = new RPageSourceFile(fNTupleName, fOptions);
514 clone->fFile = fFile->Clone();
515 clone->fReader = ROOT::Internal::RMiniFileReader(clone->fFile.get());
516 return std::unique_ptr<RPageSourceFile>(clone);
517}
518
519std::unique_ptr<ROOT::Internal::RCluster>
521 std::vector<ROOT::Internal::RRawFile::RIOVec> &readRequests)
522{
523 struct ROnDiskPageLocator {
524 ROOT::DescriptorId_t fColumnId = 0;
525 ROOT::NTupleSize_t fPageNo = 0;
526 std::uint64_t fOffset = 0;
527 std::uint64_t fSize = 0;
528 std::size_t fBufPos = 0;
529 };
530
531 std::vector<ROnDiskPageLocator> onDiskPages;
532 auto activeSize = 0;
533 auto pageZeroMap = std::make_unique<ROnDiskPageMap>();
534 PrepareLoadCluster(
538 const auto &pageLocator = pageInfo.GetLocator();
540 throw RException(R__FAIL("tried to read a page with an unknown locator"));
541 const auto nBytes = pageLocator.GetNBytesOnStorage() + pageInfo.HasChecksum() * kNBytesPageChecksum;
543 onDiskPages.push_back({physicalColumnId, pageNo, pageLocator.GetPosition<std::uint64_t>(), nBytes, 0});
544 });
545
546 // Linearize the page requests by file offset
547 std::sort(onDiskPages.begin(), onDiskPages.end(),
548 [](const ROnDiskPageLocator &a, const ROnDiskPageLocator &b) { return a.fOffset < b.fOffset; });
549
550 // In order to coalesce close-by pages, we collect the sizes of the gaps between pages on disk. We then order
551 // the gaps by size, sum them up and find a cutoff for the largest gap that we tolerate when coalescing pages.
552 // The size of the cutoff is given by the fraction of extra bytes we are willing to read in order to reduce
553 // the number of read requests. We thus schedule the lowest number of requests given a tolerable fraction
554 // of extra bytes.
555 // TODO(jblomer): Eventually we may want to select the parameter at runtime according to link latency and speed,
556 // memory consumption, device block size.
557 float maxOverhead = 0.25 * float(activeSize);
558 std::vector<std::size_t> gaps;
559 if (onDiskPages.size())
560 gaps.reserve(onDiskPages.size() - 1);
561 for (unsigned i = 1; i < onDiskPages.size(); ++i) {
562 std::int64_t gap =
563 static_cast<int64_t>(onDiskPages[i].fOffset) - (onDiskPages[i - 1].fSize + onDiskPages[i - 1].fOffset);
564 gaps.emplace_back(std::max(gap, std::int64_t(0)));
565 // If the pages overlap, substract the overlapped bytes from `activeSize`
566 activeSize += std::min(gap, std::int64_t(0));
567 }
568 std::sort(gaps.begin(), gaps.end());
569 std::size_t gapCut = 0;
570 std::size_t currentGap = 0;
571 float szExtra = 0.0;
572 for (auto g : gaps) {
573 if (g != currentGap) {
575 currentGap = g;
576 }
577 szExtra += g;
578 if (szExtra > maxOverhead)
579 break;
580 }
581
582 // In a first step, we coalesce the read requests and calculate the cluster buffer size.
583 // In a second step, we'll fix-up the memory destinations for the read calls given the
584 // address of the allocated buffer. We must not touch, however, the read requests from previous
585 // calls to PrepareSingleCluster()
586 const auto currentReadRequestIdx = readRequests.size();
587
589 // To simplify the first loop iteration, pretend an empty request starting at the first page's fOffset.
590 if (!onDiskPages.empty())
591 req.fOffset = onDiskPages[0].fOffset;
592 std::size_t szPayload = 0;
593 std::size_t szOverhead = 0;
594 const std::uint64_t maxKeySize = fReader.GetMaxKeySize();
595 for (auto &s : onDiskPages) {
596 R__ASSERT(s.fSize > 0);
597 const std::int64_t readUpTo = req.fOffset + req.fSize;
598 // Note: byte ranges of pages may overlap
599 const std::uint64_t overhead = std::max(static_cast<std::int64_t>(s.fOffset) - readUpTo, std::int64_t(0));
600 const std::uint64_t extent = std::max(static_cast<std::int64_t>(s.fOffset + s.fSize) - readUpTo, std::int64_t(0));
601 if (req.fSize + extent < maxKeySize && overhead <= gapCut) {
604 s.fBufPos = reinterpret_cast<intptr_t>(req.fBuffer) + s.fOffset - req.fOffset;
605 req.fSize += extent;
606 continue;
607 }
608
609 // close the current request and open new one
610 if (req.fSize > 0)
611 readRequests.emplace_back(req);
612
613 req.fBuffer = reinterpret_cast<unsigned char *>(req.fBuffer) + req.fSize;
614 s.fBufPos = reinterpret_cast<intptr_t>(req.fBuffer);
615
616 szPayload += s.fSize;
617 req.fOffset = s.fOffset;
618 req.fSize = s.fSize;
619 }
620 readRequests.emplace_back(req);
621 fCounters->fSzReadPayload.Add(szPayload);
622 fCounters->fSzReadOverhead.Add(szOverhead);
623
624 // Register the on disk pages in a page map
625 auto buffer = new unsigned char[reinterpret_cast<intptr_t>(req.fBuffer) + req.fSize];
626 auto pageMap = std::make_unique<ROOT::Internal::ROnDiskPageMapHeap>(std::unique_ptr<unsigned char[]>(buffer));
627 for (const auto &s : onDiskPages) {
628 ROnDiskPage::Key key(s.fColumnId, s.fPageNo);
629 pageMap->Register(key, ROnDiskPage(buffer + s.fBufPos, s.fSize));
630 }
631 fCounters->fNPageRead.Add(onDiskPages.size());
632 for (auto i = currentReadRequestIdx; i < readRequests.size(); ++i) {
633 readRequests[i].fBuffer = buffer + reinterpret_cast<intptr_t>(readRequests[i].fBuffer);
634 }
635
636 auto cluster = std::make_unique<RCluster>(clusterKey.fClusterId);
637 cluster->Adopt(std::move(pageMap));
638 cluster->Adopt(std::move(pageZeroMap));
639 for (auto colId : clusterKey.fPhysicalColumnSet)
640 cluster->SetColumnAvailable(colId);
641 return cluster;
642}
643
644std::vector<std::unique_ptr<ROOT::Internal::RCluster>>
646{
647 fCounters->fNClusterLoaded.Add(clusterKeys.size());
648
649 std::vector<std::unique_ptr<ROOT::Internal::RCluster>> clusters;
650 std::vector<ROOT::Internal::RRawFile::RIOVec> readRequests;
651
652 clusters.reserve(clusterKeys.size());
653 for (const auto &key : clusterKeys) {
654 clusters.emplace_back(PrepareSingleCluster(key, readRequests));
655 }
656
657 auto nReqs = readRequests.size();
658 auto readvLimits = fFile->GetReadVLimits();
659 // We never want to do vectorized reads of split blobs, so we limit our single size to maxKeySize.
660 readvLimits.fMaxSingleSize = std::min<size_t>(readvLimits.fMaxSingleSize, fReader.GetMaxKeySize());
661
662 int iReq = 0;
663 while (nReqs > 0) {
664 auto nBatch = std::min(nReqs, readvLimits.fMaxReqs);
665
666 if (readvLimits.HasSizeLimit()) {
667 std::uint64_t totalSize = 0;
668 for (std::size_t i = 0; i < nBatch; ++i) {
669 if (readRequests[iReq + i].fSize > readvLimits.fMaxSingleSize) {
670 nBatch = i;
671 break;
672 }
673
674 totalSize += readRequests[iReq + i].fSize;
675 if (totalSize > readvLimits.fMaxTotalSize) {
676 nBatch = i;
677 break;
678 }
679 }
680 }
681
682 // Track seek distance for each read request (excluding file structure reads)
683 R__ASSERT(fFileCounters);
684 for (std::size_t i = 0; i < nBatch; ++i) {
685 const auto offset = readRequests[iReq + i].fOffset;
686 if (fLastOffset != 0) {
687 const auto distance = static_cast<std::uint64_t>(std::abs(
688 static_cast<std::int64_t>(offset) - static_cast<std::int64_t>(fLastOffset)));
689 fFileCounters->fSzSkip.Add(distance);
690 }
691 fLastOffset = offset + readRequests[iReq + i].fSize;
692 }
693
694 if (nBatch <= 1) {
695 nBatch = 1;
696 RNTupleAtomicTimer timer(fCounters->fTimeWallRead, fCounters->fTimeCpuRead);
697 fReader.ReadBuffer(readRequests[iReq].fBuffer, readRequests[iReq].fSize, readRequests[iReq].fOffset);
698 } else {
699 RNTupleAtomicTimer timer(fCounters->fTimeWallRead, fCounters->fTimeCpuRead);
700 fFile->ReadV(&readRequests[iReq], nBatch);
701 }
702 fCounters->fNReadV.Inc();
703 fCounters->fNRead.Add(nBatch);
704
705 iReq += nBatch;
706 nReqs -= nBatch;
707 }
708
709 return clusters;
710}
711
713{
714 fReader.LoadStreamerInfo();
715}
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:130
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:142
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
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.
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.
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:65
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:4721
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.