@@ -691,3 +691,114 @@ void ROOT::Experimental::RNTupleInspector::PrintSchemaProfile([[maybe_unused]] E
691691
692692 PrintSpeedscopeFrames (frames, output);
693693}
694+
695+ void ROOT::Experimental::RNTupleInspector::PrintDiskProfile ([[maybe_unused]] ESchemaProfileFormat format,
696+ std::ostream &output) const
697+ {
698+ // There is only one format at the moment
699+ assert (format == ESchemaProfileFormat::kSpeedscopeJSON );
700+
701+ const auto &descriptor = GetDescriptor ();
702+
703+ struct RDiskPageLeaf {
704+ std::uint64_t fPosition = 0 ;
705+ std::uint64_t fSize = 0 ;
706+ std::string fName ;
707+ std::array<DescriptorId_t, 3 > fAncestors ;
708+ };
709+ static constexpr std::array<const char *, 3 > kAncestorsNames = {" cluster group" , " cluster" , " column range" };
710+ std::vector<RDiskPageLeaf> pageLeaves;
711+
712+ // Collect all pageLeaves in whichever order the iterator provides
713+ for (const auto &clusterGroupDescriptor : descriptor.GetClusterGroupIterable ()) {
714+ const auto groupId = clusterGroupDescriptor.GetId ();
715+
716+ for (const auto clusterId : clusterGroupDescriptor.GetClusterIds ()) {
717+ const auto &clusterDescriptor = descriptor.GetClusterDescriptor (clusterId);
718+
719+ for (const auto &columnRange : clusterDescriptor.GetColumnRangeIterable ()) {
720+ const auto columnId = columnRange.GetPhysicalColumnId ();
721+
722+ const auto &pageRange = clusterDescriptor.GetPageRange (columnId);
723+ for (const auto &pageInfo : pageRange.GetPageInfos ()) {
724+ const auto &locator = pageInfo.GetLocator ();
725+
726+ RDiskPageLeaf pageLeaf;
727+ pageLeaf.fPosition = locator.GetPosition <std::uint64_t >();
728+ pageLeaf.fSize = locator.GetNBytesOnStorage ();
729+ pageLeaf.fName = " [page @" + std::to_string (pageLeaf.fPosition ) + " ]" ;
730+ pageLeaf.fAncestors = {groupId, clusterId, columnId};
731+ pageLeaves.push_back (pageLeaf);
732+ }
733+ }
734+ }
735+ }
736+
737+ // Sort pageLeafs by on-disk address
738+ std::sort (pageLeaves.begin (), pageLeaves.end (),
739+ [](const RDiskPageLeaf &a, const RDiskPageLeaf &b) { return a.fPosition < b.fPosition ; });
740+
741+ // Remove aliases (the ntuple specification allows complete, but not partial, overlap between pages)
742+ pageLeaves.erase (
743+ std::unique (pageLeaves.begin (), pageLeaves.end (),
744+ [](const RDiskPageLeaf &a, const RDiskPageLeaf &b) { return a.fPosition == b.fPosition ; }),
745+ pageLeaves.end ());
746+
747+ std::vector<SpeedscopeFrame> frames;
748+
749+ struct ROpenFrame {
750+ ROOT ::DescriptorId_t fId = 0 ; // clusterGroup, cluster, columnRange id
751+ std::size_t fIndex = 0 ; // index in frames vector
752+ };
753+ std::vector<ROpenFrame> openFrames;
754+
755+ std::uint64_t previouspageLeafEnd = 0 ;
756+
757+ // Construct frames from the bottom (leafs ordered by disk address) upwards
758+ for (const auto &pageLeaf : pageLeaves) {
759+ std::size_t sharedDepth = 0 ;
760+
761+ // How many of the currently open ancestors does this pageLeaf share?
762+ while (sharedDepth < openFrames.size () && sharedDepth < pageLeaf.fAncestors .size () &&
763+ openFrames[sharedDepth].fId == pageLeaf.fAncestors [sharedDepth]) {
764+ sharedDepth++;
765+ }
766+
767+ // Close ancestors not shared with this pageLeaf (innermost first order)
768+ while (openFrames.size () > sharedDepth) {
769+ frames[openFrames.back ().fIndex ].fClosingPosition = previouspageLeafEnd;
770+ openFrames.pop_back ();
771+ }
772+
773+ // Open the ancestors this pageLeaf needs (outermost first order)
774+ for (std::size_t depth = sharedDepth; depth < pageLeaf.fAncestors .size (); ++depth) {
775+ SpeedscopeFrame ancestorFrame;
776+ ancestorFrame.fString =
777+ " [" + std::string (kAncestorsNames [depth]) + " " + std::to_string (pageLeaf.fAncestors [depth]) + " ]" ;
778+ ancestorFrame.fOpeningPosition = pageLeaf.fPosition ;
779+ frames.push_back (ancestorFrame);
780+
781+ ROpenFrame openFrame;
782+ openFrame.fId = pageLeaf.fAncestors [depth];
783+ openFrame.fIndex = frames.size () - 1 ;
784+ openFrames.push_back (openFrame);
785+ }
786+
787+ // Emit the pageLeaf itself
788+ SpeedscopeFrame pageLeafFrame;
789+ pageLeafFrame.fString = pageLeaf.fName ;
790+ pageLeafFrame.fOpeningPosition = pageLeaf.fPosition ;
791+ pageLeafFrame.fClosingPosition = pageLeaf.fPosition + pageLeaf.fSize ;
792+ frames.push_back (pageLeafFrame);
793+
794+ previouspageLeafEnd = pageLeaf.fPosition + pageLeaf.fSize ;
795+ }
796+
797+ // Close whatever is still open after the last pageLeaf
798+ while (!openFrames.empty ()) {
799+ frames[openFrames.back ().fIndex ].fClosingPosition = previouspageLeafEnd;
800+ openFrames.pop_back ();
801+ }
802+
803+ PrintSpeedscopeFrames (frames, output);
804+ }
0 commit comments