Files
John Shumway a213ce676b Add python analysis scripts for Clang's time trace (#3644)
This PR introduces a Python toolkit for analyzing Clang's `-ftime-trace` build performance data. This is the foundation for our systematic effort to reduce CK and CK-Tile build times (#3575).

The toolkit provides fast parsing of trace JSON files into pandas DataFrames using orjson, with specialized functions for analyzing template instantiation costs and compilation phase breakdowns. It includes a core library (`trace_analysis/`), example scripts for quick analysis, a comprehensive README with usage documentation, and an interactive Jupyter notebook demonstration.

Key features include memory-efficient DataFrame schemas with optimized dtypes, recursive hierarchical phase analysis, automatic metadata extraction (source file, compilation timing), and template instantiation filtering. The design supports both standalone scripts and interactive Jupyter notebook workflows.

This single-file analysis capability lays the groundwork for future multi-file analysis across thousands of compilation units, enabling data-driven optimization and build time regression detection.
2026-01-26 13:44:36 -08:00

302 lines
8.0 KiB
Python

# Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
# SPDX-License-Identifier: MIT
"""
Template detail string parser for C++ template instantiations.
This module provides functions to parse the arg_detail strings from
Clang's -ftime-trace output into structured components.
"""
import re
from typing import Dict
def parse_template_detail(detail_str: str) -> Dict[str, any]:
"""
Parse a template detail string into structured components.
Args:
detail_str: The arg_detail string from -ftime-trace
Returns:
Dictionary with parsed fields:
- namespace: Top-level namespace (e.g., 'std', 'ck')
- template_name: Template name without parameters
- full_qualified_name: Full namespace::template_name
- param_count: Number of template parameters
- is_ck_type: Boolean indicating if this is a CK library type
- is_nested: Boolean indicating if contains nested templates
Example:
>>> parse_template_detail('std::basic_string<char>')
{
'namespace': 'std',
'template_name': 'basic_string',
'full_qualified_name': 'std::basic_string',
'param_count': 1,
'is_ck_type': False,
'is_nested': False
}
"""
# Handle empty or invalid strings
if not detail_str or not isinstance(detail_str, str):
return _empty_result()
# Remove surrounding quotes if present
detail_str = detail_str.strip('"')
# Extract components
namespace = extract_namespace(detail_str)
template_name = extract_template_name(detail_str)
full_qualified_name = extract_full_qualified_name(detail_str)
param_count = count_template_params(detail_str)
is_ck = is_ck_template(detail_str)
is_nested = is_nested_template(detail_str)
return {
"namespace": namespace,
"template_name": template_name,
"full_qualified_name": full_qualified_name,
"param_count": param_count,
"is_ck_type": is_ck,
"is_nested": is_nested,
}
def extract_namespace(detail_str: str) -> str:
"""
Extract the top-level namespace from a template detail string.
Args:
detail_str: The template detail string
Returns:
The top-level namespace, or empty string if none found
Example:
>>> extract_namespace('std::basic_string<char>')
'std'
>>> extract_namespace('ck::tensor_operation::device::DeviceConv2d<...>')
'ck'
"""
if not detail_str:
return ""
# Remove quotes
detail_str = detail_str.strip('"')
# Find first :: separator
match = re.match(r"^([a-zA-Z_][a-zA-Z0-9_]*)::", detail_str)
if match:
return match.group(1)
# No namespace found - check if it's a simple type
match = re.match(r"^([a-zA-Z_][a-zA-Z0-9_]*)", detail_str)
if match:
return match.group(1)
return ""
def extract_template_name(detail_str: str) -> str:
"""
Extract the template name without namespace or parameters.
Args:
detail_str: The template detail string
Returns:
The template name without namespace or parameters
Example:
>>> extract_template_name('std::basic_string<char>')
'basic_string'
>>> extract_template_name('ck::GridwiseGemm_k0mk1_k0nk1_mn_xdlops_v2r3<...>')
'GridwiseGemm_k0mk1_k0nk1_mn_xdlops_v2r3'
"""
if not detail_str:
return ""
# Remove quotes
detail_str = detail_str.strip('"')
# Find the last component before < or end of string
# This handles nested namespaces like ck::tensor_operation::device::DeviceConv2d
match = re.search(r"::([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:<|$)", detail_str)
if match:
return match.group(1)
# No :: found, try to get name before <
match = re.match(r"^([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:<|$)", detail_str)
if match:
return match.group(1)
return ""
def extract_full_qualified_name(detail_str: str) -> str:
"""
Extract the full qualified name (namespace::...::template_name).
Args:
detail_str: The template detail string
Returns:
The full qualified name without template parameters
Example:
>>> extract_full_qualified_name('std::basic_string<char>')
'std::basic_string'
>>> extract_full_qualified_name('ck::tensor_operation::device::DeviceConv2d<...>')
'ck::tensor_operation::device::DeviceConv2d'
"""
if not detail_str:
return ""
# Remove quotes
detail_str = detail_str.strip('"')
# Match everything up to the first < or end of string
match = re.match(r"^([a-zA-Z_:][a-zA-Z0-9_:]*)\s*(?:<|$)", detail_str)
if match:
return match.group(1)
return ""
def count_template_params(detail_str: str) -> int:
"""
Count the number of top-level template parameters.
This counts commas at the top level of template brackets,
not commas inside nested templates.
Args:
detail_str: The template detail string
Returns:
Number of template parameters, or 0 if not a template
Example:
>>> count_template_params('std::basic_string<char>')
1
>>> count_template_params('std::tuple<int, float, double>')
3
"""
if not detail_str or "<" not in detail_str:
return 0
# Remove quotes
detail_str = detail_str.strip('"')
# Find the template parameter section
start = detail_str.find("<")
if start == -1:
return 0
# Track bracket depth to only count top-level commas
depth = 0
param_count = 1 # Start with 1 (if there's a <, there's at least one param)
in_template = False
for i in range(start, len(detail_str)):
char = detail_str[i]
if char == "<":
depth += 1
in_template = True
elif char == ">":
depth -= 1
if depth == 0:
# We've closed the outermost template
break
elif char == "," and depth == 1:
# Top-level comma
param_count += 1
return param_count if in_template else 0
def is_ck_template(detail_str: str) -> bool:
"""
Check if this is a CK library template.
Args:
detail_str: The template detail string
Returns:
True if this is a CK library type, False otherwise
Example:
>>> is_ck_template('ck::tensor_operation::device::DeviceConv2d<...>')
True
>>> is_ck_template('std::basic_string<char>')
False
"""
if not detail_str:
return False
# Remove quotes
detail_str = detail_str.strip('"')
# Check if it starts with ck:: or contains ::ck::
return detail_str.startswith("ck::") or "::ck::" in detail_str
def is_nested_template(detail_str: str) -> bool:
"""
Check if this template contains nested template instantiations.
Args:
detail_str: The template detail string
Returns:
True if contains nested templates, False otherwise
Example:
>>> is_nested_template('std::vector<int>')
False
>>> is_nested_template('std::vector<std::string>')
True
"""
if not detail_str or "<" not in detail_str:
return False
# Remove quotes
detail_str = detail_str.strip('"')
# Find the template parameter section
start = detail_str.find("<")
if start == -1:
return False
# Look for nested < after the first one
depth = 0
for i in range(start, len(detail_str)):
char = detail_str[i]
if char == "<":
depth += 1
if depth > 1:
# Found a nested template
return True
elif char == ">":
depth -= 1
if depth == 0:
break
return False
def _empty_result() -> Dict[str, any]:
"""Return an empty result dictionary with default values."""
return {
"namespace": "",
"template_name": "",
"full_qualified_name": "",
"param_count": 0,
"is_ck_type": False,
"is_nested": False,
}