44#include <unordered_map>
78 auto charBuf =
reinterpret_cast<const unsigned char *
>(
fBuffer);
79 auto checksumBuf =
const_cast<unsigned char *
>(charBuf) + GetDataSize();
80 std::uint64_t xxhash3;
91 return R__FAIL(
"page checksum verification failed, data corruption detected");
98 return R__FAIL(
"invalid attempt to extract non-existing page checksum");
100 assert(fBufferSize >= kNBytesPageChecksum);
101 std::uint64_t checksum;
103 reinterpret_cast<const unsigned char *
>(
fBuffer) + fBufferSize - kNBytesPageChecksum, checksum);
112 auto [itr,
_] = fColumnInfos.emplace(physicalColumnId, std::vector<RColumnInfo>());
113 for (
auto &columnInfo : itr->second) {
114 if (columnInfo.fElementId == elementId) {
115 columnInfo.fRefCounter++;
119 itr->second.emplace_back(
RColumnInfo{elementId, 1});
125 auto itr = fColumnInfos.find(physicalColumnId);
127 for (std::size_t i = 0; i < itr->second.size(); ++i) {
128 if (itr->second[i].fElementId != elementId)
131 itr->second[i].fRefCounter--;
132 if (itr->second[i].fRefCounter == 0) {
133 itr->second.erase(itr->second.begin() + i);
134 if (itr->second.empty()) {
135 fColumnInfos.erase(itr);
145 for (
const auto &[physicalColumnId,
_] : fColumnInfos)
146 result.insert(physicalColumnId);
176std::unique_ptr<ROOT::Internal::RPageSource>
180 if (ntupleName.empty()) {
183 if (location.empty()) {
186 if (location.find(
"daos://") == 0)
188 return std::make_unique<ROOT::Experimental::Internal::RPageSourceDaos>(ntupleName, location, options);
195 return std::make_unique<ROOT::Experimental::Internal::RPageSourceS3>(ntupleName, location, options);
197 throw RException(
R__FAIL(
"This RNTuple build does not support S3. Rebuild ROOT with the 'curl' "
198 "cmake option enabled (-Dcurl=ON) to enable the S3 backend."));
202 return std::make_unique<ROOT::Internal::RPageSourceFile>(ntupleName, location, options);
233 fHasStructure =
true;
241 fDeserializationMode =
mode;
246 auto descGuard = GetExclDescriptorGuard();
247 descGuard.MoveIn(AttachImpl());
248 fStructureBuffer.Reset();
251 fCumulativeClusterCounts.reserve(descGuard->GetNClusterGroups());
252 for (
const auto &cgDesc : descGuard->GetClusterGroupIterable()) {
253 sumClusterCount += cgDesc.GetNClusters();
254 fCumulativeClusterCounts.emplace_back(sumClusterCount);
266 auto descGuard = GetExclDescriptorGuard();
268 for (
const auto &cgDesc : descGuard->GetClusterGroupIterable()) {
269 LoadPageList(cgDesc.GetId(), descGuard);
273 fClusterGroupAccessSequence.clear();
279 if (cgDesc.HasClusterDetails())
283 MakeUninitArray<unsigned char>(cgDesc.GetPageListLength() + cgDesc.GetPageListLocator().GetNBytesOnStorage());
284 auto zipBuffer = buffer.get() + cgDesc.GetPageListLength();
285 LoadPageListImpl(cgDesc.GetPageListLocator(), zipBuffer);
289 fDeserializationMode);
294 auto clone = CloneImpl();
296 clone->GetExclDescriptorGuard().MoveIn(GetSharedDescriptorGuard()->Clone());
297 clone->fHasStructure =
true;
298 clone->fIsAttached =
true;
299 clone->fDeserializationMode = fDeserializationMode;
300 clone->fCumulativeClusterCounts = fCumulativeClusterCounts;
313 auto itrSeq = std::find(fClusterGroupAccessSequence.begin(), fClusterGroupAccessSequence.end(), cgId);
316 (itrSeq == fClusterGroupAccessSequence.begin())) {
322 auto exclGuard = GetExclDescriptorGuard();
324 itrSeq = std::find(fClusterGroupAccessSequence.begin(), fClusterGroupAccessSequence.end(), cgId);
326 if (exclGuard->GetClusterGroupDescriptor(cgId).HasClusterDetails()) {
329 assert(itrSeq != fClusterGroupAccessSequence.end());
330 fClusterGroupAccessSequence.erase(itrSeq);
331 fClusterGroupAccessSequence.push_front(cgId);
335 assert(itrSeq == fClusterGroupAccessSequence.end());
336 const auto nClusterGroups = fClusterGroupAccessSequence.size();
338 assert(clusterGroupBudget > 0);
339 if ((nClusterGroups >= clusterGroupBudget) && (nClusterGroups > fPinnedClusterGroups.size())) {
342 auto nRemove = std::min(nClusterGroups - fPinnedClusterGroups.size(), nClusterGroups - clusterGroupBudget + 1);
345 for (
auto lru = fClusterGroupAccessSequence.rbegin(); nRemove > 0;) {
346 if (fPinnedClusterGroups.count(*lru) > 0) {
350 exclGuard->DropClusterGroupDetails(*lru);
353 auto next = fClusterGroupAccessSequence.erase(std::prev(lru.base()));
355 lru = std::make_reverse_iterator(next);
358 assert(fClusterGroupAccessSequence.size() > 0);
360 fClusterGroupAccessSequence.push_front(cgId);
362 LoadPageList(cgId, exclGuard);
371 auto iter = std::lower_bound(fCumulativeClusterCounts.begin(), fCumulativeClusterCounts.end(), clusterId + 1);
372 if (iter == fCumulativeClusterCounts.end())
374 return std::distance(fCumulativeClusterCounts.begin(), iter);
379 return GetSharedDescriptorGuard()->GetNEntries();
384 auto sharedGuard = GetSharedDescriptorGuard();
385 if (sharedGuard->GetNClusters() == 0)
388 const auto lastClusterGroupId = sharedGuard->GetNClusterGroups() - 1;
389 auto descGuard = EnsureClusterDetails(lastClusterGroupId, std::move(sharedGuard));
391 const auto &cgDesc = descGuard->GetClusterGroupDescriptor(lastClusterGroupId);
392 const auto &cd = descGuard->GetClusterDescriptor(cgDesc.GetClusterIds().back());
393 R__ASSERT(cd.ContainsColumn(physicalColumnId));
395 const auto &columnRange = cd.GetColumnRange(physicalColumnId);
396 return columnRange.GetFirstElementIndex() + columnRange.GetNElements();
401 fPinnedClusters.insert(clusterId);
402 ++fPinnedClusterGroups[FindClusterGroupId(clusterId)];
407 fPinnedClusters.erase(clusterId);
408 auto itr = fPinnedClusterGroups.find(FindClusterGroupId(clusterId));
409 assert(itr != fPinnedClusterGroups.end() && itr->second > 0);
410 if (--itr->second == 0)
411 fPinnedClusterGroups.erase(itr);
420 auto sharedGuard = GetSharedDescriptorGuard();
421 assert(clusterId < sharedGuard->GetNClusters());
422 nextId = clusterId + 1;
423 if (nextId == sharedGuard->GetNClusters()) {
427 return EnsureClusterDetails(FindClusterGroupId(nextId), std::move(sharedGuard));
442 std::size_t cgLeft = 0;
444 while (cgLeft <= cgRight) {
445 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
446 const auto &cgDesc = *(cgIter + cgMidpoint);
448 if (cgDesc.GetMinEntry() > entryIdx) {
450 cgRight = cgMidpoint - 1;
454 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
455 cgLeft = cgMidpoint + 1;
461 const auto tgtClusterGroupId = cgDesc.GetId();
462 descGuard = EnsureClusterDetails(tgtClusterGroupId, std::move(descGuard));
465 std::size_t clusterLeft = 0;
466 std::size_t clusterRight = clusterIds.size() - 1;
467 while (clusterLeft <= clusterRight) {
468 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
471 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
473 clusterRight = clusterMidpoint - 1;
477 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
478 clusterLeft = clusterMidpoint + 1;
482 cid = clusterIds[clusterMidpoint];
495const std::vector<ROOT::DescriptorId_t> *
503 if (firstColumnRange.GetFirstElementIndex() >
index) {
511 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <=
index) {
524 const std::vector<ROOT::DescriptorId_t> &clusterIds,
527 std::size_t clusterLeft = 0;
528 std::size_t clusterRight = clusterIds.size() - 1;
529 while (clusterLeft <= clusterRight) {
530 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
531 const auto clusterId = clusterIds[clusterMidpoint];
534 if (columnRange.Contains(
index)) {
538 if (columnRange.GetFirstElementIndex() >
index) {
540 clusterRight = clusterMidpoint - 1;
544 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <=
index) {
545 clusterLeft = clusterMidpoint + 1;
568 const std::vector<DescriptorId_t> *clusterIds =
nullptr;
574 for (
int i = 0; i < 2; ++i) {
575 descGuard = EnsureClusterDetails(cgHint, std::move(descGuard));
576 if ((clusterIds = SearchInClusterGroup(physicalColumnId,
index, cgHint, descGuard))) {
577 cid = FindClusterIdInList(physicalColumnId,
index, *clusterIds, descGuard);
583 assert(cgHint != cgGuess);
587 const bool isRightSided = cgHint > cgGuess;
588 for (
auto [cgPinned,
_] : fPinnedClusterGroups) {
589 if (isRightSided && (cgPinned < cgHint)) {
592 }
else if (!isRightSided && (cgPinned > cgHint)) {
597 descGuard = EnsureClusterDetails(cgPinned, std::move(descGuard));
599 if ((clusterIds = SearchInClusterGroup(physicalColumnId,
index, cgHint, descGuard))) {
600 cid = FindClusterIdInList(physicalColumnId,
index, *clusterIds, descGuard);
604 if (cgHint < cgPinned) {
612 if (columnDesc.GetIndex() == 0) {
625 return FindClusterId(
index, cid);
633 std::size_t cgLeft = 0;
635 while (cgLeft <= cgRight) {
636 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
637 descGuard = EnsureClusterDetails(cgMidpoint, std::move(descGuard));
640 clusterIds = SearchInClusterGroup(physicalColumnId,
index, cgHint, descGuard);
643 cid = FindClusterIdInList(physicalColumnId,
index, *clusterIds, descGuard);
649 }
else if (cgHint < cgMidpoint) {
651 cgRight = cgMidpoint - 1;
654 cgLeft = cgMidpoint + 1;
665 RNTupleAtomicTimer timer(fCounters->fTimeWallUnzip, fCounters->fTimeCpuUnzip);
667 const auto clusterId = cluster->
GetId();
668 auto descriptorGuard = EnsureClusterDetails(FindClusterGroupId(clusterId), GetSharedDescriptorGuard());
669 const auto &clusterDescriptor = descriptorGuard->GetClusterDescriptor(clusterId);
671 fPreloadedClusters[clusterDescriptor.GetFirstEntryIndex()] = clusterId;
673 std::atomic<bool> foundChecksumFailure{
false};
677 std::vector<std::pair<Internal::RPage, Internal::RPagePool::RKey>> allPages;
681 std::size_t nPages = 0;
683 for (
const auto columnId : columnsInCluster) {
684 if (fActivePhysicalColumns.HasColumnInfos(columnId)) {
685 nPages += fActivePhysicalColumns.GetColumnInfos(columnId).size() *
686 clusterDescriptor.GetPageRange(columnId).GetPageInfos().size();
689 allPages.resize(nPages);
690 std::size_t iPage = 0;
692 std::vector<std::unique_ptr<RColumnElementBase>> allElements;
693 for (
const auto columnId : columnsInCluster) {
697 if (!fActivePhysicalColumns.HasColumnInfos(columnId))
699 const auto &columnInfos = fActivePhysicalColumns.GetColumnInfos(columnId);
700 const auto indexOffset = clusterDescriptor.GetColumnRange(columnId).GetFirstElementIndex();
702 allElements.reserve(allElements.size() + columnInfos.size());
703 for (
const auto &info : columnInfos) {
705 const auto element = allElements.back().get();
707 const auto &pageRange = clusterDescriptor.GetPageRange(columnId);
708 std::uint64_t pageNo = 0;
709 std::uint64_t firstInPage = 0;
710 for (
const auto &pi : pageRange.GetPageInfos()) {
715 sealedPage.
SetBufferSize(pi.GetLocator().GetNBytesOnStorage() + pi.HasChecksum() * kNBytesPageChecksum);
716 sealedPage.
SetBuffer(onDiskPage->GetAddress());
719 auto taskFunc = [
this, columnId, clusterId, firstInPage, sealedPage, element, iPage, indexOffset,
720 &foundChecksumFailure, &allPages]() {
721 auto rv = UnsealPage(sealedPage, *element);
723 foundChecksumFailure =
true;
726 auto newPage = rv.Unwrap();
727 fCounters->fSzUnzip.Add(element->GetSize() * sealedPage.
GetNElements());
729 newPage.SetWindow(indexOffset + firstInPage,
732 allPages[iPage] = std::make_pair(std::move(newPage), keyPagePool);
735 if (sealedPage.
GetNElements() * element->GetSize() < kInlineDecompressionThreshold) {
738 fTaskScheduler->AddTask(taskFunc);
741 firstInPage += pi.GetNElements();
748 fTaskScheduler->Wait();
749 fCounters->fNPageUnsealed.Add(nPages);
751 if (foundChecksumFailure) {
752 throw RException(
R__FAIL(
"page checksum verification failed, data corruption detected"));
755 fPagePool.PreloadPageV(allPages);
763 auto descriptorGuard = EnsureClusterDetails(FindClusterGroupId(clusterKey.
fClusterId), GetSharedDescriptorGuard());
764 const auto &clusterDesc = descriptorGuard->GetClusterDescriptor(clusterKey.
fClusterId);
767 if (clusterDesc.GetColumnRange(physicalColumnId).IsSuppressed())
770 const auto &pageRange = clusterDesc.GetPageRange(physicalColumnId);
772 for (
const auto &pageInfo : pageRange.GetPageInfos()) {
776 pageInfo.GetLocator().GetNBytesOnStorage()));
778 perPageFunc(physicalColumnId, pageNo, pageInfo);
787 if (fLastUsedCluster == clusterId)
792 auto descGuard = EnsureClusterDetails(FindClusterGroupId(clusterId), GetSharedDescriptorGuard());
795 auto itr = fPreloadedClusters.begin();
796 while ((itr != fPreloadedClusters.end()) && (itr->first < firstEntryIndex)) {
797 if (fPinnedClusters.count(itr->second) > 0) {
800 fPagePool.Evict(itr->second);
801 itr = fPreloadedClusters.erase(itr);
804 std::size_t poolWindow = 0;
805 while ((itr != fPreloadedClusters.end()) &&
810 while (itr != fPreloadedClusters.end()) {
811 if (fPinnedClusters.count(itr->second) > 0) {
814 fPagePool.Evict(itr->second);
815 itr = fPreloadedClusters.erase(itr);
819 fLastUsedCluster = clusterId;
829 auto descriptorGuard = EnsureClusterDetails(FindClusterGroupId(clusterId), GetSharedDescriptorGuard());
830 const auto &clusterDescriptor = descriptorGuard->GetClusterDescriptor(clusterId);
831 pageInfo = clusterDescriptor.GetPageRange(physicalColumnId).Find(localIndex.
GetIndexInCluster());
843 LoadSealedPageImpl(pageInfo.
GetLocator(), sealedPage);
850 const auto &pageInfo = pageSummary.
fPageInfo;
854 const auto elementSize = element->
GetSize();
855 const auto elementInMemoryType = element->GetIdentifier().fInMemoryType;
857 auto pageZero = fPageAllocator->NewPage(elementSize, pageInfo.GetNElements());
858 pageZero.GrowUnchecked(pageInfo.GetNElements());
859 std::memset(pageZero.GetBuffer(), 0, pageZero.GetNBytes());
860 pageZero.SetWindow(pageSummary.
fColumnOffset + pageInfo.GetFirstElementIndex(),
871 return LoadZeroPage(columnHandle, pageSummary);
875 const auto &clusterId = pageSummary.
fClusterId;
876 const auto &pageInfo = pageSummary.
fPageInfo;
879 const auto elementSize = element->
GetSize();
880 const auto elementInMemoryType = element->GetIdentifier().fInMemoryType;
882 UpdateLastUsedCluster(clusterId);
887 sealedPage.
SetBufferSize(pageInfo.GetLocator().GetNBytesOnStorage() + pageInfo.HasChecksum() * kNBytesPageChecksum);
888 std::unique_ptr<unsigned char[]> directReadBuffer;
891 directReadBuffer = MakeUninitArray<unsigned char>(sealedPage.
GetBufferSize());
892 sealedPage.
SetBuffer(directReadBuffer.get());
893 LoadSealedPageImpl(pageInfo.GetLocator(), sealedPage);
895 fCounters->fNPageRead.Inc();
896 fCounters->fNRead.Inc();
899 if (!fCurrentCluster || (fCurrentCluster->GetId() != clusterId) || !fCurrentCluster->ContainsColumn(columnId))
900 fCurrentCluster = fClusterPool.GetCluster(clusterId, fActivePhysicalColumns.ToColumnSet());
901 R__ASSERT(fCurrentCluster->ContainsColumn(columnId));
906 if (!cachedPageRef.Get().IsNull())
907 return cachedPageRef;
910 auto onDiskPage = fCurrentCluster->GetOnDiskPage(key);
912 sealedPage.
SetBuffer(onDiskPage->GetAddress());
917 RNTupleAtomicTimer timer(fCounters->fTimeWallUnzip, fCounters->fTimeCpuUnzip);
918 newPage = UnsealPage(sealedPage, *element).Unwrap();
919 fCounters->fSzUnzip.Add(elementSize * pageInfo.GetNElements());
924 fCounters->fNPageUnsealed.Inc();
926 return fPagePool.RegisterPage(std::move(newPage),
RPagePool::RKey{columnId, elementInMemoryType});
936 if (!cachedPageRef.Get().IsNull()) {
937 UpdateLastUsedCluster(cachedPageRef.Get().GetClusterInfo().GetId());
938 return cachedPageRef;
943 auto descriptorGuard = FindClusterId(columnId, globalIndex, pageSummary.
fClusterId);
946 throw RException(
R__FAIL(
"entry with index " + std::to_string(globalIndex) +
" out of bounds"));
948 const auto &clusterDescriptor = descriptorGuard->GetClusterDescriptor(pageSummary.
fClusterId);
949 const auto &columnRange = clusterDescriptor.GetColumnRange(columnId);
950 if (columnRange.IsSuppressed())
953 pageSummary.
fColumnOffset = columnRange.GetFirstElementIndex();
955 pageSummary.
fPageInfo = clusterDescriptor.GetPageRange(columnId).Find(globalIndex - pageSummary.
fColumnOffset);
958 return LoadPageFromSummary(columnHandle, pageSummary);
969 if (!cachedPageRef.Get().IsNull()) {
970 UpdateLastUsedCluster(clusterId);
971 return cachedPageRef;
979 auto descriptorGuard = EnsureClusterDetails(FindClusterGroupId(clusterId), GetSharedDescriptorGuard());
980 const auto &clusterDescriptor = descriptorGuard->GetClusterDescriptor(clusterId);
981 const auto &columnRange = clusterDescriptor.GetColumnRange(columnId);
982 if (columnRange.IsSuppressed())
986 pageSummary.
fColumnOffset = columnRange.GetFirstElementIndex();
990 return LoadPageFromSummary(columnHandle, pageSummary);
996 fMetrics.ObserveMetrics(fClusterPool.GetMetrics());
997 fMetrics.ObserveMetrics(fPagePool.GetMetrics());
998 fCounters = std::make_unique<RCounters>(
RCounters{
1001 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"szReadPayload",
"B",
"volume read from storage (required)"),
1002 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"szReadOverhead",
"B",
"volume read from storage (overhead)"),
1005 "number of partial clusters preloaded from storage"),
1006 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"nPageRead",
"",
"number of pages read from storage"),
1007 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"nPageUnsealed",
"",
"number of pages unzipped and decoded"),
1008 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"timeWallRead",
"ns",
"wall clock time spent reading"),
1009 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"timeWallUnzip",
"ns",
"wall clock time spent decompressing"),
1012 "CPU time spent decompressing"),
1014 "bwRead",
"MB/s",
"bandwidth compressed bytes read per second", fMetrics,
1016 if (
const auto szReadPayload = metrics.GetLocalCounter(
"szReadPayload")) {
1017 if (
const auto szReadOverhead = metrics.GetLocalCounter(
"szReadOverhead")) {
1018 if (
const auto timeWallRead = metrics.GetLocalCounter(
"timeWallRead")) {
1019 if (
auto walltime = timeWallRead->GetValueAsInt()) {
1020 double payload = szReadPayload->GetValueAsInt();
1021 double overhead = szReadOverhead->GetValueAsInt();
1023 return {
true, (1000. * (payload + overhead) / walltime)};
1028 return {
false, -1.};
1031 "bwReadUnzip",
"MB/s",
"bandwidth uncompressed bytes read per second", fMetrics,
1034 if (
const auto timeWallRead = metrics.
GetLocalCounter(
"timeWallRead")) {
1035 if (
auto walltime = timeWallRead->GetValueAsInt()) {
1038 return {
true, 1000. * unzip / walltime};
1042 return {
false, -1.};
1045 "bwUnzip",
"MB/s",
"decompression bandwidth of uncompressed bytes per second", fMetrics,
1048 if (
const auto timeWallUnzip = metrics.
GetLocalCounter(
"timeWallUnzip")) {
1049 if (
auto walltime = timeWallUnzip->GetValueAsInt()) {
1052 return {
true, 1000. * unzip / walltime};
1056 return {
false, -1.};
1059 "rtReadEfficiency",
"",
"ratio of payload over all bytes read", fMetrics,
1061 if (
const auto szReadPayload = metrics.
GetLocalCounter(
"szReadPayload")) {
1062 if (
const auto szReadOverhead = metrics.
GetLocalCounter(
"szReadOverhead")) {
1063 if (
auto payload = szReadPayload->GetValueAsInt()) {
1065 return {
true, 1. / (1. + (1. * szReadOverhead->GetValueAsInt()) / payload)};
1069 return {
false, -1.};
1071 *fMetrics.MakeCounter<
RNTupleCalcPerf *>(
"rtCompression",
"",
"ratio of compressed bytes / uncompressed bytes",
1072 fMetrics, [](
const RNTupleMetrics &metrics) -> std::pair<bool, double> {
1073 if (
const auto szReadPayload =
1076 if (
auto unzip = szUnzip->GetValueAsInt()) {
1077 return {
true, (1. * szReadPayload->GetValueAsInt()) / unzip};
1081 return {
false, -1.};
1088 return UnsealPage(sealedPage, element, *fPageAllocator);
1100 memset(page.GetBuffer(), 0, page.GetNBytes());
1117 memcpy(page.GetBuffer(), sealedPage.
GetBuffer(), bytesPacked);
1123 page = std::move(tmp);
1132 if (fHasStreamerInfosRegistered)
1135 for (
const auto &extraTypeInfo : fDescriptor.GetExtraTypeInfoIterable()) {
1143 fHasStreamerInfosRegistered =
true;
1158 if (fMaxAllocatedBytes - fCurrentAllocatedBytes >= targetAvailableSize)
1161 auto itr = fColumnsSortedByPageSize.begin();
1162 while (itr != fColumnsSortedByPageSize.end()) {
1163 if (itr->fCurrentPageSize <= pageSizeLimit)
1165 if (itr->fCurrentPageSize == itr->fInitialPageSize) {
1171 auto itrFlush = itr++;
1174 if (itr != fColumnsSortedByPageSize.end())
1178 if (fMaxAllocatedBytes - fCurrentAllocatedBytes >= targetAvailableSize)
1183 itr = fColumnsSortedByPageSize.find(next);
1192 auto itr = fColumnsSortedByPageSize.find(key);
1193 if (itr == fColumnsSortedByPageSize.end()) {
1194 if (!TryEvict(newWritePageSize, 0))
1196 fColumnsSortedByPageSize.insert({&column, newWritePageSize, newWritePageSize});
1197 fCurrentAllocatedBytes += newWritePageSize;
1202 assert(newWritePageSize >= elem.fInitialPageSize);
1204 if (newWritePageSize == elem.fCurrentPageSize)
1207 fColumnsSortedByPageSize.erase(itr);
1209 if (newWritePageSize < elem.fCurrentPageSize) {
1211 fCurrentAllocatedBytes -= elem.fCurrentPageSize - newWritePageSize;
1212 elem.fCurrentPageSize = newWritePageSize;
1213 fColumnsSortedByPageSize.insert(elem);
1218 const auto diffBytes = newWritePageSize - elem.fCurrentPageSize;
1219 if (!TryEvict(diffBytes, elem.fCurrentPageSize)) {
1222 fColumnsSortedByPageSize.insert(elem);
1225 fCurrentAllocatedBytes += diffBytes;
1226 elem.fCurrentPageSize = newWritePageSize;
1227 fColumnsSortedByPageSize.insert(elem);
1234 :
RPageStorage(
name), fOptions(options.Clone()), fWritePageMemoryManager(options.GetPageBufferBudget())
1243 assert(config.
fPage);
1247 unsigned char *pageBuf =
reinterpret_cast<unsigned char *
>(config.
fPage->
GetBuffer());
1248 bool isAdoptedBuffer =
true;
1250 auto nBytesChecksum = config.
fWriteChecksum * kNBytesPageChecksum;
1254 pageBuf =
new unsigned char[nBytesPacked];
1255 isAdoptedBuffer =
false;
1258 auto nBytesZipped = nBytesPacked;
1264 if (!isAdoptedBuffer)
1266 pageBuf =
reinterpret_cast<unsigned char *
>(config.
fBuffer);
1267 isAdoptedBuffer =
true;
1273 sealedPage.ChecksumIfEnabled();
1281 const auto nBytes = page.
GetNBytes() + GetWriteOptions().GetEnablePageChecksums() * kNBytesPageChecksum;
1282 if (fSealPageBuffer.size() < nBytes)
1283 fSealPageBuffer.resize(nBytes);
1286 config.
fPage = &page;
1289 config.
fWriteChecksum = GetWriteOptions().GetEnablePageChecksums();
1291 config.
fBuffer = fSealPageBuffer.data();
1293 return SealPage(config);
1298 for (
const auto &cb : fOnDatasetCommitCallbacks)
1300 return CommitDatasetImpl();
1307 const auto nBytes = elementSize * nElements;
1308 if (!fWritePageMemoryManager.TryUpdate(*columnHandle.
fColumn, nBytes))
1310 return fPageAllocator->NewPage(elementSize, nElements);
1315std::unique_ptr<ROOT::Internal::RPageSink>
1319 if (ntupleName.empty()) {
1322 if (location.empty()) {
1325 if (location.find(
"daos://") == 0) {
1326#ifdef R__ENABLE_DAOS
1327 return std::make_unique<ROOT::Experimental::Internal::RPageSinkDaos>(ntupleName, location, options);
1335 return std::make_unique<ROOT::Experimental::Internal::RPageSinkS3>(ntupleName, location, options);
1337 throw RException(
R__FAIL(
"This RNTuple build does not support S3. Rebuild ROOT with the 'curl' "
1338 "cmake option enabled (-Dcurl=ON) to enable the S3 backend."));
1343 return std::make_unique<ROOT::Internal::RPageSinkFile>(ntupleName, location, options);
1357 auto columnId = fDescriptorBuilder.GetDescriptor().GetNPhysicalColumns();
1372 fDescriptorBuilder.AddColumn(columnBuilder.
MoveDescriptor().Unwrap());
1379 if (fIsInitialized) {
1387 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1389 if (descriptor.GetNLogicalColumns() > descriptor.GetNPhysicalColumns()) {
1393 const auto &reps =
f.GetColumnRepresentatives();
1396 return reps.size() * reps[0].size();
1398 std::uint32_t nNewPhysicalColumns = 0;
1400 nNewPhysicalColumns += getNColumns(*
f);
1401 for (
const auto &descendant : *
f)
1402 nNewPhysicalColumns += getNColumns(descendant);
1404 fDescriptorBuilder.ShiftAliasColumns(nNewPhysicalColumns);
1408 auto fieldId = descriptor.GetNFields();
1409 fDescriptorBuilder.AddField(
f, fieldId);
1410 fDescriptorBuilder.AddFieldLink(
f.GetParent()->GetOnDiskId(), fieldId);
1411 f.SetOnDiskId(fieldId);
1415 auto fieldId = descriptor.GetNFields();
1416 auto sourceFieldId =
1418 fDescriptorBuilder.AddField(
f, fieldId);
1419 fDescriptorBuilder.AddFieldLink(
f.GetParent()->GetOnDiskId(), fieldId);
1420 fDescriptorBuilder.AddFieldProjection(sourceFieldId, fieldId);
1421 f.SetOnDiskId(fieldId);
1422 for (
const auto &source : descriptor.GetColumnIterable(sourceFieldId)) {
1423 auto targetId = descriptor.GetNLogicalColumns();
1429 .ValueRange(source.GetValueRange())
1430 .Type(source.GetType())
1431 .Index(source.GetIndex())
1432 .RepresentationIndex(source.GetRepresentationIndex());
1433 fDescriptorBuilder.AddColumn(columnBuilder.
MoveDescriptor().Unwrap());
1437 R__ASSERT(firstEntry >= fPrevClusterNEntries);
1438 const auto nColumnsBeforeUpdate = descriptor.GetNPhysicalColumns();
1441 for (
auto &descendant : *
f)
1442 addField(descendant);
1445 addProjectedField(*
f);
1446 for (
auto &descendant : *
f)
1447 addProjectedField(descendant);
1450 const auto nColumns = descriptor.GetNPhysicalColumns();
1451 fOpenColumnRanges.reserve(fOpenColumnRanges.size() + (nColumns - nColumnsBeforeUpdate));
1452 fOpenPageRanges.reserve(fOpenPageRanges.size() + (nColumns - nColumnsBeforeUpdate));
1462 fOpenColumnRanges.emplace_back(columnRange);
1465 fOpenPageRanges.emplace_back(std::move(pageRange));
1470 if (fSerializationContext.GetHeaderSize() > 0)
1471 fSerializationContext.MapSchema(descriptor,
true);
1477 throw RException(
R__FAIL(
"ROOT bug: unexpected type extra info in UpdateExtraTypeInfo()"));
1484 fDescriptorBuilder.SetNTuple(fNTupleName, model.GetDescription());
1485 fDescriptorBuilder.SetVersionForWriting();
1486 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1489 fDescriptorBuilder.AddField(fieldZero, 0);
1490 fieldZero.SetOnDiskId(0);
1492 projectedFields.GetFieldZero().SetOnDiskId(0);
1495 initialChangeset.fAddedFields.reserve(fieldZero.GetMutableSubfields().size());
1496 for (
auto f : fieldZero.GetMutableSubfields())
1497 initialChangeset.fAddedFields.emplace_back(
f);
1498 initialChangeset.fAddedProjectedFields.reserve(projectedFields.GetFieldZero().GetMutableSubfields().size());
1499 for (
auto f : projectedFields.GetFieldZero().GetMutableSubfields())
1500 initialChangeset.fAddedProjectedFields.emplace_back(
f);
1501 UpdateSchema(initialChangeset, 0U);
1504 auto buffer = MakeUninitArray<unsigned char>(fSerializationContext.GetHeaderSize());
1506 InitImpl(buffer.get(), fSerializationContext.GetHeaderSize());
1508 fDescriptorBuilder.BeginHeaderExtension();
1511std::unique_ptr<ROOT::RNTupleModel>
1515 fDescriptorBuilder.SetSchemaFromExisting(srcDescriptor);
1517 fDescriptorBuilder.SetVersionForWriting();
1518 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1521 const auto nColumns = descriptor.GetNPhysicalColumns();
1522 R__ASSERT(fOpenColumnRanges.empty() && fOpenPageRanges.empty());
1523 fOpenColumnRanges.reserve(nColumns);
1524 fOpenPageRanges.reserve(nColumns);
1526 const auto &column = descriptor.GetColumnDescriptor(i);
1532 fOpenColumnRanges.emplace_back(columnRange);
1535 fOpenPageRanges.emplace_back(std::move(pageRange));
1542 auto nEntries = cluster.GetNEntries();
1543 for (
unsigned int i = 0; i < fOpenColumnRanges.size(); ++i) {
1544 R__ASSERT(fOpenColumnRanges[i].GetPhysicalColumnId() == i);
1545 if (!cluster.ContainsColumn(i))
1547 const auto &columnRange = cluster.GetColumnRange(i);
1548 R__ASSERT(columnRange.GetPhysicalColumnId() == i);
1550 fOpenColumnRanges[i].IncrementFirstElementIndex(columnRange.GetNElements());
1552 fDescriptorBuilder.AddCluster(cluster.Clone());
1553 fPrevClusterNEntries += nEntries;
1559 modelOpts.SetReconstructProjections(
true);
1561 modelOpts.SetEmulateUnknownTypes(
true);
1562 auto model = descriptor.CreateModel(modelOpts);
1563 if (!copyClusters) {
1565 projectedFields.GetFieldZero().SetOnDiskId(model->GetConstFieldZero().GetOnDiskId());
1570 auto buffer = MakeUninitArray<unsigned char>(fSerializationContext.GetHeaderSize());
1572 InitImpl(buffer.get(), fSerializationContext.GetHeaderSize());
1574 fDescriptorBuilder.BeginHeaderExtension();
1577 fIsInitialized =
true;
1584 std::span<const RColumnFormat> newRepresentation,
1585 std::uint64_t clusterOffset)
1587 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1589 assert(&descriptor.GetFieldDescriptor(field.
GetId()) == &field);
1595 const std::size_t firstPhysicalIndex = fDescriptorBuilder.GetDescriptor().GetNPhysicalColumns();
1598 fDescriptorBuilder.ShiftAliasColumns(newRepresentation.size());
1600 std::uint16_t columnIndex = 0;
1601 for (
auto columnRepr : newRepresentation) {
1602 std::size_t bitsOnStorage = columnRepr.fBitWidth;
1603 if (!bitsOnStorage) {
1605 if (rangeMin != rangeMax) {
1608 bitsOnStorage = rangeMin;
1612 const auto &firstReprColumnRange = fOpenColumnRanges.at(firstReprColumnId);
1615 const std::uint64_t newReprFirstElemIndex = firstReprColumnRange.GetFirstElementIndex() + clusterOffset;
1622 .
Type(columnRepr.fType)
1627 if (newReprFirstElemIndex)
1629 fDescriptorBuilder.AddColumn(columnBuilder.
MoveDescriptor().Unwrap());
1631 if (newReprFirstElemIndex != 0) {
1648 fOpenColumnRanges.emplace_back(columnRange);
1652 fOpenPageRanges.emplace_back(std::move(pageRange));
1654 fSerializationContext.MapPhysicalColumnId(columnId);
1659 fDescriptorBuilder.EnsureValidDescriptor().ThrowOnError();
1661 return firstPhysicalIndex;
1669 assert(!pointedColumn.IsAliasColumn());
1672 const auto columnId = fDescriptorBuilder.GetDescriptor().GetNLogicalColumns();
1677 .
Type(pointedColumn.GetType())
1678 .
Index(pointedColumn.GetIndex())
1683 fDescriptorBuilder.AddColumn(columnBuilder.
MoveDescriptor().Unwrap());
1685 fDescriptorBuilder.EnsureValidDescriptor().ThrowOnError();
1690 fOpenColumnRanges.at(columnHandle.
fPhysicalId).SetIsSuppressed(
true);
1700 RNTupleAtomicTimer timer(fCounters->fTimeWallZip, fCounters->fTimeCpuZip);
1701 sealedPage = SealPage(page, *element);
1703 fCounters->fSzZip.Add(page.
GetNBytes());
1708 pageInfo.
SetHasChecksum(GetWriteOptions().GetEnablePageChecksums());
1709 fOpenPageRanges.at(columnHandle.
fPhysicalId).GetPageInfos().emplace_back(pageInfo);
1715 fOpenColumnRanges.at(physicalColumnId).IncrementNElements(sealedPage.
GetNElements());
1719 pageInfo.
SetLocator(CommitSealedPageImpl(physicalColumnId, sealedPage));
1721 fOpenPageRanges.at(physicalColumnId).GetPageInfos().emplace_back(pageInfo);
1724std::vector<ROOT::RNTupleLocator>
1726 const std::vector<bool> &
mask)
1728 std::vector<ROOT::RNTupleLocator> locators;
1729 locators.reserve(
mask.size());
1731 for (
auto &range : ranges) {
1732 for (
auto sealedPageIt = range.fFirst; sealedPageIt != range.fLast; ++sealedPageIt) {
1734 locators.push_back(CommitSealedPageImpl(range.fPhysicalColumnId, *sealedPageIt));
1737 locators.shrink_to_fit();
1744 struct RSealedPageLink {
1746 std::size_t fLocatorIdx = 0;
1749 std::vector<bool>
mask;
1751 std::vector<std::size_t> locatorIndexes;
1753 std::unordered_map<std::uint64_t, RSealedPageLink> originalPages;
1754 std::size_t iLocator = 0;
1755 for (
auto &range : ranges) {
1756 const auto rangeSize = std::distance(range.fFirst, range.fLast);
1757 mask.reserve(
mask.size() + rangeSize);
1758 locatorIndexes.reserve(locatorIndexes.size() + rangeSize);
1760 for (
auto sealedPageIt = range.fFirst; sealedPageIt != range.fLast; ++sealedPageIt) {
1761 if (!fFeatures.fCanMergePages || !fOptions->GetEnableSamePageMerging()) {
1762 mask.emplace_back(
true);
1763 locatorIndexes.emplace_back(iLocator++);
1767 R__ASSERT(sealedPageIt->GetHasChecksum());
1769 const auto chk = sealedPageIt->GetChecksum().Unwrap();
1770 auto itr = originalPages.find(chk);
1771 if (itr == originalPages.end()) {
1772 originalPages.insert({chk, {&(*sealedPageIt), iLocator}});
1773 mask.emplace_back(
true);
1774 locatorIndexes.emplace_back(iLocator++);
1778 const auto *
p = itr->second.fSealedPage;
1779 if ((sealedPageIt->GetDataSize() !=
p->GetDataSize()) ||
1780 (memcmp(sealedPageIt->GetBuffer(),
p->GetBuffer(),
p->GetDataSize()) != 0)) {
1781 mask.emplace_back(
true);
1782 locatorIndexes.emplace_back(iLocator++);
1786 mask.emplace_back(
false);
1787 locatorIndexes.emplace_back(itr->second.fLocatorIdx);
1790 mask.shrink_to_fit();
1791 locatorIndexes.shrink_to_fit();
1794 auto locators = CommitSealedPageVImpl(ranges,
mask);
1797 for (
auto &range : ranges) {
1798 for (
auto sealedPageIt = range.fFirst; sealedPageIt != range.fLast; ++sealedPageIt) {
1799 fOpenColumnRanges.at(range.fPhysicalColumnId).IncrementNElements(sealedPageIt->GetNElements());
1803 pageInfo.
SetLocator(locators[locatorIndexes[i++]]);
1805 fOpenPageRanges.at(range.fPhysicalColumnId).GetPageInfos().emplace_back(pageInfo);
1817 for (
unsigned int i = 0; i < fOpenColumnRanges.size(); ++i) {
1818 RStagedCluster::RColumnInfo columnInfo;
1819 columnInfo.fCompressionSettings = fOpenColumnRanges[i].GetCompressionSettings().value();
1820 if (fOpenColumnRanges[i].IsSuppressed()) {
1821 assert(fOpenPageRanges[i].GetPageInfos().empty());
1822 columnInfo.fPageRange.SetPhysicalColumnId(i);
1823 columnInfo.fIsSuppressed =
true;
1825 fOpenColumnRanges[i].SetNElements(0);
1826 fOpenColumnRanges[i].SetIsSuppressed(
false);
1828 std::swap(columnInfo.fPageRange, fOpenPageRanges[i]);
1829 fOpenPageRanges[i].SetPhysicalColumnId(i);
1831 columnInfo.fNElements = fOpenColumnRanges[i].GetNElements();
1832 fOpenColumnRanges[i].SetNElements(0);
1834 stagedCluster.
fColumnInfos.push_back(std::move(columnInfo));
1837 return stagedCluster;
1842 for (
const auto &cluster : clusters) {
1844 clusterBuilder.
ClusterId(fDescriptorBuilder.GetDescriptor().GetNActiveClusters())
1845 .FirstEntryIndex(fPrevClusterNEntries)
1847 for (
const auto &columnInfo : cluster.fColumnInfos) {
1848 const auto colId = columnInfo.fPageRange.GetPhysicalColumnId();
1849 if (columnInfo.fIsSuppressed) {
1850 assert(columnInfo.fPageRange.GetPageInfos().empty());
1853 clusterBuilder.
CommitColumnRange(colId, fOpenColumnRanges[colId].GetFirstElementIndex(),
1854 columnInfo.fCompressionSettings, columnInfo.fPageRange);
1855 fOpenColumnRanges[colId].IncrementFirstElementIndex(columnInfo.fNElements);
1860 for (
const auto &columnInfo : cluster.fColumnInfos) {
1861 if (!columnInfo.fIsSuppressed)
1863 const auto colId = columnInfo.fPageRange.GetPhysicalColumnId();
1867 const auto &columnRangeFromDesc = clusterBuilder.
GetColumnRange(colId);
1869 columnRangeFromDesc.GetNElements());
1872 fDescriptorBuilder.AddCluster(clusterBuilder.
MoveDescriptor().Unwrap());
1873 fPrevClusterNEntries += cluster.fNEntries;
1879 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1881 const auto nClusters = descriptor.GetNActiveClusters();
1882 std::vector<ROOT::DescriptorId_t> physClusterIDs;
1883 physClusterIDs.reserve(nClusters);
1884 for (
auto i = fNextClusterInGroup; i < nClusters; ++i) {
1885 physClusterIDs.emplace_back(fSerializationContext.MapClusterId(i));
1890 auto bufPageList = MakeUninitArray<unsigned char>(szPageList);
1893 const auto clusterGroupId = descriptor.GetNClusterGroups();
1894 const auto locator = CommitClusterGroupImpl(bufPageList.get(), szPageList);
1897 if (fNextClusterInGroup == nClusters) {
1900 const auto &firstClusterDesc = descriptor.GetClusterDescriptor(fNextClusterInGroup);
1901 const auto &lastClusterDesc = descriptor.GetClusterDescriptor(nClusters - 1);
1902 cgBuilder.
MinEntry(firstClusterDesc.GetFirstEntryIndex())
1903 .
EntrySpan(lastClusterDesc.GetFirstEntryIndex() + lastClusterDesc.GetNEntries() -
1904 firstClusterDesc.GetFirstEntryIndex())
1905 .
NClusters(nClusters - fNextClusterInGroup);
1907 std::vector<ROOT::DescriptorId_t> clusterIds;
1908 clusterIds.reserve(nClusters);
1909 for (
auto i = fNextClusterInGroup; i < nClusters; ++i) {
1910 clusterIds.emplace_back(i);
1913 fDescriptorBuilder.AddClusterGroup(cgBuilder.
MoveDescriptor().Unwrap());
1914 fSerializationContext.MapClusterGroupId(clusterGroupId);
1916 fNextClusterInGroup = nClusters;
1925 auto attrSetDesc = attrSetDescBuilder.
SchemaVersion(kSchemaVersionMajor, kSchemaVersionMinor)
1931 fDescriptorBuilder.AddAttributeSet(std::move(attrSetDesc)).ThrowOnError();
1936 if (!fInfosOfStreamerFields.empty()) {
1939 for (
const auto &etDesc : fDescriptorBuilder.GetDescriptor().GetExtraTypeInfoIterable()) {
1943 R__ASSERT(etDesc.GetTypeName().empty());
1944 R__ASSERT(etDesc.GetTypeVersion() == 0);
1946 fInfosOfStreamerFields.merge(etInfo);
1953 fDescriptorBuilder.ReplaceExtraTypeInfo(extraInfoBuilder.
MoveDescriptor().Unwrap());
1956 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1959 auto bufFooter = MakeUninitArray<unsigned char>(szFooter);
1962 return CommitDatasetImpl(bufFooter.get(), szFooter);
1968 fCounters = std::make_unique<RCounters>(
RCounters{
1969 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"nPageCommitted",
"",
"number of pages committed to storage"),
1970 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"szWritePayload",
"B",
"volume written for committed pages"),
1972 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"timeWallWrite",
"ns",
"wall clock time spent writing"),
1973 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"timeWallZip",
"ns",
"wall clock time spent compressing"),
1976 "CPU time spent compressing")});
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
winID h TVirtualViewer3D TVirtualGLPainter p
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 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 index
Option_t Option_t TPoint TPoint const char mode
A thread-safe integral performance counter.
A metric element that computes its floating point value from other counters.
std::int64_t GetValueAsInt() const override
A collection of Counter objects with a name, a unit, and a description.
const RNTuplePerfCounter * GetLocalCounter(std::string_view name) const
Searches counters registered in this object only. Returns nullptr if name is not found.
Record wall time and CPU time between construction and destruction.
A helper class for piece-wise construction of an RClusterDescriptor.
RResult< void > MarkSuppressedColumnRange(ROOT::DescriptorId_t physicalId)
Books the given column ID as being suppressed in this cluster.
RResult< void > CommitColumnRange(ROOT::DescriptorId_t physicalId, std::uint64_t firstElementIndex, std::uint32_t compressionSettings, const RClusterDescriptor::RPageRange &pageRange)
RClusterDescriptorBuilder & NEntries(std::uint64_t nEntries)
RResult< void > CommitSuppressedColumnRanges(const RNTupleDescriptor &desc)
Sets the first element index and number of elements for all the suppressed column ranges.
RResult< RClusterDescriptor > MoveDescriptor()
Move out the full cluster descriptor including page locations.
const RClusterDescriptor::RColumnRange & GetColumnRange(ROOT::DescriptorId_t physicalId)
RClusterDescriptorBuilder & ClusterId(ROOT::DescriptorId_t clusterId)
A helper class for piece-wise construction of an RClusterGroupDescriptor.
RClusterGroupDescriptorBuilder & EntrySpan(std::uint64_t entrySpan)
RClusterGroupDescriptorBuilder & PageListLocator(const RNTupleLocator &pageListLocator)
RClusterGroupDescriptorBuilder & PageListLength(std::uint64_t pageListLength)
RClusterGroupDescriptorBuilder & MinEntry(std::uint64_t minEntry)
void AddSortedClusters(const std::vector< ROOT::DescriptorId_t > &clusterIds)
RResult< RClusterGroupDescriptor > MoveDescriptor()
RClusterGroupDescriptorBuilder & ClusterGroupId(ROOT::DescriptorId_t clusterGroupId)
RClusterGroupDescriptorBuilder & NClusters(std::uint32_t nClusters)
An in-memory subset of the packed and compressed pages of a cluster.
std::unordered_set< ROOT::DescriptorId_t > ColumnSet_t
const ColumnSet_t & GetAvailPhysicalColumns() const
const ROnDiskPage * GetOnDiskPage(const ROnDiskPage::Key &key) const
ROOT::DescriptorId_t GetId() const
A helper class for piece-wise construction of an RColumnDescriptor.
RColumnDescriptorBuilder & SetSuppressedDeferred()
RColumnDescriptorBuilder & LogicalColumnId(ROOT::DescriptorId_t logicalColumnId)
RColumnDescriptorBuilder & FieldId(ROOT::DescriptorId_t fieldId)
RColumnDescriptorBuilder & BitsOnStorage(std::uint16_t bitsOnStorage)
RColumnDescriptorBuilder & ValueRange(double min, double max)
RColumnDescriptorBuilder & Type(ROOT::ENTupleColumnType type)
RColumnDescriptorBuilder & PhysicalColumnId(ROOT::DescriptorId_t physicalColumnId)
RColumnDescriptorBuilder & FirstElementIndex(std::uint64_t firstElementIdx)
RColumnDescriptorBuilder & Index(std::uint32_t index)
RColumnDescriptorBuilder & RepresentationIndex(std::uint16_t representationIndex)
RResult< RColumnDescriptor > MoveDescriptor()
Attempt to make a column descriptor.
A column element encapsulates the translation between basic C++ types and their column representation...
virtual bool IsMappable() const
Derived, typed classes tell whether the on-storage layout is bitwise identical to the memory layout.
static std::pair< std::uint16_t, std::uint16_t > GetValidBitRange(ROOT::ENTupleColumnType type)
Most types have a fixed on-disk bit width.
virtual void Unpack(void *destination, const void *source, std::size_t count) const
If the on-storage layout and the in-memory layout differ, unpacking creates a memory page from an on-...
std::size_t GetSize() const
virtual RIdentifier GetIdentifier() const =0
virtual void Pack(void *destination, const void *source, std::size_t count) const
If the on-storage layout and the in-memory layout differ, packing creates an on-disk page from an in-...
std::size_t GetPackedSize(std::size_t nElements=1U) const
A column is a storage-backed array of a simple, fixed-size type, from which pages can be mapped into ...
std::optional< std::pair< double, double > > GetValueRange() const
std::uint16_t GetRepresentationIndex() const
ROOT::Internal::RColumnElementBase * GetElement() const
ROOT::ENTupleColumnType GetType() const
ROOT::NTupleSize_t GetFirstElementIndex() const
std::size_t GetWritePageCapacity() const
ROOT::DescriptorId_t GetOnDiskId() const
std::uint16_t GetBitsOnStorage() const
std::uint32_t GetIndex() const
RNTupleAttrSetDescriptorBuilder & Name(std::string_view name)
RNTupleAttrSetDescriptorBuilder & SchemaVersion(std::uint16_t major, std::uint16_t minor)
RResult< ROOT::RNTupleAttrSetDescriptor > MoveDescriptor()
Attempt to make an AttributeSet descriptor.
RNTupleAttrSetDescriptorBuilder & AnchorLocator(const RNTupleLocator &loc)
RNTupleAttrSetDescriptorBuilder & AnchorLength(std::uint32_t length)
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.
static unsigned int GetClusterBunchSize(const RNTupleReadOptions &options)
static unsigned int GetClusterGroupBudget(const RNTupleReadOptions &options)
static std::uint32_t SerializeXxHash3(const unsigned char *data, std::uint64_t length, std::uint64_t &xxhash3, void *buffer)
Writes a XxHash-3 64bit checksum of the byte range given by data and length.
static RResult< void > DeserializePageList(const void *buffer, std::uint64_t bufSize, ROOT::DescriptorId_t clusterGroupId, RNTupleDescriptor &desc, EDescriptorDeserializeMode mode)
static RResult< StreamerInfoMap_t > DeserializeStreamerInfos(const std::string &extraTypeInfoContent)
static RResult< void > VerifyXxHash3(const unsigned char *data, std::uint64_t length, std::uint64_t &xxhash3)
Expects an xxhash3 checksum in the 8 bytes following data + length and verifies it.
EDescriptorDeserializeMode
static RResult< std::uint32_t > SerializePageList(void *buffer, const RNTupleDescriptor &desc, std::span< ROOT::DescriptorId_t > physClusterIDs, const RContext &context)
static RResult< std::uint32_t > SerializeFooter(void *buffer, const RNTupleDescriptor &desc, const RContext &context)
static std::uint32_t DeserializeUInt64(const void *buffer, std::uint64_t &val)
static RResult< RContext > SerializeHeader(void *buffer, const RNTupleDescriptor &desc)
static std::string SerializeStreamerInfos(const StreamerInfoMap_t &infos)
A memory region that contains packed and compressed pages.
void Register(const ROnDiskPage::Key &key, const ROnDiskPage &onDiskPage)
Inserts information about a page stored in fMemory.
A page as being stored on disk, that is packed and compressed.
Uses standard C++ memory allocation for the column data pages.
Abstract interface to allocate and release pages.
virtual RPage NewPage(std::size_t elementSize, std::size_t nElements)=0
Reserves memory large enough to hold nElements of the given size.
RStagedCluster StageCluster(ROOT::NTupleSize_t nNewEntries) final
Stage the current cluster and create a new one for the following data.
void UpdateSchema(const ROOT::Internal::RNTupleModelChangeset &changeset, ROOT::NTupleSize_t firstEntry) override
Incorporate incremental changes to the model into the ntuple descriptor.
void CommitSealedPage(ROOT::DescriptorId_t physicalColumnId, const RPageStorage::RSealedPage &sealedPage) final
Write a preprocessed page to storage. The column must have been added before.
std::unique_ptr< RNTupleModel > InitFromDescriptor(const ROOT::RNTupleDescriptor &descriptor, bool copyClusters)
Initialize sink based on an existing descriptor and fill into the descriptor builder,...
void UpdateExtraTypeInfo(const ROOT::RExtraTypeInfoDescriptor &extraTypeInfo) final
Adds an extra type information record to schema.
void CommitAttributeSet(std::string_view attrSetName, const RNTupleLink &attrAnchorInfo) final
Adds the given anchor information (name + locator) into the main RNTuple's descriptor as an attribute...
ColumnHandle_t AddColumn(ROOT::DescriptorId_t fieldId, ROOT::Internal::RColumn &column) final
Register a new column.
virtual std::vector< RNTupleLocator > CommitSealedPageVImpl(std::span< RPageStorage::RSealedPageGroup > ranges, const std::vector< bool > &mask)
Vector commit of preprocessed pages.
RPagePersistentSink(std::string_view ntupleName, const ROOT::RNTupleWriteOptions &options)
void CommitSuppressedColumn(ColumnHandle_t columnHandle) final
Commits a suppressed column for the current cluster.
void AddAliasColumn(const ROOT::RNTupleDescriptor &desc, const ROOT::RFieldDescriptor &field, ROOT::DescriptorId_t physicalId)
Adds a new alias column pointing to an existing column with the given physical id to the given field.
void CommitStagedClusters(std::span< RStagedCluster > clusters) final
Commit staged clusters, logically appending them to the ntuple descriptor.
static std::unique_ptr< RPageSink > Create(std::string_view ntupleName, std::string_view location, const ROOT::RNTupleWriteOptions &options=ROOT::RNTupleWriteOptions())
Guess the concrete derived page source from the location.
void CommitPage(ColumnHandle_t columnHandle, const ROOT::Internal::RPage &page) final
Write a page to the storage. The column must have been added before.
RNTupleLink CommitDatasetImpl() final
~RPagePersistentSink() override
virtual void InitImpl(unsigned char *serializedHeader, std::uint32_t length)=0
void CommitClusterGroup() final
Write out the page locations (page list envelope) for all the committed clusters since the last call ...
void CommitSealedPageV(std::span< RPageStorage::RSealedPageGroup > ranges) final
Write a vector of preprocessed pages to storage. The corresponding columns must have been added befor...
void EnableDefaultMetrics(const std::string &prefix)
Enables the default set of metrics provided by RPageSink.
ROOT::DescriptorId_t AddColumnRepresentation(const ROOT::RFieldDescriptor &field, std::span< const ROOT::Internal::RColumnFormat > newRepresentation, std::uint64_t clusterOffset)
Adds a new column representation to the given field.
Abstract interface to write data into an ntuple.
RNTupleLink CommitDataset()
Run the registered callbacks and finalize the current cluster and the entrire data set.
virtual ROOT::Internal::RPage ReservePage(ColumnHandle_t columnHandle, std::size_t nElements)
Get a new, empty page for the given column that can be filled with up to nElements; nElements must be...
RSealedPage SealPage(const ROOT::Internal::RPage &page, const ROOT::Internal::RColumnElementBase &element)
Helper for streaming a page.
RPageSink(std::string_view ntupleName, const ROOT::RNTupleWriteOptions &options)
void Insert(ROOT::DescriptorId_t physicalColumnId, ROOT::Internal::RColumnElementBase::RIdentifier elementId)
ROOT::Internal::RCluster::ColumnSet_t ToColumnSet() const
void Erase(ROOT::DescriptorId_t physicalColumnId, ROOT::Internal::RColumnElementBase::RIdentifier elementId)
Either a shared or an exclusive lock guard.
An RAII wrapper used for the writable access to RPageSource::fDescriptor. See GetSharedDescriptorGuar...
RAnyDescriptorGuard FindNextClusterId(ROOT::DescriptorId_t clusterId, ROOT::DescriptorId_t &nextId)
Uses FindClusterId to search for the cluster with the entry index following the last entry index of t...
void LoadStructure()
Loads header and footer without decompressing or deserializing them.
virtual ROOT::Internal::RPageRef LoadPage(ColumnHandle_t columnHandle, ROOT::NTupleSize_t globalIndex)
Allocates and fills a page that contains the index-th element.
void LoadAllPageLists()
Load all remaining cluster details.
void RegisterStreamerInfos()
Builds the streamer info records from the descriptor's extra type info section.
ROOT::NTupleSize_t GetNElements(ROOT::DescriptorId_t physicalColumnId)
RAnyDescriptorGuard EnsureClusterDetails(DescriptorId_t cgId, RAnyDescriptorGuard descGuard)
Ensures that fDescriptor has cluster details loaded for the given cluster group ID.
void Attach(ROOT::Internal::RNTupleSerializer::EDescriptorDeserializeMode mode=ROOT::Internal::RNTupleSerializer::EDescriptorDeserializeMode::kForReading)
Open the physical storage container and deserialize header and footer.
ColumnHandle_t AddColumn(ROOT::DescriptorId_t fieldId, ROOT::Internal::RColumn &column) override
Register a new column.
void UnzipCluster(ROOT::Internal::RCluster *cluster)
Parallel decompression and unpacking of the pages in the given cluster.
void EnableDefaultMetrics(const std::string &prefix)
Enables the default set of metrics provided by RPageSource.
ROOT::NTupleSize_t GetNEntries()
ROOT::Internal::RPageRef LoadZeroPage(ColumnHandle_t columnHandle, const RPageSummary &pageSummary)
void UpdateLastUsedCluster(ROOT::DescriptorId_t clusterId)
Does nothing if fLastUsedCluster == clusterId.
void UnpinCluster(ROOT::DescriptorId_t clusterId)
Allows the given cluster to be evicted from the cluster pool and page pool.
ROOT::Internal::RPageRef LoadPageFromSummary(ColumnHandle_t columnHandle, const RPageSummary &pageSummary)
void PinCluster(ROOT::DescriptorId_t clusterId)
Instructs the cluster pool and page pool to consider the given cluster as active (should stay cached)...
void DropColumn(ColumnHandle_t columnHandle) override
Unregisters a column.
RAnyDescriptorGuard FindClusterId(ROOT::DescriptorId_t physicalColumnId, ROOT::NTupleSize_t index, ROOT::DescriptorId_t &cid)
Returns a shared descriptor guard to ensure that the returned cluster id is useable,...
void LoadSealedPage(ROOT::DescriptorId_t physicalColumnId, RNTupleLocalIndex localIndex, RSealedPage &sealedPage)
Read the packed and compressed bytes of a page into the memory buffer provided by sealedPage.
void LoadPageList(DescriptorId_t clusterGroupId, const RExclDescriptorGuard &exclGuard)
RPageSource(std::string_view ntupleName, const ROOT::RNTupleReadOptions &fOptions)
DescriptorId_t FindClusterGroupId(DescriptorId_t clusterId) const
Uses binary search in fCumulativeClusterCounts to determine the cluster group that the cluster ID bel...
void PrepareLoadCluster(const ROOT::Internal::RCluster::RKey &clusterKey, ROOT::Internal::ROnDiskPageMap &pageZeroMap, const std::function< void(ROOT::DescriptorId_t, ROOT::NTupleSize_t, const ROOT::RClusterDescriptor::RPageInfo &)> &perPageFunc)
Prepare a page range read for the column set in clusterKey.
void SetEntryRange(const REntryRange &range)
Promise to only read from the given entry range.
std::unique_ptr< RPageSource > Clone() const
Open the same storage multiple time, e.g.
static std::unique_ptr< RPageSource > Create(std::string_view ntupleName, std::string_view location, const ROOT::RNTupleReadOptions &options=ROOT::RNTupleReadOptions())
Guess the concrete derived page source from the file name (location)
static RResult< ROOT::Internal::RPage > UnsealPage(const RSealedPage &sealedPage, const ROOT::Internal::RColumnElementBase &element, ROOT::Internal::RPageAllocator &pageAlloc)
Helper for unstreaming a page.
Common functionality of an ntuple storage for both reading and writing.
RPageStorage(std::string_view name)
Stores information about the cluster in which this page resides.
A page is a slice of a column that is mapped into memory.
std::uint32_t GetNElements() const
std::size_t GetNBytes() const
The space taken by column elements in the buffer.
void * GrowUnchecked(std::uint32_t nElements)
Increases the number elements in the page.
static const void * GetPageZeroBuffer()
Return a pointer to the page zero buffer used if there is no on-disk data for a particular deferred c...
void SetWindow(const ROOT::NTupleSize_t rangeFirst, const RClusterInfo &clusterInfo)
Seek the page to a certain position of the column.
const ROOT::RFieldBase * GetSourceField(const ROOT::RFieldBase *target) const
bool TryEvict(std::size_t targetAvailableSize, std::size_t pageSizeLimit)
Flush columns in order of allocated write page size until the sum of all write page allocations leave...
bool TryUpdate(ROOT::Internal::RColumn &column, std::size_t newWritePageSize)
Try to register the new write page size for the given column.
The window of element indexes of a particular column in a particular cluster.
void SetCompressionSettings(std::optional< std::uint32_t > comp)
void SetPhysicalColumnId(ROOT::DescriptorId_t id)
void SetFirstElementIndex(ROOT::NTupleSize_t idx)
void SetNElements(ROOT::NTupleSize_t n)
Metadata for RNTuple clusters.
ROOT::NTupleSize_t GetNEntries() const
const RColumnRange & GetColumnRange(ROOT::DescriptorId_t physicalId) const
ROOT::NTupleSize_t GetFirstEntryIndex() const
bool HasClusterDetails() const
A cluster group is loaded in two stages.
const std::vector< ROOT::DescriptorId_t > & GetClusterIds() const
Base class for all ROOT issued exceptions.
A field translates read and write calls from/to underlying columns to/from tree values.
ROOT::DescriptorId_t GetOnDiskId() const
Metadata stored for every field of an RNTuple.
ROOT::DescriptorId_t GetId() const
const std::vector< ROOT::DescriptorId_t > & GetLogicalColumnIds() const
ROOT::ENTupleStructure GetStructure() const
std::uint32_t GetColumnCardinality() const
ROOT::DescriptorId_t GetParentId() const
bool IsProjectedField() const
The on-storage metadata of an RNTuple.
const RClusterGroupDescriptor & GetClusterGroupDescriptor(ROOT::DescriptorId_t clusterGroupId) const
const RColumnDescriptor & GetColumnDescriptor(ROOT::DescriptorId_t columnId) const
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
RClusterDescriptorIterable GetActiveClusterIterable() const
RClusterGroupDescriptorIterable GetClusterGroupIterable() const
std::size_t GetNActiveClusters() const
ROOT::NTupleSize_t R__DEPRECATED(6, 46, "This function is ill-conceived in the descriptor " "as not all cluster descriptors may be present. This interface is no longer publicly exposed.") GetNElements(ROOT ROOT::DescriptorId_t GetFieldZeroId() const
Returns the logical parent of all top-level RNTuple data fields.
std::size_t GetNClusters() const
@ kFeatureFlag_NestedDeferredColumns
Signals that the RNTuple contains at least one deferred column that is part of a collection and was e...
const RClusterDescriptor & GetClusterDescriptor(ROOT::DescriptorId_t clusterId) const
std::size_t GetNClusterGroups() const
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
ROOT::NTupleSize_t GetIndexInCluster() const
ROOT::DescriptorId_t GetClusterId() const
std::uint64_t GetNBytesOnStorage() const
ELocatorType GetType() const
For non-disk locators, the value for the Type field.
The RNTupleModel encapulates the schema of an RNTuple.
Common user-tunable settings for reading RNTuples.
Common user-tunable settings for storing RNTuples.
void ThrowOnError()
Short-hand method to throw an exception in the case of errors.
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
ROOT::RFieldZero & GetFieldZeroOfModel(RNTupleModel &model)
RResult< void > EnsureValidNameForRNTuple(std::string_view name, std::string_view where)
Check whether a given string is a valid name according to the RNTuple specification.
RProjectedFields & GetProjectedFieldsOfModel(RNTupleModel &model)
std::unique_ptr< RColumnElementBase > GenerateColumnElement(std::type_index inMemoryType, ROOT::ENTupleColumnType onDiskType)
void CallConnectPageSinkOnField(RFieldBase &, ROOT::Internal::RPageSink &, ROOT::NTupleSize_t firstEntry=0)
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr NTupleSize_t kInvalidNTupleIndex
bool StartsWith(std::string_view string, std::string_view prefix)
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
The identifiers that specifies the content of a (partial) cluster.
ROOT::DescriptorId_t fClusterId
ColumnSet_t fPhysicalColumnSet
Every concrete RColumnElement type is identified by its on-disk type (column type) and the in-memory ...
std::uint32_t fLength
Uncompressed length of the anchor, including the checksum.
The incremental changes to a RNTupleModel
std::vector< ROOT::RFieldBase * > fAddedProjectedFields
Points to the projected fields in fModel that were added as part of an updater transaction.
std::vector< ROOT::RFieldBase * > fAddedFields
Points to the fields in fModel that were added as part of an updater transaction.
On-disk pages within a page source are identified by the column and page number.
Default I/O performance counters that get registered in fMetrics.
std::type_index fInMemoryType
Parameters for the SealPage() method.
bool fWriteChecksum
Adds a 8 byte little-endian xxhash3 checksum to the page payload.
std::uint32_t fCompressionSettings
Compression algorithm and level to apply.
void * fBuffer
Location for sealed output. The memory buffer has to be large enough.
const ROOT::Internal::RPage * fPage
Input page to be sealed.
bool fAllowAlias
If false, the output buffer must not point to the input page buffer, which would otherwise be an opti...
const ROOT::Internal::RColumnElementBase * fElement
Corresponds to the page's elements, for size calculation etc.
Cluster that was staged, but not yet logically appended to the RNTuple.
ROOT::NTupleSize_t fNEntries
std::uint64_t fNBytesWritten
std::vector< RColumnInfo > fColumnInfos
Default I/O performance counters that get registered in fMetrics
Used in SetEntryRange / GetEntryRange.
ROOT::NTupleSize_t fNEntries
bool IntersectsWith(const ROOT::RClusterDescriptor &clusterDesc) const
Returns true if the given cluster has entries within the entry range.
ROOT::NTupleSize_t fFirstEntry
Summarizes meta-data necessary to load a certain page. Used by LoadPageFromSummary().
ROOT::RClusterDescriptor::RPageInfoExtended fPageInfo
Location of the page on disk.
std::uint64_t fColumnOffset
The first element number of the page's column in the given cluster.
ROOT::DescriptorId_t fClusterId
ROOT::Internal::RColumn * fColumn
ROOT::DescriptorId_t fPhysicalId
A sealed page contains the bytes of a page as written to storage (packed & compressed).
RResult< void > VerifyChecksumIfEnabled() const
void SetBuffer(const void *buffer)
std::uint32_t GetNElements() const
void SetHasChecksum(bool hasChecksum)
const void * GetBuffer() const
void SetNElements(std::uint32_t nElements)
bool GetHasChecksum() const
RResult< std::uint64_t > GetChecksum() const
Returns a failure if the sealed page has no checksum.
std::size_t GetBufferSize() const
void SetBufferSize(std::size_t bufferSize)
std::size_t GetDataSize() const
ROOT::Internal::RColumn * fColumn
std::size_t fCurrentPageSize
bool operator>(const RColumnInfo &other) const
Information about a single page in the context of a cluster's page range.
void SetLocator(const RNTupleLocator &locator)
std::uint32_t GetNElements() const
const RNTupleLocator & GetLocator() const
void SetHasChecksum(bool hasChecksum)
void SetNElements(std::uint32_t n)
Modifiers passed to CreateModel()