From 8d05277f7eed2e014415e62529f661f6c8336f32 Mon Sep 17 00:00:00 2001 From: Jokeren Date: Wed, 27 May 2026 10:23:05 -0400 Subject: [PATCH 1/6] Prune empty Proton MsgPack leaf frames --- third_party/proton/csrc/lib/Data/TreeData.cpp | 61 ++++++++++++++++--- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/third_party/proton/csrc/lib/Data/TreeData.cpp b/third_party/proton/csrc/lib/Data/TreeData.cpp index 4b395a77edd8..88eb003af4ce 100644 --- a/third_party/proton/csrc/lib/Data/TreeData.cpp +++ b/third_party/proton/csrc/lib/Data/TreeData.cpp @@ -712,9 +712,25 @@ TreeData::buildHatchetMsgPack(TreeData::Tree *tree, } } } + // Linked flexible metrics attached to generated helper leaves are promoted + // into the parent metrics map above. Once promoted, a helper leaf with no + // linked fixed metrics and no children carries no information in Hatchet. + uint32_t linkedChildCount = 0; + for (const auto &child : virtualNode.children) { + const auto &childNode = virtualTree->getNode(child.id); + if (!childNode.children.empty() || + linkedMetrics.find(child.id) != linkedMetrics.end()) { + ++linkedChildCount; + } + } writer.packFixStrLiteral("children"); - writer.packArray(static_cast(virtualNode.children.size())); + writer.packArray(linkedChildCount); for (const auto &child : virtualNode.children) { + const auto &childNode = virtualTree->getNode(child.id); + if (childNode.children.empty() && + linkedMetrics.find(child.id) == linkedMetrics.end()) { + continue; + } packLinkedVirtualNode(packLinkedVirtualNode, treeNode, child.id); } }; @@ -733,18 +749,47 @@ TreeData::buildHatchetMsgPack(TreeData::Tree *tree, const bool hasLinkedTargets = !treeNode.metricSet.linkedMetrics.empty() || !treeNode.metricSet.linkedFlexibleMetrics.empty(); - uint32_t linkedChildCount = - hasLinkedTargets - ? static_cast(virtualRootNode.children.size()) - : 0; + uint32_t linkedChildCount = 0; + if (hasLinkedTargets) { + for (const auto &child : virtualRootNode.children) { + const auto &childNode = virtualTree->getNode(child.id); + if (!childNode.children.empty() || + treeNode.metricSet.linkedMetrics.find(child.id) != + treeNode.metricSet.linkedMetrics.end()) { + ++linkedChildCount; + } + } + } + uint32_t concreteChildCount = 0; + for (const auto &child : treeNode.children) { + const auto &childNode = tree->getNode(child.id); + if (!childNode.children.empty() || !childNode.metricSet.metrics.empty() || + !childNode.metricSet.flexibleMetrics.empty() || + !childNode.metricSet.linkedMetrics.empty() || + !childNode.metricSet.linkedFlexibleMetrics.empty()) { + ++concreteChildCount; + } + } writer.packFixStrLiteral("children"); - writer.packArray(static_cast(treeNode.children.size()) + - linkedChildCount); + writer.packArray(concreteChildCount + linkedChildCount); for (const auto &child : treeNode.children) { - packNode(packNode, tree->getNode(child.id)); + auto &childNode = tree->getNode(child.id); + if (childNode.children.empty() && childNode.metricSet.metrics.empty() && + childNode.metricSet.flexibleMetrics.empty() && + childNode.metricSet.linkedMetrics.empty() && + childNode.metricSet.linkedFlexibleMetrics.empty()) { + continue; + } + packNode(packNode, childNode); } if (hasLinkedTargets) { for (const auto &virtualChild : virtualRootNode.children) { + const auto &childNode = virtualTree->getNode(virtualChild.id); + if (childNode.children.empty() && + treeNode.metricSet.linkedMetrics.find(virtualChild.id) == + treeNode.metricSet.linkedMetrics.end()) { + continue; + } packLinkedVirtualNode(packLinkedVirtualNode, treeNode, virtualChild.id); } } From 39a919903c19873d7e29647975cd7a5d1dce9474 Mon Sep 17 00:00:00 2001 From: Jokeren Date: Wed, 27 May 2026 12:09:48 -0400 Subject: [PATCH 2/6] Keep concrete Proton children during MsgPack dump --- third_party/proton/csrc/lib/Data/TreeData.cpp | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/third_party/proton/csrc/lib/Data/TreeData.cpp b/third_party/proton/csrc/lib/Data/TreeData.cpp index 88eb003af4ce..365b24e3d391 100644 --- a/third_party/proton/csrc/lib/Data/TreeData.cpp +++ b/third_party/proton/csrc/lib/Data/TreeData.cpp @@ -760,26 +760,11 @@ TreeData::buildHatchetMsgPack(TreeData::Tree *tree, } } } - uint32_t concreteChildCount = 0; - for (const auto &child : treeNode.children) { - const auto &childNode = tree->getNode(child.id); - if (!childNode.children.empty() || !childNode.metricSet.metrics.empty() || - !childNode.metricSet.flexibleMetrics.empty() || - !childNode.metricSet.linkedMetrics.empty() || - !childNode.metricSet.linkedFlexibleMetrics.empty()) { - ++concreteChildCount; - } - } writer.packFixStrLiteral("children"); - writer.packArray(concreteChildCount + linkedChildCount); + writer.packArray(static_cast(treeNode.children.size()) + + linkedChildCount); for (const auto &child : treeNode.children) { auto &childNode = tree->getNode(child.id); - if (childNode.children.empty() && childNode.metricSet.metrics.empty() && - childNode.metricSet.flexibleMetrics.empty() && - childNode.metricSet.linkedMetrics.empty() && - childNode.metricSet.linkedFlexibleMetrics.empty()) { - continue; - } packNode(packNode, childNode); } if (hasLinkedTargets) { From 8544ad9ca7ec9a64fc6652f7f882b386a53d3496 Mon Sep 17 00:00:00 2001 From: Jokeren Date: Wed, 27 May 2026 12:29:44 -0400 Subject: [PATCH 3/6] Match Proton concrete MsgPack traversal --- third_party/proton/csrc/lib/Data/TreeData.cpp | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/third_party/proton/csrc/lib/Data/TreeData.cpp b/third_party/proton/csrc/lib/Data/TreeData.cpp index 365b24e3d391..d0401e509742 100644 --- a/third_party/proton/csrc/lib/Data/TreeData.cpp +++ b/third_party/proton/csrc/lib/Data/TreeData.cpp @@ -749,17 +749,10 @@ TreeData::buildHatchetMsgPack(TreeData::Tree *tree, const bool hasLinkedTargets = !treeNode.metricSet.linkedMetrics.empty() || !treeNode.metricSet.linkedFlexibleMetrics.empty(); - uint32_t linkedChildCount = 0; - if (hasLinkedTargets) { - for (const auto &child : virtualRootNode.children) { - const auto &childNode = virtualTree->getNode(child.id); - if (!childNode.children.empty() || - treeNode.metricSet.linkedMetrics.find(child.id) != - treeNode.metricSet.linkedMetrics.end()) { - ++linkedChildCount; - } - } - } + uint32_t linkedChildCount = + hasLinkedTargets + ? static_cast(virtualRootNode.children.size()) + : 0; writer.packFixStrLiteral("children"); writer.packArray(static_cast(treeNode.children.size()) + linkedChildCount); @@ -769,12 +762,6 @@ TreeData::buildHatchetMsgPack(TreeData::Tree *tree, } if (hasLinkedTargets) { for (const auto &virtualChild : virtualRootNode.children) { - const auto &childNode = virtualTree->getNode(virtualChild.id); - if (childNode.children.empty() && - treeNode.metricSet.linkedMetrics.find(virtualChild.id) == - treeNode.metricSet.linkedMetrics.end()) { - continue; - } packLinkedVirtualNode(packLinkedVirtualNode, treeNode, virtualChild.id); } } From d6de9fc502a496a638bddbd16cd19a7f3c61a1b0 Mon Sep 17 00:00:00 2001 From: Jokeren Date: Wed, 27 May 2026 13:13:36 -0400 Subject: [PATCH 4/6] Skip empty Proton concrete capture leaves --- third_party/proton/csrc/lib/Data/TreeData.cpp | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/third_party/proton/csrc/lib/Data/TreeData.cpp b/third_party/proton/csrc/lib/Data/TreeData.cpp index d0401e509742..2fc0329c4e2f 100644 --- a/third_party/proton/csrc/lib/Data/TreeData.cpp +++ b/third_party/proton/csrc/lib/Data/TreeData.cpp @@ -753,11 +753,31 @@ TreeData::buildHatchetMsgPack(TreeData::Tree *tree, hasLinkedTargets ? static_cast(virtualRootNode.children.size()) : 0; + // CUDA stream capture can create concrete launch-name leaves before the + // launch callback exits early without correlating metrics. Graph replay + // metrics are attached through linked virtual nodes instead, so a concrete + // leaf with no metrics, linked metrics, flexible metrics, or children adds + // no Hatchet information. + uint32_t concreteChildCount = 0; + for (const auto &child : treeNode.children) { + const auto &childNode = tree->getNode(child.id); + if (!childNode.children.empty() || !childNode.metricSet.metrics.empty() || + !childNode.metricSet.flexibleMetrics.empty() || + !childNode.metricSet.linkedMetrics.empty() || + !childNode.metricSet.linkedFlexibleMetrics.empty()) { + ++concreteChildCount; + } + } writer.packFixStrLiteral("children"); - writer.packArray(static_cast(treeNode.children.size()) + - linkedChildCount); + writer.packArray(concreteChildCount + linkedChildCount); for (const auto &child : treeNode.children) { auto &childNode = tree->getNode(child.id); + if (childNode.children.empty() && childNode.metricSet.metrics.empty() && + childNode.metricSet.flexibleMetrics.empty() && + childNode.metricSet.linkedMetrics.empty() && + childNode.metricSet.linkedFlexibleMetrics.empty()) { + continue; + } packNode(packNode, childNode); } if (hasLinkedTargets) { From 377c1e10dc527ce127fb6d17fef2795bfcbde074 Mon Sep 17 00:00:00 2001 From: Jokeren Date: Wed, 27 May 2026 13:33:48 -0400 Subject: [PATCH 5/6] Avoid repeated Proton concrete child filtering --- third_party/proton/csrc/lib/Data/TreeData.cpp | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/third_party/proton/csrc/lib/Data/TreeData.cpp b/third_party/proton/csrc/lib/Data/TreeData.cpp index 2fc0329c4e2f..54d52cc92598 100644 --- a/third_party/proton/csrc/lib/Data/TreeData.cpp +++ b/third_party/proton/csrc/lib/Data/TreeData.cpp @@ -758,27 +758,37 @@ TreeData::buildHatchetMsgPack(TreeData::Tree *tree, // metrics are attached through linked virtual nodes instead, so a concrete // leaf with no metrics, linked metrics, flexible metrics, or children adds // no Hatchet information. + // Most captured launch scopes have only a few concrete children. Keep that + // common case allocation-free while still collecting children once so the + // MsgPack array header has an exact count. + constexpr size_t kInlineConcreteChildCount = 8; + std::array + inlineConcreteChildren; + std::vector overflowConcreteChildren; uint32_t concreteChildCount = 0; for (const auto &child : treeNode.children) { - const auto &childNode = tree->getNode(child.id); + auto &childNode = tree->getNode(child.id); if (!childNode.children.empty() || !childNode.metricSet.metrics.empty() || !childNode.metricSet.flexibleMetrics.empty() || !childNode.metricSet.linkedMetrics.empty() || !childNode.metricSet.linkedFlexibleMetrics.empty()) { + if (concreteChildCount < inlineConcreteChildren.size()) { + inlineConcreteChildren[concreteChildCount] = &childNode; + } else { + overflowConcreteChildren.push_back(&childNode); + } ++concreteChildCount; } } writer.packFixStrLiteral("children"); writer.packArray(concreteChildCount + linkedChildCount); - for (const auto &child : treeNode.children) { - auto &childNode = tree->getNode(child.id); - if (childNode.children.empty() && childNode.metricSet.metrics.empty() && - childNode.metricSet.flexibleMetrics.empty() && - childNode.metricSet.linkedMetrics.empty() && - childNode.metricSet.linkedFlexibleMetrics.empty()) { - continue; - } - packNode(packNode, childNode); + const auto inlineConcreteCount = + std::min(concreteChildCount, inlineConcreteChildren.size()); + for (size_t i = 0; i < inlineConcreteCount; ++i) { + packNode(packNode, *inlineConcreteChildren[i]); + } + for (auto *childNode : overflowConcreteChildren) { + packNode(packNode, *childNode); } if (hasLinkedTargets) { for (const auto &virtualChild : virtualRootNode.children) { From 46e93a1fcc09717d3d70cd1e5ab6fb2a37de45b9 Mon Sep 17 00:00:00 2001 From: Jokeren Date: Wed, 27 May 2026 13:41:08 -0400 Subject: [PATCH 6/6] Use vectors for Proton child filtering --- third_party/proton/csrc/lib/Data/TreeData.cpp | 43 ++++++------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/third_party/proton/csrc/lib/Data/TreeData.cpp b/third_party/proton/csrc/lib/Data/TreeData.cpp index 54d52cc92598..6ed0b67a80d5 100644 --- a/third_party/proton/csrc/lib/Data/TreeData.cpp +++ b/third_party/proton/csrc/lib/Data/TreeData.cpp @@ -715,23 +715,19 @@ TreeData::buildHatchetMsgPack(TreeData::Tree *tree, // Linked flexible metrics attached to generated helper leaves are promoted // into the parent metrics map above. Once promoted, a helper leaf with no // linked fixed metrics and no children carries no information in Hatchet. - uint32_t linkedChildCount = 0; + std::vector linkedChildren; + linkedChildren.reserve(virtualNode.children.size()); for (const auto &child : virtualNode.children) { const auto &childNode = virtualTree->getNode(child.id); if (!childNode.children.empty() || linkedMetrics.find(child.id) != linkedMetrics.end()) { - ++linkedChildCount; + linkedChildren.push_back(child.id); } } writer.packFixStrLiteral("children"); - writer.packArray(linkedChildCount); - for (const auto &child : virtualNode.children) { - const auto &childNode = virtualTree->getNode(child.id); - if (childNode.children.empty() && - linkedMetrics.find(child.id) == linkedMetrics.end()) { - continue; - } - packLinkedVirtualNode(packLinkedVirtualNode, treeNode, child.id); + writer.packArray(static_cast(linkedChildren.size())); + for (auto childId : linkedChildren) { + packLinkedVirtualNode(packLinkedVirtualNode, treeNode, childId); } }; auto packNode = [&](auto &&packNode, @@ -758,36 +754,21 @@ TreeData::buildHatchetMsgPack(TreeData::Tree *tree, // metrics are attached through linked virtual nodes instead, so a concrete // leaf with no metrics, linked metrics, flexible metrics, or children adds // no Hatchet information. - // Most captured launch scopes have only a few concrete children. Keep that - // common case allocation-free while still collecting children once so the - // MsgPack array header has an exact count. - constexpr size_t kInlineConcreteChildCount = 8; - std::array - inlineConcreteChildren; - std::vector overflowConcreteChildren; - uint32_t concreteChildCount = 0; + std::vector concreteChildren; + concreteChildren.reserve(treeNode.children.size()); for (const auto &child : treeNode.children) { auto &childNode = tree->getNode(child.id); if (!childNode.children.empty() || !childNode.metricSet.metrics.empty() || !childNode.metricSet.flexibleMetrics.empty() || !childNode.metricSet.linkedMetrics.empty() || !childNode.metricSet.linkedFlexibleMetrics.empty()) { - if (concreteChildCount < inlineConcreteChildren.size()) { - inlineConcreteChildren[concreteChildCount] = &childNode; - } else { - overflowConcreteChildren.push_back(&childNode); - } - ++concreteChildCount; + concreteChildren.push_back(&childNode); } } writer.packFixStrLiteral("children"); - writer.packArray(concreteChildCount + linkedChildCount); - const auto inlineConcreteCount = - std::min(concreteChildCount, inlineConcreteChildren.size()); - for (size_t i = 0; i < inlineConcreteCount; ++i) { - packNode(packNode, *inlineConcreteChildren[i]); - } - for (auto *childNode : overflowConcreteChildren) { + writer.packArray(static_cast(concreteChildren.size()) + + linkedChildCount); + for (auto *childNode : concreteChildren) { packNode(packNode, *childNode); } if (hasLinkedTargets) {