From 90d51fea0b15a34f4027545d55bedb1fb2932358 Mon Sep 17 00:00:00 2001 From: papertager <2567587994@qq.com> Date: Tue, 9 Jun 2026 22:10:19 +0800 Subject: [PATCH 1/2] Add mcoplib operator complexity report --- tools/op_complexity_report.py | 53 ++++++++++++++++++++++++++ unit_test/test_op_complexity_report.py | 23 +++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tools/op_complexity_report.py create mode 100644 unit_test/test_op_complexity_report.py diff --git a/tools/op_complexity_report.py b/tools/op_complexity_report.py new file mode 100644 index 0000000..4e3298e --- /dev/null +++ b/tools/op_complexity_report.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Summarize CUDA/MACA operator source complexity for validation planning.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +SOURCE_SUFFIXES = {".cu", ".cuh", ".cpp", ".h"} + + +def analyze_file(path: Path, root: Path) -> dict[str, object]: + text = path.read_text(encoding="utf-8", errors="replace") + return { + "path": path.relative_to(root).as_posix(), + "lines": len(text.splitlines()), + "kernel_launches": text.count("<<<"), + "templates": text.count("template"), + "torch_bindings": text.count("PYBIND11_MODULE"), + } + + +def build_report(root: Path) -> dict[str, object]: + files = [ + analyze_file(path, root) + for path in sorted((root / "op").rglob("*")) + if path.is_file() and path.suffix in SOURCE_SUFFIXES + ] + return { + "file_count": len(files), + "total_lines": sum(int(item["lines"]) for item in files), + "top_by_lines": sorted(files, key=lambda item: int(item["lines"]), reverse=True)[:20], + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, default=Path.cwd(), help="repository root") + parser.add_argument("--output", type=Path, help="write JSON report to this path") + args = parser.parse_args() + + text = json.dumps(build_report(args.root), indent=2, ensure_ascii=False) + if args.output: + args.output.write_text(text + "\n", encoding="utf-8") + else: + print(text) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/unit_test/test_op_complexity_report.py b/unit_test/test_op_complexity_report.py new file mode 100644 index 0000000..7f8b268 --- /dev/null +++ b/unit_test/test_op_complexity_report.py @@ -0,0 +1,23 @@ +import tempfile +import unittest +from pathlib import Path + +from tools.op_complexity_report import build_report + + +class OpComplexityReportTest(unittest.TestCase): + def test_counts_operator_sources(self): + with tempfile.TemporaryDirectory() as tmpdir: + root = Path(tmpdir) + op = root / "op" + op.mkdir() + (op / "kernel.cu").write_text("template \nvoid f(){ k<<<1,1>>>(); }\n", encoding="utf-8") + + report = build_report(root) + + self.assertEqual(report["file_count"], 1) + self.assertEqual(report["top_by_lines"][0]["kernel_launches"], 1) + + +if __name__ == "__main__": + unittest.main() From 20b9b75a11c9d4a179266073e65fc287cfed2469 Mon Sep 17 00:00:00 2001 From: papertager <2567587994@qq.com> Date: Thu, 11 Jun 2026 00:59:49 +0800 Subject: [PATCH 2/2] Handle missing op directory in complexity report --- tools/op_complexity_report.py | 10 +++++++--- unit_test/test_op_complexity_report.py | 6 ++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tools/op_complexity_report.py b/tools/op_complexity_report.py index 4e3298e..e3196c6 100644 --- a/tools/op_complexity_report.py +++ b/tools/op_complexity_report.py @@ -23,15 +23,19 @@ def analyze_file(path: Path, root: Path) -> dict[str, object]: def build_report(root: Path) -> dict[str, object]: + op_dir = root / "op" + if not op_dir.is_dir(): + return {"file_count": 0, "total_lines": 0, "top_by_lines": []} + files = [ analyze_file(path, root) - for path in sorted((root / "op").rglob("*")) + for path in sorted(op_dir.rglob("*")) if path.is_file() and path.suffix in SOURCE_SUFFIXES ] return { "file_count": len(files), - "total_lines": sum(int(item["lines"]) for item in files), - "top_by_lines": sorted(files, key=lambda item: int(item["lines"]), reverse=True)[:20], + "total_lines": sum(item["lines"] for item in files), + "top_by_lines": sorted(files, key=lambda item: item["lines"], reverse=True)[:20], } diff --git a/unit_test/test_op_complexity_report.py b/unit_test/test_op_complexity_report.py index 7f8b268..a66fffb 100644 --- a/unit_test/test_op_complexity_report.py +++ b/unit_test/test_op_complexity_report.py @@ -18,6 +18,12 @@ def test_counts_operator_sources(self): self.assertEqual(report["file_count"], 1) self.assertEqual(report["top_by_lines"][0]["kernel_launches"], 1) + def test_returns_empty_report_when_op_directory_is_missing(self): + with tempfile.TemporaryDirectory() as tmpdir: + report = build_report(Path(tmpdir)) + + self.assertEqual(report, {"file_count": 0, "total_lines": 0, "top_by_lines": []}) + if __name__ == "__main__": unittest.main()