From c8d0449639705fe9ec980ccdef41bbc6eee81f5c Mon Sep 17 00:00:00 2001 From: albfsg Date: Mon, 6 Jul 2026 17:17:31 +0200 Subject: [PATCH 1/4] [ntuple] Remove root field from PrintSchemaProfile After discussing it with my supervisors, this implementation detail was removed from the output as it adds no functional value to the schema profile. --- tree/ntupleutil/src/RNTupleInspector.cxx | 6 +++++- tree/ntupleutil/test/ntuple_inspector.cxx | 9 +++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tree/ntupleutil/src/RNTupleInspector.cxx b/tree/ntupleutil/src/RNTupleInspector.cxx index 7a7681176bcbc..3a54d53dd80cd 100644 --- a/tree/ntupleutil/src/RNTupleInspector.cxx +++ b/tree/ntupleutil/src/RNTupleInspector.cxx @@ -686,7 +686,11 @@ void ROOT::Experimental::RNTupleInspector::PrintSchemaProfile(ESchemaProfileForm return subTreeSize; }; - visitFieldsRecursive(visitFieldsRecursive, rootFieldDescriptor); + const auto &topLevelIds = rootFieldDescriptor.GetLinkIds(); + for (const auto &childId : topLevelIds) { + const auto &childFieldDescriptor = tupleDescriptor.GetFieldDescriptor(childId); + visitFieldsRecursive(visitFieldsRecursive, childFieldDescriptor); + } PrintSpeedscopeFrames(frames, output); } diff --git a/tree/ntupleutil/test/ntuple_inspector.cxx b/tree/ntupleutil/test/ntuple_inspector.cxx index 7d5f439e9281b..aa7046c9f497e 100644 --- a/tree/ntupleutil/test/ntuple_inspector.cxx +++ b/tree/ntupleutil/test/ntuple_inspector.cxx @@ -886,7 +886,6 @@ TEST(RNTupleInspector, SchemaProfile) "$schema":"https://www.speedscope.app/file-format-schema.json", "shared":{ "frames":[ - { "name":"", "file":"Type: , Size: 80B" }, { "name":"float1", "file":"Type: float, Size: 40B" }, { "name":"float1 [col#0]", "file":"Type: SplitReal32, Size: 40B" }, { "name":"int", "file":"Type: std::int32_t, Size: 40B" }, @@ -903,14 +902,12 @@ TEST(RNTupleInspector, SchemaProfile) "events":[ {"type":"O","frame":0,"at":0}, {"type":"O","frame":1,"at":0}, - {"type":"O","frame":2,"at":0}, - {"type":"C","frame":2,"at":40}, {"type":"C","frame":1,"at":40}, + {"type":"C","frame":0,"at":40}, + {"type":"O","frame":2,"at":40}, {"type":"O","frame":3,"at":40}, - {"type":"O","frame":4,"at":40}, - {"type":"C","frame":4,"at":80}, {"type":"C","frame":3,"at":80}, - {"type":"C","frame":0,"at":80} + {"type":"C","frame":2,"at":80} ] } ] From a42ae6557da540a19426971aa4c62b5d1d563742 Mon Sep 17 00:00:00 2001 From: albfsg Date: Mon, 6 Jul 2026 17:50:21 +0200 Subject: [PATCH 2/4] [ntuple] Move column index to the front of name string This is an attempt to make Speedscope render columns in similar colors, so they are visually distinguishable from fields. Speedscope does not allow explicit control over frame colors, but it appears to generate them based on the first characters of each string. This behavior was discovered through trial and error, is not officially supported and may still be improvable. --- tree/ntupleutil/src/RNTupleInspector.cxx | 4 ++-- tree/ntupleutil/test/ntuple_inspector.cxx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tree/ntupleutil/src/RNTupleInspector.cxx b/tree/ntupleutil/src/RNTupleInspector.cxx index 3a54d53dd80cd..c08b6b44e973f 100644 --- a/tree/ntupleutil/src/RNTupleInspector.cxx +++ b/tree/ntupleutil/src/RNTupleInspector.cxx @@ -670,8 +670,8 @@ void ROOT::Experimental::RNTupleInspector::PrintSchemaProfile(ESchemaProfileForm std::size_t columnSize = columnInfo.GetCompressedSize(); SpeedscopeFrame columnSpeedscopeFrame; - columnSpeedscopeFrame.fPrimaryString = tupleDescriptor.GetQualifiedFieldName(fieldDescriptor.GetId()) + - " [col#" + std::to_string(columnDescriptor.GetPhysicalId()) + "]"; + columnSpeedscopeFrame.fPrimaryString = "[col#" + std::to_string(columnDescriptor.GetPhysicalId()) + "]" + + tupleDescriptor.GetQualifiedFieldName(fieldDescriptor.GetId()); columnSpeedscopeFrame.fSecondaryString = ROOT::Internal::RColumnElementBase::GetColumnTypeName(columnDescriptor.GetType()); columnSpeedscopeFrame.fOpeningPosition = positionCursor; diff --git a/tree/ntupleutil/test/ntuple_inspector.cxx b/tree/ntupleutil/test/ntuple_inspector.cxx index aa7046c9f497e..6e3a32d7c364c 100644 --- a/tree/ntupleutil/test/ntuple_inspector.cxx +++ b/tree/ntupleutil/test/ntuple_inspector.cxx @@ -887,9 +887,9 @@ TEST(RNTupleInspector, SchemaProfile) "shared":{ "frames":[ { "name":"float1", "file":"Type: float, Size: 40B" }, - { "name":"float1 [col#0]", "file":"Type: SplitReal32, Size: 40B" }, + { "name":"[col#0]float1", "file":"Type: SplitReal32, Size: 40B" }, { "name":"int", "file":"Type: std::int32_t, Size: 40B" }, - { "name":"int [col#1]", "file":"Type: SplitInt32, Size: 40B" } + { "name":"[col#1]int", "file":"Type: SplitInt32, Size: 40B" } ] }, "profiles":[ From c526ff9a83dbea5238e27ba6c083e5b1af18506b Mon Sep 17 00:00:00 2001 From: albfsg Date: Mon, 6 Jul 2026 17:51:15 +0200 Subject: [PATCH 3/4] [ntuple] Merge Speedscopes strings Now everything is printed in the `name` field of the JSON schema. This is because fSecondaryString was mapped into the `file` field of the schema, which forced it to end with ":". --- tree/ntupleutil/src/RNTupleInspector.cxx | 22 +++++++++------------- tree/ntupleutil/test/ntuple_inspector.cxx | 12 ++++++------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/tree/ntupleutil/src/RNTupleInspector.cxx b/tree/ntupleutil/src/RNTupleInspector.cxx index c08b6b44e973f..0fa281f6f116b 100644 --- a/tree/ntupleutil/src/RNTupleInspector.cxx +++ b/tree/ntupleutil/src/RNTupleInspector.cxx @@ -568,8 +568,7 @@ void ROOT::Experimental::RNTupleInspector::PrintFieldTreeAsDot(const ROOT::RFiel namespace { struct SpeedscopeFrame { - std::string fPrimaryString; - std::string fSecondaryString; + std::string fString; std::uint64_t fOpeningPosition = 0; std::uint64_t fClosingPosition = 0; }; @@ -582,10 +581,7 @@ static void PrintSpeedscopeFrames(const std::vector &frames, st output << " \"frames\":[\n"; for (std::size_t i = 0; i < frames.size(); ++i) { - output << " { \"name\":\"" << frames[i].fPrimaryString - << "\", \"file\":\"Type: " << frames[i].fSecondaryString - << ", Size: " << frames[i].fClosingPosition - frames[i].fOpeningPosition << "B\" }" - << (i + 1 < frames.size() ? ",\n" : "\n"); + output << " { \"name\":\"" << frames[i].fString << "\" }" << (i + 1 < frames.size() ? ",\n" : "\n"); } output << " ]\n"; @@ -650,12 +646,12 @@ void ROOT::Experimental::RNTupleInspector::PrintSchemaProfile(ESchemaProfileForm // Returns size of the visited field auto visitFieldsRecursive = [&](auto &self, const ROOT::RFieldDescriptor &fieldDescriptor) -> std::size_t { SpeedscopeFrame fieldSpeedscopeFrame; - fieldSpeedscopeFrame.fPrimaryString = tupleDescriptor.GetQualifiedFieldName(fieldDescriptor.GetId()); - fieldSpeedscopeFrame.fSecondaryString = fieldDescriptor.GetTypeName(); + fieldSpeedscopeFrame.fString = + tupleDescriptor.GetQualifiedFieldName(fieldDescriptor.GetId()) + " (" + fieldDescriptor.GetTypeName() + ")"; fieldSpeedscopeFrame.fOpeningPosition = positionCursor; frames.push_back(fieldSpeedscopeFrame); - const std::size_t fieldSpeedscopeFrameIndex = frames.size() - 1; + std::size_t fieldSpeedscopeFrameIndex = frames.size() - 1; std::size_t subTreeSize = 0; const auto &childIds = fieldDescriptor.GetLinkIds(); @@ -670,10 +666,10 @@ void ROOT::Experimental::RNTupleInspector::PrintSchemaProfile(ESchemaProfileForm std::size_t columnSize = columnInfo.GetCompressedSize(); SpeedscopeFrame columnSpeedscopeFrame; - columnSpeedscopeFrame.fPrimaryString = "[col#" + std::to_string(columnDescriptor.GetPhysicalId()) + "]" + - tupleDescriptor.GetQualifiedFieldName(fieldDescriptor.GetId()); - columnSpeedscopeFrame.fSecondaryString = - ROOT::Internal::RColumnElementBase::GetColumnTypeName(columnDescriptor.GetType()); + columnSpeedscopeFrame.fString = + "[col#" + std::to_string(columnDescriptor.GetPhysicalId()) + "] " + + tupleDescriptor.GetQualifiedFieldName(fieldDescriptor.GetId()) + " (" + + ROOT::Internal::RColumnElementBase::GetColumnTypeName(columnDescriptor.GetType()) + ")"; columnSpeedscopeFrame.fOpeningPosition = positionCursor; positionCursor += columnSize; columnSpeedscopeFrame.fClosingPosition = positionCursor; diff --git a/tree/ntupleutil/test/ntuple_inspector.cxx b/tree/ntupleutil/test/ntuple_inspector.cxx index 6e3a32d7c364c..5c3b4c326778b 100644 --- a/tree/ntupleutil/test/ntuple_inspector.cxx +++ b/tree/ntupleutil/test/ntuple_inspector.cxx @@ -882,14 +882,14 @@ TEST(RNTupleInspector, SchemaProfile) std::ostringstream schemaProfileStream; inspector->PrintSchemaProfile(ROOT::Experimental::ESchemaProfileFormat::kSpeedscopeJSON, schemaProfileStream); const std::string schemaProfile = schemaProfileStream.str(); - const std::string expected = R"({ + const std::string expected = R"foo({ "$schema":"https://www.speedscope.app/file-format-schema.json", "shared":{ "frames":[ - { "name":"float1", "file":"Type: float, Size: 40B" }, - { "name":"[col#0]float1", "file":"Type: SplitReal32, Size: 40B" }, - { "name":"int", "file":"Type: std::int32_t, Size: 40B" }, - { "name":"[col#1]int", "file":"Type: SplitInt32, Size: 40B" } + { "name":"float1 (float)" }, + { "name":"[col#0] float1 (SplitReal32)" }, + { "name":"int (std::int32_t)" }, + { "name":"[col#1] int (SplitInt32)" } ] }, "profiles":[ @@ -912,6 +912,6 @@ TEST(RNTupleInspector, SchemaProfile) } ] } -)"; +)foo"; EXPECT_EQ(schemaProfile, expected); } From 557fbe47c95917f95928e8b6d916552d7c2a8a58 Mon Sep 17 00:00:00 2001 From: albfsg Date: Mon, 6 Jul 2026 17:03:22 +0200 Subject: [PATCH 4/4] [ntuple] Add [[maybe_unused]] to suppress warning --- tree/ntupleutil/inc/ROOT/RNTupleInspector.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree/ntupleutil/inc/ROOT/RNTupleInspector.hxx b/tree/ntupleutil/inc/ROOT/RNTupleInspector.hxx index 9ba5b9816fdbd..a0caf9fac6132 100644 --- a/tree/ntupleutil/inc/ROOT/RNTupleInspector.hxx +++ b/tree/ntupleutil/inc/ROOT/RNTupleInspector.hxx @@ -502,7 +502,7 @@ public: ///////////////////////////////////////////////////////////////////////////// /// \brief Print a string that represents the tree of the (sub)fields and columns of an RNTuple in a format which a /// performance profile visualizer can render - void PrintSchemaProfile(ESchemaProfileFormat format, std::ostream &output = std::cout) const; + void PrintSchemaProfile([[maybe_unused]] ESchemaProfileFormat format, std::ostream &output = std::cout) const; }; } // namespace Experimental } // namespace ROOT