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
This commit is contained in:
167
cccl_upstream/ci/compile_time/prepare_traces.py
Executable file
167
cccl_upstream/ci/compile_time/prepare_traces.py
Executable file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
DETAIL_EVENT_NAMES = {
|
||||
"Code Generation Function",
|
||||
"CodeGen Function",
|
||||
"ExecuteCompiler",
|
||||
"Frontend",
|
||||
"Instantiating Template Class",
|
||||
"Instantiating Template Function",
|
||||
"InstantiateClass",
|
||||
"InstantiateFunction",
|
||||
"OptFunction",
|
||||
"ParseClass",
|
||||
"PerformPendingInstantiations",
|
||||
"Processing Header File",
|
||||
"RunPass",
|
||||
"Scanning Function Body",
|
||||
"Source",
|
||||
}
|
||||
|
||||
DETAIL_PREFIXES_TO_STRIP = (
|
||||
"libcudacxx/include/",
|
||||
"cudax/include/",
|
||||
"c/parallel/include/",
|
||||
)
|
||||
|
||||
DETAIL_PREFIXES_TO_COLLAPSE = (
|
||||
("cub/cub/", "cub/"),
|
||||
("thrust/thrust/", "thrust/"),
|
||||
)
|
||||
|
||||
|
||||
def normalize_detail(detail: str, repo_root: Path) -> str:
|
||||
detail_path = Path(detail)
|
||||
if detail_path.is_absolute():
|
||||
try:
|
||||
rel = detail_path.resolve(strict=False).relative_to(repo_root)
|
||||
detail = rel.as_posix()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
for prefix in DETAIL_PREFIXES_TO_STRIP:
|
||||
if detail.startswith(prefix):
|
||||
detail = detail[len(prefix) :]
|
||||
break
|
||||
|
||||
for prefix, replacement in DETAIL_PREFIXES_TO_COLLAPSE:
|
||||
if detail.startswith(prefix):
|
||||
detail = replacement + detail[len(prefix) :]
|
||||
break
|
||||
|
||||
return detail
|
||||
|
||||
|
||||
def display_detail(detail: str, repo_root: Path, max_detail_len: int | None) -> str:
|
||||
detail = normalize_detail(detail, repo_root)
|
||||
if (
|
||||
max_detail_len is not None
|
||||
and max_detail_len > 0
|
||||
and len(detail) > max_detail_len
|
||||
):
|
||||
return detail[: max_detail_len - 1] + "..."
|
||||
return detail
|
||||
|
||||
|
||||
def rewrite_event_name(
|
||||
event: dict, repo_root: Path, max_detail_len: int | None
|
||||
) -> bool:
|
||||
name = event.get("name")
|
||||
if name not in DETAIL_EVENT_NAMES:
|
||||
return False
|
||||
|
||||
args = event.get("args")
|
||||
if not isinstance(args, dict):
|
||||
return False
|
||||
|
||||
detail = args.get("detail")
|
||||
if not detail:
|
||||
return False
|
||||
|
||||
args.setdefault("original_name", name)
|
||||
event["name"] = f"{name}: {display_detail(str(detail), repo_root, max_detail_len)}"
|
||||
return True
|
||||
|
||||
|
||||
def prepare_trace(
|
||||
input_path: Path, output_path: Path, repo_root: Path, max_detail_len: int | None
|
||||
) -> int:
|
||||
with input_path.open(encoding="utf-8") as f:
|
||||
trace = json.load(f)
|
||||
|
||||
rewritten = 0
|
||||
for event in trace.get("traceEvents", []):
|
||||
if rewrite_event_name(event, repo_root, max_detail_len):
|
||||
rewritten += 1
|
||||
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with output_path.open("w", encoding="utf-8") as f:
|
||||
json.dump(trace, f, separators=(",", ":"))
|
||||
|
||||
return rewritten
|
||||
|
||||
|
||||
def iter_input_traces(input_path: Path) -> list[Path]:
|
||||
if input_path.is_file():
|
||||
return [input_path]
|
||||
return sorted(input_path.rglob("*.json"))
|
||||
|
||||
|
||||
def output_path_for(input_trace: Path, input_root: Path, output_path: Path) -> Path:
|
||||
if input_root.is_file():
|
||||
if output_path.is_dir() or not output_path.suffix:
|
||||
return output_path / f"{input_trace.stem}.perfetto.json"
|
||||
return output_path
|
||||
|
||||
rel = input_trace.relative_to(input_root)
|
||||
return output_path / rel.parent / f"{rel.stem}.perfetto.json"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Prepare NVCC device-time-trace JSON files for Perfetto by promoting args.detail into event names."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--input", required=True, type=Path, help="Input trace JSON file or directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output", required=True, type=Path, help="Output trace JSON file or directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--repo-root", default=Path(__file__).resolve().parents[2], type=Path
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-detail-len",
|
||||
default=0,
|
||||
type=int,
|
||||
help="Truncate promoted detail text to this many characters; 0 keeps full details",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
input_path = args.input.resolve(strict=False)
|
||||
output_path = args.output.resolve(strict=False)
|
||||
repo_root = args.repo_root.resolve(strict=False)
|
||||
max_detail_len = args.max_detail_len if args.max_detail_len > 0 else None
|
||||
|
||||
traces = iter_input_traces(input_path)
|
||||
if not traces:
|
||||
raise SystemExit(f"no JSON traces found under {args.input}")
|
||||
|
||||
total_rewritten = 0
|
||||
for trace_path in traces:
|
||||
total_rewritten += prepare_trace(
|
||||
trace_path,
|
||||
output_path_for(trace_path, input_path, output_path),
|
||||
repo_root,
|
||||
max_detail_len,
|
||||
)
|
||||
|
||||
print(f"prepared {len(traces)} trace(s); renamed {total_rewritten} event(s)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user