Files
project_6/cccl_upstream/ci/compile_time/test_summarize_events.py
muh-bot 2a7ca101d7 feat(cccl): integrate missing CCCL directories — python/, ci/, .agent/, docs/, test/
Sparse-checkout from NVIDIA/cccl main branch to complete cccl_upstream:

Added:
- python/cuda_cccl/ (226 files) — Python bindings for device-level algorithms
  Critical for muh toolchain: cuda.compute.reduce_into, scan, radix_sort, etc.
  Includes 204 .py files with full test coverage for all 27 algorithms
- ci/ (163 files) — Build/test infrastructure
  build_cub.sh, test_cub.sh, build_and_test_targets.sh, matrix.yaml
  Directly maps to our [INFRA-CI] and [INFRA-BUILD] items
- .agent/skills/ (7 files) — NVIDIA's own agent skills for CCCL
  cccl-style/SKILL.md, cccl-test/SKILL.md, sass-diff/SKILL.md
- docs/ (491 files) — Official CCCL documentation
  CI references, CMake guides, Python compute docs, libcudacxx PTX docs
- test/ (12 files) — Top-level integration tests (cuda_smoke, stdpar)
- Root configs: .clang-format, .clang-tidy, CONTRIBUTING.md, pyproject.toml
- CLAUDE.md symlink → AGENTS.md (NVIDIA's standard)

cccl_upstream now mirrors full NVIDIA/cccl structure:
  Before: 42M (cub + thrust + libcudacxx + cudax + c + examples + benchmarks)
  After:  53M (+python +ci +docs +.agent +test +configs)

This completes the CCCL base needed for:
- [muh-bench] items: ci/util/build_and_test_targets.sh for targeted builds
- [CCCL-verify] items: python/cuda_cccl/tests/ as reference implementations
- [CCCL-test] items: ci/test_cub.sh, ci/test_thrust.sh
- Agent workflow: .agent/skills/ for consistent style and test patterns
2026-08-07 02:34:33 +00:00

1732 lines
55 KiB
Python

#!/usr/bin/env python3
import csv
import json
import os
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from ci.compile_time import summarize_tus
REPO_ROOT = Path(__file__).resolve().parents[2]
SUMMARY_SCRIPT = REPO_ROOT / "ci" / "compile_time" / "summarize_events.py"
PREPARE_SCRIPT = REPO_ROOT / "ci" / "compile_time" / "prepare_traces.py"
PARSE_MATRIX_SCRIPT = REPO_ROOT / "ci" / "compile_time" / "parse_matrix.py"
RENDER_COMMENT_SCRIPT = REPO_ROOT / "ci" / "compile_time" / "render_pr_comment.py"
WRAPPER_SCRIPT = REPO_ROOT / "ci" / "build_compile_time_bench.sh"
PULL_REQUEST_WORKFLOW = (
REPO_ROOT / ".github" / "workflows" / "ci-workflow-pull-request.yml"
)
def csv_rows(path: Path) -> list[dict[str, str]]:
with path.open(newline="", encoding="utf-8") as f:
return list(csv.DictReader(f))
class TraceBuilder:
def __init__(self, root: Path):
self.root = root
def project_detail(self, rel: str) -> str:
return (self.root / rel).as_posix()
def event(
self,
name: str,
detail: str,
ts: int,
dur: int,
*,
pid: int = 1,
tid: int = 1,
) -> dict:
return {
"ph": "X",
"pid": pid,
"tid": tid,
"name": name,
"ts": ts,
"dur": dur,
"args": {"detail": detail},
}
def write_trace(self, path: Path, events: list[dict], input_name: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
json.dumps(
{
"traceEvents": events,
"otherData": {
"inputFiles": [f"/generated/headers/target/{input_name}.cu"]
},
}
),
encoding="utf-8",
)
class SummarizeEventsBaselineCompareTest(unittest.TestCase):
def setUp(self) -> None:
self.tempdir = tempfile.TemporaryDirectory()
self.work = Path(self.tempdir.name)
self.traces = TraceBuilder(REPO_ROOT)
self.wrapper_infix = f"compile-time-test-{self.work.name}"
self.wrapper_preset = "all-dev"
self.wrapper_build_dir = (
REPO_ROOT / "build" / self.wrapper_infix / self.wrapper_preset
)
def tearDown(self) -> None:
shutil.rmtree(REPO_ROOT / "build" / self.wrapper_infix, ignore_errors=True)
self.tempdir.cleanup()
def comparison_csv(self, output_dir: Path, name: str) -> Path:
return output_dir / "comparison" / name
def wrapper_trace_dir(self) -> Path:
return self.wrapper_build_dir / "compile_time" / "raw_traces"
def wrapper_output_dir(self) -> Path:
return self.wrapper_build_dir / "compile_time" / "event_reports"
def assert_empty_csv(self, path: Path) -> None:
self.assertTrue(path.exists())
self.assertEqual(csv_rows(path), [])
def run_summary(
self,
current: Path,
baseline: Path,
output_dir: Path,
*args: str,
baseline_repo_root: Path | None = None,
) -> subprocess.CompletedProcess[str]:
command = [
sys.executable,
SUMMARY_SCRIPT.as_posix(),
current.as_posix(),
"--baseline-dir",
baseline.as_posix(),
"-o",
output_dir.as_posix(),
]
if baseline_repo_root is not None:
command.extend(["--baseline-repo-root", baseline_repo_root.as_posix()])
command.extend(args)
return subprocess.run(
command,
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
def run_wrapper(
self,
*args: str,
no_tu_csv: bool = True,
common_args: tuple[str, ...] = (),
) -> subprocess.CompletedProcess[str]:
command = [
"bash",
WRAPPER_SCRIPT.as_posix(),
"-skip-configure",
"-skip-build",
"-no-prepare-perfetto",
*common_args,
]
if no_tu_csv:
command.append("-no-tu-csv")
command.extend(["--", *args])
env = os.environ.copy()
env["CCCL_BUILD_INFIX"] = self.wrapper_infix
return subprocess.run(
command,
cwd=REPO_ROOT,
env=env,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
def test_comparison_outputs_and_unmatched_children(self) -> None:
baseline = self.work / "baseline"
current = self.work / "current"
output = self.work / "reports"
parent = self.traces.project_detail("libcudacxx/include/cuda/std/parent.h")
matched_child = self.traces.project_detail(
"libcudacxx/include/cuda/std/matched_child.h"
)
better = self.traces.project_detail("libcudacxx/include/cuda/std/better.h")
baseline_only_child = self.traces.project_detail(
"libcudacxx/include/cuda/std/baseline_only_child.h"
)
baseline_only = self.traces.project_detail(
"libcudacxx/include/cuda/std/only_baseline.h"
)
current_only = self.traces.project_detail(
"libcudacxx/include/cuda/std/only_current.h"
)
self.traces.write_trace(
baseline / "target" / "match.json",
[
self.traces.event("Parent", parent, 0, 100),
self.traces.event("Child", matched_child, 10, 30),
self.traces.event("Child", baseline_only_child, 50, 20),
self.traces.event("Better", better, 200, 70),
self.traces.event("OnlyBaseline", baseline_only, 300, 20),
],
"match",
)
self.traces.write_trace(
current / "target" / "match.json",
[
self.traces.event("Parent", parent, 0, 150),
self.traces.event("Child", matched_child, 10, 40),
self.traces.event("Better", better, 200, 40),
self.traces.event("OnlyCurrent", current_only, 300, 90),
],
"match",
)
self.traces.write_trace(
baseline / "target" / "baseline_only_trace.json",
[self.traces.event("Parent", parent, 0, 999)],
"baseline_only_trace",
)
self.traces.write_trace(
current / "target" / "current_only_trace.json",
[self.traces.event("Parent", parent, 0, 999)],
"current_only_trace",
)
self.run_summary(
current,
baseline,
output,
"-f",
"all",
"-e",
"--sort",
"total",
"-n",
"10",
"--tag",
"synthetic",
)
worse = csv_rows(
self.comparison_csv(
output, "top-10-all-exclusive-all-by-total-worse-synthetic.csv"
)
)
better_rows = csv_rows(
self.comparison_csv(
output, "top-10-all-exclusive-all-by-total-better-synthetic.csv"
)
)
parent_row = next(row for row in worse if row["event_name"] == "Parent")
self.assertEqual(parent_row["baseline_selected_s"], "0.000070")
self.assertEqual(parent_row["current_selected_s"], "0.000110")
self.assertEqual(parent_row["impact_magnitude_s"], "0.000040")
better_row = next(row for row in better_rows if row["event_name"] == "Better")
self.assertEqual(better_row["baseline_selected_s"], "0.000070")
self.assertEqual(better_row["current_selected_s"], "0.000040")
self.assertEqual(better_row["impact_magnitude_s"], "0.000030")
all_comparison_keys = {row["event_key"] for row in worse + better_rows}
self.assertFalse(any("only_baseline" in key for key in all_comparison_keys))
self.assertFalse(any("only_current" in key for key in all_comparison_keys))
def test_comparison_threshold_filters_small_changes(self) -> None:
baseline = self.work / "baseline"
current = self.work / "current"
output = self.work / "reports"
small = self.traces.project_detail("libcudacxx/include/cuda/std/small.h")
large = self.traces.project_detail("libcudacxx/include/cuda/std/large.h")
self.traces.write_trace(
baseline / "target" / "match.json",
[
self.traces.event("Same", small, 0, 10),
self.traces.event("Same", large, 100, 10),
],
"match",
)
self.traces.write_trace(
current / "target" / "match.json",
[
self.traces.event("Same", small, 0, 12),
self.traces.event("Same", large, 100, 20),
],
"match",
)
self.run_summary(
current,
baseline,
output,
"-f",
"all",
"-i",
"--sort",
"total",
"-n",
"10",
"--threshold",
"0.000005",
"--tag",
"threshold",
)
worse = csv_rows(
self.comparison_csv(
output, "top-10-all-inclusive-by-total-worse-threshold.csv"
)
)
self.assertEqual(len(worse), 1)
self.assertTrue(worse[0]["event_key"].endswith("large.h"))
self.assertEqual(worse[0]["impact_magnitude_s"], "0.000010")
def test_comparison_ranks_by_total_impact_not_selected_metric(self) -> None:
baseline = self.work / "baseline"
current = self.work / "current"
output = self.work / "reports"
repeated = self.traces.project_detail("libcudacxx/include/cuda/std/repeated.h")
one_trace = self.traces.project_detail(
"libcudacxx/include/cuda/std/one_trace.h"
)
self.traces.write_trace(
baseline / "target" / "first.json",
[
self.traces.event("Same", repeated, 0, 10),
self.traces.event("Same", one_trace, 100, 10),
],
"first",
)
self.traces.write_trace(
current / "target" / "first.json",
[
self.traces.event("Same", repeated, 0, 16),
self.traces.event("Same", one_trace, 100, 20),
],
"first",
)
self.traces.write_trace(
baseline / "target" / "second.json",
[self.traces.event("Same", repeated, 0, 10)],
"second",
)
self.traces.write_trace(
current / "target" / "second.json",
[self.traces.event("Same", repeated, 0, 16)],
"second",
)
self.run_summary(
current,
baseline,
output,
"-f",
"all",
"-i",
"--sort",
"max",
"-n",
"10",
"--tag",
"impact",
)
worse = csv_rows(
self.comparison_csv(output, "top-10-all-inclusive-by-max-worse-impact.csv")
)
self.assertTrue(worse[0]["event_key"].endswith("repeated.h"))
self.assertEqual(worse[0]["impact_magnitude_s"], "0.000012")
self.assertEqual(worse[0]["selected_magnitude_s"], "0.000006")
def test_threshold_requires_comparison_mode(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
same = self.traces.project_detail("libcudacxx/include/cuda/std/same.h")
self.traces.write_trace(
traces / "target" / "same.json",
[self.traces.event("Same", same, 0, 10)],
"same",
)
completed = subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"--threshold",
"0.000001",
],
cwd=REPO_ROOT,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
self.assertNotEqual(completed.returncode, 0)
self.assertIn(
"--threshold can only be used together with --baseline-dir",
completed.stderr,
)
def test_file_processing_same_filter_keeps_unmatched_child_in_parent_cost(
self,
) -> None:
baseline = self.work / "baseline"
current = self.work / "current"
output = self.work / "reports"
parent = self.traces.project_detail("libcudacxx/include/cuda/std/parent.h")
matched_child = self.traces.project_detail(
"libcudacxx/include/cuda/std/matched_child.h"
)
unmatched_child = self.traces.project_detail(
"libcudacxx/include/cuda/std/baseline_only_child.h"
)
self.traces.write_trace(
baseline / "target" / "same.json",
[
self.traces.event("Processing Header File", parent, 0, 100),
self.traces.event("Processing Header File", matched_child, 10, 30),
self.traces.event("Processing Header File", unmatched_child, 50, 20),
],
"same",
)
self.traces.write_trace(
current / "target" / "same.json",
[
self.traces.event("Processing Header File", parent, 0, 130),
self.traces.event("Processing Header File", matched_child, 10, 40),
],
"same",
)
self.run_summary(
current,
baseline,
output,
"-f",
"file-processing",
"-e",
"--sort",
"total",
"-n",
"5",
"--tag",
"file-processing",
)
worse = csv_rows(
self.comparison_csv(
output,
"top-5-file-processing-exclusive-same-filter-by-total-worse-file-processing.csv",
)
)
parent_row = next(row for row in worse if row["event_key"].endswith("parent.h"))
self.assertEqual(parent_row["baseline_selected_s"], "0.000070")
self.assertEqual(parent_row["current_selected_s"], "0.000090")
def test_same_filter_exclusive_uses_direct_children_only(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
parent = self.traces.project_detail("libcudacxx/include/cuda/std/parent.h")
nested = self.traces.project_detail("libcudacxx/include/cuda/std/nested.h")
self.traces.write_trace(
traces / "target" / "direct.json",
[
self.traces.event("Processing Header File", parent, 0, 100),
self.traces.event("Scanning Function Body", "not-a-header", 10, 80),
self.traces.event("Processing Header File", nested, 20, 10),
],
"direct",
)
subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"-f",
"file-processing",
"-e",
"--sort",
"total",
"-n",
"5",
"--tag",
"direct",
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
rows = csv_rows(
output / "top-5-file-processing-exclusive-same-filter-by-total-direct.csv"
)
parent_row = next(row for row in rows if row["event_key"].endswith("parent.h"))
self.assertEqual(parent_row["selected_total_s"], "0.000100")
def test_empty_comparisons_still_write_csvs(self) -> None:
baseline = self.work / "baseline"
current = self.work / "current"
output = self.work / "reports"
foo = self.traces.project_detail("libcudacxx/include/cuda/std/foo.h")
bar = self.traces.project_detail("libcudacxx/include/cuda/std/bar.h")
self.traces.write_trace(
baseline / "target" / "same.json",
[self.traces.event("Foo", foo, 0, 10)],
"same",
)
self.traces.write_trace(
current / "target" / "same.json",
[self.traces.event("Bar", bar, 0, 20)],
"same",
)
self.run_summary(
current,
baseline,
output,
"-f",
"all",
"-i",
"--sort",
"total",
"-n",
"5",
"--tag",
"empty",
)
self.assert_empty_csv(
self.comparison_csv(output, "top-5-all-inclusive-by-total-worse-empty.csv")
)
self.assert_empty_csv(
self.comparison_csv(output, "top-5-all-inclusive-by-total-better-empty.csv")
)
with (output / "summary.json").open(encoding="utf-8") as f:
manifest = json.load(f)
self.assertIn(
"no comparable event keys",
" ".join(manifest["slices"][0]["warnings"]),
)
def test_wrapper_forwarding_writes_current_report(self) -> None:
current = self.wrapper_trace_dir()
output = self.wrapper_output_dir()
same = self.traces.project_detail("libcudacxx/include/cuda/std/same.h")
self.traces.write_trace(
current / "target" / "current_name.json",
[self.traces.event("Same", same, 0, 100)],
"current_name",
)
self.run_wrapper(
"-f",
"all",
"-i",
"--sort",
"total",
"-n",
"5",
"--tag",
"wrapper",
common_args=("-arch", "70"),
)
self.assertTrue((output / "top-5-all-inclusive-by-total-wrapper.csv").exists())
def test_comparison_matches_paths_from_distinct_repo_roots(self) -> None:
baseline_repo = self.work / "baseline-src"
baseline = self.work / "baseline"
current = self.work / "current"
output = self.work / "reports"
baseline_traces = TraceBuilder(baseline_repo)
baseline_same = baseline_traces.project_detail(
"libcudacxx/include/cuda/std/same.h"
)
current_same = self.traces.project_detail("libcudacxx/include/cuda/std/same.h")
self.traces.write_trace(
current / "target" / "same.json",
[self.traces.event("Same", current_same, 0, 12)],
"same",
)
baseline_traces.write_trace(
baseline / "target" / "same.json",
[baseline_traces.event("Same", baseline_same, 0, 10)],
"same",
)
self.run_summary(
current,
baseline,
output,
"-f",
"all",
"-i",
"--sort",
"total",
"-n",
"5",
baseline_repo_root=baseline_repo,
)
worse = csv_rows(
self.comparison_csv(output, "top-5-all-inclusive-by-total-worse.csv")
)
self.assertEqual(worse[0]["event_key"], "libcudacxx/include/cuda/std/same.h")
self.assertEqual(worse[0]["impact_magnitude_s"], "0.000002")
def test_wrapper_uses_computed_event_output_dir(self) -> None:
current = self.wrapper_trace_dir()
wrapper_default = self.wrapper_output_dir()
explicit_output = self.work / "explicit-output"
same = self.traces.project_detail("libcudacxx/include/cuda/std/same.h")
self.traces.write_trace(
current / "target" / "same.json",
[self.traces.event("Same", same, 0, 12)],
"same",
)
self.run_wrapper(
f"--output-dir={explicit_output.as_posix()}",
"-f",
"all",
"-i",
"--sort",
"total",
"-n",
"5",
"--tag",
"equals-output",
)
self.assertTrue(
(
wrapper_default / "top-5-all-inclusive-by-total-equals-output.csv"
).exists()
)
self.assertFalse(
(
explicit_output / "top-5-all-inclusive-by-total-equals-output.csv"
).exists()
)
def test_sort_by_average_per_root_tu(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
one_tu = self.traces.project_detail("libcudacxx/include/cuda/std/one_tu.h")
two_tus = self.traces.project_detail("libcudacxx/include/cuda/std/two_tus.h")
self.traces.write_trace(
traces / "target" / "first.json",
[
self.traces.event("Same", one_tu, 0, 80),
self.traces.event("Same", two_tus, 100, 60),
],
"first",
)
self.traces.write_trace(
traces / "target" / "second.json",
[self.traces.event("Same", two_tus, 0, 40)],
"second",
)
subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"-f",
"all",
"-i",
"--sort",
"avg-root-tu",
"-n",
"2",
"--tag",
"avg-root-tu",
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
rows = csv_rows(output / "top-2-all-inclusive-by-avg-root-tu-avg-root-tu.csv")
self.assertTrue(rows[0]["event_key"].endswith("one_tu.h"))
self.assertEqual(rows[0]["selected_avg_per_root_tu_s"], "0.000080")
self.assertTrue(rows[1]["event_key"].endswith("two_tus.h"))
self.assertEqual(rows[1]["selected_avg_per_root_tu_s"], "0.000050")
def test_host_compiler_filter_matches_host_phase_events(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
self.traces.write_trace(
traces / "target" / "host.json",
[
self.traces.event(
"g++ (preprocessing 1)", "g++ (preprocessing 1)", 0, 10
),
self.traces.event("gcc (compiling)", "gcc (compiling)", 20, 30),
self.traces.event("CUDA C++ Front-End", "frontend", 60, 40),
],
"host",
)
subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"-f",
"host-compiler",
"-i",
"--sort",
"total",
"-n",
"5",
"--tag",
"host",
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
rows = csv_rows(output / "top-5-host-compiler-inclusive-by-total-host.csv")
self.assertEqual(
{row["event_name"] for row in rows},
{"g++ (preprocessing 1)", "gcc (compiling)"},
)
def test_scope_filter_defaults_to_cccl_demangled_symbols(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
self.traces.write_trace(
traces / "target" / "symbols.json",
[
self.traces.event(
"Scanning Function Body",
"std::basic_string_view::size() const noexcept",
0,
100,
),
self.traces.event(
"Scanning Function Body",
"cuda::std::__4::basic_string_view::size() const noexcept",
200,
80,
),
self.traces.event(
"Scanning Function Body",
'cuda::std::literals::operator ""sv(const char *, unsigned long)',
400,
60,
),
],
"symbols",
)
subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"-f",
"scanning-function-body",
"-i",
"--sort",
"total",
"-n",
"5",
"--tag",
"scope-default",
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
rows = csv_rows(
output / "top-5-scanning-function-body-inclusive-by-total-scope-default.csv"
)
self.assertEqual(len(rows), 2)
self.assertIn("cuda::std", rows[0]["event_key"])
self.assertIn("operator", rows[1]["event_key"])
def test_empty_scope_filter_disables_symbol_scope_filtering(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
self.traces.write_trace(
traces / "target" / "symbols.json",
[
self.traces.event(
"Scanning Function Body",
"std::basic_string_view::size() const noexcept",
0,
100,
),
self.traces.event(
"Scanning Function Body",
"cuda::std::__4::basic_string_view::size() const noexcept",
200,
80,
),
],
"symbols",
)
subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"-f",
"scanning-function-body",
"-i",
"--sort",
"total",
"-n",
"5",
"--scope-filter",
"",
"--tag",
"scope-disabled",
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
rows = csv_rows(
output
/ "top-5-scanning-function-body-inclusive-by-total-scope-disabled.csv"
)
self.assertEqual(len(rows), 2)
self.assertIn("std::basic_string_view", rows[0]["event_key"])
self.assertIn("cuda::std", rows[1]["event_key"])
def test_scope_filter_uses_reported_template_symbol_scope(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
self.traces.write_trace(
traces / "target" / "templates.json",
[
self.traces.event(
"Instantiating Template Function",
(
"nvtx3::v1::domain::get "
"[nvtx3::v1::domain::get<cuda::__4::__nvtx_cccl_domain>()]"
),
0,
100,
),
self.traces.event(
"Instantiating Template Function",
"cub::detail::load [cub::detail::load<int>()]",
200,
80,
),
],
"templates",
)
subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"-f",
"template-instantiation",
"-i",
"--sort",
"total",
"-n",
"5",
"--tag",
"template-scope",
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
rows = csv_rows(
output
/ "top-5-template-instantiation-inclusive-by-total-template-scope.csv"
)
self.assertEqual(len(rows), 1)
self.assertIn("cub::detail::load", rows[0]["event_key"])
def test_scope_filter_matches_mangled_cccl_namespaces(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
self.traces.write_trace(
traces / "target" / "mangled.json",
[
self.traces.event(
"Generating Function IR",
"_ZN4cuda3std3__43fooEv",
0,
100,
),
self.traces.event(
"Generating Function IR",
"_ZN6thrust6detail3barEv",
200,
80,
),
self.traces.event(
"Generating Function IR",
"_ZNSt6vectorIiE4sizeEv",
400,
200,
),
],
"mangled",
)
subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"-f",
"code-generation",
"-i",
"--sort",
"total",
"-n",
"5",
"--tag",
"mangled-scope",
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
rows = csv_rows(
output / "top-5-code-generation-inclusive-by-total-mangled-scope.csv"
)
self.assertEqual(
{row["event_key"] for row in rows},
{
"_ZN4cuda3std3__43fooEv",
"_ZN6thrust6detail3barEv",
},
)
def test_total_compilation_filter_uses_trace_wall_span(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
self.traces.write_trace(
traces / "target" / "total.json",
[
self.traces.event(
"g++ (preprocessing 1)", "g++ (preprocessing 1)", 10, 20
),
self.traces.event("CUDA C++ Front-End", "frontend", 50, 40),
],
"total",
)
subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"-f",
"total-compilation",
"-i",
"--sort",
"total",
"-n",
"5",
"--tag",
"total",
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
total_rows = csv_rows(
output / "top-5-total-compilation-inclusive-by-total-total.csv"
)
self.assertEqual(len(total_rows), 1)
self.assertEqual(total_rows[0]["event_name"], "Total Compilation Time")
self.assertEqual(total_rows[0]["selected_total_s"], "0.000080")
subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"-f",
"all",
"-i",
"--sort",
"total",
"-n",
"5",
"--tag",
"all-no-total",
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
all_rows = csv_rows(output / "top-5-all-inclusive-by-total-all-no-total.csv")
self.assertFalse(
any(row["event_name"] == "Total Compilation Time" for row in all_rows)
)
def test_same_dir_comparison_is_empty(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
same = self.traces.project_detail("libcudacxx/include/cuda/std/same.h")
self.traces.write_trace(
traces / "target" / "same.json",
[self.traces.event("Same", same, 0, 10)],
"same",
)
self.run_summary(
traces,
traces,
output,
"-f",
"all",
"-i",
"--sort",
"max",
"-n",
"5",
"--tag",
"same",
)
self.assert_empty_csv(
self.comparison_csv(output, "top-5-all-inclusive-by-max-worse-same.csv")
)
self.assert_empty_csv(
self.comparison_csv(output, "top-5-all-inclusive-by-max-better-same.csv")
)
def test_multi_slice_writes_manifest_and_allows_empty_slice(self) -> None:
traces = self.work / "traces"
output = self.work / "reports"
slices = self.work / "slices.json"
same = self.traces.project_detail("libcudacxx/include/cuda/std/same.h")
self.traces.write_trace(
traces / "target" / "same.json",
[self.traces.event("Same", same, 0, 10)],
"same",
)
slices.write_text(
json.dumps(
{
"slices": [
{
"id": "all-events",
"title": "All events",
"filter": "all",
"timing": "inclusive",
"sort": "total",
"top": 5,
"threshold": 0,
},
{
"id": "empty-events",
"title": "Empty events",
"filter": "does-not-match",
"timing": "inclusive",
"sort": "total",
"top": 5,
"threshold": 0,
},
]
}
),
encoding="utf-8",
)
subprocess.run(
[
sys.executable,
SUMMARY_SCRIPT.as_posix(),
traces.as_posix(),
"-o",
output.as_posix(),
"--slices",
slices.as_posix(),
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
with (output / "summary.json").open(encoding="utf-8") as f:
manifest = json.load(f)
self.assertEqual(
[item["id"] for item in manifest["slices"]], ["all-events", "empty-events"]
)
self.assertEqual(manifest["slices"][0]["reports"]["current"]["row_count"], 1)
self.assertEqual(manifest["slices"][1]["reports"]["current"]["row_count"], 0)
self.assertTrue(
(
output
/ "empty-events"
/ "top-5-regex-does-not-match-inclusive-by-total.csv"
).exists()
)
class CompileTimeMatrixAndCommentTest(unittest.TestCase):
def setUp(self) -> None:
self.tempdir = tempfile.TemporaryDirectory()
self.work = Path(self.tempdir.name)
def tearDown(self) -> None:
self.tempdir.cleanup()
def test_parse_matrix_disabled_when_section_missing(self) -> None:
matrix = self.work / "matrix.yaml"
matrix.write_text("workflows: {}\n", encoding="utf-8")
completed = subprocess.run(
[
sys.executable,
PARSE_MATRIX_SCRIPT.as_posix(),
matrix.as_posix(),
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
self.assertEqual(json.loads(completed.stdout), {"include": []})
def test_parse_matrix_valid_config(self) -> None:
matrix = self.work / "matrix.yaml"
matrix.write_text(
"""
compile_time:
pull_request:
- id: public-headers
name: Public headers
gpu: rtx2080
launch_args: "--cuda 13.3 --host gcc13"
baseline_ref: origin/main
preset: all-dev
targets: [cub.headers.base]
args: "-arch native"
slices:
- id: total-compilation
title: TU total compilation
filter: total-compilation
timing: inclusive
sort: total
top: 15
threshold: 0.001
""",
encoding="utf-8",
)
completed = subprocess.run(
[
sys.executable,
PARSE_MATRIX_SCRIPT.as_posix(),
matrix.as_posix(),
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
include = json.loads(completed.stdout)["include"]
self.assertEqual(len(include), 1)
self.assertEqual(
include[0]["comment_header"], "compile-time-bench-public-headers"
)
self.assertEqual(json.loads(include[0]["targets_json"]), ["cub.headers.base"])
self.assertEqual(
json.loads(include[0]["slices_json"])["slices"][0]["id"],
"total-compilation",
)
def test_parse_matrix_rejects_duplicate_slice_ids(self) -> None:
matrix = self.work / "matrix.yaml"
matrix.write_text(
"""
compile_time:
pull_request:
- id: public-headers
name: Public headers
gpu: rtx2080
launch_args: "--cuda 13.3 --host gcc13"
baseline_ref: origin/main
preset: all-dev
targets: [cub.headers.base]
slices:
- id: repeated
title: First
filter: all
timing: inclusive
sort: total
top: 15
threshold: 0
- id: repeated
title: Second
filter: all
timing: inclusive
sort: total
top: 15
threshold: 0
""",
encoding="utf-8",
)
completed = subprocess.run(
[
sys.executable,
PARSE_MATRIX_SCRIPT.as_posix(),
matrix.as_posix(),
],
cwd=REPO_ROOT,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
self.assertNotEqual(completed.returncode, 0)
self.assertIn("duplicate slice id", completed.stderr)
def test_parse_matrix_rejects_bool_numeric_fields(self) -> None:
matrix = self.work / "matrix.yaml"
matrix.write_text(
"""
compile_time:
pull_request:
- id: public-headers
name: Public headers
gpu: rtx2080
launch_args: "--cuda 13.3 --host gcc13"
baseline_ref: origin/main
preset: all-dev
targets: [cub.headers.base]
artifact_retention_days: true
slices:
- id: total-compilation
title: TU total compilation
filter: total-compilation
timing: inclusive
sort: total
top: true
threshold: false
""",
encoding="utf-8",
)
completed = subprocess.run(
[
sys.executable,
PARSE_MATRIX_SCRIPT.as_posix(),
matrix.as_posix(),
],
cwd=REPO_ROOT,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
self.assertNotEqual(completed.returncode, 0)
self.assertIn("positive integer", completed.stderr)
def test_pull_request_workflow_supports_compile_time_bench_skip_tag(self) -> None:
workflow = PULL_REQUEST_WORKFLOW.read_text(encoding="utf-8")
self.assertIn("[skip-compile-time-bench]", workflow)
self.assertIn("compile_time_enabled=false", workflow)
self.assertIn('compile_time_matrix={"include":[]}', workflow)
def test_render_comment_omits_empty_sections_and_splits_directions(self) -> None:
summary = self.work / "summary.json"
config = self.work / "config.json"
output = self.work / "comment.md"
summary.write_text(
json.dumps(
{
"slices": [
{
"id": "nonempty",
"title": "Nonempty",
"filter": "all",
"timing": "inclusive",
"sort": "total",
"comparison": {
"worse": {
"rows": [
{
"rank": 1,
"event_name": "Scanning Function Body",
"event_key": "cuda::std::__4::same<char>(int)",
"baseline_selected_s": "0.000001",
"current_selected_s": "0.000003",
"impact_magnitude_s": "0.000010",
"selected_delta_s": "0.000002",
"matched_trace_count": 1,
}
]
},
"better": {"rows": []},
},
"children": [
{
"id": "empty-child",
"title": "Empty child",
"filter": "all",
"timing": "inclusive",
"sort": "total",
"comparison": {
"worse": {"rows": []},
"better": {"rows": []},
},
"children": [],
}
],
}
]
}
),
encoding="utf-8",
)
config.write_text(
json.dumps(
{
"id": "public-headers",
"name": "Public headers",
"baseline_ref": "origin/main",
"preset": "all-dev",
"targets": ["cub.headers.base"],
"gpu": "rtx2080",
"launch_args": "--cuda 13.3 --host gcc13",
}
),
encoding="utf-8",
)
subprocess.run(
[
sys.executable,
RENDER_COMMENT_SCRIPT.as_posix(),
"--summary",
summary.as_posix(),
"--config",
config.as_posix(),
"--artifacts-url",
"https://example.test/artifacts",
"-o",
output.as_posix(),
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
rendered = output.read_text(encoding="utf-8")
self.assertIn("<!-- cccl-compile-time-bench: public-headers -->", rendered)
self.assertIn("Regressions", rendered)
self.assertIn("Regression impact", rendered)
self.assertIn(
"Scanning Function Body: `cuda::std::__4::same<char>(int)`",
rendered,
)
self.assertIn("0.000010", rendered)
self.assertNotIn("Improvements</strong>", rendered)
self.assertNotIn("Empty child", rendered)
self.assertIn("https://example.test/artifacts", rendered)
def test_render_comment_reports_empty_slice_warnings(self) -> None:
summary = self.work / "summary.json"
config = self.work / "config.json"
output = self.work / "comment.md"
summary.write_text(
json.dumps(
{
"slices": [
{
"id": "empty",
"title": "Empty slice",
"filter": "typo-filter",
"timing": "inclusive",
"sort": "total",
"warnings": [
"baseline report matched no events for this slice"
],
"comparison": {
"worse": {"rows": []},
"better": {"rows": []},
},
"children": [],
}
]
}
),
encoding="utf-8",
)
config.write_text(
json.dumps(
{
"id": "public-headers",
"name": "Public headers",
"baseline_ref": "origin/main",
"preset": "all-dev",
"targets": ["cub.headers.base"],
"gpu": "rtx2080",
"launch_args": "--cuda 13.3 --host gcc13",
}
),
encoding="utf-8",
)
subprocess.run(
[
sys.executable,
RENDER_COMMENT_SCRIPT.as_posix(),
"--summary",
summary.as_posix(),
"--config",
config.as_posix(),
"--artifacts-url",
"https://example.test/artifacts",
"-o",
output.as_posix(),
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
rendered = output.read_text(encoding="utf-8")
self.assertIn("1 warning(s)", rendered)
self.assertIn("Empty slice — Warnings", rendered)
self.assertIn("baseline report matched no events", rendered)
self.assertNotIn(
"No compile-time benchmark changes exceeded the configured thresholds.",
rendered,
)
def test_render_comment_separates_top_level_slice_sections(self) -> None:
summary = self.work / "summary.json"
config = self.work / "config.json"
output = self.work / "comment.md"
row = {
"rank": 1,
"event_name": "Same",
"event_key": "cuda/std/same",
"baseline_selected_s": "0.000001",
"current_selected_s": "0.000003",
"impact_magnitude_s": "0.000010",
"selected_delta_s": "0.000002",
"matched_trace_count": 1,
}
summary.write_text(
json.dumps(
{
"slices": [
{
"id": "first",
"title": "First",
"filter": "all",
"timing": "inclusive",
"sort": "total",
"comparison": {
"worse": {"rows": [row]},
"better": {"rows": []},
},
"children": [],
},
{
"id": "second",
"title": "Second",
"filter": "all",
"timing": "inclusive",
"sort": "total",
"comparison": {
"worse": {"rows": [row]},
"better": {"rows": []},
},
"children": [],
},
]
}
),
encoding="utf-8",
)
config.write_text(
json.dumps(
{
"id": "public-headers",
"name": "Public headers",
"baseline_ref": "origin/main",
"preset": "all-dev",
"targets": ["cub.headers.base"],
"gpu": "rtx2080",
"launch_args": "--cuda 13.3 --host gcc13",
}
),
encoding="utf-8",
)
subprocess.run(
[
sys.executable,
RENDER_COMMENT_SCRIPT.as_posix(),
"--summary",
summary.as_posix(),
"--config",
config.as_posix(),
"--artifacts-url",
"https://example.test/artifacts",
"-o",
output.as_posix(),
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
rendered = output.read_text(encoding="utf-8")
self.assertIn("</details>\n\n### Second", rendered)
self.assertNotIn("</details>\n### Second", rendered)
class PrepareTracesTest(unittest.TestCase):
def setUp(self) -> None:
self.tempdir = tempfile.TemporaryDirectory()
self.work = Path(self.tempdir.name)
def tearDown(self) -> None:
self.tempdir.cleanup()
def test_promotes_project_details_into_perfetto_event_names(self) -> None:
input_dir = self.work / "raw"
output_dir = self.work / "perfetto"
trace_path = input_dir / "target" / "trace.json"
trace_path.parent.mkdir(parents=True, exist_ok=True)
trace_path.write_text(
json.dumps(
{
"traceEvents": [
{
"ph": "X",
"name": "Processing Header File",
"args": {
"detail": (
REPO_ROOT
/ "libcudacxx/include/cuda/std/string_view"
).as_posix()
},
},
{
"ph": "X",
"name": "Other Event",
"args": {"detail": "not promoted"},
},
]
}
),
encoding="utf-8",
)
subprocess.run(
[
sys.executable,
PREPARE_SCRIPT.as_posix(),
"--input",
input_dir.as_posix(),
"--output",
output_dir.as_posix(),
"--repo-root",
REPO_ROOT.as_posix(),
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output_trace = output_dir / "target" / "trace.perfetto.json"
with output_trace.open(encoding="utf-8") as f:
events = json.load(f)["traceEvents"]
self.assertEqual(
events[0]["name"], "Processing Header File: cuda/std/string_view"
)
self.assertEqual(events[0]["args"]["original_name"], "Processing Header File")
self.assertEqual(events[1]["name"], "Other Event")
def test_single_file_input_accepts_output_directory(self) -> None:
input_trace = self.work / "trace.json"
output_dir = self.work / "perfetto"
input_trace.write_text(
json.dumps(
{
"traceEvents": [
{
"ph": "X",
"name": "Processing Header File",
"args": {"detail": "cuda/std/string_view"},
}
]
}
),
encoding="utf-8",
)
subprocess.run(
[
sys.executable,
PREPARE_SCRIPT.as_posix(),
"--input",
input_trace.as_posix(),
"--output",
output_dir.as_posix(),
"--repo-root",
REPO_ROOT.as_posix(),
],
cwd=REPO_ROOT,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
self.assertTrue((output_dir / "trace.perfetto.json").exists())
class SummarizeTusTest(unittest.TestCase):
def test_generated_tu_input(self) -> None:
build_dir = Path("/tmp/build")
pp_path = (
build_dir
/ "libcudacxx/test"
/ "headers"
/ "libcudacxx.test.public_headers"
/ "cuda/std/string_view.cpp4.ii"
)
self.assertEqual(
summarize_tus.generated_tu_input(pp_path.with_name("string_view.cpp")),
"cuda/std/string_view",
)
def test_finds_nvcc_and_clang_preprocessed_tus(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
build_dir = Path(tmp)
header_dir = build_dir / "target" / "headers" / "generated"
header_dir.mkdir(parents=True)
nvcc_tu = header_dir / "cuda_std_span.cpp4.ii"
clang_tu = header_dir / "cuda_std_string_view.ii"
nvcc_tu.write_text("", encoding="utf-8")
clang_tu.write_text("", encoding="utf-8")
self.assertEqual(
summarize_tus.find_preprocessed_tus(build_dir),
[nvcc_tu, clang_tu],
)
self.assertEqual(
summarize_tus.tu_source_for_preprocessed_tu(nvcc_tu),
header_dir / "cuda_std_span",
)
self.assertEqual(
summarize_tus.tu_source_for_preprocessed_tu(clang_tu),
header_dir / "cuda_std_string_view",
)
if __name__ == "__main__":
unittest.main()